diff --git a/.eslintignore b/.eslintignore
index 2702e0a4d..3811f290c 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -13,5 +13,6 @@ plugins/*
!plugins/talk-plugin-comment-content
!plugins/talk-plugin-permalink
!plugins/talk-plugin-featured-comments
+!plugins/talk-plugin-sort-oldest
node_modules
diff --git a/.gitignore b/.gitignore
index 882c24883..3f5cbc085 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,5 +29,6 @@ plugins/*
!plugins/talk-plugin-comment-content
!plugins/talk-plugin-permalink
!plugins/talk-plugin-featured-comments
+!plugins/talk-plugin-sort-oldest
**/node_modules/*
diff --git a/client/coral-embed-stream/src/actions/stream.js b/client/coral-embed-stream/src/actions/stream.js
index c20760259..d7301f8a1 100644
--- a/client/coral-embed-stream/src/actions/stream.js
+++ b/client/coral-embed-stream/src/actions/stream.js
@@ -5,6 +5,8 @@ import queryString from 'query-string';
export const setActiveReplyBox = (id) => ({type: actions.SET_ACTIVE_REPLY_BOX, id});
+export const setSort = ({order, by}) => ({type: actions.SET_SORT, order, by});
+
export const viewAllComments = () => {
const search = queryString.stringify({
diff --git a/client/coral-embed-stream/src/components/Stream.js b/client/coral-embed-stream/src/components/Stream.js
index b37d232f7..544515da4 100644
--- a/client/coral-embed-stream/src/components/Stream.js
+++ b/client/coral-embed-stream/src/components/Stream.js
@@ -13,7 +13,7 @@ import t, {timeago} from 'coral-framework/services/i18n';
import CommentBox from 'talk-plugin-commentbox/CommentBox';
import QuestionBox from 'talk-plugin-questionbox/QuestionBox';
import {isCommentActive} from 'coral-framework/utils';
-import {Button, Tab, TabCount, TabPane} from 'coral-ui';
+import {Spinner, Button, Tab, TabCount, TabPane} from 'coral-ui';
import cn from 'classnames';
import {getTopLevelParent, attachCommentToParent} from '../graphql/utils';
@@ -23,6 +23,8 @@ import StreamTabPanel from '../containers/StreamTabPanel';
import styles from './Stream.css';
+const SpinnerWhenLoading = ({loading, children}) => loading ? :
{children}
;
+
class Stream extends React.Component {
constructor(props) {
@@ -49,15 +51,14 @@ class Stream extends React.Component {
);
};
- render() {
+ renderHighlightedComment() {
const {
data,
root,
activeReplyBox,
setActiveReplyBox,
- appendItemArray,
commentClassNames,
- root: {asset, asset: {comment, comments, totalCommentCount}},
+ root: {asset, asset: {comment}},
postComment,
addNotification,
editComment,
@@ -65,23 +66,15 @@ class Stream extends React.Component {
postDontAgree,
deleteAction,
showSignInDialog,
- updateItem,
ignoreUser,
- activeStreamTab,
- setActiveStreamTab,
loadNewReplies,
- loadMoreComments,
- viewAllComments,
- auth: {loggedIn, user},
- editName,
+ auth: {user},
emit,
} = this.props;
- const {keepCommentBox} = this.state;
- const open = !asset.isClosed;
// even though the permalinked comment is the highlighted one, we're displaying its parent + replies
- let highlightedComment = comment && getTopLevelParent(comment);
- if (highlightedComment) {
+ let topLevelComment = getTopLevelParent(comment);
+ if (topLevelComment) {
// Inactive comments can be viewed by moderators and admins (e.g. using permalinks).
const isInactive = !isCommentActive(comment.status);
@@ -89,10 +82,152 @@ class Stream extends React.Component {
// the highlighted comment is not active and as such not in the replies, so we
// attach it to the right parent.
- highlightedComment = attachCommentToParent(highlightedComment, comment);
+ topLevelComment = attachCommentToParent(topLevelComment, comment);
}
}
+ return (
+
+
+
+ );
+ }
+
+ renderTabPanel() {
+ const {
+ data,
+ root,
+ activeReplyBox,
+ setActiveReplyBox,
+ commentClassNames,
+ root: {asset, asset: {comments, totalCommentCount}},
+ postComment,
+ addNotification,
+ editComment,
+ postFlag,
+ postDontAgree,
+ deleteAction,
+ showSignInDialog,
+ ignoreUser,
+ activeStreamTab,
+ setActiveStreamTab,
+ loadNewReplies,
+ loadMoreComments,
+ auth: {user},
+ emit,
+ sortOrder,
+ sortBy,
+ } = this.props;
+
+ const slotProps = {data};
+ const slotQueryData = {root, asset};
+
+ // `key` of `StreamTabPanel` depends on sorting so that we always reset
+ // the state when changing sorting.
+ return (
+
+
+
+
+
+ All Comments {totalCommentCount}
+
+ }
+ appendTabPanes={
+
+
+
+ }
+ sub
+ />
+
+ );
+ }
+
+ render() {
+ const {
+ data,
+ root,
+ appendItemArray,
+ root: {asset, asset: {comment: highlightedComment, comments}},
+ postComment,
+ addNotification,
+ updateItem,
+ viewAllComments,
+ auth: {loggedIn, user},
+ editName,
+ loading,
+ } = this.props;
+ const {keepCommentBox} = this.state;
+ const open = !asset.isClosed;
+
const banned = user && user.status === 'BANNED';
const temporarilySuspended =
user &&
@@ -103,7 +238,7 @@ class Stream extends React.Component {
const slotProps = {data};
const slotQueryData = {root, asset};
- if (!comment && !comments) {
+ if (!highlightedComment && !comments) {
console.error('Talk: No comments came back from the graph given that query. Please, check the query params.');
return ;
}
@@ -111,7 +246,7 @@ class Stream extends React.Component {
return (
- {comment &&
+ {highlightedComment &&
)}
- {/* the highlightedComment is isolated after the user followed a permalink */}
- {highlightedComment
- ? (
-
-
-
- )
- :
-
-
-
-
- All Comments {totalCommentCount}
-
- }
- appendTabPanes={
-
-
-
- }
- sub
- />
-
+
+ {highlightedComment
+ ? this.renderHighlightedComment()
+ : this.renderTabPanel()
}
+
);
}
diff --git a/client/coral-embed-stream/src/constants/stream.js b/client/coral-embed-stream/src/constants/stream.js
index 7ac9427cf..b4deb24e1 100644
--- a/client/coral-embed-stream/src/constants/stream.js
+++ b/client/coral-embed-stream/src/constants/stream.js
@@ -6,3 +6,4 @@ export const ADD_COMMENT_CLASSNAME = 'ADD_COMMENT_CLASSNAME';
export const REMOVE_COMMENT_CLASSNAME = 'REMOVE_COMMENT_CLASSNAME';
export const THREADING_LEVEL = process.env.TALK_THREADING_LEVEL;
export const SET_ACTIVE_TAB = 'CORAL_STREAM_SET_ACTIVE_TAB';
+export const SET_SORT = 'CORAL_STREAM_SET_SORT';
diff --git a/client/coral-embed-stream/src/containers/Embed.js b/client/coral-embed-stream/src/containers/Embed.js
index 5a0ea534d..289d370a1 100644
--- a/client/coral-embed-stream/src/containers/Embed.js
+++ b/client/coral-embed-stream/src/containers/Embed.js
@@ -158,7 +158,7 @@ const EMBED_QUERY = gql`
$hasComment: Boolean!,
$excludeIgnored: Boolean,
$sortBy: SORT_COMMENTS_BY!,
- $sort: SORT_ORDER!,
+ $sortOrder: SORT_ORDER!,
) {
me {
id
@@ -171,7 +171,7 @@ const EMBED_QUERY = gql`
`;
export const withEmbedQuery = withQuery(EMBED_QUERY, {
- options: ({auth, commentId, assetId, assetUrl, sortBy, sort}) => ({
+ options: ({auth, commentId, assetId, assetUrl, sortBy, sortOrder}) => ({
variables: {
assetId,
assetUrl,
@@ -179,7 +179,7 @@ export const withEmbedQuery = withQuery(EMBED_QUERY, {
hasComment: commentId !== '',
excludeIgnored: Boolean(auth && auth.user && auth.user.id),
sortBy,
- sort,
+ sortOrder,
},
}),
});
@@ -191,7 +191,7 @@ const mapStateToProps = (state) => ({
assetUrl: state.stream.assetUrl,
activeTab: state.embed.activeTab,
config: state.config,
- sort: state.stream.sort,
+ sortOrder: state.stream.sortOrder,
sortBy: state.stream.sortBy,
});
diff --git a/client/coral-embed-stream/src/containers/Stream.js b/client/coral-embed-stream/src/containers/Stream.js
index 4720a6889..b98d7e9cf 100644
--- a/client/coral-embed-stream/src/containers/Stream.js
+++ b/client/coral-embed-stream/src/containers/Stream.js
@@ -102,7 +102,7 @@ class StreamContainer extends React.Component {
cursor: comment.replies.endCursor,
parent_id,
asset_id: this.props.root.asset.id,
- sort: 'ASC',
+ sortOrder: 'ASC',
excludeIgnored: this.props.data.variables.excludeIgnored,
},
updateQuery: (prev, {fetchMoreResult:{comments}}) => {
@@ -119,7 +119,7 @@ class StreamContainer extends React.Component {
cursor: this.props.root.asset.comments.endCursor,
parent_id: null,
asset_id: this.props.root.asset.id,
- sort: this.props.data.variables.sort,
+ sortOrder: this.props.data.variables.sortOrder,
sortBy: this.props.data.variables.sortBy,
excludeIgnored: this.props.data.variables.excludeIgnored,
},
@@ -141,24 +141,43 @@ class StreamContainer extends React.Component {
clearInterval(this.countPoll);
}
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.sortBy === 'CREATED_AT' && nextProps.sortOrder === 'DESC') {
+
+ // When switching to 'Newest first' we refetch and subscribe so that we always have the newest comments.
+ if (this.props.sortOrder !== nextProps.sortOrder || this.props.sortBy !== nextProps.sortBy) {
+ nextProps.data.refetch();
+ this.subscribeToUpdates();
+ }
+ } else {
+
+ // TODO: only unsubscribe from posting comments.
+ // We only subscribe to new comments during 'Newest first'.
+ this.unsubscribe();
+ }
+ }
+
userIsDegraged({auth: {user}} = this.props) {
return !can(user, 'INTERACT_WITH_COMMUNITY');
}
render() {
- if (this.props.refetching
- || !this.props.root.asset
+ if (!this.props.root.asset
|| !this.props.root.asset.comment
&& !this.props.root.asset.comments
) {
return ;
}
+
+ const streamLoading = this.props.refetching || this.props.data.loading;
+
return ;
}
}
@@ -209,7 +228,7 @@ const LOAD_MORE_QUERY = gql`
$cursor: Cursor
$parent_id: ID
$asset_id: ID
- $sort: SORT_ORDER
+ $sortOrder: SORT_ORDER
$sortBy: SORT_COMMENTS_BY = CREATED_AT
$excludeIgnored: Boolean
) {
@@ -219,7 +238,7 @@ const LOAD_MORE_QUERY = gql`
cursor: $cursor
parent_id: $parent_id
asset_id: $asset_id
- sort: $sort
+ sort: $sortOrder
sortBy: $sortBy
excludeIgnored: $excludeIgnored
}
@@ -274,7 +293,7 @@ const fragments = {
}
commentCount @skip(if: $hasComment)
totalCommentCount @skip(if: $hasComment)
- comments(query: {limit: 10, excludeIgnored: $excludeIgnored, sort: $sort, sortBy: $sortBy}) @skip(if: $hasComment) {
+ comments(query: {limit: 10, excludeIgnored: $excludeIgnored, sort: $sortOrder, sortBy: $sortBy}) @skip(if: $hasComment) {
nodes {
...CoralEmbedStream_Stream_comment
}
@@ -317,6 +336,8 @@ const mapStateToProps = (state) => ({
previousStreamTab: state.stream.previousTab,
commentClassNames: state.stream.commentClassNames,
pluginConfig: state.config.plugin_config,
+ sortOrder: state.stream.sortOrder,
+ sortBy: state.stream.sortBy,
});
const mapDispatchToProps = (dispatch) =>
diff --git a/client/coral-embed-stream/src/reducers/stream.js b/client/coral-embed-stream/src/reducers/stream.js
index 02d6933ba..dd1817769 100644
--- a/client/coral-embed-stream/src/reducers/stream.js
+++ b/client/coral-embed-stream/src/reducers/stream.js
@@ -24,7 +24,7 @@ const initialState = {
activeTab: process.env.TALK_DEFAULT_STREAM_TAB,
previousTab: '',
sortBy: 'CREATED_AT',
- sort: 'DESC',
+ sortOrder: 'DESC',
};
export default function stream(state = initialState, action) {
@@ -68,6 +68,12 @@ export default function stream(state = initialState, action) {
...state.commentClassNames.slice(action.idx + 1)
]
};
+ case actions.SET_SORT :
+ return {
+ ...state,
+ sortOrder: action.order ? action.order : state.order,
+ sortBy: action.by ? action.by : state.by,
+ };
default:
return state;
}
diff --git a/client/coral-framework/components/Slot.js b/client/coral-framework/components/Slot.js
index c0cca143c..c4d680afb 100644
--- a/client/coral-framework/components/Slot.js
+++ b/client/coral-framework/components/Slot.js
@@ -6,6 +6,7 @@ import {getSlotElements, getSlotComponentProps} from 'coral-framework/helpers/pl
import omit from 'lodash/omit';
import isEqual from 'lodash/isEqual';
import {getShallowChanges} from 'coral-framework/utils';
+import PropTypes from 'prop-types';
const emptyConfig = {};
@@ -25,7 +26,18 @@ class Slot extends React.Component {
return changes.length !== 0;
}
- getSlotProps({fill: _a, inline: _b, className: _c, reduxState: _d, defaultComponent_: _e, queryData: _f, ...rest} = this.props) {
+ getSlotProps(props = this.props) {
+ const {
+ fill: _a,
+ inline: _b,
+ className: _c,
+ reduxState: _d,
+ defaultComponent_: _e,
+ queryData: _f,
+ childFactory: _g,
+ component: _h,
+ ...rest
+ } = props;
return rest;
}
@@ -34,26 +46,60 @@ class Slot extends React.Component {
}
render() {
- const {inline = false, className, reduxState, defaultComponent: DefaultComponent, queryData} = this.props;
+ const {
+ inline = false,
+ className,
+ reduxState,
+ component: Component,
+ childFactory,
+ defaultComponent: DefaultComponent,
+ queryData,
+ } = this.props;
let children = this.getChildren();
const pluginConfig = reduxState.config.pluginConfig || emptyConfig;
if (children.length === 0 && DefaultComponent) {
children = ;
}
+ if (childFactory) {
+ children = children.map(childFactory);
+ }
+
return (
-
+
{children}
-
+
);
}
}
+Slot.defaultProps = {
+ component: 'div',
+};
+
Slot.propTypes = {
- fill: React.PropTypes.string.isRequired,
+ fill: PropTypes.string.isRequired,
+
+ /**
+ * You may specify the component to use as the root wrapper.
+ * Defaults to 'div'.
+ */
+ component: PropTypes.any,
// props coming from graphql must be passed through this property.
- queryData: React.PropTypes.object,
+ queryData: PropTypes.object,
+
+ /**
+ * You may need to apply reactive updates to a child as it is exiting.
+ * This is generally done by using `cloneElement` however in the case of an exiting
+ * child the element has already been removed and not accessible to the consumer.
+ *
+ * If you do need to update a child as it leaves you can provide a `childFactory`
+ * to wrap every child, even the ones that are leaving.
+ *
+ * @type Function(child: ReactElement) -> ReactElement
+ */
+ childFactory: PropTypes.func,
};
const mapStateToProps = (state) => ({
diff --git a/client/coral-framework/utils/index.js b/client/coral-framework/utils/index.js
index 634b6661f..26320df97 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';
+export * from 'coral-framework/helpers/strings';
export const getTotalActionCount = (type, comment) => {
return comment.action_summaries
diff --git a/plugin-api/beta/client/actions/stream.js b/plugin-api/beta/client/actions/stream.js
new file mode 100644
index 000000000..8171357ba
--- /dev/null
+++ b/plugin-api/beta/client/actions/stream.js
@@ -0,0 +1 @@
+export {setSort} from 'coral-embed-stream/src/actions/stream';
diff --git a/plugin-api/beta/client/components/SortOption.css b/plugin-api/beta/client/components/SortOption.css
new file mode 100644
index 000000000..f2df56082
--- /dev/null
+++ b/plugin-api/beta/client/components/SortOption.css
@@ -0,0 +1,8 @@
+.input {
+ cursor: pointer;
+}
+
+.label {
+ cursor: pointer;
+ padding-left: 4px;
+}
diff --git a/plugin-api/beta/client/components/SortOption.js b/plugin-api/beta/client/components/SortOption.js
new file mode 100644
index 000000000..42e1c7ba5
--- /dev/null
+++ b/plugin-api/beta/client/components/SortOption.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import styles from './SortOption.css';
+
+export default class SortOption extends React.Component {
+ render() {
+ return (
+
+
+
+ {this.props.label}
+
+
+ );
+ }
+}
diff --git a/plugin-api/beta/client/components/index.js b/plugin-api/beta/client/components/index.js
index 8b0448f4e..4367d3420 100644
--- a/plugin-api/beta/client/components/index.js
+++ b/plugin-api/beta/client/components/index.js
@@ -1,3 +1,5 @@
export {Slot} from 'coral-framework/components';
export {default as ClickOutside} from 'coral-framework/components/ClickOutside';
+export {default as IfSlotIsEmpty} from 'coral-framework/components/IfSlotIsEmpty';
+export {default as IfSlotIsNotEmpty} from 'coral-framework/components/IfSlotIsNotEmpty';
export {default as CommentAuthorName} from 'coral-framework/components/CommentAuthorName';
diff --git a/plugin-api/beta/client/hocs/index.js b/plugin-api/beta/client/hocs/index.js
index 60b118522..1bc47868e 100644
--- a/plugin-api/beta/client/hocs/index.js
+++ b/plugin-api/beta/client/hocs/index.js
@@ -1,5 +1,6 @@
export {default as withReaction} from './withReaction';
export {default as withTags} from './withTags';
+export {default as withSortOption} from './withSortOption';
export {default as withFragments} from 'coral-framework/hocs/withFragments';
export {default as excludeIf} from 'coral-framework/hocs/excludeIf';
export {default as connect} from 'coral-framework/hocs/connect';
diff --git a/plugin-api/beta/client/hocs/withSortOption.js b/plugin-api/beta/client/hocs/withSortOption.js
new file mode 100644
index 000000000..16162710d
--- /dev/null
+++ b/plugin-api/beta/client/hocs/withSortOption.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import {connect} from 'plugin-api/beta/client/hocs';
+import {bindActionCreators} from 'redux';
+import {sortOrderSelector, sortBySelector} from 'plugin-api/beta/client/selectors/stream';
+import {setSort} from 'plugin-api/beta/client/actions/stream';
+import hoistStatics from 'recompose/hoistStatics';
+
+const mapStateToProps = (state) => ({
+ sortOrder: sortOrderSelector(state),
+ sortBy: sortBySelector(state),
+});
+
+const mapDispatchToProps = (dispatch) =>
+ bindActionCreators(
+ {
+ setSort
+ },
+ dispatch
+ );
+
+export default ({by = 'created_at', order = 'DESC', label}) => hoistStatics((WrappedComponent) => {
+ class WithSortOption extends React.Component {
+ setSort = () => {
+ this.props.setSort({by, order});
+ }
+
+ render() {
+ const active = this.props.sortOrder === order && this.props.sortBy === by;
+ const resolvedLabel = typeof label === 'function' ? label() : label;
+ return (
+
+ );
+ }
+ }
+ return connect(mapStateToProps, mapDispatchToProps)(WithSortOption);
+});
diff --git a/plugin-api/beta/client/selectors/stream.js b/plugin-api/beta/client/selectors/stream.js
new file mode 100644
index 000000000..6d8934bd3
--- /dev/null
+++ b/plugin-api/beta/client/selectors/stream.js
@@ -0,0 +1,2 @@
+export const sortOrderSelector = (state) => state.stream.sortOrder;
+export const sortBySelector = (state) => state.stream.sortBy;
diff --git a/plugin-api/beta/client/utils/index.js b/plugin-api/beta/client/utils/index.js
index e0ff635e0..f652b2b66 100644
--- a/plugin-api/beta/client/utils/index.js
+++ b/plugin-api/beta/client/utils/index.js
@@ -3,5 +3,6 @@ export {
insertCommentsSorted,
getSlotFragmentSpreads,
forEachError,
+ capitalize,
getDefinitionName,
} from 'coral-framework/utils';
diff --git a/plugins/talk-plugin-featured-comments/client/containers/TabPane.js b/plugins/talk-plugin-featured-comments/client/containers/TabPane.js
index 9a2280233..6d9781ec4 100644
--- a/plugins/talk-plugin-featured-comments/client/containers/TabPane.js
+++ b/plugins/talk-plugin-featured-comments/client/containers/TabPane.js
@@ -18,7 +18,7 @@ class TabPaneContainer extends React.Component {
limit: 5,
cursor: this.props.asset.featuredComments.endCursor,
asset_id: this.props.asset.id,
- sort: this.props.data.variables.sort,
+ sortOrder: this.props.data.variables.sortOrder,
sortBy: this.props.data.variables.sortBy,
excludeIgnored: this.props.data.variables.excludeIgnored,
},
@@ -52,7 +52,7 @@ const LOAD_MORE_QUERY = gql`
$limit: Int = 5
$cursor: Cursor
$asset_id: ID
- $sort: SORT_ORDER
+ $sortOrder: SORT_ORDER
$sortBy: SORT_COMMENTS_BY
$excludeIgnored: Boolean
) {
@@ -62,7 +62,7 @@ const LOAD_MORE_QUERY = gql`
cursor: $cursor
tags: ["FEATURED"]
asset_id: $asset_id,
- sort: $sort
+ sort: $sortOrder
sortBy: $sortBy
excludeIgnored: $excludeIgnored
}
@@ -100,7 +100,7 @@ const enhance = compose(
featuredComments: comments(
query: {
tags: ["FEATURED"]
- sort: $sort
+ sort: $sortOrder
sortBy: $sortBy
}
deep: true
diff --git a/plugins/talk-plugin-offtopic/client/components/styles.css b/plugins/talk-plugin-offtopic/client/components/styles.css
index c2f3e4e36..22d1f787a 100644
--- a/plugins/talk-plugin-offtopic/client/components/styles.css
+++ b/plugins/talk-plugin-offtopic/client/components/styles.css
@@ -19,7 +19,6 @@
}
.viewingOption {
- padding: 5px 0;
}
:global(.talk-plugin-off-topic-comment) {
diff --git a/plugins/talk-plugin-offtopic/client/index.js b/plugins/talk-plugin-offtopic/client/index.js
index 24bfc095f..f7156a0ec 100644
--- a/plugins/talk-plugin-offtopic/client/index.js
+++ b/plugins/talk-plugin-offtopic/client/index.js
@@ -6,7 +6,7 @@ import reducer from './reducer';
/**
* talk-plugin-offtopic depends on talk-plugin-viewing-options
- * in other to display filter and use the streamViewingOptions slot
+ * in other to display filter.
*/
export default {
@@ -15,6 +15,6 @@ export default {
slots: {
commentInputDetailArea: [OffTopicCheckbox],
commentInfoBar: [OffTopicTag],
- viewingOptions: [OffTopicFilter]
+ viewingOptionsFilter: [OffTopicFilter]
}
};
diff --git a/plugins/talk-plugin-sort-oldest/client/.babelrc b/plugins/talk-plugin-sort-oldest/client/.babelrc
new file mode 100644
index 000000000..60be246eb
--- /dev/null
+++ b/plugins/talk-plugin-sort-oldest/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/talk-plugin-sort-oldest/client/.eslintrc.json b/plugins/talk-plugin-sort-oldest/client/.eslintrc.json
new file mode 100644
index 000000000..9fe56bd14
--- /dev/null
+++ b/plugins/talk-plugin-sort-oldest/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/talk-plugin-sort-oldest/client/components/SortOption.css b/plugins/talk-plugin-sort-oldest/client/components/SortOption.css
new file mode 100644
index 000000000..b8d6cacc5
--- /dev/null
+++ b/plugins/talk-plugin-sort-oldest/client/components/SortOption.css
@@ -0,0 +1,9 @@
+
+.input {
+ cursor: pointer;
+}
+
+.label {
+ cursor: pointer;
+ padding-left: 4px;
+}
diff --git a/plugins/talk-plugin-sort-oldest/client/components/SortOption.js b/plugins/talk-plugin-sort-oldest/client/components/SortOption.js
new file mode 100644
index 000000000..42e1c7ba5
--- /dev/null
+++ b/plugins/talk-plugin-sort-oldest/client/components/SortOption.js
@@ -0,0 +1,15 @@
+import React from 'react';
+import styles from './SortOption.css';
+
+export default class SortOption extends React.Component {
+ render() {
+ return (
+
+
+
+ {this.props.label}
+
+
+ );
+ }
+}
diff --git a/plugins/talk-plugin-sort-oldest/client/containers/SortOption.js b/plugins/talk-plugin-sort-oldest/client/containers/SortOption.js
new file mode 100644
index 000000000..9c4567f18
--- /dev/null
+++ b/plugins/talk-plugin-sort-oldest/client/containers/SortOption.js
@@ -0,0 +1,4 @@
+import {withSortOption} from 'plugin-api/beta/client/hocs';
+import SortOption from '../components/SortOption';
+
+export default withSortOption({by: 'CREATED_AT', order: 'ASC', label: 'Oldest first'})(SortOption);
diff --git a/plugins/talk-plugin-sort-oldest/client/containers/SortOptionNewest.js b/plugins/talk-plugin-sort-oldest/client/containers/SortOptionNewest.js
new file mode 100644
index 000000000..7ab5dcad7
--- /dev/null
+++ b/plugins/talk-plugin-sort-oldest/client/containers/SortOptionNewest.js
@@ -0,0 +1,4 @@
+import {withSortOption} from 'plugin-api/beta/client/hocs';
+import SortOption from '../components/SortOption';
+
+export default withSortOption({by: 'CREATED_AT', order: 'DESC', label: 'Newest first'})(SortOption);
diff --git a/plugins/talk-plugin-sort-oldest/client/index.js b/plugins/talk-plugin-sort-oldest/client/index.js
new file mode 100644
index 000000000..5d3f08ee8
--- /dev/null
+++ b/plugins/talk-plugin-sort-oldest/client/index.js
@@ -0,0 +1,15 @@
+import translations from './translations.yml';
+import SortOption from './containers/SortOption';
+import SortOptionNewest from './containers/SortOptionNewest';
+
+/**
+ * talk-plugin-sort-oldest depends on talk-plugin-viewing-options
+ * in other to display sort option.
+ */
+
+export default {
+ translations,
+ slots: {
+ viewingOptionsSort: [SortOptionNewest, SortOption]
+ }
+};
diff --git a/plugins/talk-plugin-sort-oldest/client/translations.yml b/plugins/talk-plugin-sort-oldest/client/translations.yml
new file mode 100644
index 000000000..50645be77
--- /dev/null
+++ b/plugins/talk-plugin-sort-oldest/client/translations.yml
@@ -0,0 +1,4 @@
+en:
+ talk-plugin-sort-oldest:
+es:
+ talk-plugin-sort-oldest:
diff --git a/plugins/talk-plugin-sort-oldest/index.js b/plugins/talk-plugin-sort-oldest/index.js
new file mode 100644
index 000000000..7be35b6b6
--- /dev/null
+++ b/plugins/talk-plugin-sort-oldest/index.js
@@ -0,0 +1,2 @@
+module.exports = {
+};
diff --git a/plugins/talk-plugin-viewing-options/client/components/Category.css b/plugins/talk-plugin-viewing-options/client/components/Category.css
new file mode 100644
index 000000000..758975fce
--- /dev/null
+++ b/plugins/talk-plugin-viewing-options/client/components/Category.css
@@ -0,0 +1,15 @@
+.title {
+ font-weight: bold;
+ padding: 12px 8px 0px 8px;
+}
+
+.list {
+ padding: 0;
+ margin: 0;
+}
+
+.listItem {
+ padding: 10px;
+ list-style: none;
+ white-space: nowrap;
+}
diff --git a/plugins/talk-plugin-viewing-options/client/components/Category.js b/plugins/talk-plugin-viewing-options/client/components/Category.js
new file mode 100644
index 000000000..0ef08b6d0
--- /dev/null
+++ b/plugins/talk-plugin-viewing-options/client/components/Category.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import styles from './Category.css';
+import {Slot, IfSlotIsNotEmpty} from 'plugin-api/beta/client/components';
+
+const childFactory = (child) => {child} ;
+
+const ViewingOptions = ({slot, title}) => {
+ return (
+
+ {title}
+
+
+ );
+};
+
+export default ViewingOptions;
diff --git a/plugins/talk-plugin-viewing-options/client/components/ViewingOptions.css b/plugins/talk-plugin-viewing-options/client/components/ViewingOptions.css
index c51d87f11..c71094e33 100644
--- a/plugins/talk-plugin-viewing-options/client/components/ViewingOptions.css
+++ b/plugins/talk-plugin-viewing-options/client/components/ViewingOptions.css
@@ -9,13 +9,15 @@
cursor: pointer;
}
-.list {
+.menu {
background: white;
position: absolute;
box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.15);
right: 0px;
top: 20px;
z-index: 10;
+ min-height: 32px;
+ min-width: 64px;
}
.list > ul, .list > ul > li {
diff --git a/plugins/talk-plugin-viewing-options/client/components/ViewingOptions.js b/plugins/talk-plugin-viewing-options/client/components/ViewingOptions.js
index 8988dd1ec..a7d6040e1 100644
--- a/plugins/talk-plugin-viewing-options/client/components/ViewingOptions.js
+++ b/plugins/talk-plugin-viewing-options/client/components/ViewingOptions.js
@@ -3,51 +3,60 @@ import cn from 'classnames';
import styles from './ViewingOptions.css';
import {t} from 'plugin-api/beta/client/services';
import {Icon} from 'plugin-api/beta/client/components/ui';
-import {Slot, ClickOutside} from 'plugin-api/beta/client/components';
+import {ClickOutside} from 'plugin-api/beta/client/components';
+import {capitalize} from 'plugin-api/beta/client/utils';
+import Category from './Category';
-const ViewingOptions = (props) => {
- const toggleOpen = () => {
- if (!props.open) {
- props.openMenu();
+class ViewingOptions extends React.Component {
+
+ categories = {
+ sort: t('talk-plugin-viewing-options.sort'),
+ filter: t('talk-plugin-viewing-options.filter'),
+ };
+
+ toggleOpen = () => {
+ const {open, openMenu, closeMenu} = this.props;
+ if (!open) {
+ openMenu();
} else {
- props.closeMenu();
+ closeMenu();
}
};
- const handleClickOutside = () => {
- if (props.open) {
- props.closeMenu();
+ handleClickOutside = () => {
+ const {open, closeMenu} = this.props;
+ if (open) {
+ closeMenu();
}
};
- return (
-
-
-
-
-
- {t('talk-plugin-viewing-options.viewing_options')}
- {props.open ? : }
-
-
- {
- props.open ? (
-
-
+ render() {
+ const {open} = this.props;
+ return (
+
+
+
+
+
+ {t('talk-plugin-viewing-options.viewing_options')}
+ {open ? : }
+
+
+ {
+ open ? (
+
{
- React.Children.map( , (component) => {
- return React.createElement('li', {
- className: 'talk-plugin-viewing-options-item'
- }, component);
- })
+ Object.keys(this.categories).map((category) =>
+
+ )
}
-
-
- ) : null
- }
-
-
- );
-};
+
+ ) : null
+ }
+
+
+ );
+ }
+}
export default ViewingOptions;
diff --git a/plugins/talk-plugin-viewing-options/client/translations.yml b/plugins/talk-plugin-viewing-options/client/translations.yml
index d1b1c1896..8560b79cc 100644
--- a/plugins/talk-plugin-viewing-options/client/translations.yml
+++ b/plugins/talk-plugin-viewing-options/client/translations.yml
@@ -1,6 +1,8 @@
en:
talk-plugin-viewing-options:
viewing_options: "Viewing Options"
+ sort: Sort
+ filter: Filter
es:
talk-plugin-viewing-options:
viewing_options: "Opciones de visualización"