diff --git a/README.md b/README.md index a58ac7c74..4bec86d07 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Talk [![CircleCI](https://circleci.com/gh/coralproject/talk.svg?style=svg)](https://circleci.com/gh/coralproject/talk) +[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://dashboard.heroku.com/new?template=https%3A%2F%2Fgithub.com%2Fcoralproject%2Ftalk&env[TALK_FACEBOOK_APP_ID]=ignore&env[TALK_FACEBOOK_APP_SECRET]=ignore) Online comments are broken. Our open-source Talk tool rethinks how moderation, comment display, and conversation function, creating the opportunity for safer, smarter discussions around your work. [Read more about Talk here.](https://coralproject.net/products/talk.html) @@ -19,7 +20,7 @@ endpoint when the server is running with built assets. - Blog: https://blog.coralproject.net -- Community Guides for Journalism: https://guides.coralproject.net/ +- Community Guides for Journalism: https://guides.coralproject.net/ ## License diff --git a/app.json b/app.json index 5faa23bc2..6c85e3cc2 100644 --- a/app.json +++ b/app.json @@ -1,10 +1,15 @@ { "name": "The Coral Project: Talk", "env": { - "TALK_SESSION_SECRET": { + "TALK_JWT_SECRET": { "description": "The session secret", "generator": "secret" }, + "TALK_ROOT_URL": { + "description": "Please copy the App Name you choose above. If you did not choose one, please do so now and copy it here. Talk on Heroku will not work without this setting.", + "value":"https://.herokuapp.com", + "required": true + }, "TALK_FACEBOOK_APP_ID": { "value": "", "required": true @@ -14,8 +19,7 @@ "required": true }, "NODE_ENV": "production", - "TALK_SMTP_PORT": "2525", - "REWRITE_ENV": "TALK_PORT:PORT,TALK_MONGO_URL:MONGO_URI,TALK_REDIS_URL:REDIS_URL,TALK_SMTP_HOST:POSTMARK_SMTP_SERVER,TALK_SMTP_USERNAME:POSTMARK_API_TOKEN,TALK_SMTP_PASSWORD:POSTMARK_API_TOKEN", + "REWRITE_ENV": "TALK_MONGO_URL:MONGO_URI,TALK_REDIS_URL:REDIS_URL,TALK_SMTP_HOST:MAILGUN_SMTP_SERVER,TALK_SMTP_PORT:MAILGUN_SMTP_PORT,TALK_SMTP_USERNAME:MAILGUN_SMTP_LOGIN,TALK_SMTP_PASSWORD:MAILGUN_SMTP_PASSWORD", "NPM_CONFIG_PRODUCTION": "false" }, "addons": [{ @@ -25,8 +29,8 @@ "plan": "rediscloud:30", "as": "REDIS" }, { - "plan": "postmark:10k", - "as": "POSTMARK" + "plan": "mailgun:starter", + "as": "MAILGUN" }], "image": "heroku/nodejs", "success_url": "/admin/install" diff --git a/client/coral-embed-stream/src/components/Comment.js b/client/coral-embed-stream/src/components/Comment.js index 79dfb1b34..3eecc80ea 100644 --- a/client/coral-embed-stream/src/components/Comment.js +++ b/client/coral-embed-stream/src/components/Comment.js @@ -447,6 +447,14 @@ export default class Comment extends React.Component { {isStaff(comment.tags) ? Staff : null} + + { diff --git a/client/coral-embed-stream/src/containers/Comment.js b/client/coral-embed-stream/src/containers/Comment.js index 6ddba46f8..a4bfbc0eb 100644 --- a/client/coral-embed-stream/src/containers/Comment.js +++ b/client/coral-embed-stream/src/containers/Comment.js @@ -16,7 +16,8 @@ const slots = [ 'commentContent', 'commentReactions', 'commentAvatar', - 'commentAuthorName' + 'commentAuthorName', + 'commentAuthorTags' ]; /** diff --git a/config.js b/config.js index 0a9912654..f48159444 100644 --- a/config.js +++ b/config.js @@ -108,7 +108,7 @@ const CONFIG = { //------------------------------------------------------------------------------ // Port to bind to. - PORT: process.env.TALK_PORT || '3000', + PORT: process.env.TALK_PORT || process.env.PORT || '3000', // The URL for this Talk Instance as viewable from the outside. ROOT_URL: process.env.TALK_ROOT_URL || null, diff --git a/test/server/graph/queries/asset.js b/test/server/graph/queries/asset.js index 68270f9ba..4d96be74a 100644 --- a/test/server/graph/queries/asset.js +++ b/test/server/graph/queries/asset.js @@ -10,10 +10,13 @@ const CommentsService = require('../../../../services/comments'); const {expect} = require('chai'); describe('graph.queries.asset', () => { - let asset, users; + let assets, users, comments; beforeEach(async () => { await SettingsService.init(); - asset = await Asset.create({id: '1', url: 'https://example.com'}); + assets = await Asset.create([ + {id: '1', url: 'https://example.com/?id=1'}, + {id: '2', url: 'https://example.com/?id=2'} + ]); users = await UsersService.createLocalUsers([ { email: 'usernameA@example.com', @@ -31,17 +34,63 @@ describe('graph.queries.asset', () => { username: 'usernameC' } ]); + comments = await CommentsService.publicCreate([0, 1, 0, 1].map((idx) => ({ + author_id: users[idx].id, + asset_id: assets[idx].id, + body: `hello there! ${String(Math.random()).slice(2)}`, + }))); + }); + + it('will not show the same asset stream across multiple assets', async () => { + const context = new Context({user: users[0]}); + + const query = ` + fragment assetFragment on Asset { + comments(query: {limit: 2}) { + nodes { + id + } + hasNextPage + } + } + + query assetQuery($id: ID!, $otherID: ID!) { + asset(id: $id) { + ...assetFragment + } + otherAsset: asset(id: $otherID) { + ...assetFragment + } + } + `; + + let res = await graphql(schema, query, {}, context, {id: assets[0].id, otherID: assets[1].id}); + if (res.errors) { + console.error(res.errors); + } + expect(res.errors).is.empty; + + let {asset: {comments: asset}, otherAsset: {comments: otherAsset}} = res.data; + + expect(asset.nodes).to.have.length(2); + expect(asset.hasNextPage).to.be.false; + expect(asset.nodes[0]).to.have.property('id', comments[2].id); + expect(asset.nodes[1]).to.have.property('id', comments[0].id); + expect(otherAsset.nodes).to.have.length(2); + expect(otherAsset.hasNextPage).to.be.false; + expect(otherAsset.nodes[0]).to.have.property('id', comments[3].id); + expect(otherAsset.nodes[1]).to.have.property('id', comments[1].id); + + for (let node of asset.nodes) { + for (let otherNode of otherAsset.nodes) { + expect(node.id).to.not.equal(otherNode.id); + } + } }); it('can get comments edge', async () => { const context = new Context({user: users[0]}); - await CommentsService.publicCreate([1, 2].map(() => ({ - author_id: users[0].id, - asset_id: asset.id, - body: `hello there! ${String(Math.random()).slice(2)}`, - }))); - const assetCommentsQuery = ` query assetCommentsQuery($id: ID!) { asset(id: $id) { @@ -58,8 +107,7 @@ describe('graph.queries.asset', () => { } } `; - const res = await graphql(schema, assetCommentsQuery, {}, context, {id: asset.id}); - expect(res.errors).is.empty; + const res = await graphql(schema, assetCommentsQuery, {}, context, {id: assets[0].id}); const {nodes, startCursor, endCursor, hasNextPage} = res.data.asset.comments; expect(nodes.length).to.equal(2); expect(startCursor).to.equal(nodes[0].created_at); @@ -70,12 +118,6 @@ describe('graph.queries.asset', () => { it('can query comments edge to exclude comments ignored by user', async () => { const context = new Context({user: users[0]}); - await Promise.all(users.slice(1, 3).map((user) => CommentsService.publicCreate({ - author_id: user.id, - asset_id: asset.id, - body: `hello there! ${String(Math.random()).slice(2)}`, - }))); - // Add the second user to the list of ignored users. context.user.ignoresUsers.push(users[1].id); @@ -94,16 +136,13 @@ describe('graph.queries.asset', () => { { const res = await graphql(schema, query, {}, context, { - id: asset.id, - url: asset.url, + id: assets[0].id, + url: assets[0].url, excludeIgnored: true }); - if (res.errors && res.errors.length) { - console.error(res.errors); - } expect(res.errors).is.empty; - const nodes = res.data.asset.comments.nodes; - expect(nodes.length).to.equal(1); + const {nodes} = res.data.asset.comments; + expect(nodes.length).to.equal(2); } });