diff --git a/plugin-api/beta/client/actions/notification.js b/plugin-api/beta/client/actions/notification.js
new file mode 100644
index 000000000..c1a75cb7a
--- /dev/null
+++ b/plugin-api/beta/client/actions/notification.js
@@ -0,0 +1 @@
+export {addNotification} from 'coral-framework/actions/notification';
diff --git a/plugin-api/beta/client/services/index.js b/plugin-api/beta/client/services/index.js
index ddaa346ab..ab0ab60b2 100644
--- a/plugin-api/beta/client/services/index.js
+++ b/plugin-api/beta/client/services/index.js
@@ -1,3 +1,3 @@
-export {t} from 'coral-framework/services/i18n';
+export {t, timeago} from 'coral-framework/services/i18n';
export {can} from 'coral-framework/services/perms';
export {isSlotEmpty} from 'coral-framework/helpers/plugins';
diff --git a/plugin-api/beta/client/utils/index.js b/plugin-api/beta/client/utils/index.js
index aa63ccad2..5b9748699 100644
--- a/plugin-api/beta/client/utils/index.js
+++ b/plugin-api/beta/client/utils/index.js
@@ -1,2 +1,6 @@
-export {isTagged, insertCommentsSorted} from 'coral-framework/utils';
-export {getSlotFragmentSpreads} from 'coral-framework/utils';
+export {
+ isTagged,
+ insertCommentsSorted,
+ getSlotFragmentSpreads,
+ forEachError,
+} from 'coral-framework/utils';
diff --git a/plugins/talk-plugin-featured-comments/client/components/Comment.js b/plugins/talk-plugin-featured-comments/client/components/Comment.js
index cce9d000d..7ac2869b1 100644
--- a/plugins/talk-plugin-featured-comments/client/components/Comment.js
+++ b/plugins/talk-plugin-featured-comments/client/components/Comment.js
@@ -2,7 +2,7 @@ import React from 'react';
import cn from 'classnames';
import styles from './Comment.css';
import {name} from '../../package.json';
-import {timeago} from 'coral-framework/services/i18n';
+import {timeago} from 'plugin-api/beta/client/services';
import {Slot} from 'plugin-api/beta/client/components';
import {Icon} from 'plugin-api/beta/client/components/ui';
diff --git a/plugins/talk-plugin-featured-comments/client/components/LoadMore.js b/plugins/talk-plugin-featured-comments/client/components/LoadMore.js
new file mode 100644
index 000000000..a168ed43a
--- /dev/null
+++ b/plugins/talk-plugin-featured-comments/client/components/LoadMore.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import {Button} from 'coral-ui';
+import t from 'coral-framework/services/i18n';
+import cn from 'classnames';
+
+class LoadMore extends React.Component {
+ render () {
+ const {loadingState, loadMore} = this.props;
+ const disabled = loadingState === 'loading';
+ return (
+
+
+
+ );
+ }
+}
+
+LoadMore.propTypes = {
+ loadMore: PropTypes.func.isRequired,
+ loadingState: PropTypes.oneOf(['', 'loading', 'success', 'error']),
+};
+
+export default LoadMore;
diff --git a/plugins/talk-plugin-featured-comments/client/components/Tab.js b/plugins/talk-plugin-featured-comments/client/components/Tab.js
index 643d74574..1b535f982 100644
--- a/plugins/talk-plugin-featured-comments/client/components/Tab.js
+++ b/plugins/talk-plugin-featured-comments/client/components/Tab.js
@@ -1,9 +1,9 @@
import React from 'react';
import {TabCount} from 'plugin-api/beta/client/components/ui';
+import {t} from 'plugin-api/beta/client/services';
-// TODO: This is just example code, and needs to replaced by an actual implementation.
export default ({active, asset: {featuredCommentsCount}}) => (
- Featured {featuredCommentsCount}
+ {t('featured')} {featuredCommentsCount}
);
diff --git a/plugins/talk-plugin-featured-comments/client/components/TabPane.js b/plugins/talk-plugin-featured-comments/client/components/TabPane.js
index 25d43aa7d..3cf8b2cfd 100644
--- a/plugins/talk-plugin-featured-comments/client/components/TabPane.js
+++ b/plugins/talk-plugin-featured-comments/client/components/TabPane.js
@@ -1,16 +1,47 @@
import React from 'react';
import Comment from '../containers/Comment';
+import LoadMore from './LoadMore';
+import {forEachError} from 'plugin-api/beta/client/utils';
-export default ({root, data, asset: {featuredComments, ...asset}, viewComment}) => (
-
- {featuredComments.nodes.map((comment) =>
-
- )}
-
-);
+class TabPane extends React.Component {
+ state = {
+ loadingState: '',
+ };
+
+ loadMore = () => {
+ this.setState({loadingState: 'loading'});
+ this.props.loadMore()
+ .then(() => {
+ this.setState({loadingState: 'success'});
+ })
+ .catch((error) => {
+ this.setState({loadingState: 'error'});
+ forEachError(error, ({msg}) => {this.props.addNotification('error', msg);});
+ });
+ }
+
+ render() {
+ const {root, data, asset: {featuredComments, ...asset}, viewComment} = this.props;
+ return (
+
+ {featuredComments.nodes.map((comment) =>
+
+ )}
+ {featuredComments.hasNextPage &&
+
+ }
+
+ );
+ }
+}
+
+export default TabPane;
diff --git a/plugins/talk-plugin-featured-comments/client/containers/TabPane.js b/plugins/talk-plugin-featured-comments/client/containers/TabPane.js
index 0dd83c92d..f7989099a 100644
--- a/plugins/talk-plugin-featured-comments/client/containers/TabPane.js
+++ b/plugins/talk-plugin-featured-comments/client/containers/TabPane.js
@@ -1,3 +1,4 @@
+import React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {compose, gql} from 'react-apollo';
@@ -5,12 +6,66 @@ import TabPane from '../components/TabPane';
import {withFragments} from 'plugin-api/beta/client/hocs';
import Comment from '../containers/Comment';
import {getDefinitionName} from 'coral-framework/utils';
-
+import {addNotification} from 'plugin-api/beta/client/actions/notification';
import {viewComment} from 'coral-embed-stream/src/actions/stream';
+import {insertCommentsSorted} from 'plugin-api/beta/client/utils';
+import update from 'immutability-helper';
+
+class TabPaneContainer extends React.Component {
+
+ loadMore = () => {
+ return this.props.data.fetchMore({
+ query: LOAD_MORE_QUERY,
+ variables: {
+ limit: 5,
+ cursor: this.props.root.asset.featuredComments.endCursor,
+ asset_id: this.props.root.asset.id,
+ sort: 'REVERSE_CHRONOLOGICAL',
+ excludeIgnored: this.props.data.variables.excludeIgnored,
+ },
+ updateQuery: (previous, {fetchMoreResult:{comments}}) => {
+ const updated = update(previous, {
+ asset: {
+ featuredComments: {
+ nodes: {
+ $apply: (nodes) => insertCommentsSorted(nodes, comments.nodes, 'REVERSE_CHRONOLOGICAL'),
+ },
+ hasNextPage: {$set: comments.hasNextPage},
+ endCursor: {$set: comments.endCursor},
+ },
+ }
+ });
+ return updated;
+ },
+ });
+ };
+
+ render() {
+ return ;
+ }
+}
+
+const LOAD_MORE_QUERY = gql`
+ query CoralEmbedStream_LoadMoreComments($limit: Int = 5, $cursor: Date, $asset_id: ID, $sort: SORT_ORDER, $excludeIgnored: Boolean) {
+ comments(query: {limit: $limit, cursor: $cursor, tags: ["FEATURED"], asset_id: $asset_id, sort: $sort, excludeIgnored: $excludeIgnored}) {
+ nodes {
+ ...${getDefinitionName(Comment.fragments.comment)}
+ }
+ hasNextPage
+ startCursor
+ endCursor
+ }
+ }
+ ${Comment.fragments.comment}
+`;
const mapDispatchToProps = (dispatch) =>
bindActionCreators({
viewComment,
+ addNotification,
}, dispatch);
const enhance = compose(
@@ -42,4 +97,4 @@ const enhance = compose(
}),
);
-export default enhance(TabPane);
+export default enhance(TabPaneContainer);