diff --git a/.eslintrc.json b/.eslintrc.json index d7ed3908e..324bc9666 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -24,13 +24,14 @@ "promise/no-promise-in-callback": "warn", "promise/no-callback-in-promise": "warn", - // TODO: Change all these to "error", or remove and use defaults. "react/display-name": "off", - "react/jsx-no-duplicate-props": "warn", - "react/jsx-no-target-blank": "warn", - "react/no-find-dom-node": "warn", - "react/no-unknown-property": "warn", - "react/prop-types": "off", + + // We'll keeping using this, as we'll switch over to Preact and we don't + // need to worry about deprecation. + "react/no-find-dom-node": "off", + + // TODO: Use default after fixing the errors. + "react/prop-types": "warn", "array-callback-return": "warn", "arrow-parens": ["warn", "always"], diff --git a/app.js b/app.js index 741dac93f..4e74062d5 100644 --- a/app.js +++ b/app.js @@ -5,7 +5,12 @@ const path = require('path'); const helmet = require('helmet'); const compression = require('compression'); const cookieParser = require('cookie-parser'); -const {BASE_URL, BASE_PATH, MOUNT_PATH} = require('./url'); +const { + BASE_URL, + BASE_PATH, + MOUNT_PATH, + STATIC_URL, +} = require('./url'); const routes = require('./routes'); const debug = require('debug')('talk:app'); @@ -55,6 +60,7 @@ app.set('view engine', 'ejs'); app.locals.BASE_URL = BASE_URL; app.locals.BASE_PATH = BASE_PATH; app.locals.MOUNT_PATH = MOUNT_PATH; +app.locals.STATIC_URL = STATIC_URL; debug(`mounting routes on the ${MOUNT_PATH} path`); diff --git a/client/coral-admin/src/components/ActionButton.js b/client/coral-admin/src/components/ActionButton.js index b8778bef0..79fa2f182 100644 --- a/client/coral-admin/src/components/ActionButton.js +++ b/client/coral-admin/src/components/ActionButton.js @@ -28,7 +28,11 @@ const ActionButton = ({type = '', active, ...props}) => { }; ActionButton.propTypes = { - active: PropTypes.bool + active: PropTypes.bool, + type: PropTypes.oneOf(['APPROVE', 'REJECT']), + minimal: PropTypes.bool, + acceptComment: PropTypes.func, + rejectComment: PropTypes.func, }; export default ActionButton; diff --git a/client/coral-admin/src/components/ActionsMenu.js b/client/coral-admin/src/components/ActionsMenu.js index 6e236bf1c..25aee1fbc 100644 --- a/client/coral-admin/src/components/ActionsMenu.js +++ b/client/coral-admin/src/components/ActionsMenu.js @@ -58,6 +58,7 @@ class ActionsMenu extends React.Component { ActionsMenu.propTypes = { icon: PropTypes.string, + children: PropTypes.node, }; export default ActionsMenu; diff --git a/client/coral-admin/src/components/ActionsMenuItem.js b/client/coral-admin/src/components/ActionsMenuItem.js index 82dab96f0..d9497a6ce 100644 --- a/client/coral-admin/src/components/ActionsMenuItem.js +++ b/client/coral-admin/src/components/ActionsMenuItem.js @@ -1,9 +1,14 @@ import React from 'react'; import cn from 'classnames'; import {MenuItem} from 'react-mdl'; +import PropTypes from 'prop-types'; import styles from './ActionsMenu.css'; const ActionsMenuItem = (props) => ; +ActionsMenuItem.propTypes = { + className: PropTypes.string, +}; + export default ActionsMenuItem; diff --git a/client/coral-admin/src/components/AdminLogin.js b/client/coral-admin/src/components/AdminLogin.js index 5f9712127..be8dd8b09 100644 --- a/client/coral-admin/src/components/AdminLogin.js +++ b/client/coral-admin/src/components/AdminLogin.js @@ -105,7 +105,9 @@ AdminLogin.propTypes = { handleLogin: PropTypes.func.isRequired, passwordRequestSuccess: PropTypes.string, loginError: PropTypes.string, - recaptchaPublic: PropTypes.string + recaptchaPublic: PropTypes.string, + requestPasswordReset: PropTypes.func, + errorMessage: PropTypes.string, }; export default AdminLogin; diff --git a/client/coral-admin/src/components/CommentAnimatedEdit.js b/client/coral-admin/src/components/CommentAnimatedEdit.js index 2515d7d94..e24496138 100644 --- a/client/coral-admin/src/components/CommentAnimatedEdit.js +++ b/client/coral-admin/src/components/CommentAnimatedEdit.js @@ -2,8 +2,9 @@ import React from 'react'; import {murmur3} from 'murmurhash-js'; import {CSSTransitionGroup} from 'react-transition-group'; import styles from './CommentAnimatedEdit.css'; +import PropTypes from 'prop-types'; -export default ({children, body}) => { +const CommentBodyHighlighter = ({children, body}) => { return ( { ); }; + +CommentBodyHighlighter.propTypes = { + children: PropTypes.node, + body: PropTypes.string, +}; + +export default CommentBodyHighlighter; diff --git a/client/coral-admin/src/components/CommentBox.css b/client/coral-admin/src/components/CommentBox.css deleted file mode 100644 index 34c7ca6ef..000000000 --- a/client/coral-admin/src/components/CommentBox.css +++ /dev/null @@ -1,4 +0,0 @@ - -.textareaContainer { - width: 100%; -} diff --git a/client/coral-admin/src/components/UserDetail.js b/client/coral-admin/src/components/UserDetail.js index fd44d5f17..b6b239057 100644 --- a/client/coral-admin/src/components/UserDetail.js +++ b/client/coral-admin/src/components/UserDetail.js @@ -183,7 +183,6 @@ export default class UserDetail extends React.Component { key={comment.id} user={user} comment={comment} - selected={false} suspectWords={suspectWords} bannedWords={bannedWords} actions={actionsMap[status]} diff --git a/client/coral-admin/src/components/ui/Header.js b/client/coral-admin/src/components/ui/Header.js index 560935c2a..74dc85a17 100644 --- a/client/coral-admin/src/components/ui/Header.js +++ b/client/coral-admin/src/components/ui/Header.js @@ -73,12 +73,12 @@ const CoralHeader = ({ showShortcuts(true)}>{t('configure.shortcuts')} - + View latest version - + Report a bug or give feedback diff --git a/client/coral-admin/src/routes/Community/components/Table.js b/client/coral-admin/src/routes/Community/components/Table.js index c5bb494c0..18f52592d 100644 --- a/client/coral-admin/src/routes/Community/components/Table.js +++ b/client/coral-admin/src/routes/Community/components/Table.js @@ -28,7 +28,8 @@ export default ({headers, commenters, onHeaderClickHandler, onRoleChange, onComm {row.created_at} - onCommenterStatusChange(row.id, status)}> @@ -37,7 +38,8 @@ export default ({headers, commenters, onHeaderClickHandler, onRoleChange, onComm - onRoleChange(row.id, role)}> diff --git a/client/talk-plugin-commentbox/CommentBox.js b/client/talk-plugin-commentbox/CommentBox.js index c85dfb306..e5717e08a 100644 --- a/client/talk-plugin-commentbox/CommentBox.js +++ b/client/talk-plugin-commentbox/CommentBox.js @@ -150,7 +150,6 @@ class CommentBox extends React.Component { return
{ @@ -20,11 +17,6 @@ router.get('/password-reset', (req, res) => { }); router.get('*', (req, res) => { - const data = { - TALK_RECAPTCHA_PUBLIC: RECAPTCHA_PUBLIC, - LIVE_URI: WEBSOCKET_LIVE_URI, - }; - res.render('admin', {data}); }); diff --git a/routes/embed/index.js b/routes/embed/index.js index 256d03b30..c360c1f76 100644 --- a/routes/embed/index.js +++ b/routes/embed/index.js @@ -1,20 +1,12 @@ const express = require('express'); const router = express.Router(); const SettingsService = require('../../services/settings'); -const { - RECAPTCHA_PUBLIC, - WEBSOCKET_LIVE_URI, -} = require('../../config'); +const {data} = require('../static'); router.use('/:embed', async (req, res, next) => { switch (req.params.embed) { case 'stream': { const {customCssUrl} = await SettingsService.retrieve(); - const data = { - TALK_RECAPTCHA_PUBLIC: RECAPTCHA_PUBLIC, - LIVE_URI: WEBSOCKET_LIVE_URI, - }; - return res.render('embed/stream', {customCssUrl, data}); } } diff --git a/routes/index.js b/routes/index.js index 6a8920dc3..23e8d8a47 100644 --- a/routes/index.js +++ b/routes/index.js @@ -11,6 +11,7 @@ const errors = require('../errors'); const {createGraphOptions} = require('../graph'); const accepts = require('accepts'); const apollo = require('graphql-server-express'); +const {DISABLE_STATIC_SERVER} = require('../config'); const router = express.Router(); @@ -39,20 +40,26 @@ if (process.env.NODE_ENV === 'production') { }); } -router.use('/client', express.static(path.join(__dirname, '../dist'))); -router.use('/public', express.static(path.join(__dirname, '../public'))); +if (!DISABLE_STATIC_SERVER) { -/** - * Serves a file based on a relative path. - */ -const serveFile = (filename) => (req, res) => res.sendFile(path.join(__dirname, filename)); + /** + * Serve the directories under public/dist from this router. + */ + router.use('/client', express.static(path.join(__dirname, '../dist'))); + router.use('/public', express.static(path.join(__dirname, '../public'))); -/** - * Serves the embed javascript files. - */ -router.get('/embed.js', serveFile('../dist/embed.js')); -router.get('/embed.js.gz', serveFile('../dist/embed.js.gz')); -router.get('/embed.js.map', serveFile('../dist/embed.js.map')); + /** + * Serves a file based on a relative path. + */ + const serveFile = (filename) => (req, res) => res.sendFile(path.join(__dirname, filename)); + + /** + * Serves the embed javascript files. + */ + router.get('/embed.js', serveFile('../dist/embed.js')); + router.get('/embed.js.gz', serveFile('../dist/embed.js.gz')); + router.get('/embed.js.map', serveFile('../dist/embed.js.map')); +} //============================================================================== // PASSPORT MIDDLEWARE diff --git a/routes/static.js b/routes/static.js new file mode 100644 index 000000000..12bf01282 --- /dev/null +++ b/routes/static.js @@ -0,0 +1,13 @@ +const { + RECAPTCHA_PUBLIC, + WEBSOCKET_LIVE_URI, +} = require('../config'); +const { + STATIC_URL, +} = require('../url'); + +module.exports.data = { + TALK_RECAPTCHA_PUBLIC: RECAPTCHA_PUBLIC, + LIVE_URI: WEBSOCKET_LIVE_URI, + STATIC_URL, +}; diff --git a/url.js b/url.js index 1def7a707..10013ce35 100644 --- a/url.js +++ b/url.js @@ -1,9 +1,15 @@ -const {ROOT_URL, ROOT_URL_MOUNT_PATH} = require('./config'); +const { + ROOT_URL, + ROOT_URL_MOUNT_PATH, + STATIC_URI, +} = require('./config'); const {URL} = require('url'); +const trailingSlash = (url) => url && url.length > 0 && url[url.length - 1] === '/' ? url : `${url}/`; + // Set the BASE_URL as the ROOT_URL, here we derive the root url by ensuring // that it ends in a `/`. -const BASE_URL = ROOT_URL && ROOT_URL.length > 0 && ROOT_URL[ROOT_URL.length - 1] === '/' ? ROOT_URL : `${ROOT_URL}/`; +const BASE_URL = trailingSlash(ROOT_URL); // The BASE_PATH is simply the path component of the BASE_URL. const BASE_PATH = new URL(BASE_URL).pathname; @@ -12,8 +18,12 @@ const BASE_PATH = new URL(BASE_URL).pathname; // This will mount all the application routes onto it. const MOUNT_PATH = ROOT_URL_MOUNT_PATH ? BASE_PATH : '/'; +// The STATIC_URL is the url where static assets should be loaded from. +const STATIC_URL = trailingSlash(STATIC_URI); + module.exports = { - BASE_URL: BASE_URL, - BASE_PATH: BASE_PATH, - MOUNT_PATH: MOUNT_PATH, + BASE_URL, + BASE_PATH, + MOUNT_PATH, + STATIC_URL, }; diff --git a/views/admin.ejs b/views/admin.ejs index 2aa23aa58..70fed054e 100644 --- a/views/admin.ejs +++ b/views/admin.ejs @@ -4,19 +4,19 @@ Talk - Coral Admin - - - - - - - - - - - - - + + + + + + + + + + + + + @@ -42,6 +42,6 @@
- + diff --git a/views/admin/docs.ejs b/views/admin/docs.ejs index d13dff064..ae2fe2dcf 100644 --- a/views/admin/docs.ejs +++ b/views/admin/docs.ejs @@ -28,6 +28,6 @@
- + diff --git a/views/article.ejs b/views/article.ejs index 0f8c75594..0bda3a6e0 100644 --- a/views/article.ejs +++ b/views/article.ejs @@ -24,7 +24,7 @@

<%= body %>

Admin - All Assets

- +