From e4a359e0b4ae4865cb7f1ad9bd5377b3d01c4234 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 15 Nov 2017 13:38:02 +0100 Subject: [PATCH 01/39] Add mutation to framework --- client/coral-framework/graphql/fragments.js | 1 + client/coral-framework/graphql/mutations.js | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/client/coral-framework/graphql/fragments.js b/client/coral-framework/graphql/fragments.js index c2a03131b..a99dc0ee2 100644 --- a/client/coral-framework/graphql/fragments.js +++ b/client/coral-framework/graphql/fragments.js @@ -17,6 +17,7 @@ export default { 'IgnoreUserResponse', 'StopIgnoringUserResponse', 'UpdateSettingsResponse', + 'UpdateAssetSettingsResponse', ) }; diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js index 447d297ab..895213054 100644 --- a/client/coral-framework/graphql/mutations.js +++ b/client/coral-framework/graphql/mutations.js @@ -88,7 +88,7 @@ export const withRemoveTag = withMutation( asset_id: assetId, item_type: itemType, }, - optimisticResponse: { + o3timisticResponse: { removeTag: { __typename: 'ModifyTagResponse', errors: null, @@ -358,3 +358,22 @@ export const withUpdateSettings = withMutation( }); }}), }); + +export const withUpdateAssetSettings = withMutation( + gql` + mutation UpdateAssetSettings(id: ID!, $input: AssetSettingsInput!) { + updateAssetSettings(id: $id, input: $input) { + ...UpdateAssetSettingsResponse + } + } + `, { + props: ({mutate}) => ({ + updateAssetSettings: (id, input) => { + return mutate({ + variables: { + id, + input, + }, + }); + }}), + }); From e02537df13f2dcd5a2f9ce3aa7d19b40631f1dcd Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 15 Nov 2017 17:53:24 +0100 Subject: [PATCH 02/39] Fix mutation syntax --- client/coral-framework/graphql/mutations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js index 895213054..86cd54b0b 100644 --- a/client/coral-framework/graphql/mutations.js +++ b/client/coral-framework/graphql/mutations.js @@ -361,7 +361,7 @@ export const withUpdateSettings = withMutation( export const withUpdateAssetSettings = withMutation( gql` - mutation UpdateAssetSettings(id: ID!, $input: AssetSettingsInput!) { + mutation UpdateAssetSettings($id: ID!, $input: AssetSettingsInput!) { updateAssetSettings(id: $id, input: $input) { ...UpdateAssetSettingsResponse } From ce4ad426a7cc880e62bcd50d98ccef6421ee9670 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 15 Nov 2017 19:38:25 +0100 Subject: [PATCH 03/39] Extendable embed stream tabs --- .../src/components/Embed.js | 78 +++++++++++-------- ...eamTabPanel.css => ExtendableTabPanel.css} | 0 ...treamTabPanel.js => ExtendableTabPanel.js} | 8 +- .../src/components/Stream.js | 25 +++--- .../src/containers/Embed.js | 3 + ...treamTabPanel.js => ExtendableTabPanel.js} | 57 +++++++++----- .../src/containers/Stream.js | 1 + .../client/index.js | 6 +- 8 files changed, 108 insertions(+), 70 deletions(-) rename client/coral-embed-stream/src/components/{StreamTabPanel.css => ExtendableTabPanel.css} (100%) rename client/coral-embed-stream/src/components/{StreamTabPanel.js => ExtendableTabPanel.js} (85%) rename client/coral-embed-stream/src/containers/{StreamTabPanel.js => ExtendableTabPanel.js} (66%) diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index 5ce7021fb..132b3be7f 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -5,7 +5,8 @@ import Slot from 'coral-framework/components/Slot'; import {can} from 'coral-framework/services/perms'; import t from 'coral-framework/services/i18n'; -import {TabBar, Tab, TabContent, TabPane} from 'coral-ui'; +import ExtendableTabPanel from '../containers/ExtendableTabPanel'; +import {Tab, TabPane} from 'coral-ui'; import ProfileContainer from 'coral-settings/containers/ProfileContainer'; import Popup from 'coral-framework/components/Popup'; import IfSlotIsNotEmpty from 'coral-framework/components/IfSlotIsNotEmpty'; @@ -28,9 +29,28 @@ export default class Embed extends React.Component { this.props.setActiveTab(tab); }; + getTabs() { + const {user} = this.props.auth; + const tabs = [ + + {t('embed_comments_tab')} + , + + {t('framework.my_profile')} + , + ]; + if (can(user, 'UPDATE_CONFIG')) { + tabs.push( + + {t('framework.configure_stream')} + + ); + } + return tabs; + } + render() { const {activeTab, commentId, root, data, auth: {showSignInDialog, signInDialogFocus}, blurSignInDialog, focusSignInDialog, hideSignInDialog, router: {location: {query: {parentUrl}}}} = this.props; - const {user} = this.props.auth; const hasHighlightedComment = !!commentId; return ( @@ -47,44 +67,36 @@ export default class Embed extends React.Component { onClose={hideSignInDialog} /> - - - {t('embed_comments_tab')} - - - {t('framework.my_profile')} - - {can(user, 'UPDATE_CONFIG') && - - {t('framework.configure_stream')} - - } - + - - - - - - - - - - - + setActiveTab={this.changeTab} + fallbackTab='stream' + tabSlot='embedStreamTabs' + tabSlotPrepend='embedStreamTabsPrepend' + tabPaneSlot='embedStreamTabPanes' + slotProps={{data}} + queryData={{root}} + tabs={this.getTabs()} + tabPanes={[ + + + , + + + , + + + , + ]} + /> ); } diff --git a/client/coral-embed-stream/src/components/StreamTabPanel.css b/client/coral-embed-stream/src/components/ExtendableTabPanel.css similarity index 100% rename from client/coral-embed-stream/src/components/StreamTabPanel.css rename to client/coral-embed-stream/src/components/ExtendableTabPanel.css diff --git a/client/coral-embed-stream/src/components/StreamTabPanel.js b/client/coral-embed-stream/src/components/ExtendableTabPanel.js similarity index 85% rename from client/coral-embed-stream/src/components/StreamTabPanel.js rename to client/coral-embed-stream/src/components/ExtendableTabPanel.js index 35c10d5d9..ec582eb84 100644 --- a/client/coral-embed-stream/src/components/StreamTabPanel.js +++ b/client/coral-embed-stream/src/components/ExtendableTabPanel.js @@ -1,9 +1,9 @@ import React from 'react'; import {Spinner, TabBar, TabContent} from 'coral-ui'; import PropTypes from 'prop-types'; -import styles from './StreamTabPanel.css'; +import styles from './ExtendableTabPanel.css'; -class StreamTabPanel extends React.Component { +class ExtendableTabPanel extends React.Component { render() { const {activeTab, setActiveTab, tabs, tabPanes, sub, loading, ...rest} = this.props; @@ -23,7 +23,7 @@ class StreamTabPanel extends React.Component { } } -StreamTabPanel.propTypes = { +ExtendableTabPanel.propTypes = { activeTab: PropTypes.string.isRequired, setActiveTab: PropTypes.func.isRequired, loading: PropTypes.bool, @@ -39,4 +39,4 @@ StreamTabPanel.propTypes = { sub: PropTypes.bool, }; -export default StreamTabPanel; +export default ExtendableTabPanel; diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js index 7ff48106b..084586eeb 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/components/Stream.js @@ -19,7 +19,7 @@ import cn from 'classnames'; import {getTopLevelParent, attachCommentToParent} from '../graphql/utils'; import AllCommentsPane from './AllCommentsPane'; import AutomaticAssetClosure from '../containers/AutomaticAssetClosure'; -import StreamTabPanel from '../containers/StreamTabPanel'; +import ExtendableTabPanel from '../containers/ExtendableTabPanel'; import styles from './Stream.css'; @@ -106,7 +106,7 @@ class Stream extends React.Component { ); } - renderTabPanel() { + renderExtendableTabPanel() { const { data, root, @@ -135,7 +135,7 @@ class Stream extends React.Component { const slotProps = {data}; const slotQueryData = {root, asset}; - // `key` of `StreamTabPanel` depends on sorting so that we always reset + // `key` of `ExtendableTabPanel` depends on sorting so that we always reset // the state when changing sorting. return (
@@ -148,23 +148,24 @@ class Stream extends React.Component { {...slotProps} />
- {t('stream.all_comments')} {totalCommentCount} } - appendTabPanes={ - + tabPanes={ + ); @@ -325,7 +326,7 @@ Stream.propTypes = { loading: PropTypes.bool, editName: PropTypes.func, appendItemArray: PropTypes.func, - updateItem: PropTypes.func, + updateItem: PropTypes.func, viewAllComments: PropTypes.func, notify: PropTypes.func.isRequired, postComment: PropTypes.func.isRequired, diff --git a/client/coral-embed-stream/src/containers/Embed.js b/client/coral-embed-stream/src/containers/Embed.js index 132e7f5e7..3fef8872d 100644 --- a/client/coral-embed-stream/src/containers/Embed.js +++ b/client/coral-embed-stream/src/containers/Embed.js @@ -151,6 +151,9 @@ const USERNAME_REJECTED_SUBSCRIPTION = gql` const slots = [ 'embed', + 'embedStreamTabs', + 'embedStreamTabsPrepend', + 'embedStreamTabPanes', ]; const EMBED_QUERY = gql` diff --git a/client/coral-embed-stream/src/containers/StreamTabPanel.js b/client/coral-embed-stream/src/containers/ExtendableTabPanel.js similarity index 66% rename from client/coral-embed-stream/src/containers/StreamTabPanel.js rename to client/coral-embed-stream/src/containers/ExtendableTabPanel.js index 9a154512d..b582f7b66 100644 --- a/client/coral-embed-stream/src/containers/StreamTabPanel.js +++ b/client/coral-embed-stream/src/containers/ExtendableTabPanel.js @@ -1,5 +1,5 @@ import React from 'react'; -import StreamTabPanel from '../components/StreamTabPanel'; +import ExtendableTabPanel from '../components/ExtendableTabPanel'; import {connect} from 'react-redux'; import omit from 'lodash/omit'; import {Tab, TabPane} from 'coral-ui'; @@ -7,17 +7,17 @@ import {getShallowChanges} from 'coral-framework/utils'; import isEqual from 'lodash/isEqual'; import PropTypes from 'prop-types'; -class StreamTabPanelContainer extends React.Component { +class ExtendableTabPanelContainer extends React.Component { static contextTypes = { plugins: PropTypes.object, }; componentDidMount() { - this.fallbackAllTab(); + this.handleFallback(); } componentWillReceiveProps(next) { - this.fallbackAllTab(next); + this.handleFallback(next); } shouldComponentUpdate(next) { @@ -35,22 +35,31 @@ class StreamTabPanelContainer extends React.Component { return changes.length !== 0; } - fallbackAllTab(props = this.props) { - if (props.activeTab !== props.fallbackTab) { - const slotPlugins = this.getSlotElements(props.tabSlot, props).map((el) => el.type.talkPluginName); - if (slotPlugins.indexOf(props.activeTab) === -1) { - props.setActiveTab(props.fallbackTab); - } + handleFallback(props = this.props) { + if (this.getTabNames(props).indexOf(props.activeTab) === -1) { + props.setActiveTab(props.fallbackTab); } } + getTabNames(props = this.props) { + return this.getTabElements(props).map((el) => el.props.tabId); + } + getSlotElements(slot, props = this.props) { const {plugins} = this.context; return plugins.getSlotElements(slot, props.reduxState, props.slotProps, props.queryData); } getPluginTabElements(props = this.props) { - return this.getSlotElements(props.tabSlot).map((el) => { + return this.getSlotTabElements(props.tabSlot); + } + + getPluginTabElementsPrepend(props = this.props) { + return this.getSlotTabElements(props.tabSlotPrepend); + } + + getSlotTabElements(slot) { + return this.getSlotElements(slot).map((el) => { return ( {React.cloneElement(el, {active: this.props.activeTab === el.type.talkPluginName})} @@ -59,6 +68,17 @@ class StreamTabPanelContainer extends React.Component { }); } + getTabElements(props = this.props) { + const elements = [...this.getPluginTabElementsPrepend(props)]; + if (Array.isArray(props.tabs)) { + elements.push(...props.tabs); + } else { + elements.push(props.tabs); + } + elements.push(...this.getPluginTabElements(props)); + return elements; + } + getPluginTabPaneElements(props = this.props) { return this.getSlotElements(props.tabPaneSlot).map((el) => { return ( @@ -71,12 +91,12 @@ class StreamTabPanelContainer extends React.Component { render() { return ( - @@ -84,19 +104,20 @@ class StreamTabPanelContainer extends React.Component { } } -StreamTabPanelContainer.propTypes = { +ExtendableTabPanelContainer.propTypes = { activeTab: PropTypes.string.isRequired, setActiveTab: PropTypes.func.isRequired, - appendTabs: PropTypes.oneOfType([ + tabs: PropTypes.oneOfType([ PropTypes.element, PropTypes.arrayOf(PropTypes.element) ]), - appendTabPanes: PropTypes.oneOfType([ + tabPanes: PropTypes.oneOfType([ PropTypes.element, PropTypes.arrayOf(PropTypes.element) ]), fallbackTab: PropTypes.string.isRequired, tabSlot: PropTypes.string.isRequired, + tabSlotPrepend: PropTypes.string.isRequired, tabPaneSlot: PropTypes.string.isRequired, slotProps: PropTypes.object.isRequired, queryData: PropTypes.object, @@ -109,4 +130,4 @@ const mapStateToProps = (state) => ({ reduxState: omit(state, 'apollo'), }); -export default connect(mapStateToProps, null)(StreamTabPanelContainer); +export default connect(mapStateToProps, null)(ExtendableTabPanelContainer); diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js index 6c3d7b02c..464ce31fc 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/containers/Stream.js @@ -271,6 +271,7 @@ const LOAD_MORE_QUERY = gql` const slots = [ 'streamTabs', + 'streamTabsPrepend', 'streamTabPanes', 'streamFilter', ]; diff --git a/plugins/talk-plugin-featured-comments/client/index.js b/plugins/talk-plugin-featured-comments/client/index.js index 7b419c545..bdcb2f470 100644 --- a/plugins/talk-plugin-featured-comments/client/index.js +++ b/plugins/talk-plugin-featured-comments/client/index.js @@ -16,7 +16,7 @@ export default { reducer, translations, slots: { - streamTabs: [Tab], + streamTabsPrepend: [Tab], streamTabPanes: [TabPane], commentInfoBar: [Tag], moderationActions: [ModActionButton], @@ -57,7 +57,7 @@ export default { } const comment = findCommentInEmbedQuery(previous, variables.id); - + if (previous.asset.comments) { updated = update(previous, { asset: { @@ -101,7 +101,7 @@ export default { updateQueries: { CoralEmbedStream_Embed: (previous) => { let updated = previous; - + if (variables.name !== 'FEATURED') { return; } From b5b6b113d4119d189adb611d64108860b999b8ff Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 16 Nov 2017 10:57:16 +0100 Subject: [PATCH 04/39] Restructure coral-embed-stream --- client/coral-embed-stream/src/components/Embed.js | 2 +- client/coral-embed-stream/src/containers/Embed.js | 2 +- .../src/{ => tabs/stream}/components/AllCommentsPane.js | 0 .../src/{ => tabs/stream}/components/Comment.css | 0 .../src/{ => tabs/stream}/components/Comment.js | 4 ++-- .../src/{ => tabs/stream}/components/CommentTombstone.css | 0 .../src/{ => tabs/stream}/components/CommentTombstone.js | 0 .../src/{ => tabs/stream}/components/CountdownSeconds.js | 0 .../{ => tabs/stream}/components/EditableCommentContent.js | 0 .../src/{ => tabs/stream}/components/IgnoreUserWizard.js | 0 .../{ => tabs/stream}/components/InactiveCommentLabel.css | 0 .../{ => tabs/stream}/components/InactiveCommentLabel.js | 0 .../src/{ => tabs/stream}/components/LoadMore.js | 0 .../src/{ => tabs/stream}/components/NewCount.js | 0 .../src/{ => tabs/stream}/components/NoComments.css | 0 .../src/{ => tabs/stream}/components/NoComments.js | 0 .../src/{ => tabs/stream}/components/Stream.css | 0 .../src/{ => tabs/stream}/components/Stream.js | 4 ++-- .../src/{ => tabs/stream}/components/StreamError.css | 0 .../src/{ => tabs/stream}/components/StreamError.js | 0 .../stream/components/SuspendedAccount.css} | 0 .../src/{ => tabs/stream}/components/SuspendedAccount.js | 2 +- .../src/{ => tabs/stream}/components/util.js | 0 .../{ => tabs/stream}/containers/AutomaticAssetClosure.js | 0 .../src/{ => tabs/stream}/containers/Comment.js | 4 ++-- .../src/{ => tabs/stream}/containers/Stream.js | 6 +++--- 26 files changed, 12 insertions(+), 12 deletions(-) rename client/coral-embed-stream/src/{ => tabs/stream}/components/AllCommentsPane.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/Comment.css (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/Comment.js (99%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/CommentTombstone.css (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/CommentTombstone.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/CountdownSeconds.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/EditableCommentContent.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/IgnoreUserWizard.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/InactiveCommentLabel.css (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/InactiveCommentLabel.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/LoadMore.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/NewCount.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/NoComments.css (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/NoComments.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/Stream.css (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/Stream.js (98%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/StreamError.css (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/StreamError.js (100%) rename client/coral-embed-stream/src/{components/SuspendAccount.css => tabs/stream/components/SuspendedAccount.css} (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/SuspendedAccount.js (98%) rename client/coral-embed-stream/src/{ => tabs/stream}/components/util.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/containers/AutomaticAssetClosure.js (100%) rename client/coral-embed-stream/src/{ => tabs/stream}/containers/Comment.js (96%) rename client/coral-embed-stream/src/{ => tabs/stream}/containers/Stream.js (98%) diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index 132b3be7f..e12201805 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Stream from '../containers/Stream'; +import Stream from '../tabs/stream/containers/Stream'; import Slot from 'coral-framework/components/Slot'; import {can} from 'coral-framework/services/perms'; import t from 'coral-framework/services/i18n'; diff --git a/client/coral-embed-stream/src/containers/Embed.js b/client/coral-embed-stream/src/containers/Embed.js index 3fef8872d..4c65d8fde 100644 --- a/client/coral-embed-stream/src/containers/Embed.js +++ b/client/coral-embed-stream/src/containers/Embed.js @@ -13,7 +13,7 @@ import * as assetActions from '../actions/asset'; import {getDefinitionName, getSlotFragmentSpreads} from 'coral-framework/utils'; import {withQuery} from 'coral-framework/hocs'; import Embed from '../components/Embed'; -import Stream from './Stream'; +import Stream from '../tabs/stream/containers/Stream'; import {notify} from 'coral-framework/actions/notification'; import t from 'coral-framework/services/i18n'; import PropTypes from 'prop-types'; diff --git a/client/coral-embed-stream/src/components/AllCommentsPane.js b/client/coral-embed-stream/src/tabs/stream/components/AllCommentsPane.js similarity index 100% rename from client/coral-embed-stream/src/components/AllCommentsPane.js rename to client/coral-embed-stream/src/tabs/stream/components/AllCommentsPane.js diff --git a/client/coral-embed-stream/src/components/Comment.css b/client/coral-embed-stream/src/tabs/stream/components/Comment.css similarity index 100% rename from client/coral-embed-stream/src/components/Comment.css rename to client/coral-embed-stream/src/tabs/stream/components/Comment.css diff --git a/client/coral-embed-stream/src/components/Comment.js b/client/coral-embed-stream/src/tabs/stream/components/Comment.js similarity index 99% rename from client/coral-embed-stream/src/components/Comment.js rename to client/coral-embed-stream/src/tabs/stream/components/Comment.js index 7ae36b5fa..3da59afdc 100644 --- a/client/coral-embed-stream/src/components/Comment.js +++ b/client/coral-embed-stream/src/tabs/stream/components/Comment.js @@ -9,13 +9,13 @@ import {can} from 'coral-framework/services/perms'; import {TransitionGroup} from 'react-transition-group'; import cn from 'classnames'; import styles from './Comment.css'; -import {THREADING_LEVEL} from '../constants/stream'; +import {THREADING_LEVEL} from '../../../constants/stream'; import merge from 'lodash/merge'; import mapValues from 'lodash/mapValues'; import LoadMore from './LoadMore'; import {getEditableUntilDate} from './util'; -import {findCommentWithId} from '../graphql/utils'; +import {findCommentWithId} from '../../../graphql/utils'; import CommentContent from 'coral-framework/components/CommentContent'; import Slot from 'coral-framework/components/Slot'; import CommentTombstone from './CommentTombstone'; diff --git a/client/coral-embed-stream/src/components/CommentTombstone.css b/client/coral-embed-stream/src/tabs/stream/components/CommentTombstone.css similarity index 100% rename from client/coral-embed-stream/src/components/CommentTombstone.css rename to client/coral-embed-stream/src/tabs/stream/components/CommentTombstone.css diff --git a/client/coral-embed-stream/src/components/CommentTombstone.js b/client/coral-embed-stream/src/tabs/stream/components/CommentTombstone.js similarity index 100% rename from client/coral-embed-stream/src/components/CommentTombstone.js rename to client/coral-embed-stream/src/tabs/stream/components/CommentTombstone.js diff --git a/client/coral-embed-stream/src/components/CountdownSeconds.js b/client/coral-embed-stream/src/tabs/stream/components/CountdownSeconds.js similarity index 100% rename from client/coral-embed-stream/src/components/CountdownSeconds.js rename to client/coral-embed-stream/src/tabs/stream/components/CountdownSeconds.js diff --git a/client/coral-embed-stream/src/components/EditableCommentContent.js b/client/coral-embed-stream/src/tabs/stream/components/EditableCommentContent.js similarity index 100% rename from client/coral-embed-stream/src/components/EditableCommentContent.js rename to client/coral-embed-stream/src/tabs/stream/components/EditableCommentContent.js diff --git a/client/coral-embed-stream/src/components/IgnoreUserWizard.js b/client/coral-embed-stream/src/tabs/stream/components/IgnoreUserWizard.js similarity index 100% rename from client/coral-embed-stream/src/components/IgnoreUserWizard.js rename to client/coral-embed-stream/src/tabs/stream/components/IgnoreUserWizard.js diff --git a/client/coral-embed-stream/src/components/InactiveCommentLabel.css b/client/coral-embed-stream/src/tabs/stream/components/InactiveCommentLabel.css similarity index 100% rename from client/coral-embed-stream/src/components/InactiveCommentLabel.css rename to client/coral-embed-stream/src/tabs/stream/components/InactiveCommentLabel.css diff --git a/client/coral-embed-stream/src/components/InactiveCommentLabel.js b/client/coral-embed-stream/src/tabs/stream/components/InactiveCommentLabel.js similarity index 100% rename from client/coral-embed-stream/src/components/InactiveCommentLabel.js rename to client/coral-embed-stream/src/tabs/stream/components/InactiveCommentLabel.js diff --git a/client/coral-embed-stream/src/components/LoadMore.js b/client/coral-embed-stream/src/tabs/stream/components/LoadMore.js similarity index 100% rename from client/coral-embed-stream/src/components/LoadMore.js rename to client/coral-embed-stream/src/tabs/stream/components/LoadMore.js diff --git a/client/coral-embed-stream/src/components/NewCount.js b/client/coral-embed-stream/src/tabs/stream/components/NewCount.js similarity index 100% rename from client/coral-embed-stream/src/components/NewCount.js rename to client/coral-embed-stream/src/tabs/stream/components/NewCount.js diff --git a/client/coral-embed-stream/src/components/NoComments.css b/client/coral-embed-stream/src/tabs/stream/components/NoComments.css similarity index 100% rename from client/coral-embed-stream/src/components/NoComments.css rename to client/coral-embed-stream/src/tabs/stream/components/NoComments.css diff --git a/client/coral-embed-stream/src/components/NoComments.js b/client/coral-embed-stream/src/tabs/stream/components/NoComments.js similarity index 100% rename from client/coral-embed-stream/src/components/NoComments.js rename to client/coral-embed-stream/src/tabs/stream/components/NoComments.js diff --git a/client/coral-embed-stream/src/components/Stream.css b/client/coral-embed-stream/src/tabs/stream/components/Stream.css similarity index 100% rename from client/coral-embed-stream/src/components/Stream.css rename to client/coral-embed-stream/src/tabs/stream/components/Stream.css diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/tabs/stream/components/Stream.js similarity index 98% rename from client/coral-embed-stream/src/components/Stream.js rename to client/coral-embed-stream/src/tabs/stream/components/Stream.js index 084586eeb..f2b45d47d 100644 --- a/client/coral-embed-stream/src/components/Stream.js +++ b/client/coral-embed-stream/src/tabs/stream/components/Stream.js @@ -16,10 +16,10 @@ import {isCommentActive} from 'coral-framework/utils'; import {Button, Tab, TabCount, TabPane} from 'coral-ui'; import cn from 'classnames'; -import {getTopLevelParent, attachCommentToParent} from '../graphql/utils'; +import {getTopLevelParent, attachCommentToParent} from '../../../graphql/utils'; import AllCommentsPane from './AllCommentsPane'; import AutomaticAssetClosure from '../containers/AutomaticAssetClosure'; -import ExtendableTabPanel from '../containers/ExtendableTabPanel'; +import ExtendableTabPanel from '../../../containers/ExtendableTabPanel'; import styles from './Stream.css'; diff --git a/client/coral-embed-stream/src/components/StreamError.css b/client/coral-embed-stream/src/tabs/stream/components/StreamError.css similarity index 100% rename from client/coral-embed-stream/src/components/StreamError.css rename to client/coral-embed-stream/src/tabs/stream/components/StreamError.css diff --git a/client/coral-embed-stream/src/components/StreamError.js b/client/coral-embed-stream/src/tabs/stream/components/StreamError.js similarity index 100% rename from client/coral-embed-stream/src/components/StreamError.js rename to client/coral-embed-stream/src/tabs/stream/components/StreamError.js diff --git a/client/coral-embed-stream/src/components/SuspendAccount.css b/client/coral-embed-stream/src/tabs/stream/components/SuspendedAccount.css similarity index 100% rename from client/coral-embed-stream/src/components/SuspendAccount.css rename to client/coral-embed-stream/src/tabs/stream/components/SuspendedAccount.css diff --git a/client/coral-embed-stream/src/components/SuspendedAccount.js b/client/coral-embed-stream/src/tabs/stream/components/SuspendedAccount.js similarity index 98% rename from client/coral-embed-stream/src/components/SuspendedAccount.js rename to client/coral-embed-stream/src/tabs/stream/components/SuspendedAccount.js index ce9a1c23c..0369ef559 100644 --- a/client/coral-embed-stream/src/components/SuspendedAccount.js +++ b/client/coral-embed-stream/src/tabs/stream/components/SuspendedAccount.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import cn from 'classnames'; import t from 'coral-framework/services/i18n'; -import styles from './SuspendAccount.css'; +import styles from './SuspendedAccount.css'; import {Button} from 'coral-ui'; import validate from 'coral-framework/helpers/validate'; import RestrictedMessageBox from 'coral-framework/components/RestrictedMessageBox'; diff --git a/client/coral-embed-stream/src/components/util.js b/client/coral-embed-stream/src/tabs/stream/components/util.js similarity index 100% rename from client/coral-embed-stream/src/components/util.js rename to client/coral-embed-stream/src/tabs/stream/components/util.js diff --git a/client/coral-embed-stream/src/containers/AutomaticAssetClosure.js b/client/coral-embed-stream/src/tabs/stream/containers/AutomaticAssetClosure.js similarity index 100% rename from client/coral-embed-stream/src/containers/AutomaticAssetClosure.js rename to client/coral-embed-stream/src/tabs/stream/containers/AutomaticAssetClosure.js diff --git a/client/coral-embed-stream/src/containers/Comment.js b/client/coral-embed-stream/src/tabs/stream/containers/Comment.js similarity index 96% rename from client/coral-embed-stream/src/containers/Comment.js rename to client/coral-embed-stream/src/tabs/stream/containers/Comment.js index 26fc872d5..eb27517f7 100644 --- a/client/coral-embed-stream/src/containers/Comment.js +++ b/client/coral-embed-stream/src/tabs/stream/containers/Comment.js @@ -4,9 +4,9 @@ import Comment from '../components/Comment'; import {withFragments} from 'coral-framework/hocs'; import {getSlotFragmentSpreads} from 'coral-framework/utils'; import {withSetCommentStatus} from 'coral-framework/graphql/mutations'; -import {THREADING_LEVEL} from '../constants/stream'; +import {THREADING_LEVEL} from '../../../constants/stream'; import hoistStatics from 'recompose/hoistStatics'; -import {nest} from '../graphql/utils'; +import {nest} from '../../../graphql/utils'; const slots = [ 'streamQuestionArea', diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/tabs/stream/containers/Stream.js similarity index 98% rename from client/coral-embed-stream/src/containers/Stream.js rename to client/coral-embed-stream/src/tabs/stream/containers/Stream.js index 464ce31fc..8798a51ae 100644 --- a/client/coral-embed-stream/src/containers/Stream.js +++ b/client/coral-embed-stream/src/tabs/stream/containers/Stream.js @@ -2,7 +2,7 @@ import React from 'react'; import {gql, compose} from 'react-apollo'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; -import {ADDTL_COMMENTS_ON_LOAD_MORE, THREADING_LEVEL} from '../constants/stream'; +import {ADDTL_COMMENTS_ON_LOAD_MORE, THREADING_LEVEL} from '../../../constants/stream'; import { withPostComment, withPostFlag, withPostDontAgree, withDeleteAction, withEditComment @@ -10,7 +10,7 @@ import { import * as authActions from 'coral-embed-stream/src/actions/auth'; import * as notificationActions from 'coral-framework/actions/notification'; -import {setActiveReplyBox, setActiveTab, viewAllComments} from '../actions/stream'; +import {setActiveReplyBox, setActiveTab, viewAllComments} from '../../../actions/stream'; import Stream from '../components/Stream'; import Comment from './Comment'; import {withFragments, withEmit} from 'coral-framework/hocs'; @@ -23,7 +23,7 @@ import { removeCommentFromEmbedQuery, insertFetchedCommentsIntoEmbedQuery, nest, -} from '../graphql/utils'; +} from '../../../graphql/utils'; const {showSignInDialog, editName} = authActions; const {notify} = notificationActions; From f7c46b1a423972742daf9144bf281e0a2a138404 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 16 Nov 2017 16:19:36 +0100 Subject: [PATCH 05/39] Move asset fragment up one level --- .../src/components/Embed.js | 11 +- .../src/containers/Embed.js | 9 ++ .../tabs/configure/components/Configure.js | 10 ++ .../tabs/configure/containers/Configure.js | 33 ++++++ .../tabs/stream/components/AllCommentsPane.js | 4 +- .../src/tabs/stream/components/Stream.js | 11 +- .../src/tabs/stream/containers/Stream.js | 106 +++++++++--------- 7 files changed, 125 insertions(+), 59 deletions(-) create mode 100644 client/coral-embed-stream/src/tabs/configure/components/Configure.js create mode 100644 client/coral-embed-stream/src/tabs/configure/containers/Configure.js diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index e12201805..4a48c54e5 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -1,6 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import Stream from '../tabs/stream/containers/Stream'; +import Configure from '../tabs/configure/containers/Configure'; import Slot from 'coral-framework/components/Slot'; import {can} from 'coral-framework/services/perms'; import t from 'coral-framework/services/i18n'; @@ -45,6 +46,11 @@ export default class Embed extends React.Component { {t('framework.configure_stream')} ); + tabs.push( + + {t('framework.configure_stream')} + + ); } return tabs; } @@ -87,7 +93,7 @@ export default class Embed extends React.Component { tabs={this.getTabs()} tabPanes={[ - + , @@ -95,6 +101,9 @@ export default class Embed extends React.Component { , + + + , ]} /> diff --git a/client/coral-embed-stream/src/containers/Embed.js b/client/coral-embed-stream/src/containers/Embed.js index 4c65d8fde..f17be9a16 100644 --- a/client/coral-embed-stream/src/containers/Embed.js +++ b/client/coral-embed-stream/src/containers/Embed.js @@ -14,6 +14,7 @@ import {getDefinitionName, getSlotFragmentSpreads} from 'coral-framework/utils'; import {withQuery} from 'coral-framework/hocs'; import Embed from '../components/Embed'; import Stream from '../tabs/stream/containers/Stream'; +import Configure from '../tabs/configure/containers/Configure'; import {notify} from 'coral-framework/actions/notification'; import t from 'coral-framework/services/i18n'; import PropTypes from 'prop-types'; @@ -170,10 +171,18 @@ const EMBED_QUERY = gql` id status } + asset(id: $assetId, url: $assetUrl) { + ...${getDefinitionName(Configure.fragments.asset)} + ...${getDefinitionName(Stream.fragments.asset)} + } ${getSlotFragmentSpreads(slots, 'root')} ...${getDefinitionName(Stream.fragments.root)} + ...${getDefinitionName(Configure.fragments.root)} } ${Stream.fragments.root} + ${Stream.fragments.asset} + ${Configure.fragments.root} + ${Configure.fragments.asset} `; export const withEmbedQuery = withQuery(EMBED_QUERY, { diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configure.js b/client/coral-embed-stream/src/tabs/configure/components/Configure.js new file mode 100644 index 000000000..610fa5fae --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/components/Configure.js @@ -0,0 +1,10 @@ +import React from 'react'; + +class Configure extends React.Component { + render() { + console.log(this.props); + return
Configure
; + } +} + +export default Configure; diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js new file mode 100644 index 000000000..a52b65822 --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js @@ -0,0 +1,33 @@ +import React from 'react'; +import {gql, compose} from 'react-apollo'; +import {withFragments} from 'coral-framework/hocs'; +import Configure from '../components/Configure'; + +class ConfigureContainer extends React.Component { + render() { + return ; + } +} + +const withConfigureFragments = withFragments({ + root: gql` + fragment CoralEmbedStream_Configure_root on RootQuery { + __typename + } + `, + asset: gql` + fragment CoralEmbedStream_Configure_asset on Asset { + settings { + moderation + } + } + `, +}); + +const enhance = compose( + withConfigureFragments, +); + +export default enhance(ConfigureContainer); diff --git a/client/coral-embed-stream/src/tabs/stream/components/AllCommentsPane.js b/client/coral-embed-stream/src/tabs/stream/components/AllCommentsPane.js index 2967b9972..3a64820c7 100644 --- a/client/coral-embed-stream/src/tabs/stream/components/AllCommentsPane.js +++ b/client/coral-embed-stream/src/tabs/stream/components/AllCommentsPane.js @@ -14,7 +14,7 @@ const hasComment = (nodes, id) => nodes.some((node) => node.id === id); // comments to show. The spare cursor functions as a backup in case one // of the comments gets deleted. function resetCursors(state, props) { - const comments = props.root.asset.comments; + const comments = props.asset.comments; if (comments && comments.nodes.length) { const idCursors = [comments.nodes[0].id]; if (comments.nodes[1]) { @@ -30,7 +30,7 @@ function resetCursors(state, props) { // using the help of the backup cursor. function invalidateCursor(invalidated, state, props) { const alt = invalidated === 1 ? 0 : 1; - const comments = props.root.asset.comments; + const comments = props.asset.comments; const idCursors = []; if (state.idCursors[alt]) { idCursors.push(state.idCursors[alt]); diff --git a/client/coral-embed-stream/src/tabs/stream/components/Stream.js b/client/coral-embed-stream/src/tabs/stream/components/Stream.js index f2b45d47d..6844d1c4f 100644 --- a/client/coral-embed-stream/src/tabs/stream/components/Stream.js +++ b/client/coral-embed-stream/src/tabs/stream/components/Stream.js @@ -47,7 +47,8 @@ class Stream extends React.Component { activeReplyBox, setActiveReplyBox, commentClassNames, - root: {asset, asset: {comment}}, + asset, + asset: {comment}, postComment, notify, editComment, @@ -113,7 +114,8 @@ class Stream extends React.Component { activeReplyBox, setActiveReplyBox, commentClassNames, - root: {asset, asset: {comments, totalCommentCount}}, + asset, + asset: {comments, totalCommentCount}, postComment, notify, editComment, @@ -169,6 +171,7 @@ class Stream extends React.Component { { @@ -62,7 +62,7 @@ class StreamContainer extends React.Component { this.commentsAddedSubscription = this.props.data.subscribeToMore({ document: COMMENTS_ADDED_SUBSCRIPTION, variables: { - assetId: this.props.root.asset.id, + assetId: this.props.asset.id, }, updateQuery: (prev, {subscriptionData: {data: {commentAdded}}}) => { @@ -116,7 +116,7 @@ class StreamContainer extends React.Component { limit: parent_id ? 999999 : ADDTL_COMMENTS_ON_LOAD_MORE, cursor: comment.replies.endCursor, parent_id, - asset_id: this.props.root.asset.id, + asset_id: this.props.asset.id, sortOrder: 'ASC', excludeIgnored: this.props.data.variables.excludeIgnored, }, @@ -131,9 +131,9 @@ class StreamContainer extends React.Component { query: LOAD_MORE_QUERY, variables: { limit: ADDTL_COMMENTS_ON_LOAD_MORE, - cursor: this.props.root.asset.comments.endCursor, + cursor: this.props.asset.comments.endCursor, parent_id: null, - asset_id: this.props.root.asset.id, + asset_id: this.props.asset.id, sortOrder: this.props.data.variables.sortOrder, sortBy: this.props.data.variables.sortBy, excludeIgnored: this.props.data.variables.excludeIgnored, @@ -177,9 +177,9 @@ class StreamContainer extends React.Component { } render() { - if (!this.props.root.asset - || this.props.root.asset.comment === undefined - && !this.props.root.asset.comments + if (!this.props.asset + || this.props.asset.comment === undefined + && !this.props.asset.comments ) { return ; } @@ -279,49 +279,6 @@ const slots = [ const fragments = { root: gql` fragment CoralEmbedStream_Stream_root on RootQuery { - asset(id: $assetId, url: $assetUrl) { - comment(id: $commentId) @include(if: $hasComment) { - ...CoralEmbedStream_Stream_comment - ${nest(` - parent { - ...CoralEmbedStream_Stream_comment - ...nest - } - `, THREADING_LEVEL)} - } - id - title - url - closedAt - isClosed - created_at - settings { - moderation - infoBoxEnable - infoBoxContent - premodLinksEnable - questionBoxEnable - questionBoxContent - questionBoxIcon - closedTimeout - closedMessage - charCountEnable - charCount - requireEmailConfirmation - } - commentCount @skip(if: $hasComment) - totalCommentCount @skip(if: $hasComment) - comments(query: {limit: 10, excludeIgnored: $excludeIgnored, sortOrder: $sortOrder, sortBy: $sortBy}) @skip(if: $hasComment) { - nodes { - ...CoralEmbedStream_Stream_comment - } - hasNextPage - startCursor - endCursor - } - ${getSlotFragmentSpreads(slots, 'asset')} - ...${getDefinitionName(Comment.fragments.asset)} - } me { status ignoredUsers { @@ -334,8 +291,53 @@ const fragments = { ${getSlotFragmentSpreads(slots, 'root')} ...${getDefinitionName(Comment.fragments.root)} } - ${Comment.fragments.asset} ${Comment.fragments.root} + `, + asset: gql` + fragment CoralEmbedStream_Stream_asset on Asset { + comment(id: $commentId) @include(if: $hasComment) { + ...CoralEmbedStream_Stream_comment + ${nest(` + parent { + ...CoralEmbedStream_Stream_comment + ...nest + } + `, THREADING_LEVEL)} + } + id + title + url + closedAt + isClosed + created_at + settings { + moderation + infoBoxEnable + infoBoxContent + premodLinksEnable + questionBoxEnable + questionBoxContent + questionBoxIcon + closedTimeout + closedMessage + charCountEnable + charCount + requireEmailConfirmation + } + commentCount @skip(if: $hasComment) + totalCommentCount @skip(if: $hasComment) + comments(query: {limit: 10, excludeIgnored: $excludeIgnored, sortOrder: $sortOrder, sortBy: $sortBy}) @skip(if: $hasComment) { + nodes { + ...CoralEmbedStream_Stream_comment + } + hasNextPage + startCursor + endCursor + } + ${getSlotFragmentSpreads(slots, 'asset')} + ...${getDefinitionName(Comment.fragments.asset)} + } + ${Comment.fragments.asset} ${commentFragment} `, }; From dfbb9ad96fd4bc755a39108eac714ab223bbf88f Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 16 Nov 2017 20:16:53 +0100 Subject: [PATCH 06/39] Implement AssetStatusInfo UI --- .../configure/components/AssetStatusInfo.js | 28 +++++++++++++++++++ .../tabs/configure/components/Configure.js | 18 ++++++++++-- .../tabs/configure/containers/Configure.js | 9 ++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 client/coral-embed-stream/src/tabs/configure/components/AssetStatusInfo.js diff --git a/client/coral-embed-stream/src/tabs/configure/components/AssetStatusInfo.js b/client/coral-embed-stream/src/tabs/configure/components/AssetStatusInfo.js new file mode 100644 index 000000000..e20f242cc --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/components/AssetStatusInfo.js @@ -0,0 +1,28 @@ +import React from 'react'; +import {Button} from 'coral-ui'; +import PropTypes from 'prop-types'; +import t, {timeago} from 'coral-framework/services/i18n'; + +const AssetStatusInfo = ({isClosed, closedAt, onClose, onOpen}) => ( +
+

{!isClosed ? t('configure.close') : t('configure.open')} {t('configure.comment_stream')}

+ {(!isClosed && closedAt) ?

{t('configure.comment_stream_will_close')} {timeago(closedAt)}.

: ''} +
+

+ {!isClosed ? t('configure.open_stream_configuration') : t('configure.close_stream_configuration')} +

+ +
+
+); + +AssetStatusInfo.propTypes = { + isClosed: PropTypes.bool.isRequired, + closedAt: PropTypes.object.isRequired, + onClose: PropTypes.func.isRequired, + onOpen: PropTypes.func.isRequired, +}; + +export default AssetStatusInfo; diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configure.js b/client/coral-embed-stream/src/tabs/configure/components/Configure.js index 610fa5fae..134923f74 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Configure.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Configure.js @@ -1,10 +1,24 @@ import React from 'react'; +import AssetStatusInfo from './AssetStatusInfo'; +import PropTypes from 'prop-types'; class Configure extends React.Component { render() { - console.log(this.props); - return
Configure
; + return ( +
+
+ +
+ ); } } +Configure.propTypes = { + isClosed: PropTypes.bool, + closedAt: PropTypes.object, +}; + export default Configure; diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js index a52b65822..11f81da20 100644 --- a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js +++ b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js @@ -2,15 +2,22 @@ import React from 'react'; import {gql, compose} from 'react-apollo'; import {withFragments} from 'coral-framework/hocs'; import Configure from '../components/Configure'; +import PropTypes from 'prop-types'; class ConfigureContainer extends React.Component { render() { return ; } } +ConfigureContainer.propTypes = { + asset: PropTypes.object, +}; + const withConfigureFragments = withFragments({ root: gql` fragment CoralEmbedStream_Configure_root on RootQuery { @@ -19,6 +26,8 @@ const withConfigureFragments = withFragments({ `, asset: gql` fragment CoralEmbedStream_Configure_asset on Asset { + closedAt + isClosed settings { moderation } From 78e0fb6cc6b91b5a784d8bf00534bd6f100c477e Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 16 Nov 2017 20:31:08 +0100 Subject: [PATCH 07/39] Add UpdateAssetStatus mutation --- client/coral-framework/graphql/fragments.js | 1 + client/coral-framework/graphql/mutations.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/client/coral-framework/graphql/fragments.js b/client/coral-framework/graphql/fragments.js index a99dc0ee2..8b4c19e6f 100644 --- a/client/coral-framework/graphql/fragments.js +++ b/client/coral-framework/graphql/fragments.js @@ -18,6 +18,7 @@ export default { 'StopIgnoringUserResponse', 'UpdateSettingsResponse', 'UpdateAssetSettingsResponse', + 'UpdateAssetStatusResponse', ) }; diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js index 1c43f4707..21bfd1825 100644 --- a/client/coral-framework/graphql/mutations.js +++ b/client/coral-framework/graphql/mutations.js @@ -382,3 +382,22 @@ export const withUpdateAssetSettings = withMutation( }); }}), }); + +export const withUpdateAssetStatus = withMutation( + gql` + mutation UpdateAssetStatus($id: ID!, $input: AssetStatusInput!) { + updateAssetStatus(id: $id, input: $input) { + ...UpdateAssetStatusResponse + } + } + `, { + props: ({mutate}) => ({ + updateAssetStatus: (id, input) => { + return mutate({ + variables: { + id, + input, + }, + }); + }}), + }); From c58d9068e3988c6cc4390a5283419fa89e2164dd Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 17 Nov 2017 12:39:34 +0100 Subject: [PATCH 08/39] Implement open and close asset --- .../configure/components/AssetStatusInfo.js | 4 +-- .../tabs/configure/components/Configure.js | 8 ++++-- .../tabs/configure/containers/Configure.js | 12 ++++++++- client/coral-framework/graphql/mutations.js | 25 ++++++++++++++++++- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/client/coral-embed-stream/src/tabs/configure/components/AssetStatusInfo.js b/client/coral-embed-stream/src/tabs/configure/components/AssetStatusInfo.js index e20f242cc..fec88537c 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/AssetStatusInfo.js +++ b/client/coral-embed-stream/src/tabs/configure/components/AssetStatusInfo.js @@ -6,7 +6,7 @@ import t, {timeago} from 'coral-framework/services/i18n'; const AssetStatusInfo = ({isClosed, closedAt, onClose, onOpen}) => (

{!isClosed ? t('configure.close') : t('configure.open')} {t('configure.comment_stream')}

- {(!isClosed && closedAt) ?

{t('configure.comment_stream_will_close')} {timeago(closedAt)}.

: ''} + {(!isClosed && closedAt) ?

{t('configure.comment_stream_will_close')} {timeago(new Date(closedAt))}.

: ''}

{!isClosed ? t('configure.open_stream_configuration') : t('configure.close_stream_configuration')} @@ -20,7 +20,7 @@ const AssetStatusInfo = ({isClosed, closedAt, onClose, onOpen}) => ( AssetStatusInfo.propTypes = { isClosed: PropTypes.bool.isRequired, - closedAt: PropTypes.object.isRequired, + closedAt: PropTypes.string, onClose: PropTypes.func.isRequired, onOpen: PropTypes.func.isRequired, }; diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configure.js b/client/coral-embed-stream/src/tabs/configure/components/Configure.js index 134923f74..821663ab7 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Configure.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Configure.js @@ -10,6 +10,8 @@ class Configure extends React.Component {

); @@ -17,8 +19,10 @@ class Configure extends React.Component { } Configure.propTypes = { - isClosed: PropTypes.bool, - closedAt: PropTypes.object, + isClosed: PropTypes.bool.isRequired, + closedAt: PropTypes.string, + onOpenAsset: PropTypes.func.isRequired, + onCloseAsset: PropTypes.func.isRequired, }; export default Configure; diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js index 11f81da20..973e5a2d3 100644 --- a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js +++ b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js @@ -3,19 +3,27 @@ import {gql, compose} from 'react-apollo'; import {withFragments} from 'coral-framework/hocs'; import Configure from '../components/Configure'; import PropTypes from 'prop-types'; +import {withUpdateAssetStatus} from 'coral-framework/graphql/mutations'; class ConfigureContainer extends React.Component { + + openAsset = () => this.props.updateAssetStatus(this.props.asset.id, {closedAt: null}); + closeAsset = () => this.props.updateAssetStatus(this.props.asset.id, {closedAt: new Date().toISOString()}); + render() { return ; } } ConfigureContainer.propTypes = { - asset: PropTypes.object, + asset: PropTypes.object.isRequired, + updateAssetStatus: PropTypes.func.isRequired, }; const withConfigureFragments = withFragments({ @@ -26,6 +34,7 @@ const withConfigureFragments = withFragments({ `, asset: gql` fragment CoralEmbedStream_Configure_asset on Asset { + id closedAt isClosed settings { @@ -37,6 +46,7 @@ const withConfigureFragments = withFragments({ const enhance = compose( withConfigureFragments, + withUpdateAssetStatus, ); export default enhance(ConfigureContainer); diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js index 21bfd1825..ed4a6f782 100644 --- a/client/coral-framework/graphql/mutations.js +++ b/client/coral-framework/graphql/mutations.js @@ -385,7 +385,7 @@ export const withUpdateAssetSettings = withMutation( export const withUpdateAssetStatus = withMutation( gql` - mutation UpdateAssetStatus($id: ID!, $input: AssetStatusInput!) { + mutation UpdateAssetStatus($id: ID!, $input: UpdateAssetStatusInput!) { updateAssetStatus(id: $id, input: $input) { ...UpdateAssetStatusResponse } @@ -398,6 +398,29 @@ export const withUpdateAssetStatus = withMutation( id, input, }, + optimisticResponse: { + updateAssetStatus: { + __typename: 'UpdateAssetStatusResponse', + errors: null, + } + }, + update: (proxy) => { + if (input.closedAt !== undefined) { + const fragment = gql` + fragment Talk_UpdateAssetStatusResponse on Asset { + closedAt + isClosed + }`; + + const fragmentId = `Asset_${id}`; + const data = { + __typename: 'Asset', + closedAt: input.closedAt, + isClosed: !!input.closedAt && new Date(input.closedAt).getTime() <= new Date().getTime(), + }; + proxy.writeFragment({fragment, id: fragmentId, data}); + } + } }); }}), }); From 853b0ca6dd1cbf7287028e0e2048ea1efa8134c5 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 17 Nov 2017 15:42:06 +0100 Subject: [PATCH 09/39] Automatic asset closure --- .../src/actions/configure.js | 0 .../src/components/Embed.js | 4 +- .../containers/AutomaticAssetClosure.js | 33 ++++++--- .../src/containers/Embed.js | 3 + .../configure/components/AssetStatusInfo.js | 68 +++++++++++++++---- .../src/tabs/stream/components/Stream.js | 2 - .../src/tabs/stream/containers/Stream.js | 1 - 7 files changed, 83 insertions(+), 28 deletions(-) create mode 100644 client/coral-embed-stream/src/actions/configure.js rename client/coral-embed-stream/src/{tabs/stream => }/containers/AutomaticAssetClosure.js (65%) diff --git a/client/coral-embed-stream/src/actions/configure.js b/client/coral-embed-stream/src/actions/configure.js new file mode 100644 index 000000000..e69de29bb diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index 4a48c54e5..bbdaa2f19 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -5,6 +5,7 @@ import Configure from '../tabs/configure/containers/Configure'; import Slot from 'coral-framework/components/Slot'; import {can} from 'coral-framework/services/perms'; import t from 'coral-framework/services/i18n'; +import AutomaticAssetClosure from '../containers/AutomaticAssetClosure'; import ExtendableTabPanel from '../containers/ExtendableTabPanel'; import {Tab, TabPane} from 'coral-ui'; @@ -56,11 +57,12 @@ export default class Embed extends React.Component { } render() { - const {activeTab, commentId, root, data, auth: {showSignInDialog, signInDialogFocus}, blurSignInDialog, focusSignInDialog, hideSignInDialog, router: {location: {query: {parentUrl}}}} = this.props; + const {activeTab, commentId, root, root: {asset}, data, auth: {showSignInDialog, signInDialogFocus}, blurSignInDialog, focusSignInDialog, hideSignInDialog, router: {location: {query: {parentUrl}}}} = this.props; const hasHighlightedComment = !!commentId; return (
+ ( -
-

{!isClosed ? t('configure.close') : t('configure.open')} {t('configure.comment_stream')}

- {(!isClosed && closedAt) ?

{t('configure.comment_stream_will_close')} {timeago(new Date(closedAt))}.

: ''} -
-

- {!isClosed ? t('configure.open_stream_configuration') : t('configure.close_stream_configuration')} -

- -
-
-); +class AssetStatusInfo extends React.Component { + + timer = null; + + constructor(props) { + super(props); + this.setupTimer(props); + } + + componentWillReceiveProps(nextProps) { + this.setupTimer(nextProps); + } + + interval(closedAt) { + const diff = new Date(closedAt).getTime() - new Date().getTime(); + return diff > 60000 ? 60000 : 1000; + } + + setupTimer({closedAt, isClosed} = this.props) { + if (this.timer && (isClosed || !closedAt)) { + clearTimeout(this.timer); + this.timer = null; + } + if (isClosed || !closedAt) { + this.timer = null; + return; + } + if (!this.timer) { + this.timer = setTimeout(() => { + this.timer = null; + this.forceUpdate(); + this.setupTimer(); + }, this.interval(closedAt)); + } + } + + render() { + const {isClosed, closedAt, onClose, onOpen} = this.props; + return ( +
+

{!isClosed ? t('configure.close') : t('configure.open')} {t('configure.comment_stream')}

+ {(!isClosed && closedAt) ?

{t('configure.comment_stream_will_close')} {timeago(new Date(closedAt))}.

: ''} +
+

+ {!isClosed ? t('configure.open_stream_configuration') : t('configure.close_stream_configuration')} +

+ +
+
+ ); + } +} AssetStatusInfo.propTypes = { isClosed: PropTypes.bool.isRequired, diff --git a/client/coral-embed-stream/src/tabs/stream/components/Stream.js b/client/coral-embed-stream/src/tabs/stream/components/Stream.js index 6844d1c4f..99df8c1c9 100644 --- a/client/coral-embed-stream/src/tabs/stream/components/Stream.js +++ b/client/coral-embed-stream/src/tabs/stream/components/Stream.js @@ -18,7 +18,6 @@ import cn from 'classnames'; import {getTopLevelParent, attachCommentToParent} from '../../../graphql/utils'; import AllCommentsPane from './AllCommentsPane'; -import AutomaticAssetClosure from '../containers/AutomaticAssetClosure'; import ExtendableTabPanel from '../../../containers/ExtendableTabPanel'; import styles from './Stream.css'; @@ -234,7 +233,6 @@ class Stream extends React.Component { return (
- {highlightedComment &&
diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css index 0dcbf856e..63f1edf67 100644 --- a/client/coral-embed-stream/style/default.css +++ b/client/coral-embed-stream/style/default.css @@ -275,22 +275,6 @@ button.comment__action-button[disabled], /* Close comments */ -.close-comments-intro-wrapper { - display: flex; - justify-content: space-between; - align-items: center; -} - -.close-comments-intro-wrapper button { - width: 300px; - margin-left: 20px; -} - -.close-comments-intro-wrapper button:disabled { - opacity: 0.5; - cursor: not-allowed; -} - .close-comments-message { box-sizing: border-box; width: 100%; From 3e4c9dcb48527e05366796ba0341c00549d2c2d7 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 17 Nov 2017 17:02:32 +0100 Subject: [PATCH 11/39] Refactor query tree --- .../tabs/configure/components/Configure.js | 14 ++---- .../configure/containers/AssetStatusInfo.js | 47 +++++++++++++++++++ .../tabs/configure/containers/Configure.js | 27 ++++------- 3 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 client/coral-embed-stream/src/tabs/configure/containers/AssetStatusInfo.js diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configure.js b/client/coral-embed-stream/src/tabs/configure/components/Configure.js index 821663ab7..f5f54e339 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Configure.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Configure.js @@ -1,5 +1,5 @@ import React from 'react'; -import AssetStatusInfo from './AssetStatusInfo'; +import AssetStatusInfo from '../containers/AssetStatusInfo'; import PropTypes from 'prop-types'; class Configure extends React.Component { @@ -8,10 +8,7 @@ class Configure extends React.Component {

); @@ -19,10 +16,9 @@ class Configure extends React.Component { } Configure.propTypes = { - isClosed: PropTypes.bool.isRequired, - closedAt: PropTypes.string, - onOpenAsset: PropTypes.func.isRequired, - onCloseAsset: PropTypes.func.isRequired, + data: PropTypes.object.isRequired, + root: PropTypes.object.isRequired, + asset: PropTypes.object.isRequired, }; export default Configure; diff --git a/client/coral-embed-stream/src/tabs/configure/containers/AssetStatusInfo.js b/client/coral-embed-stream/src/tabs/configure/containers/AssetStatusInfo.js new file mode 100644 index 000000000..c3360f072 --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/containers/AssetStatusInfo.js @@ -0,0 +1,47 @@ +import React from 'react'; +import {gql, compose} from 'react-apollo'; +import {withFragments} from 'coral-framework/hocs'; +import AssetStatusInfo from '../components/AssetStatusInfo'; +import PropTypes from 'prop-types'; +import {withUpdateAssetStatus} from 'coral-framework/graphql/mutations'; + +class AssetStatusInfoContainer extends React.Component { + + openAsset = () => this.props.updateAssetStatus(this.props.asset.id, {closedAt: null}); + closeAsset = () => this.props.updateAssetStatus(this.props.asset.id, {closedAt: new Date().toISOString()}); + + render() { + return ; + } +} + +AssetStatusInfoContainer.propTypes = { + asset: PropTypes.object.isRequired, + updateAssetStatus: PropTypes.func.isRequired, +}; + +const withAssetStatusInfoFragments = withFragments({ + asset: gql` + fragment CoralEmbedStream_AssetStatusInfo_asset on Asset { + id + closedAt + isClosed + settings { + moderation + } + } + `, +}); + +const enhance = compose( + withAssetStatusInfoFragments, + withUpdateAssetStatus, +); + +export default enhance(AssetStatusInfoContainer); diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js index 973e5a2d3..48c596f05 100644 --- a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js +++ b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js @@ -2,28 +2,25 @@ import React from 'react'; import {gql, compose} from 'react-apollo'; import {withFragments} from 'coral-framework/hocs'; import Configure from '../components/Configure'; +import AssetStatusInfo from './AssetStatusInfo'; import PropTypes from 'prop-types'; -import {withUpdateAssetStatus} from 'coral-framework/graphql/mutations'; +import {getDefinitionName} from 'coral-framework/utils'; class ConfigureContainer extends React.Component { - openAsset = () => this.props.updateAssetStatus(this.props.asset.id, {closedAt: null}); - closeAsset = () => this.props.updateAssetStatus(this.props.asset.id, {closedAt: new Date().toISOString()}); - render() { return ; } } ConfigureContainer.propTypes = { + data: PropTypes.object.isRequired, + root: PropTypes.object.isRequired, asset: PropTypes.object.isRequired, - updateAssetStatus: PropTypes.func.isRequired, }; const withConfigureFragments = withFragments({ @@ -34,19 +31,15 @@ const withConfigureFragments = withFragments({ `, asset: gql` fragment CoralEmbedStream_Configure_asset on Asset { - id - closedAt - isClosed - settings { - moderation - } + __typename + ...${getDefinitionName(AssetStatusInfo.fragments.asset)} } + ${AssetStatusInfo.fragments.asset} `, }); const enhance = compose( withConfigureFragments, - withUpdateAssetStatus, ); export default enhance(ConfigureContainer); From 9fb7aa26579918bf0035ce8c63b21baf4069f301 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Sat, 18 Nov 2017 11:40:44 +0100 Subject: [PATCH 12/39] Start implementing settings ui --- .../src/tabs/configure/components/Configure.js | 4 ++++ .../src/tabs/configure/containers/AssetStatusInfo.js | 3 --- .../src/tabs/configure/containers/Configure.js | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configure.js b/client/coral-embed-stream/src/tabs/configure/components/Configure.js index f5f54e339..074fe0616 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Configure.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Configure.js @@ -1,11 +1,15 @@ import React from 'react'; import AssetStatusInfo from '../containers/AssetStatusInfo'; +import Settings from '../containers/Settings'; import PropTypes from 'prop-types'; class Configure extends React.Component { render() { return (
+
Date: Mon, 20 Nov 2017 12:29:23 +0100 Subject: [PATCH 13/39] Implement configure and redux state --- .../routes/Configure/containers/Configure.js | 32 +------- .../src/actions/configure.js | 9 +++ .../src/constants/configure.js | 4 + .../src/reducers/configure.js | 42 ++++++++++ .../coral-embed-stream/src/reducers/index.js | 2 + .../tabs/configure/components/Checkbox.css | 59 ++++++++++++++ .../src/tabs/configure/components/Checkbox.js | 28 +++++++ .../configure/components/Configuration.css | 27 +++++++ .../configure/components/Configuration.js | 46 +++++++++++ .../tabs/configure/components/Settings.css | 26 ++++++ .../src/tabs/configure/components/Settings.js | 46 +++++++++++ .../src/tabs/configure/containers/Settings.js | 79 +++++++++++++++++++ client/coral-framework/hocs/index.js | 1 + .../hocs/withMergedSettings.js | 16 ++++ client/coral-framework/utils/index.js | 10 +++ 15 files changed, 399 insertions(+), 28 deletions(-) create mode 100644 client/coral-embed-stream/src/constants/configure.js create mode 100644 client/coral-embed-stream/src/reducers/configure.js create mode 100644 client/coral-embed-stream/src/tabs/configure/components/Checkbox.css create mode 100644 client/coral-embed-stream/src/tabs/configure/components/Checkbox.js create mode 100644 client/coral-embed-stream/src/tabs/configure/components/Configuration.css create mode 100644 client/coral-embed-stream/src/tabs/configure/components/Configuration.js create mode 100644 client/coral-embed-stream/src/tabs/configure/components/Settings.css create mode 100644 client/coral-embed-stream/src/tabs/configure/components/Settings.js create mode 100644 client/coral-embed-stream/src/tabs/configure/containers/Settings.js create mode 100644 client/coral-framework/hocs/withMergedSettings.js diff --git a/client/coral-admin/src/routes/Configure/containers/Configure.js b/client/coral-admin/src/routes/Configure/containers/Configure.js index 8f80a3e24..dfa8df876 100644 --- a/client/coral-admin/src/routes/Configure/containers/Configure.js +++ b/client/coral-admin/src/routes/Configure/containers/Configure.js @@ -2,38 +2,20 @@ 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 {withQuery, withMergedSettings} from 'coral-framework/hocs'; import {Spinner} from 'coral-ui'; import {notify} from 'coral-framework/actions/notification'; import PropTypes from 'prop-types'; -import assignWith from 'lodash/assignWith'; import {withUpdateSettings} from 'coral-framework/graphql/mutations'; import {getErrorMessages, getDefinitionName} from 'coral-framework/utils'; import StreamSettings from './StreamSettings'; import TechSettings from './TechSettings'; import ModerationSettings from './ModerationSettings'; import {clearPending, setActiveSection} from '../../../actions/configure'; - import Configure from '../components/Configure'; -// Like lodash merge but does not recurse into arrays. -const mergeExcludingArrays = (objValue, srcValue) => { - if (typeof srcValue === 'object' && !Array.isArray(srcValue)) { - return assignWith({}, objValue, srcValue, mergeExcludingArrays); - } - return srcValue; -}; - class ConfigureContainer extends Component { - // Merge current settings with pending settings. - getMergedSettings = (props = this.props) => { - return assignWith({}, props.root.settings, props.pending, mergeExcludingArrays); - } - - // Cached merged settings. - mergedSettings = this.getMergedSettings(); - savePending = async () => { try { await this.props.updateSettings(this.props.pending); @@ -44,14 +26,6 @@ class ConfigureContainer extends Component { } }; - componentWillReceiveProps(nextProps) { - - // Recalculate merged settings when necessary. - if (this.props.root.settings !== nextProps.root.settings || this.props.pending !== nextProps.pending) { - this.mergedSettings = this.getMergedSettings(nextProps); - } - } - render () { if(this.props.data.loading) { return ; @@ -62,7 +36,7 @@ class ConfigureContainer extends Component { auth={this.props.auth} data={this.props.data} root={this.props.root} - settings={this.mergedSettings} + settings={this.props.mergedSettings} canSave={this.props.canSave} savePending={this.savePending} setActiveSection={this.props.setActiveSection} @@ -112,6 +86,7 @@ export default compose( withUpdateSettings, withConfigureQuery, connect(mapStateToProps, mapDispatchToProps), + withMergedSettings('root.settings', 'pending', 'mergedSettings'), )(ConfigureContainer); ConfigureContainer.propTypes = { @@ -124,5 +99,6 @@ ConfigureContainer.propTypes = { root: PropTypes.object.isRequired, canSave: PropTypes.bool.isRequired, pending: PropTypes.object.isRequired, + mergedSettings: PropTypes.object.isRequired, activeSection: PropTypes.string.isRequired, }; diff --git a/client/coral-embed-stream/src/actions/configure.js b/client/coral-embed-stream/src/actions/configure.js index e69de29bb..bfc738fe0 100644 --- a/client/coral-embed-stream/src/actions/configure.js +++ b/client/coral-embed-stream/src/actions/configure.js @@ -0,0 +1,9 @@ +import * as actions from '../constants/configure'; + +export const updatePending = ({updater, errorUpdater}) => { + return {type: actions.UPDATE_PENDING, updater, errorUpdater}; +}; + +export const clearPending = () => { + return {type: actions.CLEAR_PENDING}; +}; diff --git a/client/coral-embed-stream/src/constants/configure.js b/client/coral-embed-stream/src/constants/configure.js new file mode 100644 index 000000000..b29cf6280 --- /dev/null +++ b/client/coral-embed-stream/src/constants/configure.js @@ -0,0 +1,4 @@ +const prefix = 'TALK_EMBED_STREAM_CONFIGURE'; + +export const UPDATE_PENDING = `${prefix}_UPDATE_PENDING`; +export const CLEAR_PENDING = `${prefix}_CLEAR_PENDING`; diff --git a/client/coral-embed-stream/src/reducers/configure.js b/client/coral-embed-stream/src/reducers/configure.js new file mode 100644 index 000000000..0a94d5a54 --- /dev/null +++ b/client/coral-embed-stream/src/reducers/configure.js @@ -0,0 +1,42 @@ +import * as actions from '../constants/configure'; +import isEmpty from 'lodash/isEmpty'; +import update from 'immutability-helper'; + +const initialState = { + canSave: false, + pending: {}, + errors: {}, +}; + +export default function config(state = initialState, action) { + switch (action.type) { + case actions.UPDATE_PENDING: { + let next = state; + if (action.updater) { + next = update(next, { + pending: action.updater, + }); + } + if (action.errorUpdater) { + next = update(next, { + errors: action.errorUpdater, + }); + } + const noErrors = Object.keys(next.errors).reduce((res, error) => res && !next.errors[error], true); + const canSave = !isEmpty(next.pending) && noErrors; + next = update(next, { + canSave: {$set: canSave}, + }); + + return next; + } + case actions.CLEAR_PENDING: + return { + ...state, + pending: {}, + canSave: false, + }; + default: + return state; + } +} diff --git a/client/coral-embed-stream/src/reducers/index.js b/client/coral-embed-stream/src/reducers/index.js index 5c553b04f..d6d0c7693 100644 --- a/client/coral-embed-stream/src/reducers/index.js +++ b/client/coral-embed-stream/src/reducers/index.js @@ -2,6 +2,7 @@ import auth from './auth'; import asset from './asset'; import embed from './embed'; import config from './config'; +import configure from './configure'; import stream from './stream'; import {reducer as commentBox} from '../../../talk-plugin-commentbox'; @@ -11,5 +12,6 @@ export default { commentBox, embed, config, + configure, stream, }; diff --git a/client/coral-embed-stream/src/tabs/configure/components/Checkbox.css b/client/coral-embed-stream/src/tabs/configure/components/Checkbox.css new file mode 100644 index 000000000..a25ea150f --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/components/Checkbox.css @@ -0,0 +1,59 @@ +.root { + position: relative; + display: inline-block; +} + +.input { + position: absolute; + left: 7px; + bottom: 7px; + margin: 0; + padding: 0; + outline: none; + cursor: pointer; + pointer-events: none; + opacity: 0; +} + +.checkbox { + cursor: pointer; +} + +.checkbox:before { + content: "\e835"; + color: #717171; + left: 4px; + top: 0px; + width: 18px; + height: 18px; + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + vertical-align: -6px; + text-rendering: optimizeLegibility; + font-feature-settings: 'liga'; + transition: all .2s ease; + z-index: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.checkboxChecked:before { + content: "\e834"; + color: #00a291; +} + +.input:focus + .checkbox:before { + color: #00a291; +} + +.input:focus + .checkboxChecked:before { + color: #00e291; +} diff --git a/client/coral-embed-stream/src/tabs/configure/components/Checkbox.js b/client/coral-embed-stream/src/tabs/configure/components/Checkbox.js new file mode 100644 index 000000000..98804fe60 --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/components/Checkbox.js @@ -0,0 +1,28 @@ +import React from 'react'; +import styles from './Checkbox.css'; +import cn from 'classnames'; +import PropTypes from 'prop-types'; + +const Checkbox = ({onChange, checked, className, ...rest}) => ( + +); + +Checkbox.propTypes = { + className: PropTypes.string, + onChange: PropTypes.func, + checked: PropTypes.bool, +}; + +export default Checkbox; diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configuration.css b/client/coral-embed-stream/src/tabs/configure/components/Configuration.css new file mode 100644 index 000000000..b18328a94 --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/components/Configuration.css @@ -0,0 +1,27 @@ +.root { + position: relative; + margin: 12px 12px 12px 0; +} + +.action { + display: inline-block; + position: absolute; + top: 0; + left: 0; + padding-left: 4px; +} + +.title { + font-size: 14px; + margin-bottom: 5px; + font-weight: bold; + cursor: pointer; +} + +.content { + display: inline-block; + padding: 0px 50px; + box-sizing: border-box; +} + + diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configuration.js b/client/coral-embed-stream/src/tabs/configure/components/Configuration.js new file mode 100644 index 000000000..025517be6 --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/components/Configuration.js @@ -0,0 +1,46 @@ +import React from 'react'; +import Checkbox from './Checkbox'; +import PropTypes from 'prop-types'; +import cn from 'classnames'; +import styles from './Configuration.css'; +import uuid from 'uuid/v4'; + +class Configuration extends React.Component { + + id = uuid(); + + render() { + const {title, children, className, onCheckbox, checked, ...rest} = this.props; + return ( +
+ {checked !== undefined && +
+ +
+ } +
+ +
+ {children} +
+
+
+ ); + } +} + +Configuration.propTypes = { + title: PropTypes.string.isRequired, + className: PropTypes.string, + onCheckbox: PropTypes.func, + checked: PropTypes.bool, + children: PropTypes.node, +}; + +export default Configuration; diff --git a/client/coral-embed-stream/src/tabs/configure/components/Settings.css b/client/coral-embed-stream/src/tabs/configure/components/Settings.css new file mode 100644 index 000000000..914b7332b --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/components/Settings.css @@ -0,0 +1,26 @@ +.container { + position: relative; +} + +.apply { + float: right; + margin: 0 10px; +} + +.description { + max-width: 380px; +} + +.checkbox { + vertical-align: top; + margin: 12px 12px 12px 0; +} + +.list { + margin-top: 26px; +} + +.wrapper { + margin-bottom: 20px; +} + diff --git a/client/coral-embed-stream/src/tabs/configure/components/Settings.js b/client/coral-embed-stream/src/tabs/configure/components/Settings.js new file mode 100644 index 000000000..2235369fe --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/components/Settings.js @@ -0,0 +1,46 @@ +import React from 'react'; +import {Button} from 'coral-ui'; +import PropTypes from 'prop-types'; +import t from 'coral-framework/services/i18n'; +import cn from 'classnames'; +import styles from './Settings.css'; +import Configuration from './Configuration'; + +class Settings extends React.Component { + render() { + const {settings: {moderation}, toggleModeration} = this.props; + const changed = false; + return ( +
+
+

{t('configure.title')}

+ +

{t('configure.description')}

+
+
+ + {t('configure.enable_premod_description')} + +
+
+ ); + } +} + +Settings.propTypes = { + settings: PropTypes.object.isRequired, + toggleModeration: PropTypes.func.isRequired, +}; + +export default Settings; diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js new file mode 100644 index 000000000..66625715d --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js @@ -0,0 +1,79 @@ +import React from 'react'; +import {gql, compose} from 'react-apollo'; +import {withFragments, withMergedSettings} from 'coral-framework/hocs'; +import {getErrorMessages} from 'coral-framework/utils'; +import Settings from '../components/Settings.js'; +import PropTypes from 'prop-types'; +import {withUpdateAssetSettings} from 'coral-framework/graphql/mutations'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {notify} from 'coral-framework/actions/notification'; +import {clearPending, updatePending} from '../../../actions/configure'; + +class SettingsContainer extends React.Component { + + toggleModeration = () => { + const updater = {moderation: {$set: this.props.mergedSettings.moderation === 'PRE' ? 'POST' : 'PRE'}}; + this.props.updatePending({updater}); + }; + + savePending = async () => { + try { + await this.props.updateAssetSettings(this.props.asset.id, this.props.pending); + this.props.clearPending(); + } + catch(err) { + this.props.notify('error', getErrorMessages(err)); + } + }; + + render() { + return ; + } +} + +SettingsContainer.propTypes = { + asset: PropTypes.object.isRequired, + pending: PropTypes.object.isRequired, + mergedSettings: PropTypes.object.isRequired, + updateAssetSettings: PropTypes.func.isRequired, + clearPending: PropTypes.func.isRequired, + notify: PropTypes.func.isRequired, + updatePending: PropTypes.func.isRequired, +}; + +const withSettingsFragments = withFragments({ + asset: gql` + fragment CoralEmbedStream_Settings_asset on Asset { + id + settings { + moderation + } + } + `, +}); + +const mapStateToProps = (state) => ({ + pending: state.configure.pending, + canSave: state.configure.canSave, +}); + +const mapDispatchToProps = (dispatch) => + bindActionCreators({ + notify, + clearPending, + updatePending, + }, dispatch); + +const enhance = compose( + withSettingsFragments, + withUpdateAssetSettings, + connect(mapStateToProps, mapDispatchToProps), + withMergedSettings('asset.settings', 'pending', 'mergedSettings'), +); + +export default enhance(SettingsContainer); diff --git a/client/coral-framework/hocs/index.js b/client/coral-framework/hocs/index.js index 67b47482e..b3c0219e3 100644 --- a/client/coral-framework/hocs/index.js +++ b/client/coral-framework/hocs/index.js @@ -5,3 +5,4 @@ export {default as withCopyToClipboard} from './withCopyToClipboard'; export {default as withEmit} from './withEmit'; export {default as excludeIf} from './excludeIf'; export {default as connect} from './connect'; +export {default as withMergedSettings} from './withMergedSettings'; diff --git a/client/coral-framework/hocs/withMergedSettings.js b/client/coral-framework/hocs/withMergedSettings.js new file mode 100644 index 000000000..8368475e6 --- /dev/null +++ b/client/coral-framework/hocs/withMergedSettings.js @@ -0,0 +1,16 @@ +import {mergeExcludingArrays} from 'coral-framework/utils'; +import assignWith from 'lodash/assignWith'; +import get from 'lodash/get'; +import {withPropsOnChange} from 'recompose'; + +const withMergedSettings = (settings, pending, result) => + withPropsOnChange( + (props, nextProps) => + get(props, settings) !== get(nextProps, settings) || + get(props, pending) !== get(nextProps, pending), + (props) => ({ + [result]: assignWith({}, get(props, settings), get(props, pending), mergeExcludingArrays) + }) + ); + +export default withMergedSettings; diff --git a/client/coral-framework/utils/index.js b/client/coral-framework/utils/index.js index 037c1b99f..9f71b0913 100644 --- a/client/coral-framework/utils/index.js +++ b/client/coral-framework/utils/index.js @@ -2,6 +2,7 @@ import {gql} from 'react-apollo'; import t from 'coral-framework/services/i18n'; import union from 'lodash/union'; import {capitalize} from 'coral-framework/helpers/strings'; +import assignWith from 'lodash/assignWith'; export * from 'coral-framework/helpers/strings'; export const getTotalActionCount = (type, comment) => { @@ -197,3 +198,12 @@ export function getTotalReactionsCount(actionSummaries) { .filter(({__typename}) => !NOT_REACTION_TYPES.includes(__typename)) .reduce((total, {count}) => total + count, 0); } + +// Like lodash merge but does not recurse into arrays. +export function mergeExcludingArrays(objValue, srcValue) { + if (typeof srcValue === 'object' && !Array.isArray(srcValue)) { + return assignWith({}, objValue, srcValue, mergeExcludingArrays); + } + return srcValue; +} + From 4c7c3c9e425a4bc23637303b0a1fbbb6c35c0ca1 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 20 Nov 2017 12:36:49 +0100 Subject: [PATCH 14/39] Add premodLinks configuration --- .../src/tabs/configure/components/Settings.js | 10 +++++++++- .../src/tabs/configure/containers/Settings.js | 7 +++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/client/coral-embed-stream/src/tabs/configure/components/Settings.js b/client/coral-embed-stream/src/tabs/configure/components/Settings.js index 2235369fe..6b6e14656 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Settings.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Settings.js @@ -8,7 +8,7 @@ import Configuration from './Configuration'; class Settings extends React.Component { render() { - const {settings: {moderation}, toggleModeration} = this.props; + const {settings: {moderation, premodLinksEnable}, toggleModeration, togglePremodLinks} = this.props; const changed = false; return (
@@ -32,6 +32,13 @@ class Settings extends React.Component { > {t('configure.enable_premod_description')} + + {t('configure.enable_premod_description')} +
); @@ -41,6 +48,7 @@ class Settings extends React.Component { Settings.propTypes = { settings: PropTypes.object.isRequired, toggleModeration: PropTypes.func.isRequired, + togglePremodLinks: PropTypes.func.isRequired, }; export default Settings; diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js index 66625715d..8e978e4c8 100644 --- a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js +++ b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js @@ -17,6 +17,11 @@ class SettingsContainer extends React.Component { this.props.updatePending({updater}); }; + togglePremodLinks = () => { + const updater = {premodLinksEnable: {$set: !this.props.mergedSettings.premodLinksEnable}}; + this.props.updatePending({updater}); + }; + savePending = async () => { try { await this.props.updateAssetSettings(this.props.asset.id, this.props.pending); @@ -32,6 +37,7 @@ class SettingsContainer extends React.Component { settings={this.props.mergedSettings} savePending={this.savePending} toggleModeration={this.toggleModeration} + togglePremodLinks={this.togglePremodLinks} />; } } @@ -52,6 +58,7 @@ const withSettingsFragments = withFragments({ id settings { moderation + premodLinksEnable } } `, From 8f0eef7cb5ecedc3eb1c9258adf250f10e96370a Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 20 Nov 2017 17:53:26 +0100 Subject: [PATCH 15/39] Reimplement QuestionBoxBuilder --- .../src/components/DefaultQuestionBoxIcon.css | 20 ++++ .../src/components/DefaultQuestionBoxIcon.js | 13 +++ .../src/components/QuestionBox.css | 66 +++++++++++++ .../src/components/QuestionBox.js | 28 ++++++ .../configure/components/Configuration.css | 10 +- .../configure/components/Configuration.js | 4 +- .../components/QuestionBoxBuilder.css | 60 ++++++++++++ .../components/QuestionBoxBuilder.js | 93 +++++++++++++++++++ .../tabs/configure/components/Settings.css | 3 + .../src/tabs/configure/components/Settings.js | 49 ++++++++-- .../src/tabs/configure/containers/Settings.js | 26 +++++- .../src/tabs/stream/components/Stream.js | 9 +- 12 files changed, 366 insertions(+), 15 deletions(-) create mode 100644 client/coral-embed-stream/src/components/DefaultQuestionBoxIcon.css create mode 100644 client/coral-embed-stream/src/components/DefaultQuestionBoxIcon.js create mode 100644 client/coral-embed-stream/src/components/QuestionBox.css create mode 100644 client/coral-embed-stream/src/components/QuestionBox.js create mode 100644 client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.css create mode 100644 client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.js diff --git a/client/coral-embed-stream/src/components/DefaultQuestionBoxIcon.css b/client/coral-embed-stream/src/components/DefaultQuestionBoxIcon.css new file mode 100644 index 000000000..414a48e7a --- /dev/null +++ b/client/coral-embed-stream/src/components/DefaultQuestionBoxIcon.css @@ -0,0 +1,20 @@ +.iconBubble{ + position: absolute; + top: 8px; + left: 10px; + color: #9E9E9E; + font-size: 24px; + z-index: 0; +} + +.iconPerson{ + z-index: 2; + top: 12px; + left: 12px; + position: absolute; + font-size: 33px; + color: #262626; +} + +.root { +} diff --git a/client/coral-embed-stream/src/components/DefaultQuestionBoxIcon.js b/client/coral-embed-stream/src/components/DefaultQuestionBoxIcon.js new file mode 100644 index 000000000..acb3f2e64 --- /dev/null +++ b/client/coral-embed-stream/src/components/DefaultQuestionBoxIcon.js @@ -0,0 +1,13 @@ +import React from 'react'; +import cn from 'classnames'; +import styles from './DefaultQuestionBoxIcon.css'; +import {Icon} from 'coral-ui'; + +const DefaultQuestionBoxIcon = ({className}) => ( +
+ + +
+); + +export default DefaultQuestionBoxIcon; diff --git a/client/coral-embed-stream/src/components/QuestionBox.css b/client/coral-embed-stream/src/components/QuestionBox.css new file mode 100644 index 000000000..a0889d775 --- /dev/null +++ b/client/coral-embed-stream/src/components/QuestionBox.css @@ -0,0 +1,66 @@ +.qbInfo { + top: 0; + border: 0; + background: #F0F0F0; + color: black; + width: 100%; + text-align: left; + padding-left: 0px; + margin-bottom: 0px; + font-weight: bold; + font-size: 14px; + overflow: hidden; + min-height: 50px; + display: flex; + border-radius: 3px; +} + +.icon { + z-index: 2; + top: 12px; + left: 12px; + position: absolute; + font-size: 33px; + color: #262626; +} + +.iconBubble{ + position: absolute; + top: 8px; + left: 10px; + color: #9E9E9E; + font-size: 24px; + z-index: 0; +} + +.iconPerson{ + z-index: 2; + top: 12px; + left: 12px; + position: absolute; + font-size: 33px; + color: #262626; +} + +.qbIconContainer { + position: relative; + border: 0; + color: white; + padding: 20px; + margin-left: 0px !important; + margin-right: 10px; + display: inline-block; + width: 10px; + min-height: 100%; + padding: 5px 20px; + vertical-align: middle; +} + +.qbContent { + padding: 5px; + display: flex; + align-items: center; + justify-content: center; + font-weight: 400; +} + diff --git a/client/coral-embed-stream/src/components/QuestionBox.js b/client/coral-embed-stream/src/components/QuestionBox.js new file mode 100644 index 000000000..1f34afcd7 --- /dev/null +++ b/client/coral-embed-stream/src/components/QuestionBox.js @@ -0,0 +1,28 @@ +import React from 'react'; +import cn from 'classnames'; +import styles from './QuestionBox.css'; +import {Icon} from 'coral-ui'; +import Markdown from 'coral-framework/components/Markdown'; +import DefaultQuestionBoxIcon from './DefaultQuestionBoxIcon'; + +const QuestionBox = ({content, icon, className, children}) => ( +
+ { + icon === 'default' ? ( +
+ +
+ ) : ( +
+ +
+ ) + } +
+ +
+ {children} +
+); + +export default QuestionBox; diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configuration.css b/client/coral-embed-stream/src/tabs/configure/components/Configuration.css index b18328a94..c6863fc6a 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Configuration.css +++ b/client/coral-embed-stream/src/tabs/configure/components/Configuration.css @@ -1,6 +1,6 @@ .root { position: relative; - margin: 12px 12px 12px 0; + margin: 12px 12px 24px 0; } .action { @@ -12,16 +12,22 @@ } .title { + display: block; font-size: 14px; margin-bottom: 5px; font-weight: bold; cursor: pointer; + padding-right: 50px; +} + +.description{ + padding-right: 50px; } .content { display: inline-block; - padding: 0px 50px; box-sizing: border-box; + padding-left: 50px; } diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configuration.js b/client/coral-embed-stream/src/tabs/configure/components/Configuration.js index 025517be6..0e382e886 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Configuration.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Configuration.js @@ -10,7 +10,7 @@ class Configuration extends React.Component { id = uuid(); render() { - const {title, children, className, onCheckbox, checked, ...rest} = this.props; + const {title, description, children, className, onCheckbox, checked, ...rest} = this.props; return (
{checked !== undefined && @@ -26,6 +26,7 @@ class Configuration extends React.Component { [styles.content]: checked !== undefined, })}> +
{description}
{children}
@@ -37,6 +38,7 @@ class Configuration extends React.Component { Configuration.propTypes = { title: PropTypes.string.isRequired, + description: PropTypes.string, className: PropTypes.string, onCheckbox: PropTypes.func, checked: PropTypes.bool, diff --git a/client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.css b/client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.css new file mode 100644 index 000000000..c3e0b896e --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.css @@ -0,0 +1,60 @@ +.root { +} + +.iconList { + display: flex; + padding: 0; + margin: 10px 0; +} + +.item { + list-style-type: none; +} + +.button { + composes: buttonReset from "coral-framework/styles/reset.css"; + + display: flex; + align-items: center; + justify-content: center; + text-align: center; + background: #F0F0F0; + width: 48px; + height: 48px; + font-size: 24px; + text-align: center; + line-height: 48px; + color: #252525; + border-radius: 3px; + overflow: hidden; + margin-right: 10px; + position: relative; + border: solid 2px #F0F0F0; + transition: border 0.3s cubic-bezier(.4,0,.2,1); + outline: 0; + + &:hover { + cursor: pointer; + } +} + +.button:focus { + border: solid 2px #00c96B; +} + +.buttonActive { + border: solid 2px #00796B; +} + +.defaultIcon { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} + +.questionBox { + margin: 10px 0; +} + diff --git a/client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.js b/client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.js new file mode 100644 index 000000000..0d44b4a3a --- /dev/null +++ b/client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.js @@ -0,0 +1,93 @@ +import React from 'react'; +import QuestionBox from '../../../components/QuestionBox'; +import {Icon, Spinner} from 'coral-ui'; +import DefaultQuestionBoxIcon from '../../../components/DefaultQuestionBoxIcon'; +import cn from 'classnames'; +import styles from './QuestionBoxBuilder.css'; + +const DefaultIcon = ; + +const icons = [ + {'default': DefaultIcon}, + 'forum', + 'build', + 'format_quote', +]; + +class QuestionBoxBuilder extends React.Component { + constructor() { + super(); + + this.state = { + loading: true + }; + } + + componentWillMount() { + this.loadEditor(); + } + + async loadEditor() { + const {default: MarkdownEditor} = await import('coral-framework/components/MarkdownEditor'); + + return this.setState({ + loading : false, + MarkdownEditor + }); + } + + render() { + const {questionBoxIcon, questionBoxContent, onContentChange, onIconChange} = this.props; + const {loading, MarkdownEditor} = this.state; + + if (loading) { + return ; + } + + return ( +
+

Include an Icon

+ +
    + {icons.map((item) => { + const name = typeof item === 'object' ? Object.keys(item)[0] : item; + const icon = typeof item === 'object' ? item[name] : item; + return ( +
  • + +
  • + ); + })} +
+ + + + + +
+ ); + } +} + +export default QuestionBoxBuilder; diff --git a/client/coral-embed-stream/src/tabs/configure/components/Settings.css b/client/coral-embed-stream/src/tabs/configure/components/Settings.css index 914b7332b..1a97c3fc1 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Settings.css +++ b/client/coral-embed-stream/src/tabs/configure/components/Settings.css @@ -24,3 +24,6 @@ margin-bottom: 20px; } +.questionBoxContainer { + margin-top: 24px; +} diff --git a/client/coral-embed-stream/src/tabs/configure/components/Settings.js b/client/coral-embed-stream/src/tabs/configure/components/Settings.js index 6b6e14656..c029940dc 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Settings.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Settings.js @@ -5,10 +5,24 @@ import t from 'coral-framework/services/i18n'; import cn from 'classnames'; import styles from './Settings.css'; import Configuration from './Configuration'; +import QuestionBoxBuilder from './QuestionBoxBuilder'; class Settings extends React.Component { render() { - const {settings: {moderation, premodLinksEnable}, toggleModeration, togglePremodLinks} = this.props; + const { + settings: { + moderation, + premodLinksEnable, + questionBoxEnable, + questionBoxContent, + questionBoxIcon, + }, + onToggleModeration, + onTogglePremodLinks, + onToggleQuestionBox, + onQuestionBoxIconChange, + onQuestionBoxContentChange, + } = this.props; const changed = false; return (
@@ -28,16 +42,32 @@ class Settings extends React.Component { - {t('configure.enable_premod_description')} - + description={t('configure.enable_premod_description')} + onCheckbox={onToggleModeration} + /> + - {t('configure.enable_premod_description')} + { + questionBoxEnable && +
+ +
+ }
@@ -47,8 +77,9 @@ class Settings extends React.Component { Settings.propTypes = { settings: PropTypes.object.isRequired, - toggleModeration: PropTypes.func.isRequired, - togglePremodLinks: PropTypes.func.isRequired, + onToggleModeration: PropTypes.func.isRequired, + onTogglePremodLinks: PropTypes.func.isRequired, + onToggleQuestionBox: PropTypes.func.isRequired, }; export default Settings; diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js index 8e978e4c8..4953ffef1 100644 --- a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js +++ b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js @@ -22,6 +22,21 @@ class SettingsContainer extends React.Component { this.props.updatePending({updater}); }; + toggleQuestionBox = () => { + const updater = {questionBoxEnable: {$set: !this.props.mergedSettings.questionBoxEnable}}; + this.props.updatePending({updater}); + }; + + setQuestionBoxIcon = (icon) => { + const updater = {questionBoxIcon: {$set: icon}}; + this.props.updatePending({updater}); + }; + + setQuestionBoxContent = (content) => { + const updater = {questionBoxContent: {$set: content}}; + this.props.updatePending({updater}); + }; + savePending = async () => { try { await this.props.updateAssetSettings(this.props.asset.id, this.props.pending); @@ -36,8 +51,12 @@ class SettingsContainer extends React.Component { return ; } } @@ -59,6 +78,9 @@ const withSettingsFragments = withFragments({ settings { moderation premodLinksEnable + questionBoxEnable + questionBoxIcon + questionBoxContent } } `, diff --git a/client/coral-embed-stream/src/tabs/stream/components/Stream.js b/client/coral-embed-stream/src/tabs/stream/components/Stream.js index 99df8c1c9..0b58d250d 100644 --- a/client/coral-embed-stream/src/tabs/stream/components/Stream.js +++ b/client/coral-embed-stream/src/tabs/stream/components/Stream.js @@ -252,7 +252,14 @@ class Stream extends React.Component { content={asset.settings.questionBoxContent} enable={asset.settings.questionBoxEnable} icon={asset.settings.questionBoxIcon} - /> + > + + + {!banned && temporarilySuspended && From 67f3f8cdefc21844c190decb78c815c293742f77 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 20 Nov 2017 18:14:17 +0100 Subject: [PATCH 16/39] SimpleMDE don't trap keyboard --- .../src/tabs/configure/components/QuestionBoxBuilder.css | 1 - client/coral-framework/components/MarkdownEditor.js | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.css b/client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.css index c3e0b896e..8dc6091fa 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.css +++ b/client/coral-embed-stream/src/tabs/configure/components/QuestionBoxBuilder.css @@ -30,7 +30,6 @@ margin-right: 10px; position: relative; border: solid 2px #F0F0F0; - transition: border 0.3s cubic-bezier(.4,0,.2,1); outline: 0; &:hover { diff --git a/client/coral-framework/components/MarkdownEditor.js b/client/coral-framework/components/MarkdownEditor.js index 65f603b0c..451a06019 100644 --- a/client/coral-framework/components/MarkdownEditor.js +++ b/client/coral-framework/components/MarkdownEditor.js @@ -107,6 +107,11 @@ export default class MarkdownEditor extends Component { ...config, element: this.textarea, }); + + // Don't trap the key, to stay accessible. + this.editor.codemirror.options.extraKeys['Tab'] = false; + this.editor.codemirror.options.extraKeys['Shift-Tab'] = false; + this.editor.codemirror.on('change', this.onChange); } From 873d1915c3b6c3e90781208ff164ae9ed3b99407 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 20 Nov 2017 19:20:09 +0100 Subject: [PATCH 17/39] Implement apply --- .../src/tabs/configure/components/Settings.js | 12 +++++++++--- .../src/tabs/configure/containers/Settings.js | 4 +++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/client/coral-embed-stream/src/tabs/configure/components/Settings.js b/client/coral-embed-stream/src/tabs/configure/components/Settings.js index c029940dc..99a29a527 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Settings.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Settings.js @@ -22,8 +22,9 @@ class Settings extends React.Component { onToggleQuestionBox, onQuestionBoxIconChange, onQuestionBoxContentChange, + canSave, + onApply, } = this.props; - const changed = false; return (
@@ -31,8 +32,9 @@ class Settings extends React.Component { @@ -77,9 +79,13 @@ class Settings extends React.Component { Settings.propTypes = { settings: PropTypes.object.isRequired, + canSave: PropTypes.bool.isRequired, onToggleModeration: PropTypes.func.isRequired, onTogglePremodLinks: PropTypes.func.isRequired, onToggleQuestionBox: PropTypes.func.isRequired, + onQuestionBoxContentChange: PropTypes.func.isRequired, + onQuestionBoxIconChange: PropTypes.func.isRequired, + onApply: PropTypes.func.isRequired, }; export default Settings; diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js index 4953ffef1..7d2a2a80d 100644 --- a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js +++ b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js @@ -56,7 +56,8 @@ class SettingsContainer extends React.Component { onToggleQuestionBox={this.toggleQuestionBox} onQuestionBoxIconChange={this.setQuestionBoxIcon} onQuestionBoxContentChange={this.setQuestionBoxContent} - + onApply={this.savePending} + canSave={this.props.canSave} />; } } @@ -69,6 +70,7 @@ SettingsContainer.propTypes = { clearPending: PropTypes.func.isRequired, notify: PropTypes.func.isRequired, updatePending: PropTypes.func.isRequired, + canSave: PropTypes.bool.isRequired, }; const withSettingsFragments = withFragments({ From 2d6143af625ed4027332287613d83eaccc07c593 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 20 Nov 2017 19:48:12 +0100 Subject: [PATCH 18/39] Fully implement mutation --- client/coral-admin/src/graphql/index.js | 12 +------- .../coral-embed-stream/src/graphql/index.js | 13 ++++++++ client/coral-framework/graphql/mutations.js | 6 ++++ client/coral-framework/utils/index.js | 12 +++++++- services/assets.js | 6 ++-- services/settings.js | 27 +---------------- services/utils.js | 30 +++++++++++++++++++ 7 files changed, 65 insertions(+), 41 deletions(-) create mode 100644 services/utils.js diff --git a/client/coral-admin/src/graphql/index.js b/client/coral-admin/src/graphql/index.js index d7656d9a5..42e96f382 100644 --- a/client/coral-admin/src/graphql/index.js +++ b/client/coral-admin/src/graphql/index.js @@ -1,15 +1,5 @@ import update from 'immutability-helper'; -import mapValues from 'lodash/mapValues'; - -// Map nested object leaves. Array objects are considered leaves. -function mapLeaves(o, mapper) { - return mapValues(o, (val) => { - if (typeof val === 'object' && !Array.isArray(val)) { - return mapLeaves(val, mapper); - } - return mapper(val); - }); -} +import {mapLeaves} from 'coral-framework/utils'; export default { mutations: { diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js index d18cdc435..d77e6356a 100644 --- a/client/coral-embed-stream/src/graphql/index.js +++ b/client/coral-embed-stream/src/graphql/index.js @@ -2,6 +2,7 @@ import {gql} from 'react-apollo'; import update from 'immutability-helper'; import uuid from 'uuid/v4'; import {insertCommentIntoEmbedQuery, removeCommentFromEmbedQuery} from './utils'; +import {mapLeaves} from 'coral-framework/utils'; export default { fragments: { @@ -218,6 +219,18 @@ export default { }, }, }), + UpdateAssetSettings: ({variables: {input}}) => ({ + updateQueries: { + CoralEmbedStream_Embed: (prev) => { + const updated = update(prev, { + asset: { + settings: mapLeaves(input, (leaf) => ({$set: leaf})), + }, + }); + return updated; + } + } + }), }, }; diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js index ed4a6f782..2aa526a54 100644 --- a/client/coral-framework/graphql/mutations.js +++ b/client/coral-framework/graphql/mutations.js @@ -379,6 +379,12 @@ export const withUpdateAssetSettings = withMutation( id, input, }, + optimisticResponse: { + updateAssetStatus: { + __typename: 'UpdateAssetSettingsResponse', + errors: null, + } + }, }); }}), }); diff --git a/client/coral-framework/utils/index.js b/client/coral-framework/utils/index.js index 9f71b0913..c7225f492 100644 --- a/client/coral-framework/utils/index.js +++ b/client/coral-framework/utils/index.js @@ -3,7 +3,8 @@ import t from 'coral-framework/services/i18n'; import union from 'lodash/union'; import {capitalize} from 'coral-framework/helpers/strings'; import assignWith from 'lodash/assignWith'; -export * from 'coral-framework/helpers/strings'; +import mapValues from 'lodash/mapValues'; +export * from 'coral-framework/helpers/strings' export const getTotalActionCount = (type, comment) => { return comment.action_summaries @@ -207,3 +208,12 @@ export function mergeExcludingArrays(objValue, srcValue) { return srcValue; } +// Map nested object leaves. Array objects are considered leaves. +export function mapLeaves(o, mapper) { + return mapValues(o, (val) => { + if (typeof val === 'object' && !Array.isArray(val)) { + return mapLeaves(val, mapper); + } + return mapper(val); + }); +} diff --git a/services/assets.js b/services/assets.js index 4aac2addd..3ab7ec9ff 100644 --- a/services/assets.js +++ b/services/assets.js @@ -4,6 +4,7 @@ const SettingsService = require('./settings'); const domainlist = require('./domainlist'); const errors = require('../errors'); const merge = require('lodash/merge'); +const {dotize} = require('./utils'); module.exports = class AssetsService { @@ -99,10 +100,9 @@ module.exports = class AssetsService { * @return {[type]} [description] */ static overrideSettings(id, settings) { + console.log(settings, dotize({settings})); return AssetModel.findOneAndUpdate({id}, { - $set: { - settings - } + $set: dotize({settings}) }, { new: true }); diff --git a/services/settings.js b/services/settings.js index b11b86d91..310884002 100644 --- a/services/settings.js +++ b/services/settings.js @@ -1,31 +1,6 @@ const SettingModel = require('../models/setting'); const errors = require('../errors'); - -function dotizeRecurse(result, object, path = '') { - for (const key in object) { - const newPath = path ? `${path}.${key}` : key; - if (typeof object[key] === 'object' && !Array.isArray(object[key])) { - dotizeRecurse(result, object[key], newPath); - continue; - } - result[newPath] = object[key]; - } -} - -/** - * Dotize turns a nested object into flattened object with - * dotized key notation. Arrays do not become dotized. - * - * e.g. {a: {b: 'c'}} becomes {'a.b': 'c} - * - * @param {Object} object - * @return {Object} dotized object - */ -function dotize(object) { - const result = {}; - dotizeRecurse(result, object); - return result; -} +const {dotize} = require('./utils'); /** * The selector used to uniquely identify the settings document. diff --git a/services/utils.js b/services/utils.js new file mode 100644 index 000000000..0247e2889 --- /dev/null +++ b/services/utils.js @@ -0,0 +1,30 @@ + +function dotizeRecurse(result, object, path = '') { + for (const key in object) { + const newPath = path ? `${path}.${key}` : key; + if (typeof object[key] === 'object' && !Array.isArray(object[key])) { + dotizeRecurse(result, object[key], newPath); + continue; + } + result[newPath] = object[key]; + } +} + +/** + * Dotize turns a nested object into flattened object with + * dotized key notation. Arrays do not become dotized. + * + * e.g. {a: {b: 'c'}} becomes {'a.b': 'c} + * + * @param {Object} object + * @return {Object} dotized object + */ +function dotize(object) { + const result = {}; + dotizeRecurse(result, object); + return result; +} + +module.exports = { + dotize, +}; From e675d716cb93991b8d040d28bd68346ec6ef851d Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 20 Nov 2017 20:18:14 +0100 Subject: [PATCH 19/39] Remove deprecated code --- .../components/CloseCommentsInfo.js | 29 ---- .../components/ConfigureCommentStream.css | 35 ----- .../components/ConfigureCommentStream.js | 71 --------- .../components/DefaultIcon.css | 27 ---- .../coral-configure/components/DefaultIcon.js | 13 -- client/coral-configure/components/Markdown.js | 24 --- .../components/QuestionBoxBuilder.css | 45 ------ .../components/QuestionBoxBuilder.js | 98 ------------ .../containers/ConfigureStreamContainer.js | 145 ------------------ .../src/components/Embed.js | 15 +- .../src/tabs/stream/components/Stream.js | 2 +- client/coral-framework/graphql/mutations.js | 2 +- client/coral-framework/utils/index.js | 2 +- .../talk-plugin-questionbox/QuestionBox.css | 70 --------- client/talk-plugin-questionbox/QuestionBox.js | 31 ---- 15 files changed, 4 insertions(+), 605 deletions(-) delete mode 100644 client/coral-configure/components/CloseCommentsInfo.js delete mode 100644 client/coral-configure/components/ConfigureCommentStream.css delete mode 100644 client/coral-configure/components/ConfigureCommentStream.js delete mode 100644 client/coral-configure/components/DefaultIcon.css delete mode 100644 client/coral-configure/components/DefaultIcon.js delete mode 100644 client/coral-configure/components/Markdown.js delete mode 100644 client/coral-configure/components/QuestionBoxBuilder.css delete mode 100644 client/coral-configure/components/QuestionBoxBuilder.js delete mode 100644 client/coral-configure/containers/ConfigureStreamContainer.js delete mode 100644 client/talk-plugin-questionbox/QuestionBox.css delete mode 100644 client/talk-plugin-questionbox/QuestionBox.js diff --git a/client/coral-configure/components/CloseCommentsInfo.js b/client/coral-configure/components/CloseCommentsInfo.js deleted file mode 100644 index d10419f3f..000000000 --- a/client/coral-configure/components/CloseCommentsInfo.js +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import {Button} from 'coral-ui'; -import PropTypes from 'prop-types'; -import t from 'coral-framework/services/i18n'; - -const CloseCommentsInfo = ({status, onClick}) => ( - status === 'open' ? ( -
-

- {t('configure.open_stream_configuration')} -

- -
- ) : ( -
-

- {t('configure.close_stream_configuration')} -

- -
- ) -); - -CloseCommentsInfo.propTypes = { - status: PropTypes.string, - onClick: PropTypes.func, -}; - -export default CloseCommentsInfo; \ No newline at end of file diff --git a/client/coral-configure/components/ConfigureCommentStream.css b/client/coral-configure/components/ConfigureCommentStream.css deleted file mode 100644 index 5575e073b..000000000 --- a/client/coral-configure/components/ConfigureCommentStream.css +++ /dev/null @@ -1,35 +0,0 @@ -.container { - position: relative; -} - -.apply { - float: right; - margin: 0 10px; -} - -.wrapper ul { - list-style: none; - padding: 0; -} - -.checkbox { - vertical-align: top; - margin: 12px 12px 12px 0; -} - -.wrapper h4 { - font-size: 14px; - margin-bottom: 5px; -} - -.wrapper p { - max-width: 380px; -} - -.wrapper { - margin-bottom: 20px; -} - -.hidden { - display: none; -} \ No newline at end of file diff --git a/client/coral-configure/components/ConfigureCommentStream.js b/client/coral-configure/components/ConfigureCommentStream.js deleted file mode 100644 index 20250ee86..000000000 --- a/client/coral-configure/components/ConfigureCommentStream.js +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import {Button, Checkbox} from 'coral-ui'; -import QuestionBoxBuilder from './QuestionBoxBuilder'; -import cn from 'classnames'; - -import styles from './ConfigureCommentStream.css'; - -import t from 'coral-framework/services/i18n'; - -export default ({handleChange, handleApply, changed, ...props}) => ( -
-
-
-

{t('configure.title')}

- -

{t('configure.description')}

-
-
    -
  • - -
  • -
  • - -
  • -
  • - - { - props.questionBoxEnable && - } -
  • -
-
-
-); diff --git a/client/coral-configure/components/DefaultIcon.css b/client/coral-configure/components/DefaultIcon.css deleted file mode 100644 index 6125d2f6a..000000000 --- a/client/coral-configure/components/DefaultIcon.css +++ /dev/null @@ -1,27 +0,0 @@ -.iconBubble{ - position: absolute; - top: 8px; - left: 10px; - color: #9E9E9E; - font-size: 24px; - z-index: 0; -} - -.iconPerson{ - z-index: 2; - top: 12px; - left: 12px; - position: absolute; - font-size: 33px; - color: #262626; -} - -.qbIconContainer { - position: relative; - border: 0; - color: white; - display: inline-block; - padding: 5px 20px; - vertical-align: middle; - width: 10px; -} \ No newline at end of file diff --git a/client/coral-configure/components/DefaultIcon.js b/client/coral-configure/components/DefaultIcon.js deleted file mode 100644 index 5f6f4876c..000000000 --- a/client/coral-configure/components/DefaultIcon.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import cn from 'classnames'; -import styles from './DefaultIcon.css'; -import {Icon} from 'coral-ui'; - -const DefaultIcon = ({className}) => ( -
- - -
-); - -export default DefaultIcon; diff --git a/client/coral-configure/components/Markdown.js b/client/coral-configure/components/Markdown.js deleted file mode 100644 index d9d1bc4a7..000000000 --- a/client/coral-configure/components/Markdown.js +++ /dev/null @@ -1,24 +0,0 @@ -import React, {PureComponent} from 'react'; -import PropTypes from 'prop-types'; -import marked from 'marked'; - -const renderer = new marked.Renderer(); - -// Set link target to `_parent` to work properly in an embed. -renderer.link = (href, title, text) => - `${text}`; - -marked.setOptions({renderer}); - -export default class Markdown extends PureComponent { - render() { - const {content, ...rest} = this.props; - const __html = marked(content); - return
; - } -} - -Markdown.propTypes = { - content: PropTypes.string, -}; - diff --git a/client/coral-configure/components/QuestionBoxBuilder.css b/client/coral-configure/components/QuestionBoxBuilder.css deleted file mode 100644 index 758f350b9..000000000 --- a/client/coral-configure/components/QuestionBoxBuilder.css +++ /dev/null @@ -1,45 +0,0 @@ -.qbBuilder { - margin-left: 50px; -} - -.qbItemIconList { - padding: 0; - margin: 10px 0; -} - -.qbItemIcon { - background: #F0F0F0; - width: 45px; - height: 45px; - font-size: 24px; - text-align: center; - line-height: 45px; - color: #252525; - border-radius: 3px; - display: inline-block; - overflow: hidden; - margin-right: 10px; - position: relative; - border: solid 2px #F0F0F0; - transition: border 0.3s cubic-bezier(.4,0,.2,1); -} - -.qbItemIcon:hover { - cursor: pointer; -} - -.qbItemIconActive { - border: solid 2px #00796B; -} - -.defaultIcon { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} - -.qb { - margin: 10px 0; -} \ No newline at end of file diff --git a/client/coral-configure/components/QuestionBoxBuilder.js b/client/coral-configure/components/QuestionBoxBuilder.js deleted file mode 100644 index 28dfc30b3..000000000 --- a/client/coral-configure/components/QuestionBoxBuilder.js +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; -import QuestionBox from 'talk-plugin-questionbox/QuestionBox'; -import {Icon, Spinner} from 'coral-ui'; -import DefaultIcon from './DefaultIcon'; -import cn from 'classnames'; -import styles from './QuestionBoxBuilder.css'; - -class QuestionBoxBuilder extends React.Component { - constructor() { - super(); - - this.state = { - loading: true - }; - } - - componentWillMount() { - this.loadEditor(); - } - - async loadEditor() { - const {default: MarkdownEditor} = await import('coral-framework/components/MarkdownEditor'); - - return this.setState({ - loading : false, - MarkdownEditor - }); - } - - render() { - const {handleChange, questionBoxIcon, questionBoxContent} = this.props; - const {loading, MarkdownEditor} = this.state; - - if (loading) { - return ; - } - - return ( -
-

Include an Icon

- -
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
- - - - handleChange({}, {questionBoxContent: value})} - /> - -
- ); - } -} - -export default QuestionBoxBuilder; diff --git a/client/coral-configure/containers/ConfigureStreamContainer.js b/client/coral-configure/containers/ConfigureStreamContainer.js deleted file mode 100644 index 0ab71c1d7..000000000 --- a/client/coral-configure/containers/ConfigureStreamContainer.js +++ /dev/null @@ -1,145 +0,0 @@ -import React, {Component} from 'react'; -import {connect} from 'react-redux'; -import {compose} from 'react-apollo'; -import PropTypes from 'prop-types'; -import {updateOpenStatus, updateConfiguration} from 'coral-embed-stream/src/actions/asset'; -import CloseCommentsInfo from '../components/CloseCommentsInfo'; -import ConfigureCommentStream from '../components/ConfigureCommentStream'; -import t, {timeago} from 'coral-framework/services/i18n'; - -class ConfigureStreamContainer extends Component { - constructor (props) { - super(props); - - this.state = { - changed: false, - dirtySettings: {...props.asset.settings}, - closedAt: !props.asset.isClosed ? 'open' : 'closed' - }; - - this.toggleStatus = this.toggleStatus.bind(this); - this.handleChange = this.handleChange.bind(this); - this.handleApply = this.handleApply.bind(this); - } - - handleApply (e) { - e.preventDefault(); - const {elements} = e.target; - const {questionBoxIcon, questionBoxContent} = this.state.dirtySettings; - const premod = elements.premod.checked; - const questionBoxEnable = elements.qboxenable.checked; - const premodLinksEnable = elements.plinksenable.checked; - const {changed} = this.state; - - const newConfig = { - moderation: premod ? 'PRE' : 'POST', - questionBoxEnable, - questionBoxContent, - questionBoxIcon, - premodLinksEnable - }; - - if (changed) { - this.props.updateConfiguration(newConfig); - setTimeout(() => { - this.setState({ - changed: false - }); - }, 300); - } - } - - handleChange (e, newChanges) { - let changes = {}; - - if (changes) { - changes = {...newChanges}; - } - - if (e.target && e.target.id === 'qboxenable') { - changes.questionBoxEnable = e.target.checked; - } - if (e.currentTarget && e.currentTarget.id === 'qboxicon') { - changes.questionBoxIcon = e.currentTarget.dataset.icon; - } - if (e.target && e.target.id === 'plinksenable') { - changes.premodLinksEnable = e.target.value; - } - - this.setState({ - changed: true, - dirtySettings: { - ...this.state.dirtySettings, - ...changes, - }, - }); - } - - toggleStatus () { - - // update the closedAt status for the asset - this.props.updateStatus( - this.state.closedAt === 'open' ? 'closed' : 'open' - ); - this.setState({ - closedAt: (this.state.closedAt === 'open' ? 'closed' : 'open') - }); - } - - getClosedIn () { - const {closedTimeout} = this.props.asset.settings; - const {created_at} = this.props.asset; - - return timeago(new Date(created_at).getTime() + (1000 * closedTimeout)); - } - - render () { - const {dirtySettings} = this.state; - const premod = dirtySettings.moderation === 'PRE'; - const {closedAt} = this.state; - const closedTimeout = dirtySettings.closedTimeout; - - return ( -
- -
-

{closedAt === 'open' ? t('configure.close') : t('configure.open')} {t('configure.comment_stream')}

- {(closedAt === 'open' && closedTimeout) ?

{t('configure.comment_stream_will_close')} {this.getClosedIn()}.

: ''} - -
- ); - } -} - -const mapStateToProps = (state) => ({ - asset: state.asset -}); - -const mapDispatchToProps = (dispatch) => ({ - updateStatus: (status) => dispatch(updateOpenStatus(status)), - updateConfiguration: (newConfig) => dispatch(updateConfiguration(newConfig)), -}); - -ConfigureStreamContainer.propTypes = { - updateStatus: PropTypes.func, - closedTimeout: PropTypes.string, - created_at: PropTypes.string, - updateConfiguration: PropTypes.func, - asset: PropTypes.object, -}; - -export default compose( - connect(mapStateToProps, mapDispatchToProps) -)(ConfigureStreamContainer); diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index bbdaa2f19..7445bf300 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -12,8 +12,6 @@ import {Tab, TabPane} from 'coral-ui'; import ProfileContainer from 'coral-settings/containers/ProfileContainer'; import Popup from 'coral-framework/components/Popup'; import IfSlotIsNotEmpty from 'coral-framework/components/IfSlotIsNotEmpty'; -import ConfigureStreamContainer - from 'coral-configure/containers/ConfigureStreamContainer'; import cn from 'classnames'; export default class Embed extends React.Component { @@ -24,9 +22,6 @@ export default class Embed extends React.Component { case 'profile': this.props.data.refetch(); break; - case 'config': - this.props.data.refetch(); - break; } this.props.setActiveTab(tab); }; @@ -43,12 +38,7 @@ export default class Embed extends React.Component { ]; if (can(user, 'UPDATE_CONFIG')) { tabs.push( - - {t('framework.configure_stream')} - - ); - tabs.push( - + {t('framework.configure_stream')} ); @@ -101,9 +91,6 @@ export default class Embed extends React.Component { , - - , - , ]} diff --git a/client/coral-embed-stream/src/tabs/stream/components/Stream.js b/client/coral-embed-stream/src/tabs/stream/components/Stream.js index 0b58d250d..07774f348 100644 --- a/client/coral-embed-stream/src/tabs/stream/components/Stream.js +++ b/client/coral-embed-stream/src/tabs/stream/components/Stream.js @@ -11,7 +11,7 @@ import RestrictedMessageBox from 'coral-framework/components/RestrictedMessageBox'; import t, {timeago} from 'coral-framework/services/i18n'; import CommentBox from 'talk-plugin-commentbox/CommentBox'; -import QuestionBox from 'talk-plugin-questionbox/QuestionBox'; +import QuestionBox from '../../../components/QuestionBox'; import {isCommentActive} from 'coral-framework/utils'; import {Button, Tab, TabCount, TabPane} from 'coral-ui'; import cn from 'classnames'; diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js index 2aa526a54..15d24a9ce 100644 --- a/client/coral-framework/graphql/mutations.js +++ b/client/coral-framework/graphql/mutations.js @@ -380,7 +380,7 @@ export const withUpdateAssetSettings = withMutation( input, }, optimisticResponse: { - updateAssetStatus: { + updateAssetSettings: { __typename: 'UpdateAssetSettingsResponse', errors: null, } diff --git a/client/coral-framework/utils/index.js b/client/coral-framework/utils/index.js index c7225f492..86e9e1915 100644 --- a/client/coral-framework/utils/index.js +++ b/client/coral-framework/utils/index.js @@ -4,7 +4,7 @@ import union from 'lodash/union'; import {capitalize} from 'coral-framework/helpers/strings'; import assignWith from 'lodash/assignWith'; import mapValues from 'lodash/mapValues'; -export * from 'coral-framework/helpers/strings' +export * from 'coral-framework/helpers/strings'; export const getTotalActionCount = (type, comment) => { return comment.action_summaries diff --git a/client/talk-plugin-questionbox/QuestionBox.css b/client/talk-plugin-questionbox/QuestionBox.css deleted file mode 100644 index cb9902ade..000000000 --- a/client/talk-plugin-questionbox/QuestionBox.css +++ /dev/null @@ -1,70 +0,0 @@ -.qbInfo { - top: 0; - border: 0; - background: #F0F0F0; - color: black; - width: 100%; - text-align: left; - padding-left: 0px; - margin-bottom: 0px; - font-weight: bold; - font-size: 14px; - overflow: hidden; - min-height: 50px; - display: flex; - border-radius: 3px; -} - -.icon { - z-index: 2; - top: 12px; - left: 12px; - position: absolute; - font-size: 33px; - color: #262626; -} - -.iconBubble{ - position: absolute; - top: 8px; - left: 10px; - color: #9E9E9E; - font-size: 24px; - z-index: 0; -} - -.iconPerson{ - z-index: 2; - top: 12px; - left: 12px; - position: absolute; - font-size: 33px; - color: #262626; -} - -.qbIconContainer { - position: relative; - border: 0; - color: white; - padding: 20px; - margin-left: 0px !important; - margin-right: 10px; - display: inline-block; - width: 10px; - min-height: 100%; - padding: 5px 20px; - vertical-align: middle; -} - -.qbContent { - padding: 5px; - display: flex; - align-items: center; - justify-content: center; - font-weight: 400; -} - -.hidden { - visibility: hidden; - display: none; -} \ No newline at end of file diff --git a/client/talk-plugin-questionbox/QuestionBox.js b/client/talk-plugin-questionbox/QuestionBox.js deleted file mode 100644 index f15549b7c..000000000 --- a/client/talk-plugin-questionbox/QuestionBox.js +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import cn from 'classnames'; -import styles from './QuestionBox.css'; -import {Icon} from 'coral-ui'; -import Markdown from 'coral-framework/components/Markdown'; - -import Slot from 'coral-framework/components/Slot'; - -const QuestionBox = ({content, enable, icon = '', className = ''}) => ( -
- { - icon === 'default' ? ( -
- - -
- ) : ( -
- -
- ) - } -
- -
- - -
-); - -export default QuestionBox; From 511e71f63a2a7c3c5f67f8d95ee1de5fe0edc715 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 20 Nov 2017 20:19:58 +0100 Subject: [PATCH 20/39] Change default settings=null to settings={} --- models/asset.js | 2 +- services/assets.js | 44 +++++++++++++++++++++++++--------- test/server/services/assets.js | 2 +- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/models/asset.js b/models/asset.js index b2e49dcc0..4a6aea7e0 100644 --- a/models/asset.js +++ b/models/asset.js @@ -45,7 +45,7 @@ const AssetSchema = new Schema({ // always after running `rectifySettings` against it. settings: { type: Schema.Types.Mixed, - default: null + default: {}, }, // Tags are added by the self or by administrators. diff --git a/services/assets.js b/services/assets.js index 3ab7ec9ff..e2ba0fe04 100644 --- a/services/assets.js +++ b/services/assets.js @@ -4,6 +4,7 @@ const SettingsService = require('./settings'); const domainlist = require('./domainlist'); const errors = require('../errors'); const merge = require('lodash/merge'); +const isEmpty = require('lodash/isEmpty'); const {dotize} = require('./utils'); module.exports = class AssetsService { @@ -40,7 +41,7 @@ module.exports = class AssetsService { ]); // If the asset exists and has settings then return the merged object. - if (asset && asset.settings) { + if (asset && asset.settings && !isEmpty(asset.settings)) { settings = merge({}, globalSettings, asset.settings); } else { settings = globalSettings; @@ -95,17 +96,38 @@ module.exports = class AssetsService { /** * Updates the settings for the asset. - * @param {[type]} id [description] - * @param {[type]} settings [description] - * @return {[type]} [description] + * @param {String} id id of asset + * @param {Object} settings new settings values + * @return {Promise} */ - static overrideSettings(id, settings) { - console.log(settings, dotize({settings})); - return AssetModel.findOneAndUpdate({id}, { - $set: dotize({settings}) - }, { - new: true - }); + static async overrideSettings(id, settings) { + try { + const result = await AssetModel.findOneAndUpdate({id}, { + + // The effect of dotize is that only the provided setting values are overwritten + // and does not replace the whole object. + $set: dotize({settings}) + }, { + new: true + }); + return result; + } catch (e) { + + // Legacy data models contains `settings=null` as a default which cannot be traversed. + // New data models uses `settings={}`. + if (e.code === 16837) { + + // Overwrite it fully in this case. + const result = await AssetModel.findOneAndUpdate({id}, { + $set: {settings} + }, { + new: true + }); + return result; + } else { + throw e; + } + } } /** diff --git a/test/server/services/assets.js b/test/server/services/assets.js index 6ab33321b..163210b65 100644 --- a/test/server/services/assets.js +++ b/test/server/services/assets.js @@ -91,7 +91,7 @@ describe('services.AssetsService', () => { .findOrCreateByUrl('https://override.test.com/asset') .then((asset) => { expect(asset).to.have.property('settings'); - expect(asset.settings).to.be.null; + expect(asset.settings).to.be.empty; return AssetsService.overrideSettings(asset.id, {moderation: 'PRE'}); }) From 4830aec138fd9323c023e7a2b5f8643cd550150f Mon Sep 17 00:00:00 2001 From: Kim Gardner Date: Mon, 20 Nov 2017 19:59:57 +0000 Subject: [PATCH 21/39] Update Report Bug link in Admin Settings --- client/coral-admin/src/components/ui/Header.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-admin/src/components/ui/Header.js b/client/coral-admin/src/components/ui/Header.js index bd7fbcf0c..e199d5ded 100644 --- a/client/coral-admin/src/components/ui/Header.js +++ b/client/coral-admin/src/components/ui/Header.js @@ -87,7 +87,7 @@ const CoralHeader = ({ - + Report a bug or give feedback From 0de5a328ab0909406e444f38cb43df787557d62e Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Mon, 20 Nov 2017 21:11:59 +0100 Subject: [PATCH 22/39] Slot streamSettings --- .../tabs/configure/components/Configure.js | 2 ++ .../src/tabs/configure/components/Settings.js | 11 +++++++++ .../tabs/configure/containers/Configure.js | 2 ++ .../src/tabs/configure/containers/Settings.js | 23 ++++++++++++++++--- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configure.js b/client/coral-embed-stream/src/tabs/configure/components/Configure.js index 074fe0616..a7000864e 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Configure.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Configure.js @@ -8,6 +8,8 @@ class Configure extends React.Component { return (

diff --git a/client/coral-embed-stream/src/tabs/configure/components/Settings.js b/client/coral-embed-stream/src/tabs/configure/components/Settings.js index 99a29a527..6c9a3498e 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Settings.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Settings.js @@ -6,6 +6,7 @@ import cn from 'classnames'; import styles from './Settings.css'; import Configuration from './Configuration'; import QuestionBoxBuilder from './QuestionBoxBuilder'; +import Slot from 'coral-framework/components/Slot'; class Settings extends React.Component { render() { @@ -24,6 +25,8 @@ class Settings extends React.Component { onQuestionBoxContentChange, canSave, onApply, + slotProps, + queryData, } = this.props; return (
@@ -35,6 +38,7 @@ class Settings extends React.Component { checked={canSave} cStyle={canSave ? 'green' : 'darkGrey'} onClick={onApply} + disabled={!canSave} > {t('configure.apply')} @@ -71,6 +75,11 @@ class Settings extends React.Component {
} +
); @@ -78,6 +87,8 @@ class Settings extends React.Component { } Settings.propTypes = { + queryData: PropTypes.object.isRequired, + slotProps: PropTypes.object.isRequired, settings: PropTypes.object.isRequired, canSave: PropTypes.bool.isRequired, onToggleModeration: PropTypes.func.isRequired, diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js index 3e93e6f64..e9da3fc79 100644 --- a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js +++ b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js @@ -28,6 +28,7 @@ const withConfigureFragments = withFragments({ root: gql` fragment CoralEmbedStream_Configure_root on RootQuery { __typename + ...${getDefinitionName(Settings.fragments.root)} } `, asset: gql` @@ -38,6 +39,7 @@ const withConfigureFragments = withFragments({ } ${AssetStatusInfo.fragments.asset} ${Settings.fragments.asset} + ${Settings.fragments.root} `, }); diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js index 7d2a2a80d..41d9c21b2 100644 --- a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js +++ b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js @@ -1,7 +1,7 @@ import React from 'react'; import {gql, compose} from 'react-apollo'; import {withFragments, withMergedSettings} from 'coral-framework/hocs'; -import {getErrorMessages} from 'coral-framework/utils'; +import {getErrorMessages, getSlotFragmentSpreads} from 'coral-framework/utils'; import Settings from '../components/Settings.js'; import PropTypes from 'prop-types'; import {withUpdateAssetSettings} from 'coral-framework/graphql/mutations'; @@ -10,6 +10,10 @@ import {bindActionCreators} from 'redux'; import {notify} from 'coral-framework/actions/notification'; import {clearPending, updatePending} from '../../../actions/configure'; +const slots = [ + 'streamSettings', +]; + class SettingsContainer extends React.Component { toggleModeration = () => { @@ -48,8 +52,11 @@ class SettingsContainer extends React.Component { }; render() { + const {mergedSettings, canSave, data, root, asset} = this.props; return ; } } SettingsContainer.propTypes = { + data: PropTypes.object.isRequired, + root: PropTypes.object.isRequired, asset: PropTypes.object.isRequired, pending: PropTypes.object.isRequired, mergedSettings: PropTypes.object.isRequired, @@ -74,6 +83,12 @@ SettingsContainer.propTypes = { }; const withSettingsFragments = withFragments({ + root: gql` + fragment CoralEmbedStream_Settings_root on RootQuery { + __typename + ${getSlotFragmentSpreads(slots, 'root')} + } + `, asset: gql` fragment CoralEmbedStream_Settings_asset on Asset { id @@ -83,7 +98,9 @@ const withSettingsFragments = withFragments({ questionBoxEnable questionBoxIcon questionBoxContent + ${getSlotFragmentSpreads(slots, 'settings')} } + ${getSlotFragmentSpreads(slots, 'asset')} } `, }); From 8cc3d198197361be97fd9b497ddc6ce46e990e20 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 21 Nov 2017 10:35:38 +0100 Subject: [PATCH 23/39] Fix query --- .../src/tabs/configure/containers/Configure.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js index e9da3fc79..b88306262 100644 --- a/client/coral-embed-stream/src/tabs/configure/containers/Configure.js +++ b/client/coral-embed-stream/src/tabs/configure/containers/Configure.js @@ -30,6 +30,7 @@ const withConfigureFragments = withFragments({ __typename ...${getDefinitionName(Settings.fragments.root)} } + ${Settings.fragments.root} `, asset: gql` fragment CoralEmbedStream_Configure_asset on Asset { @@ -39,7 +40,6 @@ const withConfigureFragments = withFragments({ } ${AssetStatusInfo.fragments.asset} ${Settings.fragments.asset} - ${Settings.fragments.root} `, }); From 258788fd88937bc4320b85ece4365166b50f42f0 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 21 Nov 2017 11:05:53 +0100 Subject: [PATCH 24/39] Add updatePending and errors to admin settings slots --- .../src/routes/Configure/components/ModerationSettings.js | 5 ++++- .../src/routes/Configure/components/StreamSettings.js | 4 +++- .../src/routes/Configure/components/TechSettings.js | 5 ++++- .../src/routes/Configure/containers/ModerationSettings.js | 6 +++++- .../src/routes/Configure/containers/TechSettings.js | 6 +++++- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/client/coral-admin/src/routes/Configure/components/ModerationSettings.js b/client/coral-admin/src/routes/Configure/components/ModerationSettings.js index f5db719bb..94aa7d923 100644 --- a/client/coral-admin/src/routes/Configure/components/ModerationSettings.js +++ b/client/coral-admin/src/routes/Configure/components/ModerationSettings.js @@ -39,7 +39,7 @@ class ModerationSettings extends React.Component { }; render() { - const {settings, data, root} = this.props; + const {settings, data, root, updatePending, errors} = this.props; return ( ); @@ -82,6 +84,7 @@ class ModerationSettings extends React.Component { ModerationSettings.propTypes = { updatePending: PropTypes.func.isRequired, + errors: PropTypes.object.isRequired, data: PropTypes.object.isRequired, root: PropTypes.object.isRequired, settings: PropTypes.object.isRequired, diff --git a/client/coral-admin/src/routes/Configure/components/StreamSettings.js b/client/coral-admin/src/routes/Configure/components/StreamSettings.js index 9c05711cb..ea2c570bb 100644 --- a/client/coral-admin/src/routes/Configure/components/StreamSettings.js +++ b/client/coral-admin/src/routes/Configure/components/StreamSettings.js @@ -100,7 +100,7 @@ class StreamSettings extends React.Component { }; render() { - const {settings, data, root, errors} = this.props; + const {settings, data, root, errors, updatePending} = this.props; return ( ); diff --git a/client/coral-admin/src/routes/Configure/components/TechSettings.js b/client/coral-admin/src/routes/Configure/components/TechSettings.js index 7f16b6064..1b783c9ec 100644 --- a/client/coral-admin/src/routes/Configure/components/TechSettings.js +++ b/client/coral-admin/src/routes/Configure/components/TechSettings.js @@ -31,7 +31,7 @@ class TechSettings extends React.Component { }; render() { - const {settings, data, root} = this.props; + const {settings, data, root, errors, updatePending} = this.props; return ( ); @@ -59,6 +61,7 @@ class TechSettings extends React.Component { TechSettings.propTypes = { updatePending: PropTypes.func.isRequired, + errors: PropTypes.object.isRequired, data: PropTypes.object.isRequired, root: PropTypes.object.isRequired, settings: PropTypes.object.isRequired, diff --git a/client/coral-admin/src/routes/Configure/containers/ModerationSettings.js b/client/coral-admin/src/routes/Configure/containers/ModerationSettings.js index 0583378a9..61538f4c0 100644 --- a/client/coral-admin/src/routes/Configure/containers/ModerationSettings.js +++ b/client/coral-admin/src/routes/Configure/containers/ModerationSettings.js @@ -10,6 +10,10 @@ const slots = [ 'adminModerationSettings', ]; +const mapStateToProps = (state) => ({ + errors: state.configure.errors, +}); + const mapDispatchToProps = (dispatch) => bindActionCreators({ updatePending, @@ -36,5 +40,5 @@ export default compose( } ` }), - connect(null, mapDispatchToProps), + connect(mapStateToProps, mapDispatchToProps), )(ModerationSettings); diff --git a/client/coral-admin/src/routes/Configure/containers/TechSettings.js b/client/coral-admin/src/routes/Configure/containers/TechSettings.js index df33a3f9a..1f5ac20b8 100644 --- a/client/coral-admin/src/routes/Configure/containers/TechSettings.js +++ b/client/coral-admin/src/routes/Configure/containers/TechSettings.js @@ -10,6 +10,10 @@ const slots = [ 'adminTechSettings', ]; +const mapStateToProps = (state) => ({ + errors: state.configure.errors, +}); + const mapDispatchToProps = (dispatch) => bindActionCreators({ updatePending, @@ -33,5 +37,5 @@ export default compose( } ` }), - connect(null, mapDispatchToProps), + connect(mapStateToProps, mapDispatchToProps), )(TechSettings); From a8e8b7844196a046ce7f2ded11b46ae8bebbb173 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 21 Nov 2017 11:08:18 +0100 Subject: [PATCH 25/39] Pass updatePending and errors through slots --- .../src/tabs/configure/containers/Settings.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js index 41d9c21b2..afad3482d 100644 --- a/client/coral-embed-stream/src/tabs/configure/containers/Settings.js +++ b/client/coral-embed-stream/src/tabs/configure/containers/Settings.js @@ -52,11 +52,11 @@ class SettingsContainer extends React.Component { }; render() { - const {mergedSettings, canSave, data, root, asset} = this.props; + const {mergedSettings, canSave, data, root, asset, errors, updatePending} = this.props; return ({ pending: state.configure.pending, canSave: state.configure.canSave, + errors: state.configure.errors, }); const mapDispatchToProps = (dispatch) => From 548a204665ae90a2615f5623e31f145c4990c4a1 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 21 Nov 2017 11:13:05 +0100 Subject: [PATCH 26/39] Expose StreamConfiguration --- .../tabs/configure/components/Checkbox.css | 59 ------------------- .../src/tabs/configure/components/Checkbox.js | 28 --------- .../src/tabs/configure/components/Settings.js | 2 +- .../components/StreamConfiguration.css} | 0 .../components/StreamConfiguration.js} | 4 +- client/coral-ui/components/Checkbox.css | 41 +++++-------- client/coral-ui/components/Checkbox.js | 34 +++++++---- plugin-api/beta/client/components/index.js | 1 + 8 files changed, 42 insertions(+), 127 deletions(-) delete mode 100644 client/coral-embed-stream/src/tabs/configure/components/Checkbox.css delete mode 100644 client/coral-embed-stream/src/tabs/configure/components/Checkbox.js rename client/{coral-embed-stream/src/tabs/configure/components/Configuration.css => coral-framework/components/StreamConfiguration.css} (100%) rename client/{coral-embed-stream/src/tabs/configure/components/Configuration.js => coral-framework/components/StreamConfiguration.js} (92%) diff --git a/client/coral-embed-stream/src/tabs/configure/components/Checkbox.css b/client/coral-embed-stream/src/tabs/configure/components/Checkbox.css deleted file mode 100644 index a25ea150f..000000000 --- a/client/coral-embed-stream/src/tabs/configure/components/Checkbox.css +++ /dev/null @@ -1,59 +0,0 @@ -.root { - position: relative; - display: inline-block; -} - -.input { - position: absolute; - left: 7px; - bottom: 7px; - margin: 0; - padding: 0; - outline: none; - cursor: pointer; - pointer-events: none; - opacity: 0; -} - -.checkbox { - cursor: pointer; -} - -.checkbox:before { - content: "\e835"; - color: #717171; - left: 4px; - top: 0px; - width: 18px; - height: 18px; - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 24px; - line-height: 1; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - white-space: nowrap; - direction: ltr; - vertical-align: -6px; - text-rendering: optimizeLegibility; - font-feature-settings: 'liga'; - transition: all .2s ease; - z-index: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.checkboxChecked:before { - content: "\e834"; - color: #00a291; -} - -.input:focus + .checkbox:before { - color: #00a291; -} - -.input:focus + .checkboxChecked:before { - color: #00e291; -} diff --git a/client/coral-embed-stream/src/tabs/configure/components/Checkbox.js b/client/coral-embed-stream/src/tabs/configure/components/Checkbox.js deleted file mode 100644 index 98804fe60..000000000 --- a/client/coral-embed-stream/src/tabs/configure/components/Checkbox.js +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import styles from './Checkbox.css'; -import cn from 'classnames'; -import PropTypes from 'prop-types'; - -const Checkbox = ({onChange, checked, className, ...rest}) => ( - -); - -Checkbox.propTypes = { - className: PropTypes.string, - onChange: PropTypes.func, - checked: PropTypes.bool, -}; - -export default Checkbox; diff --git a/client/coral-embed-stream/src/tabs/configure/components/Settings.js b/client/coral-embed-stream/src/tabs/configure/components/Settings.js index 6c9a3498e..4e89172d2 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Settings.js +++ b/client/coral-embed-stream/src/tabs/configure/components/Settings.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import t from 'coral-framework/services/i18n'; import cn from 'classnames'; import styles from './Settings.css'; -import Configuration from './Configuration'; +import Configuration from 'coral-framework/components/StreamConfiguration'; import QuestionBoxBuilder from './QuestionBoxBuilder'; import Slot from 'coral-framework/components/Slot'; diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configuration.css b/client/coral-framework/components/StreamConfiguration.css similarity index 100% rename from client/coral-embed-stream/src/tabs/configure/components/Configuration.css rename to client/coral-framework/components/StreamConfiguration.css diff --git a/client/coral-embed-stream/src/tabs/configure/components/Configuration.js b/client/coral-framework/components/StreamConfiguration.js similarity index 92% rename from client/coral-embed-stream/src/tabs/configure/components/Configuration.js rename to client/coral-framework/components/StreamConfiguration.js index 0e382e886..6331e8230 100644 --- a/client/coral-embed-stream/src/tabs/configure/components/Configuration.js +++ b/client/coral-framework/components/StreamConfiguration.js @@ -1,8 +1,8 @@ import React from 'react'; -import Checkbox from './Checkbox'; +import Checkbox from 'coral-ui/components/Checkbox'; import PropTypes from 'prop-types'; import cn from 'classnames'; -import styles from './Configuration.css'; +import styles from './StreamConfiguration.css'; import uuid from 'uuid/v4'; class Configuration extends React.Component { diff --git a/client/coral-ui/components/Checkbox.css b/client/coral-ui/components/Checkbox.css index d20886689..a25ea150f 100644 --- a/client/coral-ui/components/Checkbox.css +++ b/client/coral-ui/components/Checkbox.css @@ -1,10 +1,9 @@ -.label { +.root { position: relative; display: inline-block; } -.label input { - visibility: hidden; +.input { position: absolute; left: 7px; bottom: 7px; @@ -12,6 +11,7 @@ padding: 0; outline: none; cursor: pointer; + pointer-events: none; opacity: 0; } @@ -19,21 +19,9 @@ cursor: pointer; } -.label input[type="checkbox"]:checked + .checkbox:before { - content: "\e834"; -} - -.label input[type="checkbox"] + .checkbox:before { +.checkbox:before { content: "\e835"; color: #717171; -} - -.label.type--green input[type="checkbox"] + .checkbox:before { - color: #00a291; -} - -.label input[type="checkbox"] + .checkbox:before { - position: absolute; left: 4px; top: 0px; width: 18px; @@ -49,22 +37,23 @@ white-space: nowrap; direction: ltr; vertical-align: -6px; - -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; - -moz-osx-font-smoothing: grayscale; - -webkit-font-feature-settings: 'liga'; font-feature-settings: 'liga'; - -webkit-transition: all .2s ease; transition: all .2s ease; z-index: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } -.checkboxInfo { - display: inline-block; - max-width: 360px; - margin-left: 50px; +.checkboxChecked:before { + content: "\e834"; + color: #00a291; } -.checkboxInfo h4 { - margin: 0 0 5px; +.input:focus + .checkbox:before { + color: #00a291; +} + +.input:focus + .checkboxChecked:before { + color: #00e291; } diff --git a/client/coral-ui/components/Checkbox.js b/client/coral-ui/components/Checkbox.js index f80666c65..98804fe60 100644 --- a/client/coral-ui/components/Checkbox.js +++ b/client/coral-ui/components/Checkbox.js @@ -1,16 +1,28 @@ import React from 'react'; import styles from './Checkbox.css'; +import cn from 'classnames'; +import PropTypes from 'prop-types'; -export default ({name, cStyle = 'base', onChange, label, className, info, ...attrs}) => ( -