diff --git a/client/coral-admin/src/graphql/fragments/commentView.graphql b/client/coral-admin/src/graphql/fragments/commentView.graphql index 8c7310bdf..51b0a3f44 100644 --- a/client/coral-admin/src/graphql/fragments/commentView.graphql +++ b/client/coral-admin/src/graphql/fragments/commentView.graphql @@ -12,6 +12,12 @@ fragment commentView on Comment { id title } + action_summaries { + count + ... on FlagActionSummary { + reason + } + } actions { ... on FlagAction { reason diff --git a/client/coral-admin/src/graphql/queries/modQueueQuery.graphql b/client/coral-admin/src/graphql/queries/modQueueQuery.graphql index b4f2a20a0..da8760be9 100644 --- a/client/coral-admin/src/graphql/queries/modQueueQuery.graphql +++ b/client/coral-admin/src/graphql/queries/modQueueQuery.graphql @@ -15,12 +15,6 @@ query ModQueue ($asset_id: ID, $sort: SORT_ORDER) { sort: $sort }) { ...commentView - action_summaries { - count - ... on FlagActionSummary { - reason - } - } } rejected: comments(query: { statuses: [REJECTED], diff --git a/client/coral-admin/src/services/client.js b/client/coral-admin/src/services/client.js index 7f6a0567d..7d65f3f92 100644 --- a/client/coral-admin/src/services/client.js +++ b/client/coral-admin/src/services/client.js @@ -1,7 +1,9 @@ import ApolloClient, {addTypename} from 'apollo-client'; import getNetworkInterface from './transport'; +import fragmentMatcher from './fragmentMatcher'; export const client = new ApolloClient({ + fragmentMatcher, addTypename: true, queryTransformer: addTypename, dataIdFromObject: (result) => { diff --git a/client/coral-admin/src/services/fragmentMatcher.js b/client/coral-admin/src/services/fragmentMatcher.js new file mode 100644 index 000000000..daa01a538 --- /dev/null +++ b/client/coral-admin/src/services/fragmentMatcher.js @@ -0,0 +1,33 @@ +import {IntrospectionFragmentMatcher} from 'apollo-client'; + +// TODO this is a short-term fix +// we need to set up something to query the server for the schema before ApolloClient initialization +// https://github.com/apollographql/apollo-client/issues/1555#issuecomment-295834774 +const fm = new IntrospectionFragmentMatcher({ + introspectionQueryResultData: { + __schema: { + types: [ + { + kind: 'INTERFACE', + name: 'Action', + possibleTypes: [ + {name: 'FlagAction'}, + {name: 'LikeAction'}, + {name: 'DontAgreeAction'} + ], + }, + { + kind: 'INTERFACE', + name: 'ActionSummary', + possibleTypes: [ + {name: 'FlagActionSummary'}, + {name: 'LikeActionSummary'}, + {name: 'DontAgreeActionSummary'} + ], + } + ], + }, + } +}); + +export default fm; diff --git a/client/coral-embed-stream/src/AppRouter.js b/client/coral-embed-stream/src/AppRouter.js new file mode 100644 index 000000000..133a3790d --- /dev/null +++ b/client/coral-embed-stream/src/AppRouter.js @@ -0,0 +1,16 @@ +import React from 'react'; +import {Router, Route, browserHistory} from 'react-router'; + +import Embed from './Embed'; +import SignInContainer from 'coral-sign-in/containers/SignInContainer'; + +const routes = ( +
+ + +
+); + +const AppRouter = () => ; + +export default AppRouter; diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js index 51402e490..674c03ec5 100644 --- a/client/coral-embed-stream/src/Comment.js +++ b/client/coral-embed-stream/src/Comment.js @@ -19,7 +19,7 @@ import FlagComment from 'coral-plugin-flags/FlagComment'; import LikeButton from 'coral-plugin-likes/LikeButton'; import {BestButton, IfUserCanModifyBest, BEST_TAG, commentIsBest, BestIndicator} from 'coral-plugin-best/BestButton'; import LoadMore from 'coral-embed-stream/src/LoadMore'; -import {Slot} from 'coral-framework'; +import Slot from 'coral-framework/components/Slot'; import IgnoredCommentTombstone from './IgnoredCommentTombstone'; import {TopRightMenu} from './TopRightMenu'; import {getActionSummary, getTotalActionCount, iPerformedThisAction} from 'coral-framework/utils'; diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index b67e9e1c4..c7c204288 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -8,7 +8,7 @@ const lang = new I18n(translations); import {TabBar, Tab, TabContent, Spinner, Button} from 'coral-ui'; -const {logout, showSignInDialog, requestConfirmEmail} = authActions; +const {logout, showSignInDialog, requestConfirmEmail, openSignInPopUp, checkLogin} = authActions; const {addNotification, clearNotification} = notificationActions; const {fetchAssetSuccess} = assetActions; import {NEW_COMMENT_COUNT_POLL_INTERVAL} from 'coral-framework/constants/comments'; @@ -26,7 +26,6 @@ import {ModerationLink} from 'coral-plugin-moderation'; import Count from 'coral-plugin-comment-count/CommentCount'; import CommentBox from 'coral-plugin-commentbox/CommentBox'; 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 ProfileContainer from 'coral-settings/containers/ProfileContainer'; @@ -78,6 +77,7 @@ class Embed extends React.Component { componentDidMount () { pym.sendMessage('childReady'); + this.props.checkLogin(); } componentWillUnmount () { @@ -119,8 +119,7 @@ class Embed extends React.Component { setActiveReplyBox = (reactKey) => { if (!this.props.auth.user) { - const offset = document.getElementById(`c_${reactKey}`).getBoundingClientRect().top - 75; - this.props.showSignInDialog(offset); + this.props.showSignInDialog(); } else { this.setState({activeReplyBox: reactKey}); } @@ -130,7 +129,7 @@ class Embed extends React.Component { const {activeTab} = this.state; const {closedAt, countCache = {}} = this.props.asset; const {asset, refetch, comment} = this.props.data; - const {loggedIn, isAdmin, user, showSignInDialog, signInOffset} = this.props.auth; + const {loggedIn, isAdmin, user, showSignInDialog} = this.props.auth; // even though the permalinked comment is the highlighted one, we're displaying its parent + replies const highlightedComment = comment && comment.parent ? comment.parent : comment; @@ -218,11 +217,10 @@ class Embed extends React.Component { :

{asset.settings.closedMessage}

} - {!loggedIn && } - {loggedIn && user && } + + {!loggedIn && } + + {loggedIn && user && } {loggedIn && } {/* the highlightedComment is isolated after the user followed a permalink */} @@ -290,7 +288,6 @@ class Embed extends React.Component { @@ -320,10 +317,12 @@ const mapDispatchToProps = dispatch => ({ addNotification: (type, text) => addNotification(type, text), clearNotification: () => dispatch(clearNotification()), editName: (username) => dispatch(editName(username)), - showSignInDialog: (offset) => dispatch(showSignInDialog(offset)), + showSignInDialog: () => dispatch(showSignInDialog()), updateCountCache: (id, count) => dispatch(updateCountCache(id, count)), viewAllComments: () => dispatch(viewAllComments()), logout: () => dispatch(logout()), + openSignInPopUp: cb => dispatch(openSignInPopUp(cb)), + checkLogin: () => dispatch(checkLogin()), dispatch: d => dispatch(d), }); diff --git a/client/coral-embed-stream/src/index.js b/client/coral-embed-stream/src/index.js index 3dc69400c..d7ee99e73 100644 --- a/client/coral-embed-stream/src/index.js +++ b/client/coral-embed-stream/src/index.js @@ -3,13 +3,15 @@ import {render} from 'react-dom'; import {ApolloProvider} from 'react-apollo'; import {client} from 'coral-framework/services/client'; -import store from 'coral-framework/services/store'; +import localStore from 'coral-framework/services/store'; -import Embed from './Embed'; +import AppRouter from './AppRouter'; + +const store = (window.opener && window.opener.coralStore) ? window.opener.coralStore : localStore; render( - + , document.querySelector('#coralStream') ); diff --git a/client/coral-embed-stream/style/default.css b/client/coral-embed-stream/style/default.css index b99b109ac..13a200690 100644 --- a/client/coral-embed-stream/style/default.css +++ b/client/coral-embed-stream/style/default.css @@ -396,6 +396,7 @@ button.comment__action-button[disabled], margin-left: 20px; margin-top: 5px; width: 75%; + font-size: 16px; } /* Close comments */ @@ -490,4 +491,4 @@ button.comment__action-button[disabled], .commentActionsLeft.comment__action-container .coral-plugin-replies-reply-button .coral-plugin-replies-icon { visibility: visible; } -} \ No newline at end of file +} diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 11e7bd996..e112b2b6c 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -11,6 +11,16 @@ const ME_QUERY = gql` query Me { me { status + comments { + id + body + asset { + id + title + url + } + created_at + } } } `; @@ -22,8 +32,23 @@ function fetchMe() { } // Dialog Actions -export const showSignInDialog = (offset = 0) => ({type: actions.SHOW_SIGNIN_DIALOG, offset}); -export const hideSignInDialog = () => ({type: actions.HIDE_SIGNIN_DIALOG}); +export const showSignInDialog = () => dispatch => { + const signInPopUp = window.open( + '/embed/stream/login', + 'Login', + 'menubar=0,resizable=0,width=500,height=550,top=200,left=500' + ); + + signInPopUp.onbeforeunload = () => { + dispatch(checkLogin()); + fetchMe(); + }; + dispatch({type: actions.SHOW_SIGNIN_DIALOG}); +}; +export const hideSignInDialog = () => dispatch => { + dispatch({type: actions.HIDE_SIGNIN_DIALOG}); + window.close(); +}; export const createUsernameRequest = () => ({type: actions.CREATE_USERNAME_REQUEST}); export const showCreateUsernameDialog = () => ({type: actions.SHOW_CREATEUSERNAME_DIALOG}); @@ -47,29 +72,39 @@ export const createUsername = (userId, formData) => dispatch => { }); }; -export const changeView = view => dispatch => +export const changeView = view => dispatch => { dispatch({ type: actions.CHANGE_VIEW, view }); + switch(view) { + case 'SIGNUP': + window.resizeTo(500, 800); + break; + case 'FORGOT': + window.resizeTo(500, 400); + break; + default: + window.resizeTo(500, 550); + } +}; + export const cleanState = () => ({type: actions.CLEAN_STATE}); // Sign In Actions const signInRequest = () => ({type: actions.FETCH_SIGNIN_REQUEST}); -const signInSuccess = (user, isAdmin) => ({type: actions.FETCH_SIGNIN_SUCCESS, user, isAdmin}); + +// TODO: revisit login redux flow. +// const signInSuccess = (user, isAdmin) => ({type: actions.FETCH_SIGNIN_SUCCESS, user, isAdmin}); +// const signInFailure = error => ({type: actions.FETCH_SIGNIN_FAILURE, error}); export const fetchSignIn = (formData) => (dispatch) => { dispatch(signInRequest()); return coralApi('/auth/local', {method: 'POST', body: formData}) - .then(({user}) => { - const isAdmin = !!user && !!user.roles.filter(i => i === 'ADMIN').length; - dispatch(signInSuccess(user, isAdmin)); - dispatch(hideSignInDialog()); - fetchMe(); - }) + .then(() => dispatch(hideSignInDialog())) .catch(error => { if (error.metadata) { @@ -121,7 +156,7 @@ export const facebookCallback = (err, data) => dispatch => { dispatch(signInFacebookSuccess(user)); dispatch(hideSignInDialog()); dispatch(showCreateUsernameDialog()); - fetchMe(); + dispatch(hideSignInDialog()); } catch (err) { dispatch(signInFacebookFailure(err)); return; diff --git a/client/coral-framework/index.js b/client/coral-framework/index.js index 96750bffd..b85b69bcd 100644 --- a/client/coral-framework/index.js +++ b/client/coral-framework/index.js @@ -1,16 +1,12 @@ -import store from './services/store'; import pym from './services/PymConnection'; import I18n from './modules/i18n/i18n'; import actions from './actions'; -import Slot from './components/Slot'; // TODO (bc): Deprecate old actions. Spreading actions is now needed. export default { pym, - Slot, I18n, - store, actions, ...actions }; diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js index f9712c937..6e678cab9 100644 --- a/client/coral-framework/reducers/auth.js +++ b/client/coral-framework/reducers/auth.js @@ -28,8 +28,7 @@ export default function auth (state = initialState, action) { switch (action.type) { case actions.SHOW_SIGNIN_DIALOG : return state - .set('showSignInDialog', true) - .set('signInOffset', action.offset); + .set('showSignInDialog', true); case actions.HIDE_SIGNIN_DIALOG : return state.merge(Map({ isLoading: false, diff --git a/client/coral-framework/services/store.js b/client/coral-framework/services/store.js index fbc444eec..d7090f4b5 100644 --- a/client/coral-framework/services/store.js +++ b/client/coral-framework/services/store.js @@ -24,7 +24,7 @@ if (window.devToolsExtension) { middlewares.push(window.devToolsExtension()); } -export default createStore( +const store = createStore( combineReducers({ ...mainReducer, apollo: client.reducer() @@ -32,3 +32,6 @@ export default createStore( {}, compose(...middlewares) ); + +export default store; +window.coralStore = store; diff --git a/client/coral-plugin-commentbox/CommentBox.js b/client/coral-plugin-commentbox/CommentBox.js index f395b56c9..2a988711d 100644 --- a/client/coral-plugin-commentbox/CommentBox.js +++ b/client/coral-plugin-commentbox/CommentBox.js @@ -2,7 +2,7 @@ import React, {Component, PropTypes} from 'react'; import {I18n} from '../coral-framework'; import translations from './translations.json'; import {Button} from 'coral-ui'; -import {Slot} from 'coral-framework'; +import Slot from 'coral-framework/components/Slot'; import {connect} from 'react-redux'; const name = 'coral-plugin-commentbox'; diff --git a/client/coral-plugin-flags/FlagButton.js b/client/coral-plugin-flags/FlagButton.js index 054ca2fb1..16e616379 100644 --- a/client/coral-plugin-flags/FlagButton.js +++ b/client/coral-plugin-flags/FlagButton.js @@ -25,8 +25,7 @@ class FlagButton extends Component { const {localPost, localDelete} = this.state; const localFlagged = (flaggedByCurrentUser && !localDelete) || localPost; if (!currentUser) { - const offset = document.getElementById(`c_${this.props.id}`).getBoundingClientRect().top - 75; - this.props.showSignInDialog(offset); + this.props.showSignInDialog(); return; } if (localFlagged) { diff --git a/client/coral-plugin-likes/LikeButton.js b/client/coral-plugin-likes/LikeButton.js index 8f6964051..28382dbc2 100644 --- a/client/coral-plugin-likes/LikeButton.js +++ b/client/coral-plugin-likes/LikeButton.js @@ -35,8 +35,7 @@ class LikeButton extends Component { const onLikeClick = () => { if (!currentUser) { - const offset = document.getElementById(`c_${id}`).getBoundingClientRect().top - 75; - showSignInDialog(offset); + showSignInDialog(); return; } if (currentUser.banned) { diff --git a/client/coral-settings/components/NotLoggedIn.js b/client/coral-settings/components/NotLoggedIn.js index c76553c50..a5d8b0693 100644 --- a/client/coral-settings/components/NotLoggedIn.js +++ b/client/coral-settings/components/NotLoggedIn.js @@ -1,17 +1,13 @@ 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, requireEmailConfirmation}) => ( +export default ({showSignInDialog}) => (
-
- { - showSignInDialog(); - }}>{lang.t('signIn')} {lang.t('toAccess')} + {lang.t('signIn')} {lang.t('toAccess')}
{lang.t('fromSettingsPage')} diff --git a/client/coral-settings/containers/ProfileContainer.js b/client/coral-settings/containers/ProfileContainer.js index 2d756077d..553504462 100644 --- a/client/coral-settings/containers/ProfileContainer.js +++ b/client/coral-settings/containers/ProfileContainer.js @@ -2,6 +2,7 @@ import {connect} from 'react-redux'; import {compose} from 'react-apollo'; import React, {Component} from 'react'; import I18n from 'coral-framework/modules/i18n/i18n'; +import {bindActionCreators} from 'redux'; import {myCommentHistory, myIgnoredUsers} from 'coral-framework/graphql/queries'; import {stopIgnoringUser} from 'coral-framework/graphql/mutations'; @@ -12,6 +13,8 @@ import IgnoredUsers from '../components/IgnoredUsers'; import {Spinner} from 'coral-ui'; import CommentHistory from 'coral-plugin-history/CommentHistory'; +import {showSignInDialog, checkLogin} from 'coral-framework/actions/auth'; + import translations from '../translations'; const lang = new I18n(translations); @@ -32,17 +35,17 @@ class ProfileContainer extends Component { } render() { - const {loggedIn, asset, showSignInDialog, data, myIgnoredUsersData, stopIgnoringUser} = this.props; + const {loggedIn, asset, data, showSignInDialog, myIgnoredUsersData, stopIgnoringUser} = this.props; const {me} = this.props.data; - if (!loggedIn || !me) { - return ; - } - if (data.loading) { return ; } + if (!loggedIn || !me) { + return ; + } + const localProfile = this.props.user.profiles.find(p => p.provider === 'local'); const emailAddress = localProfile && localProfile.id; @@ -81,7 +84,6 @@ class ProfileContainer extends Component { :

{lang.t('userNoComment')}

} -
); } @@ -93,10 +95,8 @@ const mapStateToProps = state => ({ auth: state.auth.toJS() }); -const mapDispatchToProps = () => ({ - - // saveBio: (user_id, formData) => dispatch(saveBio(user_id, formData)) -}); +const mapDispatchToProps = dispatch => + bindActionCreators({showSignInDialog, checkLogin}, dispatch); export default compose( connect(mapStateToProps, mapDispatchToProps), diff --git a/client/coral-sign-in/components/CreateUsernameDialog.js b/client/coral-sign-in/components/CreateUsernameDialog.js index 4a7a99b81..b94b47c42 100644 --- a/client/coral-sign-in/components/CreateUsernameDialog.js +++ b/client/coral-sign-in/components/CreateUsernameDialog.js @@ -10,16 +10,12 @@ 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, formData, handleSubmitUsername, handleChange, ...props}) => { return ( + open={open}> ×
@@ -42,6 +38,7 @@ const CreateUsernameDialog = ({open, handleClose, offset, formData, handleSubmit
this.emailInput = input} type="text" + style={{fontSize: 16}} id="email" name="email" />
diff --git a/client/coral-sign-in/components/SignDialog.js b/client/coral-sign-in/components/SignDialog.js index 6243472b4..ff2464f7c 100644 --- a/client/coral-sign-in/components/SignDialog.js +++ b/client/coral-sign-in/components/SignDialog.js @@ -6,15 +6,11 @@ import SignInContent from './SignInContent'; import SignUpContent from './SignUpContent'; import ForgotContent from './ForgotContent'; -const SignDialog = ({open, view, handleClose, offset, ...props}) => ( +const SignDialog = ({open, view, handleClose, ...props}) => ( + open={open}> × {view === 'SIGNIN' && } {view === 'SIGNUP' && } diff --git a/client/coral-sign-in/components/SignInContent.js b/client/coral-sign-in/components/SignInContent.js index a0ed5b5b4..7460efb97 100644 --- a/client/coral-sign-in/components/SignInContent.js +++ b/client/coral-sign-in/components/SignInContent.js @@ -20,8 +20,8 @@ const SignInContent = ({ }) => { return ( -
-
+
+

{auth.emailVerificationFailure ? lang.t('signIn.emailVerifyCTA') : lang.t('signIn.signIn')}

@@ -42,7 +42,7 @@ const SignInContent = ({ {emailVerificationSuccess && } :
-
+
@@ -58,6 +58,7 @@ const SignInContent = ({ type="email" label={lang.t('signIn.email')} value={formData.email} + style={{fontSize: 16}} onChange={handleChange} />
@@ -80,7 +82,7 @@ const SignInContent = ({
} -
+
changeView('FORGOT')}>{lang.t('signIn.forgotYourPass')} {lang.t('signIn.needAnAccount')} diff --git a/client/coral-sign-in/components/SignUpContent.js b/client/coral-sign-in/components/SignUpContent.js index d127cc2c7..921f045be 100644 --- a/client/coral-sign-in/components/SignUpContent.js +++ b/client/coral-sign-in/components/SignUpContent.js @@ -83,6 +83,7 @@ class SignUpContent extends React.Component { type="email" label={lang.t('signIn.email')} value={formData.email} + style={{fontSize: 16}} showErrors={showErrors} errorMsg={errors.email} onChange={handleChange} @@ -93,6 +94,7 @@ class SignUpContent extends React.Component { label={lang.t('signIn.username')} value={formData.username} showErrors={showErrors} + style={{fontSize: 16}} errorMsg={errors.username} onChange={handleChange} /> @@ -102,6 +104,7 @@ class SignUpContent extends React.Component { label={lang.t('signIn.password')} value={formData.password} showErrors={showErrors} + style={{fontSize: 16}} errorMsg={errors.password} onChange={handleChange} minLength="8" @@ -112,6 +115,7 @@ class SignUpContent extends React.Component { type="password" label={lang.t('signIn.confirmPassword')} value={formData.confirmPassword} + style={{fontSize: 16}} showErrors={showErrors} errorMsg={errors.confirmPassword} onChange={handleChange} diff --git a/client/coral-sign-in/containers/ChangeUsernameContainer.js b/client/coral-sign-in/containers/ChangeUsernameContainer.js index c4450aa56..a3b4b969d 100644 --- a/client/coral-sign-in/containers/ChangeUsernameContainer.js +++ b/client/coral-sign-in/containers/ChangeUsernameContainer.js @@ -100,12 +100,11 @@ class ChangeUsernameContainer extends Component { } render() { - const {loggedIn, auth, offset} = this.props; + const {loggedIn, auth} = this.props; return (
- {!noButton && } ({ fetchSignUpFacebook: () => dispatch(fetchSignUpFacebook()), fetchForgotPassword: formData => dispatch(fetchForgotPassword(formData)), requestConfirmEmail: (email, url) => dispatch(requestConfirmEmail(email, url)), - showSignInDialog: () => dispatch(showSignInDialog()), changeView: view => dispatch(changeView(view)), handleClose: () => dispatch(hideSignInDialog()), invalidForm: error => dispatch(invalidForm(error)), diff --git a/graph/hooks.js b/graph/hooks.js index c5b7541b3..7f6be7feb 100644 --- a/graph/hooks.js +++ b/graph/hooks.js @@ -53,6 +53,71 @@ const forEachField = (schema, fn) => { }); }; +/** + * Decorates the field with the post resolvers (if available) and attaches a + * default type in the form of `Default${typeName}`. + */ +const decorateResolveFunction = (field, typeName, fieldName, post) => { + + // Cache the original resolverType function. + let resolveType = field.resolveType; + + // defaultResolveType is the default type that is resolved on a resolver + // when the interface being looked up is not defined. + const defaultResolveType = `Default${typeName}`; + + // Return the function to handle the resolveType hooks. + const defaultResolveFn = (obj, context, info) => { + let type = resolveType(obj, context, info); + + // Only if a previous resolver was unable to resolve the field type do we + // progress to the hooks (in order!) to resolve the field name until we + // have resolved it. + if (typeof type !== 'undefined' && type != null) { + return type; + } + + // All else fails, resort to the defaultResolveType. + return defaultResolveType; + }; + + // This only needs to do something if post hooks are defined. + if (post.length === 0) { + + // Set the default on the resolveType function. + field.resolveType = defaultResolveFn; + + return; + } + + // Ensure it matches the format we expect. + Joi.assert(post, Joi.array().items(Joi.func().maxArity(3)), `invalid post hooks were found for ${typeName}.${fieldName}`); + + // Return the function to handle the resolveType hooks. + field.resolveType = (obj, context, info) => { + let type = defaultResolveFn(obj, context, info); + + // Only if a previous resolver was unable to resolve the field type do we + // progress to the hooks (in order!) to resolve the field name until we + // have resolved it. + if (typeof type !== 'undefined' && type != null && type !== defaultResolveType) { + return type; + } + + // We will walk through the post hooks until we find the right one. This + // follows what redux does to combine existing reducers. + for (let i = 0; i < post.length; i++) { + let resolveType = post[i]; + let resolvedType = resolveType(obj, context, info); + if (typeof resolvedType !== 'undefined' && resolvedType != null) { + return resolvedType; + } + } + + return type; + }; +}; + /** * Decorates the schema with pre and post hooks as provided by the Plugin * Manager. @@ -115,11 +180,6 @@ const decorateWithHooks = (schema, hooks) => forEachField(schema, (field, typeNa post: [] }); - // If we have no hooks to add here, don't try to modify anything. - if (pre.length === 0 && post.length === 0) { - return; - } - // If this is a resolve type, we need to do some specific things to handle // this type of field. if (isResolveType) { @@ -129,39 +189,13 @@ const decorateWithHooks = (schema, hooks) => forEachField(schema, (field, typeNa throw new Error(`invalid pre hooks were found for ${typeName}.${fieldName}, only post hooks are supported on the __resolveType hook`); } - // This only needs to do something if post hooks are defined. - if (post.length === 0) { - return; - } - - // Ensure it matches the format we expect. - Joi.assert(post, Joi.array().items(Joi.func().maxArity(3)), `invalid post hooks were found for ${typeName}.${fieldName}`); - - // Cache the original resolverType function. - let resolveType = field.resolveType; - - // Return the function to handle the resolveType hooks. - field.resolveType = (obj, context, info) => { - let type = resolveType(obj, context, info); - - // Only if a previous resolver was unable to resolve the field type do we - // progress to the hooks (in order!) to resolve the field name until we - // have resolved it. - if (typeof type !== 'undefined' && type != null) { - return type; - } - - // We will walk through the post hooks until we find the right one. This - // follows what redux does to combine existing reducers. - for (let i = 0; i < post.length; i++) { - let resolveType = post[i]; - type = resolveType(obj, context, info); - if (typeof type !== 'undefined' && type != null) { - return type; - } - } - }; + // Decorate the resolve function on the field with the new resolveType func. + decorateResolveFunction(field, typeName, fieldName, post); + return; + } + // If we have no hooks to add here, don't try to modify anything. + if (pre.length === 0 && post.length === 0) { return; } diff --git a/graph/resolvers/action_summary.js b/graph/resolvers/action_summary.js index 1986a0648..ac2154de8 100644 --- a/graph/resolvers/action_summary.js +++ b/graph/resolvers/action_summary.js @@ -8,7 +8,7 @@ const ActionSummary = { case 'DONTAGREE': return 'DontAgreeActionSummary'; } - }, + } }; module.exports = ActionSummary; diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index eb376b0de..32c2c16c2 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -229,6 +229,22 @@ interface Action { created_at: Date } +# DefaultAction is the Action provided for undefined types. +type DefaultAction implements Action { + + # The ID of the action. + id: ID! + + # The author of the action. + user: User + + # The time when the Action was updated. + updated_at: Date + + # The time when the Action was created. + created_at: Date +} + # A summary of actions based on the specific grouping of the group_id. interface ActionSummary { @@ -239,6 +255,16 @@ interface ActionSummary { current_user: Action } +# DefaultActionSummary is the ActionSummary provided for undefined types. +type DefaultActionSummary implements ActionSummary { + + # The count of actions with this group. + count: Int + + # The current user's action. + current_user: Action +} + # A summary of actions for a specific action type on an Asset. interface AssetActionSummary { @@ -249,6 +275,16 @@ interface AssetActionSummary { actionableItemCount: Int } +# DefaultAssetActionSummary is the AssetActionSummary provided for undefined types. +type DefaultAssetActionSummary implements AssetActionSummary { + + # Number of actions associated with actionable types on this this Asset. + actionCount: Int + + # Number of unique actionable types that are referenced by the actions. + actionableItemCount: Int +} + # A summary of counts related to all the Flags on an Asset. type FlagAssetActionSummary implements AssetActionSummary { diff --git a/plugins/coral-plugin-respect/client/components/RespectButton.js b/plugins/coral-plugin-respect/client/components/RespectButton.js index 417b978ad..c1e0c1b47 100644 --- a/plugins/coral-plugin-respect/client/components/RespectButton.js +++ b/plugins/coral-plugin-respect/client/components/RespectButton.js @@ -19,8 +19,7 @@ class RespectButton extends Component { // If the current user does not exist, trigger sign in dialog. if (!me) { - const offset = document.getElementById(`c_${commentId}`).getBoundingClientRect().top - 75; - showSignInDialog(offset); + showSignInDialog(); return; } diff --git a/views/embed/stream.ejs b/views/embed/stream.ejs index 498090e32..d99d4bce1 100644 --- a/views/embed/stream.ejs +++ b/views/embed/stream.ejs @@ -1,6 +1,8 @@ + +