diff --git a/.eslintignore b/.eslintignore
index cd0c80673..6fecd9d9b 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -10,4 +10,5 @@ plugins/*
!plugins/coral-plugin-like
!plugins/coral-plugin-mod
!plugins/coral-plugin-love
+!plugins/coral-plugin-viewing-options
node_modules
diff --git a/.gitignore b/.gitignore
index 11e738d57..abd490356 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,5 +23,6 @@ plugins/*
!plugins/coral-plugin-like
!plugins/coral-plugin-mod
!plugins/coral-plugin-love
+!plugins/coral-plugin-viewing-options
**/node_modules/*
diff --git a/client/.eslintrc.json b/client/.eslintrc.json
index 1144f985d..5735a91a1 100644
--- a/client/.eslintrc.json
+++ b/client/.eslintrc.json
@@ -4,21 +4,20 @@
"es6": true,
"mocha": true
},
- "extends": "../.eslintrc.json",
"parserOptions": {
+ "sourceType": "module",
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"jsx": true
- },
- "sourceType": "module"
+ }
},
"parser": "babel-eslint",
"plugins": [
"react"
],
"rules": {
- "react/jsx-uses-react": "error",
- "react/jsx-uses-vars": "error",
- "no-console": ["warn", { "allow": ["warn", "error"] }]
+ "react/jsx-uses-react": "error",
+ "react/jsx-uses-vars": "error",
+ "no-console": ["warn", { "allow": ["warn", "error"] }]
}
}
diff --git a/client/coral-embed-stream/src/actions/stream.js b/client/coral-embed-stream/src/actions/stream.js
index 6a9e47838..9b5d94a9b 100644
--- a/client/coral-embed-stream/src/actions/stream.js
+++ b/client/coral-embed-stream/src/actions/stream.js
@@ -37,3 +37,13 @@ export const viewAllComments = () => {
return {type: actions.VIEW_ALL_COMMENTS};
};
+
+export const addCommentClassName = (className) => ({
+ type: actions.ADD_COMMENT_CLASSNAME,
+ className
+});
+
+export const removeCommentClassName = (idx) => ({
+ type: actions.REMOVE_COMMENT_CLASSNAME,
+ idx
+});
diff --git a/client/coral-embed-stream/src/components/Comment.css b/client/coral-embed-stream/src/components/Comment.css
index b0fca75c8..4ff0586da 100644
--- a/client/coral-embed-stream/src/components/Comment.css
+++ b/client/coral-embed-stream/src/components/Comment.css
@@ -98,6 +98,10 @@
text-align: right;
}
+.commentInfoBar {
+ float: right;
+}
+
@keyframes enter {
0% {background-color: rgba(0, 0, 0, 0);}
50% {background-color: rgba(255,255,0, 0.2);}
diff --git a/client/coral-embed-stream/src/components/Comment.js b/client/coral-embed-stream/src/components/Comment.js
index 228dd4c41..b5eca4ff2 100644
--- a/client/coral-embed-stream/src/components/Comment.js
+++ b/client/coral-embed-stream/src/components/Comment.js
@@ -1,8 +1,7 @@
import React, {PropTypes} from 'react';
+
import PermalinkButton from 'coral-plugin-permalinks/PermalinkButton';
-
import AuthorName from 'coral-plugin-author-name/AuthorName';
-
import TagLabel from 'coral-plugin-tag-label/TagLabel';
import Content from 'coral-plugin-commentcontent/CommentContent';
import PubDate from 'coral-plugin-pubdate/PubDate';
@@ -20,14 +19,15 @@ import {
} from 'coral-plugin-best/BestButton';
import Slot from 'coral-framework/components/Slot';
import LoadMore from './LoadMore';
-import IgnoredCommentTombstone from './IgnoredCommentTombstone';
import {TopRightMenu} from './TopRightMenu';
+import IgnoredCommentTombstone from './IgnoredCommentTombstone';
import {EditableCommentContent} from './EditableCommentContent';
import {getActionSummary, iPerformedThisAction} from 'coral-framework/utils';
import {getEditableUntilDate} from './util';
import styles from './Comment.css';
const isStaff = (tags) => !tags.every((t) => t.name !== 'STAFF');
+const hasTag = (tags, lookupTag) => !!tags.filter((tag) => tag.name === lookupTag).length;
const hasComment = (nodes, id) => nodes.some((node) => node.id === id);
// resetCursors will return the id cursors of the first and second newest comment in
@@ -73,8 +73,7 @@ const ActionButton = ({children}) => {
);
};
-class Comment extends React.Component {
-
+export default class Comment extends React.Component {
constructor(props) {
super(props);
@@ -272,28 +271,29 @@ class Comment extends React.Component {
}
render () {
const {
- comment,
- parentId,
- currentUser,
asset,
depth,
- postComment,
- addNotification,
- showSignInDialog,
- highlighted,
+ comment,
postFlag,
+ parentId,
+ ignoreUser,
+ highlighted,
+ postComment,
+ currentUser,
postDontAgree,
setActiveReplyBox,
activeReplyBox,
deleteAction,
- addCommentTag,
- removeCommentTag,
- ignoreUser,
- liveUpdates,
disableReply,
- commentIsIgnored,
maxCharCount,
- charCountEnable
+ addCommentTag,
+ addNotification,
+ charCountEnable,
+ showSignInDialog,
+ removeCommentTag,
+ liveUpdates,
+ commentIsIgnored,
+ commentClassNames = []
} = this.props;
const view = this.getVisibileReplies();
@@ -349,9 +349,39 @@ class Comment extends React.Component {
() => 'Failed to remove best comment tag'
);
+ /**
+ * classNamesToAdd
+ * adds classNames based on condition
+ * classnames is an array of objects with key as classnames and value as conditions
+ * i.e:
+ * {
+ * 'myClassName': { tags: ['STAFF']}
+ * }
+ *
+ * This will add myClassName to comments tagged with STAFF TAG.
+ * **/
+
+ const classNamesToAdd = commentClassNames.reduce((acc, className) => {
+ let res = [];
+
+ // Adding classNames based on tags
+ Object.keys(className).forEach((cn) => {
+ const condition = className[cn];
+ condition.tags.forEach((tag) => {
+ if (hasTag(comment.tags, tag)) {
+ res = [...res, cn];
+ }
+ });
+ });
+
+ // TODO: Compare rest of the comment obj to the condition if needed
+
+ return [...acc, ...res];
+ }, []);
+
return (
: {asset.settings.closedMessage}
}
- {loggedIn &&
+
+ {loggedIn && (
}
+ />
+ )}
+
+
+
+
{/* the highlightedComment is isolated after the user followed a permalink */}
{highlightedComment
@@ -235,7 +241,7 @@ class Stream extends React.Component {
editComment={this.props.editComment}
liveUpdates={true}
/>
- :
+ :
:
({
assetUrl: state.stream.assetUrl,
activeTab: state.embed.activeTab,
previousTab: state.embed.previousTab,
+ commentClassNames: state.stream.commentClassNames
});
const mapDispatchToProps = (dispatch) =>
diff --git a/client/coral-embed-stream/src/graphql/index.js b/client/coral-embed-stream/src/graphql/index.js
index 04225eb96..d9d4be12e 100644
--- a/client/coral-embed-stream/src/graphql/index.js
+++ b/client/coral-embed-stream/src/graphql/index.js
@@ -144,7 +144,7 @@ const extension = {
parent_id,
asset_id,
action_summaries: [],
- tags,
+ tags: tags.map((t) => ({name: t, __typename: 'Tag'})),
status: null,
replyCount: 0,
parent: parent_id
diff --git a/client/coral-embed-stream/src/reducers/index.js b/client/coral-embed-stream/src/reducers/index.js
index 5ddac4567..61fe950c9 100644
--- a/client/coral-embed-stream/src/reducers/index.js
+++ b/client/coral-embed-stream/src/reducers/index.js
@@ -4,6 +4,6 @@ import stream from './stream';
export default {
embed,
- stream,
config,
+ stream,
};
diff --git a/client/coral-embed-stream/src/reducers/stream.js b/client/coral-embed-stream/src/reducers/stream.js
index 59f068530..9e03e3528 100644
--- a/client/coral-embed-stream/src/reducers/stream.js
+++ b/client/coral-embed-stream/src/reducers/stream.js
@@ -20,6 +20,7 @@ const initialState = {
assetId: getQueryVariable('asset_id'),
assetUrl: getQueryVariable('asset_url'),
commentId: getQueryVariable('comment_id'),
+ commentClassNames: []
};
export default function stream(state = initialState, action) {
@@ -39,6 +40,19 @@ export default function stream(state = initialState, action) {
...state,
commentId: '',
};
+ case actions.ADD_COMMENT_CLASSNAME :
+ return {
+ ...state,
+ commentClassNames: [...state.commentClassNames, action.className]
+ };
+ case actions.REMOVE_COMMENT_CLASSNAME :
+ return {
+ ...state,
+ commentClassNames: [
+ ...state.commentClassNames.slice(0, action.idx),
+ ...state.commentClassNames.slice(action.idx + 1)
+ ]
+ };
default:
return state;
}
diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css
index 04b2536a3..c9304c0cf 100644
--- a/client/coral-embed-stream/style/default.css
+++ b/client/coral-embed-stream/style/default.css
@@ -178,6 +178,15 @@ hr {
display: none;
}
+.talk-stream-wrapper-box {
+ padding: 10px 0;
+}
+
+.talk-stream-comments-container {
+ position: relative;
+ z-index: 0;
+}
+
.commentStream {
/* prevent absolutely positioned final permalink popover from being clipped */
padding-bottom: 50px;
diff --git a/client/coral-framework/components/Slot.js b/client/coral-framework/components/Slot.js
index 939068e83..22a315623 100644
--- a/client/coral-framework/components/Slot.js
+++ b/client/coral-framework/components/Slot.js
@@ -4,9 +4,9 @@ import styles from './Slot.css';
import {connect} from 'react-redux';
import {getSlotElements} from 'coral-framework/helpers/plugins';
-function Slot ({fill, inline = false, plugin_config: config, ...rest}) {
+function Slot ({fill, inline = false, className, plugin_config: config, ...rest}) {
return (
-
+
{getSlotElements(fill, {...rest, config})}
);
diff --git a/client/coral-framework/components/index.js b/client/coral-framework/components/index.js
new file mode 100644
index 000000000..b7aaef665
--- /dev/null
+++ b/client/coral-framework/components/index.js
@@ -0,0 +1 @@
+export {default as Slot} from './Slot';
diff --git a/client/coral-framework/helpers/camelize.js b/client/coral-framework/helpers/camelize.js
new file mode 100644
index 000000000..ffc09cebb
--- /dev/null
+++ b/client/coral-framework/helpers/camelize.js
@@ -0,0 +1,16 @@
+const regExp = /[-\s]+(.)?/g;
+
+/**
+ * Convert dash separated strings to camel case.
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+export default function camelize(str) {
+ return str.replace(regExp, toUpper);
+}
+
+function toUpper(match, c) {
+ return c ? c.toUpperCase() : '';
+}
diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js
index 0eaecbadd..e0aa05c80 100644
--- a/client/coral-framework/helpers/plugins.js
+++ b/client/coral-framework/helpers/plugins.js
@@ -1,13 +1,14 @@
import React from 'react';
-import merge from 'lodash/merge';
-import flatten from 'lodash/flatten';
-import flattenDeep from 'lodash/flattenDeep';
import uniq from 'lodash/uniq';
import pick from 'lodash/pick';
+import merge from 'lodash/merge';
import plugins from 'pluginsConfig';
+import flatten from 'lodash/flatten';
+import flattenDeep from 'lodash/flattenDeep';
import {getDefinitionName, mergeDocuments} from 'coral-framework/utils';
import {loadTranslations} from 'coral-framework/services/i18n';
import {injectReducers} from 'coral-framework/services/store';
+import camelize from './camelize';
/**
* Returns React Elements for given slot.
@@ -98,7 +99,7 @@ export function injectPluginsReducers() {
const reducers = merge(
...plugins
.filter((o) => o.module.reducer)
- .map((o) => ({...o.module.reducer}))
+ .map((o) => ({[camelize(o.plugin)] : o.module.reducer}))
);
injectReducers(reducers);
}
diff --git a/client/coral-plugin-api/index.js b/client/coral-plugin-api/index.js
deleted file mode 100644
index f0f5ad9cc..000000000
--- a/client/coral-plugin-api/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export {withReaction} from '../coral-framework/hocs';
diff --git a/client/coral-plugin-moderation/styles.css b/client/coral-plugin-moderation/styles.css
index 2e928d3df..734cc1e6f 100644
--- a/client/coral-plugin-moderation/styles.css
+++ b/client/coral-plugin-moderation/styles.css
@@ -1,4 +1,6 @@
.moderationLink {
+ display: inline;
+
a {
color: #679af3;
text-decoration: none;
diff --git a/plugin-api/.babelrc b/plugin-api/.babelrc
new file mode 100644
index 000000000..63b1c53de
--- /dev/null
+++ b/plugin-api/.babelrc
@@ -0,0 +1,14 @@
+{
+ "presets": [
+ "es2015"
+ ],
+ "plugins": [
+ "add-module-exports",
+ "transform-class-properties",
+ "transform-decorators-legacy",
+ "transform-object-assign",
+ "transform-object-rest-spread",
+ "transform-async-to-generator",
+ "transform-react-jsx"
+ ]
+}
diff --git a/plugin-api/.eslintrc.json b/plugin-api/.eslintrc.json
new file mode 100644
index 000000000..5735a91a1
--- /dev/null
+++ b/plugin-api/.eslintrc.json
@@ -0,0 +1,23 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true,
+ "mocha": true
+ },
+ "parserOptions": {
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "experimentalObjectRestSpread": true,
+ "jsx": true
+ }
+ },
+ "parser": "babel-eslint",
+ "plugins": [
+ "react"
+ ],
+ "rules": {
+ "react/jsx-uses-react": "error",
+ "react/jsx-uses-vars": "error",
+ "no-console": ["warn", { "allow": ["warn", "error"] }]
+ }
+}
diff --git a/plugin-api/alpha/client/actions/index.js b/plugin-api/alpha/client/actions/index.js
new file mode 100644
index 000000000..8e8e11c0a
--- /dev/null
+++ b/plugin-api/alpha/client/actions/index.js
@@ -0,0 +1,2 @@
+export {addTag, removeTag} from 'coral-plugin-commentbox/actions';
+export {addCommentClassName, removeCommentClassName} from 'coral-embed-stream/src/actions/stream';
diff --git a/plugin-api/alpha/client/components/index.js b/plugin-api/alpha/client/components/index.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/plugin-api/alpha/client/selectors/index.js b/plugin-api/alpha/client/selectors/index.js
new file mode 100644
index 000000000..ad76326cf
--- /dev/null
+++ b/plugin-api/alpha/client/selectors/index.js
@@ -0,0 +1,2 @@
+export const commentBoxTagsSelector = (state) => state.commentBox.tags;
+export const commentClassNamesSelector = (state) => state.stream.commentClassNames;
diff --git a/plugin-api/beta/client/components/index.js b/plugin-api/beta/client/components/index.js
new file mode 100644
index 000000000..0330892bf
--- /dev/null
+++ b/plugin-api/beta/client/components/index.js
@@ -0,0 +1,2 @@
+export {Slot} from 'coral-framework/components';
+export {Icon} from 'coral-ui';
diff --git a/plugin-api/beta/client/hocs/index.js b/plugin-api/beta/client/hocs/index.js
new file mode 100644
index 000000000..7ec875656
--- /dev/null
+++ b/plugin-api/beta/client/hocs/index.js
@@ -0,0 +1 @@
+export {withReaction} from 'coral-framework/hocs';
diff --git a/plugin-api/beta/client/services/index.js b/plugin-api/beta/client/services/index.js
new file mode 100644
index 000000000..16872b9f3
--- /dev/null
+++ b/plugin-api/beta/client/services/index.js
@@ -0,0 +1 @@
+export {t} from 'coral-framework/services/i18n';
diff --git a/plugin-api/client/actions/index.js b/plugin-api/client/actions/index.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/plugin-api/client/components/index.js b/plugin-api/client/components/index.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/plugin-api/client/hocs/index.js b/plugin-api/client/hocs/index.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/plugins/coral-plugin-love/client/LoveButton.js b/plugins/coral-plugin-love/client/LoveButton.js
index 430e2658d..4e9b56d44 100644
--- a/plugins/coral-plugin-love/client/LoveButton.js
+++ b/plugins/coral-plugin-love/client/LoveButton.js
@@ -2,7 +2,7 @@ import React from 'react';
import {Icon} from 'coral-ui';
import styles from './styles.css';
import t from 'coral-framework/services/i18n';
-import {withReaction} from 'coral-plugin-api';
+import {withReaction} from 'plugin-api/beta/client/hocs';
class LoveButton extends React.Component {
handleClick = () => {
diff --git a/plugins/coral-plugin-offtopic/client/actions.js b/plugins/coral-plugin-offtopic/client/actions.js
new file mode 100644
index 000000000..9cb3947f8
--- /dev/null
+++ b/plugins/coral-plugin-offtopic/client/actions.js
@@ -0,0 +1,5 @@
+import {OFFTOPIC_TOGGLE_CHECKBOX} from './constants';
+
+export const toggleCheckbox = () => ({
+ type: OFFTOPIC_TOGGLE_CHECKBOX
+});
diff --git a/plugins/coral-plugin-offtopic/client/components/OffTopicCheckbox.js b/plugins/coral-plugin-offtopic/client/components/OffTopicCheckbox.js
index 0383507d1..70876f739 100644
--- a/plugins/coral-plugin-offtopic/client/components/OffTopicCheckbox.js
+++ b/plugins/coral-plugin-offtopic/client/components/OffTopicCheckbox.js
@@ -1,39 +1,45 @@
import React from 'react';
-import {connect} from 'react-redux';
-import {bindActionCreators} from 'redux';
-import {addTag, removeTag} from 'coral-plugin-commentbox/actions';
import styles from './styles.css';
-import t from 'coral-framework/services/i18n';
+import {t} from 'plugin-api/beta/client/services';
-class OffTopicCheckbox extends React.Component {
+export default class OffTopicCheckbox extends React.Component {
label = 'OFF_TOPIC';
- handleChange = (e) => {
- if (e.target.checked) {
- this.props.addTag(this.label);
- } else {
- const idx = this.props.commentBox.tags.indexOf(this.label);
+ componentDidMount() {
+ this.clearTagsHook = this.props.registerHook('postSubmit', () => {
+ const idx = this.props.tags.indexOf(this.label);
this.props.removeTag(idx);
+ });
+ }
+
+ componentWillUnmount() {
+ this.props.unregisterHook(this.clearTagsHook);
+ }
+
+ handleChange = (e) => {
+ const {addTag, removeTag} = this.props;
+ if (e.target.checked) {
+ addTag(this.label);
+ } else {
+ const idx = this.props.tags.indexOf(this.label);
+ removeTag(idx);
}
}
render() {
return (
-
-
- {t('off_topic')}
-
+ {
+ !this.props.isReply ? (
+
+
+ {t('off_topic')}
+
+ ) : null
+ }
);
}
}
-
-const mapStateToProps = ({commentBox}) => ({commentBox});
-
-const mapDispatchToProps = (dispatch) =>
- bindActionCreators({addTag, removeTag}, dispatch);
-
-export default connect(mapStateToProps, mapDispatchToProps)(OffTopicCheckbox);
diff --git a/plugins/coral-plugin-offtopic/client/components/OffTopicFilter.js b/plugins/coral-plugin-offtopic/client/components/OffTopicFilter.js
new file mode 100644
index 000000000..fea2ac6fb
--- /dev/null
+++ b/plugins/coral-plugin-offtopic/client/components/OffTopicFilter.js
@@ -0,0 +1,32 @@
+import React from 'react';
+import styles from './styles.css';
+
+export default class OffTopicFilter extends React.Component {
+
+ tag = 'OFF_TOPIC';
+ className = 'coral-plugin-off-topic-comment';
+ cn = {[this.className] : {tags: [this.tag]}};
+
+ handleChange = (e) => {
+ if (e.target.checked) {
+ this.props.addCommentClassName(this.cn);
+ this.props.toggleCheckbox();
+ } else {
+ const idx = this.props.commentClassNames.findIndex((i) => i[this.className]);
+ this.props.removeCommentClassName(idx);
+ this.props.toggleCheckbox();
+ }
+ this.props.closeViewingOptions();
+ }
+
+ render() {
+ return (
+
+
+
+ Hide Off-Topic Comments
+
+
+ );
+ }
+}
diff --git a/plugins/coral-plugin-offtopic/client/components/OffTopicTag.js b/plugins/coral-plugin-offtopic/client/components/OffTopicTag.js
index 89a4d03aa..e8029ce5e 100644
--- a/plugins/coral-plugin-offtopic/client/components/OffTopicTag.js
+++ b/plugins/coral-plugin-offtopic/client/components/OffTopicTag.js
@@ -1,7 +1,7 @@
import React from 'react';
import styles from './styles.css';
-import t from 'coral-framework/services/i18n';
+import {t} from 'plugin-api/beta/client/services';
const isOffTopic = (tags) => {
return !!tags.filter((tag) => tag.name === 'OFF_TOPIC').length;
@@ -10,7 +10,7 @@ const isOffTopic = (tags) => {
export default (props) => (
{
- isOffTopic(props.comment.tags) ? (
+ isOffTopic(props.comment.tags) && props.depth === 0 ? (
{t('off_topic')}
diff --git a/plugins/coral-plugin-offtopic/client/components/styles.css b/plugins/coral-plugin-offtopic/client/components/styles.css
index f3e290b09..94d7eaa85 100644
--- a/plugins/coral-plugin-offtopic/client/components/styles.css
+++ b/plugins/coral-plugin-offtopic/client/components/styles.css
@@ -8,11 +8,20 @@
}
.tag {
- background: rgba(245, 188, 168, 0.6);
+ background: #3D73D5;
+ font-size: 12px;
+ font-weight: bold;
+ color: white;
display: inline-block;
margin: 0px 5px;
padding: 5px 5px;
border-radius: 2px;
}
+.viewingOption {
+ padding: 5px 0;
+}
+:global(.coral-plugin-off-topic-comment) {
+ display: none;
+}
diff --git a/plugins/coral-plugin-offtopic/client/constants.js b/plugins/coral-plugin-offtopic/client/constants.js
new file mode 100644
index 000000000..122d7de17
--- /dev/null
+++ b/plugins/coral-plugin-offtopic/client/constants.js
@@ -0,0 +1 @@
+export const OFFTOPIC_TOGGLE_CHECKBOX = 'OFFTOPIC_TOGGLE_CHECKBOX';
diff --git a/plugins/coral-plugin-offtopic/client/containers/OffTopicCheckbox.js b/plugins/coral-plugin-offtopic/client/containers/OffTopicCheckbox.js
new file mode 100644
index 000000000..286fa8d9d
--- /dev/null
+++ b/plugins/coral-plugin-offtopic/client/containers/OffTopicCheckbox.js
@@ -0,0 +1,14 @@
+import {connect} from 'react-redux';
+import {bindActionCreators} from 'redux';
+import {addTag, removeTag} from 'plugin-api/alpha/client/actions';
+import {commentBoxTagsSelector} from 'plugin-api/alpha/client/selectors';
+import OffTopicCheckbox from '../components/OffTopicCheckbox';
+
+const mapStateToProps = (state) => ({
+ tags: commentBoxTagsSelector(state)
+});
+
+const mapDispatchToProps = (dispatch) =>
+ bindActionCreators({addTag, removeTag}, dispatch);
+
+export default connect(mapStateToProps, mapDispatchToProps)(OffTopicCheckbox);
diff --git a/plugins/coral-plugin-offtopic/client/containers/OffTopicFilter.js b/plugins/coral-plugin-offtopic/client/containers/OffTopicFilter.js
new file mode 100644
index 000000000..98f5f7aea
--- /dev/null
+++ b/plugins/coral-plugin-offtopic/client/containers/OffTopicFilter.js
@@ -0,0 +1,30 @@
+import {connect} from 'react-redux';
+import {bindActionCreators} from 'redux';
+import {toggleCheckbox} from '../actions';
+import {commentClassNamesSelector} from 'plugin-api/alpha/client/selectors';
+import OffTopicFilter from '../components/OffTopicFilter';
+import {
+ closeViewingOptions
+} from 'plugins/coral-plugin-viewing-options/client/actions';
+import {
+ addCommentClassName,
+ removeCommentClassName
+} from 'plugin-api/alpha/client/actions';
+
+const mapStateToProps = (state) => ({
+ commentClassNames: commentClassNamesSelector(state),
+ checked: state.coralPluginOfftopic.checked
+});
+
+const mapDispatchToProps = (dispatch) =>
+ bindActionCreators(
+ {
+ toggleCheckbox,
+ closeViewingOptions,
+ addCommentClassName,
+ removeCommentClassName
+ },
+ dispatch
+ );
+
+export default connect(mapStateToProps, mapDispatchToProps)(OffTopicFilter);
diff --git a/plugins/coral-plugin-offtopic/client/index.js b/plugins/coral-plugin-offtopic/client/index.js
index f3459441d..383c7351e 100644
--- a/plugins/coral-plugin-offtopic/client/index.js
+++ b/plugins/coral-plugin-offtopic/client/index.js
@@ -1,11 +1,20 @@
-import OffTopicCheckbox from './components/OffTopicCheckbox';
-import OffTopicTag from './components/OffTopicTag';
import translations from './translations.json';
+import OffTopicTag from './components/OffTopicTag';
+import OffTopicFilter from './containers/OffTopicFilter';
+import OffTopicCheckbox from './containers/OffTopicCheckbox';
+import reducer from './reducer';
+
+/**
+ * coral-plugin-offtopic depends on coral-plugin-viewing-options
+ * in other to display filter and use the streamViewingOptions slot
+ */
export default {
translations,
+ reducer,
slots: {
commentInputDetailArea: [OffTopicCheckbox],
- commentInfoBar: [OffTopicTag]
+ commentInfoBar: [OffTopicTag],
+ viewingOptions: [OffTopicFilter]
}
};
diff --git a/plugins/coral-plugin-offtopic/client/reducer.js b/plugins/coral-plugin-offtopic/client/reducer.js
new file mode 100644
index 000000000..adab987a0
--- /dev/null
+++ b/plugins/coral-plugin-offtopic/client/reducer.js
@@ -0,0 +1,18 @@
+import {OFFTOPIC_TOGGLE_CHECKBOX} from './constants';
+
+const initialState = {
+ checked: false
+};
+
+export default function offTopic (state = initialState, action) {
+ switch (action.type) {
+ case OFFTOPIC_TOGGLE_CHECKBOX: {
+ return {
+ ...state,
+ checked: !state.checked
+ };
+ }
+ default :
+ return state;
+ }
+}
diff --git a/plugins/coral-plugin-viewing-options/client/.babelrc b/plugins/coral-plugin-viewing-options/client/.babelrc
new file mode 100644
index 000000000..60be246eb
--- /dev/null
+++ b/plugins/coral-plugin-viewing-options/client/.babelrc
@@ -0,0 +1,14 @@
+{
+ "presets": [
+ "es2015"
+ ],
+ "plugins": [
+ "add-module-exports",
+ "transform-class-properties",
+ "transform-decorators-legacy",
+ "transform-object-assign",
+ "transform-object-rest-spread",
+ "transform-async-to-generator",
+ "transform-react-jsx"
+ ]
+}
\ No newline at end of file
diff --git a/plugins/coral-plugin-viewing-options/client/.eslintrc.json b/plugins/coral-plugin-viewing-options/client/.eslintrc.json
new file mode 100644
index 000000000..9fe56bd14
--- /dev/null
+++ b/plugins/coral-plugin-viewing-options/client/.eslintrc.json
@@ -0,0 +1,23 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true,
+ "mocha": true
+ },
+ "parserOptions": {
+ "sourceType": "module",
+ "ecmaFeatures": {
+ "experimentalObjectRestSpread": true,
+ "jsx": true
+ }
+ },
+ "parser": "babel-eslint",
+ "plugins": [
+ "react"
+ ],
+ "rules": {
+ "react/jsx-uses-react": "error",
+ "react/jsx-uses-vars": "error",
+ "no-console": ["warn", { "allow": ["warn", "error"] }]
+ }
+}
diff --git a/plugins/coral-plugin-viewing-options/client/actions.js b/plugins/coral-plugin-viewing-options/client/actions.js
new file mode 100644
index 000000000..8a6473e3c
--- /dev/null
+++ b/plugins/coral-plugin-viewing-options/client/actions.js
@@ -0,0 +1,9 @@
+import {VIEWING_OPTIONS_OPEN, VIEWING_OPTIONS_CLOSE} from './constants';
+
+export const openViewingOptions = () => ({
+ type: VIEWING_OPTIONS_OPEN
+});
+
+export const closeViewingOptions = () => ({
+ type: VIEWING_OPTIONS_CLOSE
+});
diff --git a/plugins/coral-plugin-viewing-options/client/components/ViewingOptions.css b/plugins/coral-plugin-viewing-options/client/components/ViewingOptions.css
new file mode 100644
index 000000000..47cda9a1a
--- /dev/null
+++ b/plugins/coral-plugin-viewing-options/client/components/ViewingOptions.css
@@ -0,0 +1,28 @@
+.root {
+ float: right;
+ text-align: right;
+ position: relative;
+ display: inline-block;
+ min-width: 220px;
+ z-index: 10;
+ position: relative;
+ cursor: pointer;
+}
+
+.list {
+ background: white;
+ position: absolute;
+ box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.15);
+ right: 3px;
+ top: 20px;
+}
+
+.list > ul, .list > ul > li {
+ padding: 0;
+ margin: 0;
+}
+
+.list > ul > li {
+ padding: 10px;
+ list-style: none;
+}
diff --git a/plugins/coral-plugin-viewing-options/client/components/ViewingOptions.js b/plugins/coral-plugin-viewing-options/client/components/ViewingOptions.js
new file mode 100644
index 000000000..513d9340b
--- /dev/null
+++ b/plugins/coral-plugin-viewing-options/client/components/ViewingOptions.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import cn from 'classnames';
+import styles from './ViewingOptions.css';
+import {Slot, Icon} from 'plugin-api/beta/client/components';
+
+const ViewingOptions = (props) => {
+ const toggleOpen = () => {
+ if (!props.open) {
+ props.openViewingOptions();
+ } else {
+ props.closeViewingOptions();
+ }
+ };
+ return (
+
+
+ {
+ props.open ? (
+
+
+ {
+ React.Children.map( , (component) => {
+ return React.createElement('li', {
+ className: 'coral-plugin-viewing-options-item'
+ }, component);
+ })
+ }
+
+
+ ) : null
+ }
+
+ );
+};
+
+export default ViewingOptions;
diff --git a/plugins/coral-plugin-viewing-options/client/constants.js b/plugins/coral-plugin-viewing-options/client/constants.js
new file mode 100644
index 000000000..586fe7a28
--- /dev/null
+++ b/plugins/coral-plugin-viewing-options/client/constants.js
@@ -0,0 +1,2 @@
+export const VIEWING_OPTIONS_OPEN = 'VIEWING_OPTIONS_OPEN';
+export const VIEWING_OPTIONS_CLOSE = 'VIEWING_OPTIONS_CLOSE';
diff --git a/plugins/coral-plugin-viewing-options/client/containers/ViewingOptions.js b/plugins/coral-plugin-viewing-options/client/containers/ViewingOptions.js
new file mode 100644
index 000000000..767a9197d
--- /dev/null
+++ b/plugins/coral-plugin-viewing-options/client/containers/ViewingOptions.js
@@ -0,0 +1,13 @@
+import {connect} from 'react-redux';
+import {bindActionCreators} from 'redux';
+import ViewingOptions from '../components/ViewingOptions';
+import {openViewingOptions, closeViewingOptions} from '../actions';
+
+const mapStateToProps = ({coralPluginViewingOptions: state}) => ({
+ open: state.open
+});
+
+const mapDispatchToProps = (dispatch) =>
+ bindActionCreators({openViewingOptions, closeViewingOptions}, dispatch);
+
+export default connect(mapStateToProps, mapDispatchToProps)(ViewingOptions);
diff --git a/plugins/coral-plugin-viewing-options/client/index.js b/plugins/coral-plugin-viewing-options/client/index.js
new file mode 100644
index 000000000..519ce42aa
--- /dev/null
+++ b/plugins/coral-plugin-viewing-options/client/index.js
@@ -0,0 +1,9 @@
+import ViewingOptions from './containers/ViewingOptions';
+import reducer from './reducer';
+
+export default {
+ reducer,
+ slots: {
+ streamBox: [ViewingOptions]
+ }
+};
diff --git a/plugins/coral-plugin-viewing-options/client/reducer.js b/plugins/coral-plugin-viewing-options/client/reducer.js
new file mode 100644
index 000000000..ab9f077df
--- /dev/null
+++ b/plugins/coral-plugin-viewing-options/client/reducer.js
@@ -0,0 +1,22 @@
+import {VIEWING_OPTIONS_OPEN, VIEWING_OPTIONS_CLOSE} from './constants';
+
+const initialState = {
+ open: false
+};
+
+export default function offTopic (state = initialState, action) {
+ switch (action.type) {
+ case VIEWING_OPTIONS_OPEN:
+ return {
+ ...state,
+ open: true
+ };
+ case VIEWING_OPTIONS_CLOSE:
+ return {
+ ...state,
+ open: false
+ };
+ default :
+ return state;
+ }
+}
diff --git a/plugins/coral-plugin-viewing-options/index.js b/plugins/coral-plugin-viewing-options/index.js
new file mode 100644
index 000000000..f053ebf79
--- /dev/null
+++ b/plugins/coral-plugin-viewing-options/index.js
@@ -0,0 +1 @@
+module.exports = {};
diff --git a/webpack.config.js b/webpack.config.js
index ddd2f9577..ce1e0bd29 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -154,6 +154,7 @@ const config = {
},
resolve: {
alias: {
+ 'plugin-api': path.resolve(__dirname, 'plugin-api/'),
plugins: path.resolve(__dirname, 'plugins/'),
pluginsConfig: pluginsConfigPath
},