Merge pull request #654 from coralproject/off-topic-features

Off Topic Features
This commit is contained in:
David Erwin
2017-06-07 15:43:03 -04:00
committed by GitHub
52 changed files with 503 additions and 75 deletions
+1
View File
@@ -10,4 +10,5 @@ plugins/*
!plugins/coral-plugin-like
!plugins/coral-plugin-mod
!plugins/coral-plugin-love
!plugins/coral-plugin-viewing-options
node_modules
+1
View File
@@ -23,5 +23,6 @@ plugins/*
!plugins/coral-plugin-like
!plugins/coral-plugin-mod
!plugins/coral-plugin-love
!plugins/coral-plugin-viewing-options
**/node_modules/*
+5 -6
View File
@@ -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"] }]
}
}
@@ -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
});
@@ -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);}
@@ -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 (
<div
className={cn(commentClass, 'talk-stream-comment-wrapper', {[styles.enter]: this.state.animateEnter})}
className={cn(commentClass, 'talk-stream-comment-wrapper', classNamesToAdd, {[styles.enter]: this.state.animateEnter})}
id={`c_${comment.id}`}
style={{marginLeft: depth * 30}}
>
@@ -376,11 +406,13 @@ class Comment extends React.Component {
</span>
<Slot
className={styles.commentInfoBar}
fill="commentInfoBar"
data={this.props.data}
root={this.props.root}
depth={depth}
comment={comment}
commentId={comment.id}
data={this.props.data}
root={this.props.root}
inline
/>
@@ -542,8 +574,6 @@ class Comment extends React.Component {
}
}
export default Comment;
// return whether the comment is editable
function commentIsStillEditable (comment) {
const editing = comment && comment.editing;
@@ -1,19 +1,18 @@
import React, {PropTypes} from 'react';
import LoadMore from './LoadMore';
import Comment from '../components/Comment';
import SuspendedAccount from './SuspendedAccount';
import RestrictedMessageBox
from 'coral-framework/components/RestrictedMessageBox';
import Slot from 'coral-framework/components/Slot';
import InfoBox from 'coral-plugin-infobox/InfoBox';
import {can} from 'coral-framework/services/perms';
import {ModerationLink} from 'coral-plugin-moderation';
import RestrictedMessageBox
from 'coral-framework/components/RestrictedMessageBox';
import t, {timeago} from 'coral-framework/services/i18n';
import CommentBox from 'coral-plugin-commentbox/CommentBox';
import QuestionBox from 'coral-plugin-questionbox/QuestionBox';
import IgnoredCommentTombstone from './IgnoredCommentTombstone';
import NewCount from './NewCount';
import t, {timeago} from 'coral-framework/services/i18n';
import {TransitionGroup} from 'react-transition-group';
const hasComment = (nodes, id) => nodes.some((node) => node.id === id);
@@ -122,6 +121,7 @@ class Stream extends React.Component {
render() {
const {
commentClassNames,
root: {asset, asset: {comments}, comment, me},
postComment,
addNotification,
@@ -130,10 +130,10 @@ class Stream extends React.Component {
deleteAction,
showSignInDialog,
addCommentTag,
removeCommentTag,
pluginProps,
ignoreUser,
auth: {loggedIn, user},
removeCommentTag,
pluginProps,
editName
} = this.props;
const view = this.getVisibleComments();
@@ -202,11 +202,17 @@ class Stream extends React.Component {
/>}
</div>
: <p>{asset.settings.closedMessage}</p>}
{loggedIn &&
{loggedIn && (
<ModerationLink
assetId={asset.id}
isAdmin={can(user, 'MODERATE_COMMENTS')}
/>}
/>
)}
<div className="talk-stream-wrapper-box">
<Slot fill="streamBox" />
</div>
{/* 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}
/>
: <div>
: <div className="talk-stream-comments-container">
<NewCount
count={comments.nodes.length - view.length}
loadMore={this.viewNewComments}
@@ -245,6 +251,7 @@ class Stream extends React.Component {
return commentIsIgnored(comment)
? <IgnoredCommentTombstone key={comment.id} />
: <Comment
commentClassNames={commentClassNames}
data={this.props.data}
root={this.props.root}
disableReply={!open}
@@ -1,3 +1,5 @@
export const SET_ACTIVE_REPLY_BOX = 'SET_ACTIVE_REPLY_BOX';
export const ADDTL_COMMENTS_ON_LOAD_MORE = 10;
export const VIEW_ALL_COMMENTS = 'VIEW_ALL_COMMENTS';
export const ADD_COMMENT_CLASSNAME = 'ADD_COMMENT_CLASSNAME';
export const REMOVE_COMMENT_CLASSNAME = 'REMOVE_COMMENT_CLASSNAME';
@@ -290,6 +290,7 @@ const mapStateToProps = (state) => ({
assetUrl: state.stream.assetUrl,
activeTab: state.embed.activeTab,
previousTab: state.embed.previousTab,
commentClassNames: state.stream.commentClassNames
});
const mapDispatchToProps = (dispatch) =>
@@ -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
@@ -4,6 +4,6 @@ import stream from './stream';
export default {
embed,
stream,
config,
stream,
};
@@ -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;
}
@@ -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;
+2 -2
View File
@@ -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 (
<div className={cn({[styles.inline]: inline, [styles.debug]: config.debug})}>
<div className={cn({[styles.inline]: inline, [styles.debug]: config.debug}, className)}>
{getSlotElements(fill, {...rest, config})}
</div>
);
@@ -0,0 +1 @@
export {default as Slot} from './Slot';
@@ -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() : '';
}
+5 -4
View File
@@ -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);
}
-1
View File
@@ -1 +0,0 @@
export {withReaction} from '../coral-framework/hocs';
@@ -1,4 +1,6 @@
.moderationLink {
display: inline;
a {
color: #679af3;
text-decoration: none;
+14
View File
@@ -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"
]
}
+23
View File
@@ -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"] }]
}
}
+2
View File
@@ -0,0 +1,2 @@
export {addTag, removeTag} from 'coral-plugin-commentbox/actions';
export {addCommentClassName, removeCommentClassName} from 'coral-embed-stream/src/actions/stream';
@@ -0,0 +1,2 @@
export const commentBoxTagsSelector = (state) => state.commentBox.tags;
export const commentClassNamesSelector = (state) => state.stream.commentClassNames;
@@ -0,0 +1,2 @@
export {Slot} from 'coral-framework/components';
export {Icon} from 'coral-ui';
+1
View File
@@ -0,0 +1 @@
export {withReaction} from 'coral-framework/hocs';
+1
View File
@@ -0,0 +1 @@
export {t} from 'coral-framework/services/i18n';
View File
View File
@@ -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 = () => {
@@ -0,0 +1,5 @@
import {OFFTOPIC_TOGGLE_CHECKBOX} from './constants';
export const toggleCheckbox = () => ({
type: OFFTOPIC_TOGGLE_CHECKBOX
});
@@ -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 (
<div className={styles.offTopic}>
<label className={styles.offTopicLabel}>
<input type="checkbox" onChange={this.handleChange}/>
{t('off_topic')}
</label>
{
!this.props.isReply ? (
<label className={styles.offTopicLabel}>
<input type="checkbox" onChange={this.handleChange}/>
{t('off_topic')}
</label>
) : null
}
</div>
);
}
}
const mapStateToProps = ({commentBox}) => ({commentBox});
const mapDispatchToProps = (dispatch) =>
bindActionCreators({addTag, removeTag}, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(OffTopicCheckbox);
@@ -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 (
<div className={styles.viewingOption}>
<label>
<input type="checkbox" onChange={this.handleChange} checked={this.props.checked} />
Hide Off-Topic Comments
</label>
</div>
);
}
}
@@ -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) => (
<span>
{
isOffTopic(props.comment.tags) ? (
isOffTopic(props.comment.tags) && props.depth === 0 ? (
<span className={styles.tag}>
{t('off_topic')}
</span>
@@ -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;
}
@@ -0,0 +1 @@
export const OFFTOPIC_TOGGLE_CHECKBOX = 'OFFTOPIC_TOGGLE_CHECKBOX';
@@ -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);
@@ -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);
+12 -3
View File
@@ -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]
}
};
@@ -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;
}
}
@@ -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"
]
}
@@ -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"] }]
}
}
@@ -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
});
@@ -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;
}
@@ -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 (
<div className={cn([styles.root, 'coral-plugin-viewing-options'])}>
<div>
<a onClick={toggleOpen}>Viewing Options
{props.open ? <Icon name="arrow_drop_up"/> : <Icon name="arrow_drop_down"/>}
</a>
</div>
{
props.open ? (
<div className={cn([styles.list, 'coral-plugin-viewing-options-list'])}>
<ul>
{
React.Children.map(<Slot fill="viewingOptions" />, (component) => {
return React.createElement('li', {
className: 'coral-plugin-viewing-options-item'
}, component);
})
}
</ul>
</div>
) : null
}
</div>
);
};
export default ViewingOptions;
@@ -0,0 +1,2 @@
export const VIEWING_OPTIONS_OPEN = 'VIEWING_OPTIONS_OPEN';
export const VIEWING_OPTIONS_CLOSE = 'VIEWING_OPTIONS_CLOSE';
@@ -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);
@@ -0,0 +1,9 @@
import ViewingOptions from './containers/ViewingOptions';
import reducer from './reducer';
export default {
reducer,
slots: {
streamBox: [ViewingOptions]
}
};
@@ -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;
}
}
@@ -0,0 +1 @@
module.exports = {};
+1
View File
@@ -154,6 +154,7 @@ const config = {
},
resolve: {
alias: {
'plugin-api': path.resolve(__dirname, 'plugin-api/'),
plugins: path.resolve(__dirname, 'plugins/'),
pluginsConfig: pluginsConfigPath
},