Merge branch 'master' into staff-notifications

This commit is contained in:
Wyatt Johnson
2018-03-01 11:51:44 -07:00
committed by GitHub
17 changed files with 290 additions and 17 deletions
+21 -6
View File
@@ -8,6 +8,7 @@ job_defaults: &job_defaults
integration_environment: &integration_environment
NODE_ENV: test
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
E2E_MAX_RETRIES: 3
# integration_job runs the integration tests and saves the test results.
integration_job: &integration_job
@@ -29,6 +30,7 @@ integration_job: &integration_job
name: Run the integration tests
command: bash .circleci/e2e.sh
- store_test_results:
when: always
path: /tmp/circleci-test-results
version: 2
@@ -104,13 +106,19 @@ jobs:
name: Setup the test results directory
command: mkdir -p /tmp/circleci-test-results
- run:
name: Run the unit tests
command: yarn test
name: Run the client unit tests
command: yarn test:client --ci
environment:
MOCHA_FILE: /tmp/circleci-test-results/junit/test-results.xml
JEST_JUNIT_OUTPUT: /tmp/circleci-test-results/jest/test-results.xml
JEST_REPORTER: jest-junit
- run:
name: Run the server unit tests
command: yarn test:server
environment:
MOCHA_FILE: /tmp/circleci-test-results/mocha/test-results.xml
MOCHA_REPORTER: mocha-junit-reporter
NODE_ENV: test
- store_test_results:
when: always
path: /tmp/circleci-test-results
# test_integration_chrome_local will run the integration tests locally with
@@ -164,6 +172,8 @@ jobs:
<<: *integration_environment
BROWSERSTACK: true
E2E_BROWSERS: ie
# TODO: remove when more reliable
E2E_MAX_RETRIES: 1
# test_integration_safari will run the integration tests with safari in
# browserstack.
@@ -173,6 +183,8 @@ jobs:
<<: *integration_environment
BROWSERSTACK: true
E2E_BROWSERS: safari
# TODO: remove when more reliable
E2E_MAX_RETRIES: 1
# deploy will deploy the application as a docker image.
deploy:
@@ -182,7 +194,7 @@ jobs:
- setup_remote_docker
- run:
name: Deploy the code
command: bash ./scripts/docker.sh
command: bash ./scripts/docker.sh deploy
# filter_deploy will add the filters for a deploy job in a workflow to make it
# only execute on a deploy related job.
@@ -274,4 +286,7 @@ workflows:
- test_unit
- test_integration_chrome
- test_integration_firefox
- test_integration_edge
- test_integration_edge
# TODO: uncomment when more reliable
# - test_integration_ie
# - test_integration_safari
+1
View File
@@ -40,6 +40,7 @@ plugins/*
!plugins/talk-plugin-mod
!plugins/talk-plugin-moderation-actions
!plugins/talk-plugin-notifications
!plugins/talk-plugin-notifications-category-featured
!plugins/talk-plugin-notifications-category-reply
!plugins/talk-plugin-notifications-category-staff
!plugins/talk-plugin-offtopic
+2 -1
View File
@@ -143,7 +143,7 @@ async function reconcileRemotePlugins({ dryRun, upgradeRemote }) {
if (fetchable.length > 0) {
console.log(
`$ yarn add --ignore-scripts ${fetchable.map(
`$ yarn add --ignore-scripts --ignore-workspace-root-check ${fetchable.map(
({ name, version }) => `${name}@${version}`.cyan
)}`
);
@@ -152,6 +152,7 @@ async function reconcileRemotePlugins({ dryRun, upgradeRemote }) {
let args = [
'add',
'--ignore-scripts',
'--ignore-workspace-root-check',
...fetchable.map(({ name, version }) => `${name}@${version}`),
];
+8
View File
@@ -149,3 +149,11 @@ Source: [plugins/talk-plugin-notifications-category-reply](https://github.com/co
Replies made to each user will trigger an email to be sent with the notification
details if enabled.
### talk-plugin-notifications-category-featured
{:.param}
Source: [plugins/talk-plugin-notifications-category-featured](https://github.com/coralproject/talk/tree/master/plugins/talk-plugin-notifications-category-featured){:target="_blank"}
When a comment is featured (via the `talk-plugin-featured-comments` plugin), the
user will receive a notification email.
+2
View File
@@ -27,6 +27,8 @@ module.exports = {
'\\.ya?ml$': '<rootDir>/test/client/yamlTransformer.js',
},
testResultsProcessor: process.env.JEST_REPORTER,
moduleNameMapper: {
'^plugin-api\\/(.*)$': '<rootDir>/plugin-api/$1',
'^plugins\\/(.*)$': '<rootDir>/plugins/$1',
+1
View File
@@ -216,6 +216,7 @@
"identity-obj-proxy": "^3.0.0",
"ip": "^1.1.5",
"jest": "^21.2.1",
"jest-junit": "^3.6.0",
"lint-staged": "^7.0.0",
"mocha": "^3.1.2",
"mocha-junit-reporter": "^1.12.1",
@@ -0,0 +1,3 @@
{
"extends": "@coralproject/eslint-config-talk/client"
}
@@ -0,0 +1,69 @@
import React from 'react';
import PropTypes from 'prop-types';
import { compose, gql } from 'react-apollo';
import Toggle from 'talk-plugin-notifications/client/components/Toggle';
import { t } from 'plugin-api/beta/client/services';
import { withFragments } from 'plugin-api/beta/client/hocs';
class ToggleContainer extends React.Component {
constructor(props) {
super(props);
props.setTurnOffInputFragment({ onFeatured: false });
if (this.getOnFeaturedSetting()) {
props.indicateOn();
}
}
componentWillReceiveProps(nextProps) {
const prevSetting = this.getOnFeaturedSetting(this.props);
const nextSetting = this.getOnFeaturedSetting(nextProps);
if (prevSetting && !nextSetting) {
nextProps.indicateOff();
} else if (!prevSetting && nextSetting) {
nextProps.indicateOn();
}
}
getOnFeaturedSetting = (props = this.props) =>
props.root.me.notificationSettings.onFeatured;
toggle = () => {
this.props.updateNotificationSettings({
onFeatured: !this.getOnFeaturedSetting(),
});
};
render() {
return (
<Toggle checked={this.getOnFeaturedSetting()} onChange={this.toggle}>
{t('talk-plugin-notifications-category-featured.toggle_description')}
</Toggle>
);
}
}
ToggleContainer.propTypes = {
data: PropTypes.object,
root: PropTypes.object,
indicateOn: PropTypes.func.isRequired,
indicateOff: PropTypes.func.isRequired,
setTurnOffInputFragment: PropTypes.func.isRequired,
updateNotificationSettings: PropTypes.func.isRequired,
};
const enhance = compose(
withFragments({
root: gql`
fragment TalkNotificationsCategoryFeatured_Toggle_root on RootQuery {
me {
notificationSettings {
onFeatured
}
}
}
`,
})
);
export default enhance(ToggleContainer);
@@ -0,0 +1,33 @@
import { gql } from 'react-apollo';
export default {
mutations: {
UpdateNotificationSettings: ({
variables: { input },
state: { auth: { user: { id } } },
}) => ({
update: proxy => {
if (input.onFeatured === undefined) {
return;
}
const fragment = gql`
fragment TalkNotificationsCategoryFeatured_User_Fragment on User {
notificationSettings {
onFeatured
}
}
`;
const fragmentId = `User_${id}`;
const data = {
__typename: 'User',
notificationSettings: {
__typename: 'NotificationSettings',
onFeatured: input.onFeatured,
},
};
proxy.writeFragment({ fragment, id: fragmentId, data });
},
}),
},
};
@@ -0,0 +1,11 @@
import Toggle from './containers/Toggle';
import translations from './translations.yml';
import graphql from './graphql';
export default {
slots: {
notificationSettings: [Toggle],
},
translations,
...graphql,
};
@@ -0,0 +1,3 @@
en:
talk-plugin-notifications-category-featured:
toggle_description: My comment is featured
@@ -0,0 +1,118 @@
const { graphql } = require('graphql');
const { get } = require('lodash');
const path = require('path');
const handle = async (ctx, { comment }) => {
const { connectors: { graph: { schema } } } = ctx;
// Check to see if this is a reply to an existing comment.
const commentID = get(comment, 'id', null);
if (commentID === null) {
ctx.log.debug('could not get comment id');
return;
}
// Execute the graph request.
const reply = await graphql(
schema,
`
query GetAuthorUserMetadata($comment_id: ID!) {
comment(id: $comment_id) {
id
user {
id
notificationSettings {
onFeatured
}
}
}
}
`,
{},
ctx,
{ comment_id: commentID }
);
if (reply.errors) {
ctx.log.error({ err: reply.errors }, 'could not query for author metadata');
return;
}
// Check if the user has notifications enabled.
const enabled = get(
reply,
'data.comment.user.notificationSettings.onFeatured',
false
);
if (!enabled) {
return;
}
const userID = get(reply, 'data.comment.user.id', null);
if (!userID) {
ctx.log.debug('could not get comment user id');
return;
}
// The user does have notifications for featured comments enabled, queue the
// notification to be sent.
return { userID, date: comment.created_at, context: comment.id };
};
const hydrate = async (ctx, category, context) => {
const { connectors: { graph: { schema } } } = ctx;
const reply = await graphql(
schema,
`
query GetNotificationData($context: ID!) {
comment(id: $context) {
id
asset {
title
url
}
}
}
`,
{},
ctx,
{ context }
);
if (reply.errors) {
throw reply.errors;
}
const comment = get(reply, 'data.comment');
const headline = get(comment, 'asset.title', null);
const assetURL = get(comment, 'asset.url', null);
const permalink = `${assetURL}?commentId=${comment.id}`;
return [headline, permalink];
};
const handler = {
handle,
category: 'featured',
event: 'commentFeatured',
hydrate,
};
module.exports = {
typeDefs: `
type NotificationSettings {
onFeatured: Boolean!
}
input NotificationSettingsInput {
onFeatured: Boolean
}
`,
resolvers: {
NotificationSettings: {
// onFeatured returns false by default if not specified.
onFeatured: settings => get(settings, 'onFeatured', false),
},
},
translations: path.join(__dirname, 'translations.yml'),
notifications: [handler],
};
@@ -0,0 +1,6 @@
en:
talk-plugin-notifications:
categories:
featured:
subject: "One of your comments was featured on [{0}]"
body: "{0}\nA member of our team has selected this comment to be featured for other readers: {1}"
@@ -1,3 +0,0 @@
{
"extends": "@coralproject/eslint-config-talk"
}
@@ -1,3 +0,0 @@
{
"extends": "@coralproject/eslint-config-talk"
}
@@ -23,9 +23,9 @@ class SettingsContainer extends React.Component {
hasNotifications: this.state.hasNotifications.filter(i => i !== plugin),
});
setTurnOffInputFragment = fragment =>
this.setState({
turnOffInput: { ...this.state.turnOffInput, ...fragment },
});
this.setState(state => ({
turnOffInput: { ...state.turnOffInput, ...fragment },
}));
turnOffAll = () => {
this.props.updateNotificationSettings(this.state.turnOffInput);
+9 -1
View File
@@ -5399,6 +5399,14 @@ jest-jasmine2@^22.4.2:
jest-util "^22.4.1"
source-map-support "^0.5.0"
jest-junit@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-3.6.0.tgz#f4c4358e5286364a4324dc14abddd526aadfbd38"
dependencies:
mkdirp "^0.5.1"
strip-ansi "^4.0.0"
xml "^1.0.1"
jest-matcher-utils@^21.2.1:
version "21.2.1"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-21.2.1.tgz#72c826eaba41a093ac2b4565f865eb8475de0f64"
@@ -10987,7 +10995,7 @@ xml-name-validator@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
xml@^1.0.0:
xml@^1.0.0, xml@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"