mirror of
https://github.com/wassname/talk.git
synced 2026-07-28 11:27:05 +08:00
Merge branch 'master' into feature/talk-plugin-toxic-comments
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import {Router, Route, IndexRedirect, browserHistory, Redirect} from 'react-router';
|
||||
import {useBasename} from 'history';
|
||||
import {Router, Route, IndexRedirect, Redirect} from 'react-router';
|
||||
import {history} from 'coral-framework/helpers/router';
|
||||
|
||||
import Configure from 'routes/Configure';
|
||||
import Dashboard from 'routes/Dashboard';
|
||||
@@ -57,8 +57,6 @@ const routes = (
|
||||
</div>
|
||||
);
|
||||
|
||||
const AppRouter = () => <Router history={useBasename(() => browserHistory)({
|
||||
basename: '/talk/'
|
||||
})} routes={routes}/>;
|
||||
const AppRouter = () => <Router history={history} routes={routes}/>;
|
||||
|
||||
export default AppRouter;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import {Router, Route, browserHistory} from 'react-router';
|
||||
import {Router, Route} from 'react-router';
|
||||
import {history} from 'coral-framework/helpers/router';
|
||||
|
||||
import Embed from './containers/Embed';
|
||||
import {LoginContainer} from 'coral-sign-in/containers/LoginContainer';
|
||||
@@ -11,6 +12,6 @@ const routes = (
|
||||
</div>
|
||||
);
|
||||
|
||||
const AppRouter = () => <Router history={browserHistory} routes={routes} />;
|
||||
const AppRouter = () => <Router history={history} routes={routes} />;
|
||||
|
||||
export default AppRouter;
|
||||
|
||||
@@ -36,7 +36,7 @@ export default class Embed extends React.Component {
|
||||
<div className={cn('talk-embed-stream', {'talk-embed-stream-highlight-comment': hasHighlightedComment})}>
|
||||
<IfSlotIsNotEmpty slot="login">
|
||||
<Popup
|
||||
href='/embed/stream/login'
|
||||
href='embed/stream/login'
|
||||
title='Login'
|
||||
features='menubar=0,resizable=0,width=500,height=550,top=200,left=500'
|
||||
open={showSignInDialog}
|
||||
|
||||
@@ -265,8 +265,8 @@ const fragments = {
|
||||
charCount
|
||||
requireEmailConfirmation
|
||||
}
|
||||
commentCount(excludeIgnored: $excludeIgnored)
|
||||
totalCommentCount(excludeIgnored: $excludeIgnored)
|
||||
commentCount(excludeIgnored: $excludeIgnored) @skip(if: $hasComment)
|
||||
totalCommentCount(excludeIgnored: $excludeIgnored) @skip(if: $hasComment)
|
||||
comments(limit: 10, excludeIgnored: $excludeIgnored) @skip(if: $hasComment) {
|
||||
nodes {
|
||||
...CoralEmbedStream_Stream_comment
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import {render} from 'react-dom';
|
||||
import {ApolloProvider} from 'react-apollo';
|
||||
|
||||
import {checkLogin} from 'coral-framework/actions/auth';
|
||||
import {checkLogin, handleAuthToken, logout} from 'coral-framework/actions/auth';
|
||||
import './graphql';
|
||||
import {addExternalConfig} from 'coral-embed-stream/src/actions/config';
|
||||
import {getStore, injectReducers, addListener} from 'coral-framework/services/store';
|
||||
@@ -43,6 +43,17 @@ if (!window.opener) {
|
||||
pym.onMessage('config', (config) => {
|
||||
init(JSON.parse(config));
|
||||
});
|
||||
|
||||
pym.onMessage('login', (token) => {
|
||||
if (token) {
|
||||
store.dispatch(handleAuthToken(token));
|
||||
}
|
||||
store.dispatch(checkLogin());
|
||||
});
|
||||
|
||||
pym.onMessage('logout', () => {
|
||||
store.dispatch(logout());
|
||||
});
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -257,6 +257,8 @@ body {
|
||||
margin: 0px 5px;
|
||||
padding: 5px 5px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* Comment Action Styles */
|
||||
|
||||
@@ -5,8 +5,6 @@ import {buildUrl} from 'coral-framework/utils';
|
||||
import queryString from 'query-string';
|
||||
import EventEmitter from 'eventemitter2';
|
||||
|
||||
const eventEmitter = new EventEmitter({wildcard: true});
|
||||
|
||||
// TODO: Styles should live in a separate file
|
||||
const snackbarStyles = {
|
||||
position: 'fixed',
|
||||
@@ -50,7 +48,7 @@ function buildStreamIframeUrl(talkBaseUrl, query) {
|
||||
|
||||
// Set up postMessage listeners/handlers on the pymParent
|
||||
// e.g. to resize the iframe, and navigate the host page
|
||||
function configurePymParent(pymParent, opts) {
|
||||
function configurePymParent(pymParent, eventEmitter, opts) {
|
||||
let notificationOffset = 200;
|
||||
let cachedHeight;
|
||||
const snackbar = document.createElement('div');
|
||||
@@ -203,6 +201,23 @@ function configurePymParent(pymParent, opts) {
|
||||
* @param {String} [opts.asset_url] - Asset URL
|
||||
* @param {String} [opts.asset_id] - Asset ID
|
||||
* @param {String} [opts.auth_token] - (optional) A jwt representing the session
|
||||
* @return {Object}
|
||||
*
|
||||
* Example:
|
||||
* ```
|
||||
* const embed = Talk.render(document.getElementById('talkStreamEmbed'), opts);
|
||||
*
|
||||
* // trigger a login with optional token.
|
||||
* embed.login(token);
|
||||
*
|
||||
* // trigger a logout.
|
||||
* embed.logout();
|
||||
*
|
||||
* // listen to events (in this case all events).
|
||||
* embed.on('**', function(value) {
|
||||
* console.log(this.event, value);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
Talk.render = function(el, opts) {
|
||||
if (!el) {
|
||||
@@ -256,14 +271,31 @@ Talk.render = function(el, opts) {
|
||||
}
|
||||
}
|
||||
|
||||
const pymParent = new pym.Parent(el.id, buildStreamIframeUrl(opts.talk, query), {
|
||||
title: opts.title,
|
||||
id: `${el.id}_iframe`,
|
||||
name: `${el.id}_iframe`
|
||||
});
|
||||
|
||||
const eventEmitter = new EventEmitter({wildcard: true});
|
||||
|
||||
configurePymParent(
|
||||
new pym.Parent(el.id, buildStreamIframeUrl(opts.talk, query), {
|
||||
title: opts.title,
|
||||
id: `${el.id}_iframe`,
|
||||
name: `${el.id}_iframe`
|
||||
}),
|
||||
pymParent,
|
||||
eventEmitter,
|
||||
opts
|
||||
);
|
||||
|
||||
return {
|
||||
on(eventName, callback) {
|
||||
eventEmitter.on(eventName, callback);
|
||||
},
|
||||
login(token) {
|
||||
pymParent.sendMessage('login', token);
|
||||
},
|
||||
logout() {
|
||||
pymParent.sendMessage('logout');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export default Coral;
|
||||
|
||||
@@ -276,9 +276,7 @@ export const fetchForgotPassword = (email) => (dispatch, getState) => {
|
||||
|
||||
export const logout = () => (dispatch) => {
|
||||
return coralApi('/auth', {method: 'DELETE'}).then(() => {
|
||||
if (!bowser.safari && !bowser.ios) {
|
||||
Storage.removeItem('token');
|
||||
}
|
||||
Storage.removeItem('token');
|
||||
|
||||
// Reset the websocket.
|
||||
resetWebsocket();
|
||||
@@ -306,9 +304,7 @@ export const checkLogin = () => (dispatch) => {
|
||||
coralApi('/auth')
|
||||
.then((result) => {
|
||||
if (!result.user) {
|
||||
if (!bowser.safari && !bowser.ios) {
|
||||
Storage.removeItem('token');
|
||||
}
|
||||
Storage.removeItem('token');
|
||||
throw new Error('Not logged in');
|
||||
}
|
||||
|
||||
@@ -325,6 +321,11 @@ export const checkLogin = () => (dispatch) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
if (error.status && error.status === 401) {
|
||||
|
||||
// Unauthorized.
|
||||
Storage.removeItem('token');
|
||||
}
|
||||
const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString();
|
||||
dispatch(checkLoginFailure(errorMessage));
|
||||
});
|
||||
|
||||
@@ -67,6 +67,7 @@ const handleResp = (res) => {
|
||||
}
|
||||
|
||||
error.message = message;
|
||||
error.status = res.status;
|
||||
throw error;
|
||||
});
|
||||
} else if (res.status === 204) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import {useBasename} from 'history';
|
||||
import {browserHistory} from 'react-router';
|
||||
import {BASE_PATH} from 'coral-framework/constants/url';
|
||||
|
||||
export const history = useBasename(() => browserHistory)({
|
||||
basename: BASE_PATH
|
||||
});
|
||||
@@ -91,7 +91,7 @@ export default class FlagButton extends Component {
|
||||
reason: null,
|
||||
message
|
||||
};
|
||||
if (reason === 'I don\'t agree with this comment') {
|
||||
if (reason === 'COMMENT_NOAGREE') {
|
||||
postDontAgree(action)
|
||||
.then(({data}) => {
|
||||
if (itemType === 'COMMENTS') {
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# Coral Talk Documentation
|
||||
|
||||
To preview the documentation, run:
|
||||
|
||||
```bash
|
||||
docker run --rm --volume=$PWD:/srv/jekyll -p 127.0.0.1:4000:4000 -it jekyll/jekyll:pages jekyll serve
|
||||
```
|
||||
|
||||
From the `docs` directory. Then visit: [http://127.0.0.1:4000/talk/](http://127.0.0.1:4000/talk/).
|
||||
@@ -34,6 +34,8 @@ docs:
|
||||
url: /docs/running/configuration/
|
||||
- title: "Secrets"
|
||||
url: /docs/running/secrets/
|
||||
- title: "Plugins"
|
||||
url: /docs/running/plugins/
|
||||
- title: "Database Migrations"
|
||||
url: /docs/running/migrations/
|
||||
- title: "Architecture"
|
||||
|
||||
@@ -58,10 +58,10 @@ Fork the Talk repo, clone it locally (no need to go through the install from sou
|
||||
|
||||
```
|
||||
cd docs
|
||||
docker build --no-cache -t mydocs .
|
||||
docker run -v "$PWD:/src" -p 4000:4000 mydocs serve -H 0.0.0.0
|
||||
docker run --rm --volume=$PWD:/srv/jekyll -p 127.0.0.1:4000:4000 -it jekyll/jekyll:pages jekyll serve
|
||||
```
|
||||
|
||||
You can edit the files in docs with any editor and view the live updates in a browser by hitting `http://localhost:4000`.
|
||||
You can edit the files in docs with any editor and view the live updates in a browser by hitting From the docs directory.
|
||||
Then visit: [http://127.0.0.1:4000/talk/](http://127.0.0.1:4000/talk/).
|
||||
|
||||
Once you've made the changes, file a PR back to the `coralproject/talk` repo.
|
||||
|
||||
@@ -18,4 +18,4 @@ While Talk can be installed in many ways, we support two install paths:
|
||||
* [Install via Docker](docker) (deployment)
|
||||
|
||||
If you have success installing Talk in another way, please consider
|
||||
[contributing to this documentation](faq#how-do-i-contribute-to-these-docs)!
|
||||
[contributing to this documentation]({{ "/docs/faq/" | absolute_url }}#how-do-i-contribute-to-these-docs)!
|
||||
|
||||
@@ -59,6 +59,10 @@ These are only used during the webpack build.
|
||||
send keep alive messages through the websocket to keep the socket alive. (Default `30s`)
|
||||
- `TALK_INSTALL_LOCK` (_optional for dynamic setup_) - When `TRUE`, disables the dynamic setup endpoint. (Default `FALSE`)
|
||||
|
||||
### Word Filter
|
||||
|
||||
- `TALK_DISABLE_AUTOFLAG_SUSPECT_WORDS` (_optional_) When `TRUE`, disables flagging of comments that match the suspect word filter. (Default `FALSE`)
|
||||
|
||||
### JWT
|
||||
|
||||
The following are configuration shared with every type of secret used.
|
||||
@@ -125,6 +129,4 @@ The default could be read as:
|
||||
|
||||
### Plugins
|
||||
|
||||
- `TALK_PLUGINS_JSON` (_optional_) - used to specify the plugin config via the
|
||||
environment.
|
||||
- `TALK_DISABLE_AUTOFLAG_SUSPECT_WORDS` (_optional_) When `TRUE`, disables flagging of comments that match the suspect word filter. (Default `FALSE`)
|
||||
Plugins configuration can be found on the [Plugins]({{ "/docs/running/plugins/" | absolute_url }}) page.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: Plugins
|
||||
permalink: /docs/running/plugins/
|
||||
---
|
||||
|
||||
Configuration for the available plugins are stored in a JSON encoded string. The format
|
||||
of this document are available with the [Plugins Overview]({{ "/docs/plugins/" | absolute_url }}).
|
||||
|
||||
You can override the plugin config by specifying the content in the `TALK_PLUGIN_JSON`
|
||||
environment variable.
|
||||
|
||||
## Bundled Plugin Configuration
|
||||
{:.no_toc}
|
||||
|
||||
Some of the core plugins that are bundled with Talk require specific configuration to be
|
||||
available.
|
||||
|
||||
{% include toc %}
|
||||
|
||||
### Facebook Authentication
|
||||
|
||||
- `TALK_FACEBOOK_APP_ID` (*required*) - the Facebook app id for your Facebook
|
||||
Login enabled app.
|
||||
- `TALK_FACEBOOK_APP_SECRET` (*required*) - the Facebook app secret for your
|
||||
Facebook Login enabled app.
|
||||
@@ -5,7 +5,7 @@ permalink: /docs/plugins/
|
||||
|
||||
## Recipes
|
||||
|
||||
Recipes are plugin templates provided by the Coral Core team. Developers can use these recipes to build their own plugins. You can find all the Talk recipes here: https://github.com/coralproject/talk-recipes/
|
||||
Recipes are plugin templates provided by the Coral Core team. Developers can use these recipes to build their own plugins. You can find all the Talk recipes here: [https://github.com/coralproject/talk-recipes/](https://github.com/coralproject/talk-recipes/).
|
||||
|
||||
## Plugin Registration
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
# talk-plugin-facebook-auth
|
||||
|
||||
This plugin provides facebook authentication support for Talk.
|
||||
|
||||
## Configuration
|
||||
|
||||
This plugin looks for the following configuration from the environment:
|
||||
|
||||
- `TALK_FACEBOOK_APP_ID` (*required*) - the Facebook app id for your Facebook
|
||||
Login enabled app.
|
||||
- `TALK_FACEBOOK_APP_SECRET` (*required*) - the Facebook app secret for your
|
||||
Facebook Login enabled app.
|
||||
|
||||
### License
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
See the License for the specific language governing permissions and limitations under the License.
|
||||
@@ -17,7 +17,7 @@ const enhance = compose(
|
||||
withFragments({
|
||||
asset: gql`
|
||||
fragment TalkFeaturedComments_Tab_asset on Asset {
|
||||
featuredCommentsCount: totalCommentCount(tags: ["FEATURED"], excludeIgnored: $excludeIgnored)
|
||||
featuredCommentsCount: totalCommentCount(tags: ["FEATURED"], excludeIgnored: $excludeIgnored) @skip(if: $hasComment)
|
||||
}`,
|
||||
}),
|
||||
excludeIf((props) => props.asset.featuredCommentsCount === 0),
|
||||
|
||||
@@ -79,7 +79,7 @@ const enhance = compose(
|
||||
asset: gql`
|
||||
fragment TalkFeaturedComments_TabPane_asset on Asset {
|
||||
id
|
||||
featuredComments: comments(tags: ["FEATURED"], excludeIgnored: $excludeIgnored, deep: true) {
|
||||
featuredComments: comments(tags: ["FEATURED"], excludeIgnored: $excludeIgnored, deep: true) @skip(if: $hasComment) {
|
||||
nodes {
|
||||
...${getDefinitionName(Comment.fragments.comment)}
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@
|
||||
<p><a href="admin">Admin</a> - <a href="assets">All Assets</a></p>
|
||||
<div id='coralStreamEmbed'></div>
|
||||
<script src="embed.js" async onload="
|
||||
Coral.Talk.render(document.getElementById('coralStreamEmbed'), {
|
||||
window.TalkEmbed = Coral.Talk.render(document.getElementById('coralStreamEmbed'), {
|
||||
talk: '<%= BASE_URL %>',
|
||||
asset_url: '<%= asset_url ? asset_url : '' %>',
|
||||
asset_id: '<%= asset_id ? asset_id : '' %>',
|
||||
|
||||
Reference in New Issue
Block a user