diff --git a/app.json b/app.json index e498c5168..5faa23bc2 100644 --- a/app.json +++ b/app.json @@ -29,5 +29,5 @@ "as": "POSTMARK" }], "image": "heroku/nodejs", - "success_url": "/admin/setup" + "success_url": "/admin/install" } diff --git a/client/coral-admin/src/components/EmptyCard.js b/client/coral-admin/src/components/EmptyCard.js new file mode 100644 index 000000000..42337a892 --- /dev/null +++ b/client/coral-admin/src/components/EmptyCard.js @@ -0,0 +1,14 @@ +import React, {PropTypes} from 'react'; +import {Card} from 'coral-ui'; + +const EmptyCard = props => ( + + {props.children} + +); + +EmptyCard.propTypes = { + children: PropTypes.node.isRequired +}; + +export default EmptyCard; diff --git a/client/coral-admin/src/components/ui/Drawer.css b/client/coral-admin/src/components/ui/Drawer.css new file mode 100644 index 000000000..e69de29bb diff --git a/client/coral-admin/src/components/ui/Drawer.js b/client/coral-admin/src/components/ui/Drawer.js index 2003ed34d..8fdf4f19d 100644 --- a/client/coral-admin/src/components/ui/Drawer.js +++ b/client/coral-admin/src/components/ui/Drawer.js @@ -1,18 +1,49 @@ import React from 'react'; import {Navigation, Drawer} from 'react-mdl'; -import {Link} from 'react-router'; -import styles from './Header.css'; +import {IndexLink, Link} from 'react-router'; +import styles from './Drawer.css'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../../translations.json'; -export default () => ( - - - {lang.t('configure.moderate')} - {lang.t('configure.community')} - {lang.t('configure.configure')} - +export default ({handleLogout, restricted = false}) => ( + + { !restricted ? +
+ + + {lang.t('configure.dashboard')} + + + {lang.t('configure.moderate')} + + + {lang.t('configure.streams')} + + + {lang.t('configure.community')} + + + {lang.t('configure.configure')} + + Sign Out + {`v${process.env.VERSION}`} + +
: null }
); const lang = new I18n(translations); + diff --git a/client/coral-admin/src/components/ui/Header.css b/client/coral-admin/src/components/ui/Header.css index e6cf85d23..e30cb46d7 100644 --- a/client/coral-admin/src/components/ui/Header.css +++ b/client/coral-admin/src/components/ui/Header.css @@ -1,7 +1,25 @@ +@custom-media --table-viewport (max-width: 1024px); + +@media (--table-viewport) { + .logo { + margin-left: 58px; + h1 { + background: #696969; + } + span { + color: #fcfcfc; + } + } + .nav { + display: none; + } +} + .header { background-color: transparent; box-shadow: none; min-height: 58px; + display: block; } .header > div { diff --git a/client/coral-admin/src/components/ui/Header.js b/client/coral-admin/src/components/ui/Header.js index 141d7fecf..556b33b6c 100644 --- a/client/coral-admin/src/components/ui/Header.js +++ b/client/coral-admin/src/components/ui/Header.js @@ -8,7 +8,7 @@ import {Logo} from './Logo'; export default ({handleLogout, restricted = false}) => (
- + { !restricted ?
diff --git a/client/coral-admin/src/components/ui/Layout.js b/client/coral-admin/src/components/ui/Layout.js index 46c7aa7fa..f28e8dd80 100644 --- a/client/coral-admin/src/components/ui/Layout.js +++ b/client/coral-admin/src/components/ui/Layout.js @@ -6,8 +6,8 @@ import styles from './Layout.css'; export const Layout = ({children, ...props}) => ( -
- +
+
{children}
diff --git a/client/coral-admin/src/components/ui/Logo.js b/client/coral-admin/src/components/ui/Logo.js index 05a7707d7..8c57cde11 100644 --- a/client/coral-admin/src/components/ui/Logo.js +++ b/client/coral-admin/src/components/ui/Logo.js @@ -2,8 +2,8 @@ import React from 'react'; import styles from './Logo.css'; import {CoralLogo} from 'coral-ui'; -export const Logo = () => ( -
+export const Logo = ({className = ''}) => ( +

Talk diff --git a/client/coral-admin/src/containers/Community/Community.js b/client/coral-admin/src/containers/Community/Community.js index bad5654cf..08a962c34 100644 --- a/client/coral-admin/src/containers/Community/Community.js +++ b/client/coral-admin/src/containers/Community/Community.js @@ -1,12 +1,12 @@ import React from 'react'; import I18n from 'coral-framework/modules/i18n/i18n'; -import translations from '../../translations.json'; +import translations from 'coral-admin/src/translations.json'; import styles from './Community.css'; import Table from './Table'; import Loading from './Loading'; -import NoResults from './NoResults'; import {Pager} from 'coral-ui'; +import EmptyCard from '../../components/EmptyCard'; const lang = new I18n(translations); @@ -54,13 +54,14 @@ const Community = ({isFetching, commenters, ...props}) => {

{ isFetching && } - { !hasResults && } - { hasResults && - + { + hasResults + ?
+ : {lang.t('community.no-results')} } ( -
- No users found with that user name or email address -
-); - -export default NoResults; diff --git a/client/coral-admin/src/containers/Configure/CommentSettings.js b/client/coral-admin/src/containers/Configure/CommentSettings.js index 5a14cf565..90057c5fe 100644 --- a/client/coral-admin/src/containers/Configure/CommentSettings.js +++ b/client/coral-admin/src/containers/Configure/CommentSettings.js @@ -52,7 +52,6 @@ const updateClosedMessage = (updateSettings) => (event) => { }; const updateCustomCssUrl = (updateSettings) => (event) => { - console.log('updateCustomCssUrl', event.target.value); const customCssUrl = event.target.value; updateSettings({customCssUrl}); }; diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js index f685ac094..b1a44533e 100644 --- a/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js +++ b/client/coral-admin/src/containers/ModerationQueue/ModerationContainer.js @@ -54,7 +54,6 @@ class ModerationContainer extends Component { } if (data.error) { - console.log(data); return
Error
; } diff --git a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js index d30f24c1f..0d48acc92 100644 --- a/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js +++ b/client/coral-admin/src/containers/ModerationQueue/ModerationQueue.js @@ -1,14 +1,21 @@ import React, {PropTypes} from 'react'; import Comment from './components/Comment'; +import EmptyCard from '../../components/EmptyCard'; import {actionsMap} from './helpers/moderationQueueActionsMap'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from 'coral-admin/src/translations'; + +const lang = new I18n(translations); const ModerationQueue = ({activeTab = 'premod', ...props}) => { + const areComments = props.data[activeTab].length; return (
-
    +
      { - props.data[activeTab].map((comment, i) => { + areComments + ? props.data[activeTab].map((comment, i) => { const status = comment.action_summaries ? 'FLAGGED' : comment.status; return { currentAsset={props.currentAsset} />; }) + : {lang.t('modqueue.emptyqueue')} }
@@ -29,7 +37,12 @@ const ModerationQueue = ({activeTab = 'premod', ...props}) => { }; ModerationQueue.propTypes = { - data: PropTypes.object.isRequired + data: PropTypes.object.isRequired, + acceptComment: PropTypes.func.isRequired, + rejectComment: PropTypes.func.isRequired, + showBanUserDialog: PropTypes.func.isRequired, + currentAsset: PropTypes.object, + suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired }; export default ModerationQueue; diff --git a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js index f8f50c6df..b39eeb2f8 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/Comment.js +++ b/client/coral-admin/src/containers/ModerationQueue/components/Comment.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {PropTypes} from 'react'; import timeago from 'timeago.js'; import Linkify from 'react-linkify'; import Highlighter from 'react-highlight-words'; @@ -17,7 +17,7 @@ const lang = new I18n(translations); const Comment = ({actions = [], ...props}) => { const links = linkify.getMatches(props.comment.body); - const actionSumaries = props.comment.action_summaries; + const actionSummaries = props.comment.action_summaries; return (
  • @@ -42,7 +42,7 @@ const Comment = ({actions = [], ...props}) => { /> )} - {props.comment.user.banned === 'banned' ? + {props.comment.user.status === 'banned' ? {lang.t('comment.banned_user')} @@ -62,11 +62,31 @@ const Comment = ({actions = [], ...props}) => {

    - {actionSumaries && } + {actionSummaries && }
  • ); }; +Comment.propTypes = { + acceptComment: PropTypes.func.isRequired, + rejectComment: PropTypes.func.isRequired, + suspectWords: PropTypes.arrayOf(PropTypes.string).isRequired, + currentAsset: PropTypes.object, + isActive: PropTypes.bool.isRequired, + comment: PropTypes.shape({ + body: PropTypes.string.isRequired, + action_summaries: PropTypes.array, + created_at: PropTypes.string.isRequired, + user: PropTypes.shape({ + status: PropTypes.string + }), + asset: PropTypes.shape({ + title: PropTypes.string, + id: PropTypes.string + }) + }) +}; + const linkStyles = { backgroundColor: 'rgb(255, 219, 135)', padding: '1px 2px' diff --git a/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js b/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js index bf5a34f29..6140abd90 100644 --- a/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js +++ b/client/coral-admin/src/containers/ModerationQueue/components/FlagBox.js @@ -5,7 +5,7 @@ const FlagBox = props => (

    Flags:

      - {props.actionSumaries.map((action, i) => + {props.actionSummaries.map((action, i) =>
    • {!action.reason ? No reason provided : action.reason} ({action.count})
    • )}
    @@ -13,7 +13,7 @@ const FlagBox = props => ( ); FlagBox.propTypes = { - actionSumaries: PropTypes.array.isRequired + actionSummaries: PropTypes.array.isRequired }; export default FlagBox; diff --git a/client/coral-admin/src/containers/Streams/Streams.js b/client/coral-admin/src/containers/Streams/Streams.js index 62f2f009e..96130fcba 100644 --- a/client/coral-admin/src/containers/Streams/Streams.js +++ b/client/coral-admin/src/containers/Streams/Streams.js @@ -8,6 +8,7 @@ import {Link} from 'react-router'; import {Pager, Icon} from 'coral-ui'; import {DataTable, TableHeader, RadioGroup, Radio} from 'react-mdl'; +import EmptyCard from 'coral-admin/src/components/EmptyCard'; const limit = 25; @@ -142,22 +143,25 @@ class Streams extends Component { {lang.t('streams.oldest')}
    -
    - - {lang.t('streams.article')} - - {lang.t('streams.pubdate')} - - - {lang.t('streams.status')} - - - -
    + { + assetsIds.length + ?
    + + {lang.t('streams.article')} + + {lang.t('streams.pubdate')} + + + {lang.t('streams.status')} + + + +
    + : {lang.t('streams.empty_result')} + } ); } diff --git a/client/coral-admin/src/index.js b/client/coral-admin/src/index.js index 306461dd0..00190847a 100644 --- a/client/coral-admin/src/index.js +++ b/client/coral-admin/src/index.js @@ -7,6 +7,9 @@ import store from './services/store'; import App from './components/App'; +import 'react-mdl/extra/material.css'; +import 'react-mdl/extra/material.js'; + render( diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json index 5b15564d7..7219a22ca 100644 --- a/client/coral-admin/src/translations.json +++ b/client/coral-admin/src/translations.json @@ -7,7 +7,7 @@ "admin": "Administrator", "moderator": "Moderator", "role": "Select role...", - "no-results": "No users found with that user name or email address.", + "no-results": "No users found with that user name or email address. They're hiding!", "status": "Status", "select-status": "Select status...", "active": "Active", @@ -32,6 +32,7 @@ "prevcomment": "Go to the previous comment", "singleview": "Toggle single comment edit view", "thismenu": "Open this menu", + "emptyqueue": "No more comments to moderate! You're all caught up. Go have some ☕️", "showshortcuts": "Show Shortcuts" }, "comment": { @@ -113,6 +114,7 @@ "comment_count": "Comments" }, "streams": { + "empty_result": "No assets match this search. Maybe try widening your search?", "search": "Search", "filter-streams": "Filter Streams", "stream-status": "Stream Status", @@ -152,6 +154,7 @@ "flagged": "marcado", "shortcuts": "Atajos de teclado", "close": "Cerrar", + "emptyqueue": "No se encontro ningún usuario. Están escondidos.", "showshortcuts": "Mostrar atajos" }, "comment": { @@ -220,6 +223,7 @@ "comment_count": "Comentarios" }, "streams": { + "empty_result": "No se encuentro articulo con esta busqueda. Tal vez extender la busqueda?", "search": "", "filter-streams": "", "stream-status": "", diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js index 1d460d580..7262d10ed 100644 --- a/client/coral-embed-stream/src/Comment.js +++ b/client/coral-embed-stream/src/Comment.js @@ -180,7 +180,7 @@ class Comment extends React.Component { comment.replies &&
    comment.replies.length} diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index 7010618ca..ac9c09eac 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -2,6 +2,9 @@ import React, {Component} from 'react'; import {compose} from 'react-apollo'; import {connect} from 'react-redux'; import isEqual from 'lodash/isEqual'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from 'coral-framework/translations'; +const lang = new I18n(translations); import {TabBar, Tab, TabContent, Spinner} from 'coral-ui'; @@ -12,6 +15,7 @@ const {fetchAssetSuccess} = assetActions; import {queryStream} from 'coral-framework/graphql/queries'; import {postComment, postFlag, postLike, postDontAgree, deleteAction} from 'coral-framework/graphql/mutations'; import {editName} from 'coral-framework/actions/user'; +import {updateCountCache} from 'coral-framework/actions/asset'; import {Notification, notificationActions, authActions, assetActions, pym} from 'coral-framework'; import Stream from './Stream'; @@ -24,10 +28,11 @@ import UserBox from 'coral-sign-in/components/UserBox'; import SignInContainer from 'coral-sign-in/containers/SignInContainer'; import SuspendedAccount from 'coral-framework/components/SuspendedAccount'; import ChangeUsernameContainer from '../../coral-sign-in/containers/ChangeUsernameContainer'; -import SettingsContainer from 'coral-settings/containers/SettingsContainer'; +import ProfileContainer from 'coral-settings/containers/ProfileContainer'; import RestrictedContent from 'coral-framework/components/RestrictedContent'; import ConfigureStreamContainer from 'coral-configure/containers/ConfigureStreamContainer'; import LoadMore from './LoadMore'; +import NewCount from './NewCount'; class Embed extends Component { @@ -82,7 +87,7 @@ class Embed extends Component { render () { const {activeTab} = this.state; - const {closedAt} = this.props.asset; + const {closedAt, countCache = {}} = this.props.asset; const {loading, asset, refetch} = this.props.data; const {loggedIn, isAdmin, user, showSignInDialog, signInOffset} = this.props.auth; @@ -98,12 +103,17 @@ class Embed extends Component { return ; } + // Find the created_at date of the first comment. If no comments exist, set the date to a week ago. + const firstCommentDate = asset.comments[0] + ? asset.comments[0].created_at + : new Date(Date.now() - 1000 * 60 * 60 * 24 * 7).toISOString(); + return (
    - Settings + {lang.t('profile')} Configure Stream {loggedIn && } @@ -132,6 +142,8 @@ class Embed extends Component { postItem={this.props.postItem} appendItemArray={this.props.appendItemArray} updateItem={this.props.updateItem} + updateCountCache={this.props.updateCountCache} + countCache={countCache[asset.id]} assetId={asset.id} premod={asset.settings.moderation} isReply={false} @@ -147,6 +159,14 @@ class Embed extends Component { } {!loggedIn && } {loggedIn && user && } + - ({ clearNotification: () => dispatch(clearNotification()), editName: (username) => dispatch(editName(username)), showSignInDialog: (offset) => dispatch(showSignInDialog(offset)), + updateCountCache: (id, count) => dispatch(updateCountCache(id, count)), logout: () => dispatch(logout()), dispatch: d => dispatch(d) }); diff --git a/client/coral-embed-stream/src/LoadMore.js b/client/coral-embed-stream/src/LoadMore.js index 89708272a..e33828bc1 100644 --- a/client/coral-embed-stream/src/LoadMore.js +++ b/client/coral-embed-stream/src/LoadMore.js @@ -12,7 +12,7 @@ const loadMoreComments = (assetId, comments, loadMore, parentId) => { } const cursor = parentId - ? comments[1].created_at + ? comments[0].created_at : comments[comments.length - 1].created_at; loadMore({ diff --git a/client/coral-embed-stream/src/NewCount.js b/client/coral-embed-stream/src/NewCount.js new file mode 100644 index 000000000..429ecc113 --- /dev/null +++ b/client/coral-embed-stream/src/NewCount.js @@ -0,0 +1,40 @@ +import React, {PropTypes} from 'react'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from 'coral-framework/translations.json'; +const lang = new I18n(translations); + +const onLoadMoreClick = ({loadMore, commentCount, firstCommentDate, assetId, updateCountCache}) => (e) => { + e.preventDefault(); + updateCountCache(assetId, commentCount); + loadMore({ + limit: 500, + cursor: firstCommentDate, + assetId, + sort: 'CHRONOLOGICAL' + }, true); +}; + +const NewCount = (props) => { + const newComments = props.commentCount - props.countCache; + + return
    + { + props.countCache && newComments > 0 && + + } +
    ; +}; + +NewCount.propTypes = { + commentCount: PropTypes.number.isRequired, + countCache: PropTypes.number, + loadMore: PropTypes.func.isRequired, + assetId: PropTypes.string.isRequired, + firstCommentDate: PropTypes.string.isRequired +}; + +export default NewCount; diff --git a/client/coral-embed-stream/src/Stream.js b/client/coral-embed-stream/src/Stream.js index 59f1e4179..a7f360d59 100644 --- a/client/coral-embed-stream/src/Stream.js +++ b/client/coral-embed-stream/src/Stream.js @@ -1,5 +1,6 @@ import React, {PropTypes} from 'react'; import Comment from './Comment'; +import {NEW_COMMENT_COUNT_POLL_INTERVAL} from 'coral-framework/constants/comments'; class Stream extends React.Component { @@ -17,10 +18,30 @@ class Stream extends React.Component { constructor(props) { super(props); - this.state = {activeReplyBox: ''}; + this.state = {activeReplyBox: '', countPoll: null}; this.setActiveReplyBox = this.setActiveReplyBox.bind(this); } + componentDidMount() { + const {asset, getCounts, updateCountCache} = this.props; + + updateCountCache(asset.id, asset.commentCount); + + // Note: Apollo's built-in polling doesn't work with fetchMore queries, so a + // setInterval is being used instead. + this.setState({ + countPoll: setInterval(() => getCounts({ + asset_id: asset.id, + limit: asset.comments.length, + sort: 'REVERSE_CHRONOLOGICAL' + }), NEW_COMMENT_COUNT_POLL_INTERVAL), + }); + } + + componentWillUnmount() { + clearInterval(this.state.countPoll); + } + setActiveReplyBox (reactKey) { if (!this.props.currentUser) { const offset = document.getElementById(`c_${reactKey}`).getBoundingClientRect().top - 75; diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css index 1deae6026..52876ff79 100644 --- a/client/coral-embed-stream/style/default.css +++ b/client/coral-embed-stream/style/default.css @@ -4,10 +4,10 @@ html, body { } body { - font-family: 'Lato', sans-serif; font-family: 'Open Sans', sans-serif; + font-family: 'Lato', sans-serif; width: 100%; - font-size: 12px; + font-size: 14px; margin: 0px; padding: 0px 0px 50px 0px; } @@ -17,16 +17,16 @@ body { } button { - padding: 5px 10px; - margin: 5px; + margin: 5px 10px 5px 0px; background: none; + padding: 0px; border: none; + font-size: inherit; } button:hover { border-radius: 2px; - color: #FFF; - background-color: rgb(155, 155, 155); + color: #767676; } button i { @@ -58,6 +58,17 @@ hr { font-weight: bold; } +/* Coral sign in button */ + +#coralSignInButton { + background-color: #2a2a2a; + color: #FFF; +} + +#coralSignInButton:hover { + background-color: #767676; +} + /* Info Box Styles */ .coral-plugin-infobox-info { top: 0; @@ -65,10 +76,9 @@ hr { background: rgb(35,118,216); color: white; width: 100%; - text-align: center; + text-align: left; padding: 10px; margin-bottom: 10px; - font-weight: bold; display: block; } @@ -157,19 +167,31 @@ hr { } .coral-plugin-commentcontent-text { - margin-bottom: 10px; + margin-bottom: 7px; } .coral-plugin-author-name-text { display: inline-block; - margin-right: 10px; - font-weight: bolder; + margin: 10px 8px 10px 0; + font-weight: bold; } .coral-plugin-author-name-bio-flag { float: right; } +/* Tag Labels */ + +.coral-plugin-tag-label { + background-color: #4C1066; + color: white; + display: inline-block; + margin: 10px 10px; + padding: 5px 5px; + border-radius: 2px; +} + + /* Reply styles */ @@ -206,8 +228,9 @@ hr { } .coral-plugin-pubdate-text { - color: #CCC; + color: #696969; display: inline-block; + font-size: .75rem; } .coral-plugin-permalinks-container { @@ -336,18 +359,26 @@ button.coral-load-more { text-align: center; color: #FFF; background-color: #2376D8; + cursor: pointer; } button.coral-load-more:hover { background-color: #4399FF; } -.coral-load-more-replies { +.coral-load-more-replies, .coral-new-comments { width: 100%; display: flex; justify-content: center; + cursor: pointer; } -.coral-load-more-replies button.coral-load-more { +.coral-new-comments { + position: relative; + top: 1.8em; + z-index: 100; +} + +.coral-load-more-replies button.coral-load-more, .coral-new-comments button.coral-load-more{ width: initial; } diff --git a/client/coral-framework/actions/asset.js b/client/coral-framework/actions/asset.js index d5db289c3..02e72ea98 100644 --- a/client/coral-framework/actions/asset.js +++ b/client/coral-framework/actions/asset.js @@ -38,6 +38,7 @@ export const updateOpenStream = closedBody => (dispatch, getState) => { const openStream = () => ({type: actions.OPEN_COMMENTS}); const closeStream = () => ({type: actions.CLOSE_COMMENTS}); +export const updateCountCache = (id, count) => ({type: actions.UPDATE_COUNT_CACHE, id, count}); export const updateOpenStatus = status => dispatch => { if (status === 'open') { diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 4a12245eb..418acc5bb 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -192,9 +192,8 @@ export const requestConfirmEmail = (email, redirectUri) => dispatch => { dispatch(verifyEmailSuccess()); }) .catch(err => { - console.log('failed to send email verification', err); // email might have already been verifyed - dispatch(verifyEmailFailure()); + dispatch(verifyEmailFailure(err)); }); }; diff --git a/client/coral-framework/constants/asset.js b/client/coral-framework/constants/asset.js index 40f746706..234095d9d 100644 --- a/client/coral-framework/constants/asset.js +++ b/client/coral-framework/constants/asset.js @@ -8,3 +8,4 @@ export const UPDATE_ASSET_SETTINGS_FAILURE = 'UPDATE_ASSET_SETTINGS_FAILURE'; export const OPEN_COMMENTS = 'OPEN_COMMENTS'; export const CLOSE_COMMENTS = 'CLOSE_COMMENTS'; +export const UPDATE_COUNT_CACHE = 'UPDATE_COUNT_CACHE'; diff --git a/client/coral-framework/constants/comments.js b/client/coral-framework/constants/comments.js index 17ea2223e..28db9cdf9 100644 --- a/client/coral-framework/constants/comments.js +++ b/client/coral-framework/constants/comments.js @@ -1 +1,2 @@ export const ADDTL_COMMENTS_ON_LOAD_MORE = 10; +export const NEW_COMMENT_COUNT_POLL_INTERVAL = 20000; diff --git a/client/coral-framework/graphql/queries/index.js b/client/coral-framework/graphql/queries/index.js index 09b3159d0..c993dfb6a 100644 --- a/client/coral-framework/graphql/queries/index.js +++ b/client/coral-framework/graphql/queries/index.js @@ -1,6 +1,7 @@ import {graphql} from 'react-apollo'; import STREAM_QUERY from './streamQuery.graphql'; import LOAD_MORE from './loadMore.graphql'; +import GET_COUNTS from './getCounts.graphql'; import MY_COMMENT_HISTORY from './myCommentHistory.graphql'; function getQueryVariable(variable) { @@ -17,6 +18,72 @@ function getQueryVariable(variable) { return 'http://localhost/default/stream'; } +export const getCounts = (data) => ({asset_id, limit, sort}) => { + return data.fetchMore({ + query: GET_COUNTS, + variables: { + asset_id, + limit, + sort + }, + updateQuery: (oldData, {fetchMoreResult:{data}}) => { + + return { + ...oldData, + asset: { + ...oldData.asset, + commentCount: data.asset.commentCount + } + }; + } + }); +}; + +export const loadMore = (data) => ({limit, cursor, parent_id, asset_id, sort}, newComments) => { + return data.fetchMore({ + query: LOAD_MORE, + variables: { + limit, + cursor, + parent_id, + asset_id, + sort + }, + updateQuery: (oldData, {fetchMoreResult:{data:{new_top_level_comments}}}) => { + + let updatedAsset; + + if (parent_id) { + + // If loading more replies + updatedAsset = { + ...oldData, + asset: { + ...oldData.asset, + comments: oldData.asset.comments.map((comment) => + comment.id === parent_id + ? {...comment, replies: [...comment.replies, ...new_top_level_comments]} + : comment) + } + }; + } else { + + // If loading more top-level comments + updatedAsset = { + ...oldData, + asset: { + ...oldData.asset, + comments: newComments ? [...new_top_level_comments.reverse(), ...oldData.asset.comments] + : [...oldData.asset.comments, ...new_top_level_comments] + } + }; + } + + return updatedAsset; + } + }); +}; + export const queryStream = graphql(STREAM_QUERY, { options: () => ({ variables: { @@ -25,40 +92,8 @@ export const queryStream = graphql(STREAM_QUERY, { }), props: ({data}) => ({ data, - loadMore: ({limit, cursor, parent_id, asset_id, sort}) => { - return data.fetchMore({ - query: LOAD_MORE, - variables: { - limit, - cursor, - parent_id, - asset_id, - sort - }, - updateQuery: (oldData, {fetchMoreResult:{data:{new_top_level_comments}}}) => - - // If loading more replies - parent_id ? { - ...oldData, - asset: { - ...oldData.asset, - comments: oldData.asset.comments.map((comment) => - comment.id === parent_id - ? {...comment, replies: [...comment.replies, ...new_top_level_comments]} - : comment) - } - } - - // If loading more top-level comments - : { - ...oldData, - asset: { - ...oldData.asset, - comments: [...oldData.asset.comments, ...new_top_level_comments] - } - } - }); - } + loadMore: loadMore(data), + getCounts: getCounts(data), }) }); diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/response.js index 64803faef..0fce878cd 100644 --- a/client/coral-framework/helpers/response.js +++ b/client/coral-framework/helpers/response.js @@ -45,7 +45,7 @@ const handleResp = res => { } if (err.error && err.error.translation_key) { - message = err.error.translation_key; + error.translation_key = err.error.translation_key; } error.message = message; diff --git a/client/coral-framework/reducers/asset.js b/client/coral-framework/reducers/asset.js index f9d0a55e3..067edfc5b 100644 --- a/client/coral-framework/reducers/asset.js +++ b/client/coral-framework/reducers/asset.js @@ -19,6 +19,9 @@ export default function asset (state = initialState, action) { case actions.UPDATE_ASSET_SETTINGS_SUCCESS: return state .setIn(['settings'], action.settings); + case actions.UPDATE_COUNT_CACHE: + return state + .setIn(['countCache', action.id], action.count); default: return state; } diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json index 2884178b6..d115aedca 100644 --- a/client/coral-framework/translations.json +++ b/client/coral-framework/translations.json @@ -1,5 +1,6 @@ { "en": { + "profile": "Profile", "successUpdateSettings": "The changes you have made have been applied to the comment stream on this article", "successNameUpdate": "Your username has been updated", "contentNotAvailable": "This content is not available", @@ -11,6 +12,9 @@ "button": "Submit", "error": "Usernames can contain letters, numbers and _ only" }, + "newCount": "View {0} more {1}", + "comment": "comment", + "comments": "comments", "error": { "emailNotVerified": "Email address {0} not verified.", "email": "Not a valid E-Mail", @@ -33,12 +37,16 @@ } }, "es": { + "profile": "Perfil", "successUpdateSettings": "La configuración de este articulo fue actualizada", "successBioUpdate": "Tu bio fue actualizada", "contentNotAvailable": "El contenido no se encuentra disponible", "bannedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes dar Like, Marcar o escribir commentarios. Por favor, contacta moderator@fakeurl for more information", "editNameMsg": "", "loadMore": "Ver más", + "newCount": "Ver {0} {1} más", + "comment": "commentario", + "comments": "commentarios", "error": { "emailNotVerified": "Dirección de correo electrónico {0} no verificada.", "email": "No es un email válido", diff --git a/client/coral-plugin-author-name/AuthorName.js b/client/coral-plugin-author-name/AuthorName.js index bca1d716b..67f61bc33 100644 --- a/client/coral-plugin-author-name/AuthorName.js +++ b/client/coral-plugin-author-name/AuthorName.js @@ -1,6 +1,5 @@ import React, {Component} from 'react'; const packagename = 'coral-plugin-author-name'; -import styles from './styles.css'; export default class AuthorName extends Component { @@ -24,8 +23,7 @@ export default class AuthorName extends Component { const {author} = this.props; return (
    + className={`${packagename}-text`}> {author && author.name}
    ); diff --git a/client/coral-plugin-commentbox/CommentBox.js b/client/coral-plugin-commentbox/CommentBox.js index 344ade150..19472ef70 100644 --- a/client/coral-plugin-commentbox/CommentBox.js +++ b/client/coral-plugin-commentbox/CommentBox.js @@ -29,6 +29,9 @@ class CommentBox extends Component { commentPostedHandler, postItem, assetId, + updateCountCache, + isReply, + countCache, parentId, addNotification, authorId @@ -44,16 +47,17 @@ class CommentBox extends Component { if (this.props.charCount && this.state.body.length > this.props.charCount) { return; } + !isReply && updateCountCache(assetId, countCache + 1); postItem(comment, 'comments') .then(({data}) => { const postedComment = data.createComment.comment; if (postedComment.status === 'REJECTED') { addNotification('error', lang.t('comment-post-banned-word')); + !isReply && updateCountCache(assetId, countCache); } else if (postedComment.status === 'PREMOD') { addNotification('success', lang.t('comment-post-notif-premod')); - } else { - addNotification('success', 'Your comment has been posted.'); + !isReply && updateCountCache(assetId, countCache); } if (commentPostedHandler) { @@ -105,7 +109,6 @@ class CommentBox extends Component { cStyle='darkGrey' className={`${name}-cancel-button`} onClick={() => { - console.log('cancel button in comment box'); cancelButtonClicked(''); }}> {lang.t('cancel')} diff --git a/client/coral-plugin-commentcontent/CommentContent.js b/client/coral-plugin-commentcontent/CommentContent.js index 2dab237dd..501005b3d 100644 --- a/client/coral-plugin-commentcontent/CommentContent.js +++ b/client/coral-plugin-commentcontent/CommentContent.js @@ -1,5 +1,5 @@ import React from 'react'; -const name = 'coral-plugin-content'; +const name = 'coral-plugin-commentcontent'; const Content = ({body, styles}) => { const textbreaks = body.split('\n'); diff --git a/client/coral-plugin-permalinks/PermalinkButton.js b/client/coral-plugin-permalinks/PermalinkButton.js index 1a99a5627..4c6cbb571 100644 --- a/client/coral-plugin-permalinks/PermalinkButton.js +++ b/client/coral-plugin-permalinks/PermalinkButton.js @@ -49,8 +49,8 @@ class PermalinkButton extends React.Component { return (
    { - console.log('it workt'); - console.log(data); - }); - } - - render() { - return
    - - -
    ; - } -} - -const postComment = gql` - fragment commentView on Comment { - id - body - user { - name: username - } - actions { - type: action_type - count - current: current_user { - id - created_at - } - } - } - - mutation CreateComment ($asset_id: ID!, $parent_id: ID, $body: String!) { - createComment(asset_id:$asset_id, parent_id:$parent_id, body:$body) { - ...commentView - } - } -`; - -const RileysAwesomeCommentBoxWithData = graphql( - postComment -)(RileysAwesomeCommentBox); - -export default RileysAwesomeCommentBoxWithData; diff --git a/client/coral-plugin-stream/Stream.js b/client/coral-plugin-stream/Stream.js deleted file mode 100644 index 72e03d4ae..000000000 --- a/client/coral-plugin-stream/Stream.js +++ /dev/null @@ -1,100 +0,0 @@ -import React, {Component} from 'react'; -import {graphql} from 'react-apollo'; -import gql from 'graphql-tag'; -import {fetchSignIn} from 'coral-framework/actions/auth'; -import RileysAwesomeCommentBox from 'coral-plugin-stream/RileysAwesomeCommentBox'; - -const assetID = '6187a94b-0b6d-4a96-ac6b-62b529cd8410'; - -// MyComponent is a "presentational" or apollo-unaware component, -// It could be a simple React class: -class Stream extends Component { - - constructor(props) { - super(props); - } - - logMeIn() { - fetchSignIn({email: 'your@example.com', password: 'dfasidfaisdufoiausdfoiuaspdoifas'})(() => {}); - } - - render() { - const {data} = this.props; - return
    - - { - data.loading - ? 'loading!' - :
    - -

    Asset ID: {data.asset.id}

    -
      - { - data.asset.comments.map(comment => { - return
    • - {comment.body} [{comment.id}] -
        - { - comment.replies.map(reply => { - return
      • {reply.body}
      • ; - }) - } -
      -
    • ; - }) - } -
    -
    - } -
    ; - } -} - -// Initialize GraphQL queries or mutations with the gql tag -const StreamQuery = gql`fragment commentView on Comment { - id - body - user { - name: username - } - tags { - name - } - actions { - type: action_type - count - current: current_user { - id - created_at - } - } -} - -query AssetQuery($asset_id: ID!) { - asset(id: $asset_id) { - id - title - url - commentCount - comments { - ...commentView - replies { - ...commentView - } - } - } -}`; - -// We then can use `graphql` to pass the query results returned by MyQuery -// to MyComponent as a prop (and update them as the results change) -const StreamWithData = graphql( - StreamQuery, { - options: { - variables: { - asset_id: assetID - } - } - } -)(Stream); - -export default StreamWithData; diff --git a/client/coral-plugin-tag-label/TagLabel.js b/client/coral-plugin-tag-label/TagLabel.js index 0bceca225..ba7403f5c 100644 --- a/client/coral-plugin-tag-label/TagLabel.js +++ b/client/coral-plugin-tag-label/TagLabel.js @@ -1,8 +1,6 @@ import React from 'react'; -import styles from './styles.css'; - -const TagLabel = ({isStaff}) =>
    +const TagLabel = ({isStaff}) =>
    {isStaff ? 'Staff' : ''}
    ; diff --git a/client/coral-plugin-tag-label/styles.css b/client/coral-plugin-tag-label/styles.css deleted file mode 100644 index 03dc3dbc8..000000000 --- a/client/coral-plugin-tag-label/styles.css +++ /dev/null @@ -1,7 +0,0 @@ -.staff { - background-color: #4C1066; - color: white; - display: inline-block; - margin: 10px 10px; - padding: 5px 5px; -} diff --git a/client/coral-settings/components/NotLoggedIn.css b/client/coral-settings/components/NotLoggedIn.css index f65e96507..c15c44c98 100644 --- a/client/coral-settings/components/NotLoggedIn.css +++ b/client/coral-settings/components/NotLoggedIn.css @@ -11,5 +11,4 @@ cursor: pointer; margin: 0px; padding-bottom: 2px; - border-bottom: solid 1px black; } diff --git a/client/coral-settings/components/NotLoggedIn.js b/client/coral-settings/components/NotLoggedIn.js index 095d43a8f..acd3ae7e6 100644 --- a/client/coral-settings/components/NotLoggedIn.js +++ b/client/coral-settings/components/NotLoggedIn.js @@ -1,21 +1,20 @@ import React from 'react'; import styles from './NotLoggedIn.css'; import SignInContainer from '../../coral-sign-in/containers/SignInContainer'; +import translations from '../translations'; +import I18n from 'coral-framework/modules/i18n/i18n'; +const lang = new I18n(translations); export default ({showSignInDialog}) => (
    { - console.log('Signin click'); showSignInDialog(); - }}>Sign In to access Settings + }}>{lang.t('signIn')} {lang.t('toAccess')}
    - From the Settings Page you can -
      -
    • See your comment history
    • -
    + {lang.t('fromSettingsPage')}
    ); diff --git a/client/coral-settings/components/SettingsHeader.css b/client/coral-settings/components/ProfileHeader.css similarity index 100% rename from client/coral-settings/components/SettingsHeader.css rename to client/coral-settings/components/ProfileHeader.css diff --git a/client/coral-settings/components/ProfileHeader.js b/client/coral-settings/components/ProfileHeader.js new file mode 100644 index 000000000..24b2222bd --- /dev/null +++ b/client/coral-settings/components/ProfileHeader.js @@ -0,0 +1,12 @@ +import React, {PropTypes} from 'react'; +import styles from './ProfileHeader.css'; + +const ProfileHeader = ({username}) => ( +
    +

    {username}

    +
    +); + +ProfileHeader.propTypes = {username: PropTypes.string.isRequired}; + +export default ProfileHeader; diff --git a/client/coral-settings/components/SettingsHeader.js b/client/coral-settings/components/SettingsHeader.js deleted file mode 100644 index 34d347314..000000000 --- a/client/coral-settings/components/SettingsHeader.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import styles from './SettingsHeader.css'; - -export default ({userData}) => ( -
    -

    {userData.username}

    - - { - - // Hiding display of users ID unless there's a use case for it. - //

    {userData.profiles.map(profile => profile.id)}

    - } -
    -); diff --git a/client/coral-settings/containers/SettingsContainer.js b/client/coral-settings/containers/ProfileContainer.js similarity index 92% rename from client/coral-settings/containers/SettingsContainer.js rename to client/coral-settings/containers/ProfileContainer.js index 9e72cdadc..4a98ed8ff 100644 --- a/client/coral-settings/containers/SettingsContainer.js +++ b/client/coral-settings/containers/ProfileContainer.js @@ -8,13 +8,13 @@ import {myCommentHistory} from 'coral-framework/graphql/queries'; import {link} from 'coral-framework/services/PymConnection'; import NotLoggedIn from '../components/NotLoggedIn'; import {Spinner} from 'coral-ui'; -import SettingsHeader from '../components/SettingsHeader'; +import ProfileHeader from '../components/ProfileHeader'; import CommentHistory from 'coral-plugin-history/CommentHistory'; import translations from '../translations'; const lang = new I18n(translations); -class SettingsContainer extends Component { +class ProfileContainer extends Component { constructor (props) { super(props); this.state = { @@ -44,7 +44,7 @@ class SettingsContainer extends Component { return (
    - + { // Hiding bio until moderation can get figured out @@ -88,4 +88,4 @@ const mapDispatchToProps = () => ({ export default compose( connect(mapStateToProps, mapDispatchToProps), myCommentHistory -)(SettingsContainer); +)(ProfileContainer); diff --git a/client/coral-settings/translations.json b/client/coral-settings/translations.json index 2a827a4f0..945d71b87 100644 --- a/client/coral-settings/translations.json +++ b/client/coral-settings/translations.json @@ -1,14 +1,22 @@ { "en":{ - "userNoComment": "This user has not yet left a comment.", + "profile": "Profile", + "userNoComment": "You've never left a comment. Join the conversation!", "allComments": "All Comments", "profileSettings": "Profile Settings", - "myCommentHistory": "My comment History" + "myCommentHistory": "My comment History", + "signIn": "Sign in", + "toAccess": " to access Profile", + "fromSettingsPage": "From the Profile Page you can see your comment history." }, "es":{ - "userNoComment": "Aún no ha escrito ningún comentario.", + "profile": "Perfil", + "userNoComment": "No has dejado áun ningún comentario. ¡Unete a la conversación!", "allComments": "Todos los comentarios", "profileSettings": "Configuración del perfil", - "myCommentHistory": "Mi historial de comentarios" + "myCommentHistory": "Mi historial de comentarios", + "signIn": "Entrar", + "toAccess": "para acceder a al perfil", + "fromSettingsPage": "Desde la peagina de configuración puede ver su historia de comentarios." } } diff --git a/client/coral-sign-in/components/CreateUsernameDialog.js b/client/coral-sign-in/components/CreateUsernameDialog.js index d245b38ac..d0e348046 100644 --- a/client/coral-sign-in/components/CreateUsernameDialog.js +++ b/client/coral-sign-in/components/CreateUsernameDialog.js @@ -3,14 +3,18 @@ import TextField from 'coral-ui/components/TextField'; import Alert from './Alert'; import Button from 'coral-ui/components/Button'; import {Dialog} from 'coral-ui'; +import FakeComment from './FakeComment'; + import styles from './styles.css'; + import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; const lang = new I18n(translations); -const CreateUsernameDialog = ({open, handleClose, offset, formData, handleSubmitUsername, handleChange, ...props}) => ( +const CreateUsernameDialog = ({open, handleClose, offset, formData, handleSubmitUsername, handleChange, ...props}) => { + return (
    - +

    {lang.t('createdisplay.yourusername')}

    + +

    {lang.t('createdisplay.ifyoudontchangeyourname')}

    { props.auth.error && {props.auth.error} }
    - - { props.errors.username && {lang.t('createdisplay.specialCharacters')} } -
    + { props.errors.username && {lang.t('createdisplay.specialCharacters')} } +
    +
    -); + ); +}; export default CreateUsernameDialog; diff --git a/client/coral-sign-in/components/FakeComment.js b/client/coral-sign-in/components/FakeComment.js new file mode 100644 index 000000000..b5af6bf1c --- /dev/null +++ b/client/coral-sign-in/components/FakeComment.js @@ -0,0 +1,66 @@ +import React from 'react'; +import styles from 'coral-embed-stream/src/Comment.css'; + +import AuthorName from 'coral-plugin-author-name/AuthorName'; +import Content from 'coral-plugin-commentcontent/CommentContent'; +import PubDate from 'coral-plugin-pubdate/PubDate'; +import {ReplyButton} from 'coral-plugin-replies'; + +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from '../translations'; + +const lang = new I18n(translations); + +class FakeComment extends React.Component { + constructor (props) { + super(props); + } + + render () { + const {username, created_at, body} = this.props; + + return ( +
    +
    + + + +
    +
    + +
    + {}} + parentCommentId={'commentID'} + currentUserId={{}} + banned={false} + /> +
    +
    +
    + +
    +
    + +
    +
    +
    + ); + } +} + +export default FakeComment; diff --git a/client/coral-sign-in/components/styles.css b/client/coral-sign-in/components/styles.css index 60a252a20..31d4a4fed 100644 --- a/client/coral-sign-in/components/styles.css +++ b/client/coral-sign-in/components/styles.css @@ -36,6 +36,7 @@ .signInButton { margin-top: 10px; + background-color: #2a2a2a; } .close { @@ -117,7 +118,7 @@ input.error{ } .action { - margin-top: 15px; + margin-top: 0px; } .passwordRequestSuccess { @@ -140,6 +141,40 @@ input.error{ display: block; } -.confirmSubmit { +/* Change username Dialog*/ +.dialogusername { + border: none; + box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2); + width: 400px; + top: 10px; +} + +.yourusername { + display: block; +} + +.example { + display: block; +} + +.ifyoudont { + display: block; + margin-top: 15px; +} + +.saveusername { + display: block; + width: 100%; +} + +.savebutton { + display: inline; + background-color: rgb(105,105,105); + color: white; +} + +.fakeComment { + display: block; + margin-bottom: 5px; } diff --git a/client/coral-sign-in/containers/ChangeUsernameContainer.js b/client/coral-sign-in/containers/ChangeUsernameContainer.js index 33ae63614..c4450aa56 100644 --- a/client/coral-sign-in/containers/ChangeUsernameContainer.js +++ b/client/coral-sign-in/containers/ChangeUsernameContainer.js @@ -29,6 +29,7 @@ class ChangeUsernameContainer extends Component { constructor(props) { super(props); + this.initialState.formData.username = props.user.username; this.state = this.initialState; this.handleChange = this.handleChange.bind(this); this.handleSubmitUsername = this.handleSubmitUsername.bind(this); @@ -103,7 +104,7 @@ class ChangeUsernameContainer extends Component { return (
    { item_id: comment.id, item_type: 'COMMENTS', action_type: 'FLAG', - metadata: { - field: 'body', - details: 'Matched suspect word filters.' - } + group_id: 'Matched suspect word filter', + metadata: {} }) .then(() => comment); } diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index aad6026eb..fbdeb4279 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -486,7 +486,7 @@ type RootQuery { # Comments returned based on a query. comments(query: CommentsQuery!): [Comment] - # Returne the count of comments satisfied by the query. Note that this edge is + # Return the count of comments satisfied by the query. Note that this edge is # expensive as it is not batched. Requires the `ADMIN` role. commentCount(query: CommentCountQuery!): Int diff --git a/routes/api/auth/index.js b/routes/api/auth/index.js index dcce15933..fc3b87e51 100644 --- a/routes/api/auth/index.js +++ b/routes/api/auth/index.js @@ -2,7 +2,6 @@ const express = require('express'); const passport = require('../../../services/passport'); const authorization = require('../../../middleware/authorization'); const errors = require('../../../errors'); -const UsersService = require('../../../services/users'); const router = express.Router(); @@ -61,6 +60,7 @@ const HandleAuthCallback = (req, res, next) => (err, user) => { /** * Returns the response to the login attempt via a popup callback with some JS. */ + const HandleAuthPopupCallback = (req, res, next) => (err, user) => { if (err) { return res.render('auth-callback', {err: JSON.stringify(err), data: null}); @@ -70,20 +70,15 @@ const HandleAuthPopupCallback = (req, res, next) => (err, user) => { return res.render('auth-callback', {err: JSON.stringify(errors.ErrNotAuthorized), data: null}); } - // Authorize the user to edit their username. - UsersService.toggleNameEdit(user.id, true) - .then(() => { - - // Perform the login of the user! - req.logIn(user, (err) => { - if (err) { - return res.render('auth-callback', {err: JSON.stringify(err), data: null}); - } + // Perform the login of the user! + req.logIn(user, (err) => { + if (err) { + return res.render('auth-callback', {err: JSON.stringify(err), data: null}); + } - // We logged in the user! Let's send back the user data. - res.render('auth-callback', {err: null, data: JSON.stringify(user)}); - }); - }); + // We logged in the user! Let's send back the user data. + res.render('auth-callback', {err: null, data: JSON.stringify(user)}); + }); }; /** diff --git a/services/users.js b/services/users.js index cf3a0063d..b58776a75 100644 --- a/services/users.js +++ b/services/users.js @@ -124,6 +124,8 @@ module.exports = class UsersService { return user; } + // User does not exist and need to be created. + let username = UsersService.castUsername(displayName); // The user was not found, lets create them! @@ -131,7 +133,8 @@ module.exports = class UsersService { username, lowercaseUsername: username.toLowerCase(), roles: [], - profiles: [{id, provider}] + profiles: [{id, provider}], + canEditName: true }); return user.save(); diff --git a/test/client/coral-framework/reducers/assetReducer.spec.js b/test/client/coral-framework/reducers/assetReducer.spec.js new file mode 100644 index 000000000..c54f51737 --- /dev/null +++ b/test/client/coral-framework/reducers/assetReducer.spec.js @@ -0,0 +1,19 @@ +import {Map} from 'immutable'; +import {expect} from 'chai'; +import assetReducer from '../../../../client/coral-framework/reducers/asset'; +import * as actions from '../../../../client/coral-framework/constants/asset'; + +describe ('coral-embed-stream assetReducer', () => { + describe('UPDATE_COUNT_CACHE', () => { + it('should update the count cache', () => { + const action = { + type: actions.UPDATE_COUNT_CACHE, + id: '123', + count: 456 + }; + const store = new Map({}); + const result = assetReducer(store, action); + expect(result.getIn(['countCache', '123'])).to.equal(456); + }); + }); +}); diff --git a/test/client/coral-framework/reducers/notificationReducer.spec.js b/test/client/coral-framework/reducers/notificationReducer.spec.js new file mode 100644 index 000000000..40c50d11a --- /dev/null +++ b/test/client/coral-framework/reducers/notificationReducer.spec.js @@ -0,0 +1,35 @@ +import {Map} from 'immutable'; +import {expect} from 'chai'; +import notificationReducer from '../../../../client/coral-framework/reducers/notification'; +import * as actions from '../../../../client/coral-framework/actions/notification'; + +describe ('notificationsReducer', () => { + describe('ADD_NOTIFICATION', () => { + it('should add a notification', () => { + const action = { + type: actions.ADD_NOTIFICATION, + text: 'Test notification', + notifType: 'test' + }; + const store = new Map({}); + const result = notificationReducer(store, action); + expect(result.get('text')).to.equal(action.text); + expect(result.get('type')).to.equal(action.notifType); + }); + }); + + describe('CLEAR_NOTIFICATION', () => { + it('should clear a notification', () => { + const action = { + type: actions.CLEAR_NOTIFICATION + }; + const store = new Map({ + text: 'Test notification', + type: 'test' + }); + const result = notificationReducer(store, action); + expect(result.get('text')).to.equal(''); + expect(result.get('type')).to.equal(''); + }); + }); +}); diff --git a/views/embed/stream.ejs b/views/embed/stream.ejs index 77b392b5f..fe7baf919 100644 --- a/views/embed/stream.ejs +++ b/views/embed/stream.ejs @@ -3,7 +3,7 @@ - + <% if (locals.customCssUrl) { %>