mirror of
https://github.com/wassname/talk.git
synced 2026-07-19 11:28:50 +08:00
Restructure frontend plugins
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import React, {Component} from 'react';
|
||||
import {compose} from 'react-apollo';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import I18n from 'coral-framework/modules/i18n/i18n';
|
||||
@@ -17,7 +16,7 @@ import {queryStream} from 'coral-framework/graphql/queries';
|
||||
import {postComment, postFlag, postLike, postDontAgree, deleteAction, addCommentTag, removeCommentTag} from 'coral-framework/graphql/mutations';
|
||||
import {editName} from 'coral-framework/actions/user';
|
||||
import {updateCountCache} from 'coral-framework/actions/asset';
|
||||
import {notificationActions, authActions, assetActions, pym, actions} from 'coral-framework';
|
||||
import {notificationActions, authActions, assetActions, pym} from 'coral-framework';
|
||||
|
||||
import Stream from './Stream';
|
||||
import InfoBox from 'coral-plugin-infobox/InfoBox';
|
||||
@@ -66,11 +65,6 @@ class Embed extends Component {
|
||||
removeCommentTag: React.PropTypes.func,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.pluginActions = bindActionCreators(actions.pluginActions, props.dispatch);
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
pym.sendMessage('childReady');
|
||||
}
|
||||
|
||||
@@ -1,45 +1,29 @@
|
||||
import React from 'react';
|
||||
import values from 'lodash/values';
|
||||
import flatten from 'lodash/flatten';
|
||||
import merge from 'lodash/merge';
|
||||
import flatten from 'lodash/flatten';
|
||||
import plugins from 'pluginsConfig';
|
||||
|
||||
/**
|
||||
* Returns the config object of given plugin.
|
||||
*/
|
||||
function getConfig(plugin) {
|
||||
return plugins.configs.filter(o => o.plugin === plugin)[0].getModule();
|
||||
}
|
||||
const components = flatten(
|
||||
plugins
|
||||
.map(o => o.module.components)
|
||||
.filter(o => o)
|
||||
);
|
||||
|
||||
export const pluginReducers = merge(
|
||||
...plugins
|
||||
.filter(o => o.module.reducer)
|
||||
.map(o => ({...o.module.reducer}))
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns React Elements for given slot.
|
||||
*/
|
||||
export function getSlotElements(slot, props = {}) {
|
||||
return plugins.components
|
||||
.map((o, i) => ({
|
||||
component: o.getModule(),
|
||||
props: {...getConfig(o.plugin), ...props, key: i},
|
||||
return components
|
||||
.map((component, i) => ({
|
||||
component,
|
||||
props: {...props, key: i},
|
||||
}))
|
||||
.filter(o => o.props.slot === slot)
|
||||
.filter(o => o.component.slot === slot)
|
||||
.map(o => React.createElement(o.component, o.props));
|
||||
}
|
||||
|
||||
// Returns a map of redux actions.
|
||||
export function getPluginActions() {
|
||||
return merge(...plugins.actions.map(o => ({...o.getModule()})));
|
||||
}
|
||||
|
||||
// Returns a map of redux reducers.
|
||||
export function getPluginReducers() {
|
||||
merge(...plugins.reducers.map(o => ({...o.getModule()})));
|
||||
}
|
||||
|
||||
// Returns an array of graphQL queries and mutators.
|
||||
export function getPluginQueriesAndMutators() {
|
||||
return flatten(
|
||||
merge(
|
||||
plugins.queries.map(o => values(o.getModule())),
|
||||
plugins.mutators.map(o => values(o.getModule())),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,38 +1,13 @@
|
||||
/**
|
||||
* Executes `source` to retrieve plugins configuration
|
||||
* and loads all modules of specified plugins.
|
||||
* and loads the `index.js` of specified plugins.
|
||||
*
|
||||
* Outputs a module that looks like the following:
|
||||
*
|
||||
* module.exports.actions = [{plugin: string, getModule: callback}, ...]
|
||||
* module.exports.reducer = [{plugin: string, getModule: callback}, ...]
|
||||
* [...]
|
||||
* module.exports = [{plugin: string, module: object}, ...]
|
||||
*
|
||||
*/
|
||||
const {stripIndent} = require('common-tags');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function moduleExists(loc) {
|
||||
const fileExists = fs.existsSync(path.resolve(__dirname, loc));
|
||||
if (fileExists) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const hasExtension = /\.[^\/\\]*$/.test(loc);
|
||||
if (hasExtension) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find file with appended `.js` extension.
|
||||
return fs.existsSync(path.resolve(__dirname, `${loc}.js`));
|
||||
}
|
||||
|
||||
function addIfExists(list, plugin, resource) {
|
||||
if (moduleExists(`../../../plugins/${plugin}/client/${resource}`)) {
|
||||
list.push(`{getModule: () => require('plugins/${plugin}/client/${resource}'), plugin: '${plugin}'}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getPluginList(config) {
|
||||
return config.client.map(x => typeof x === 'string' ? x : Object.keys(x)[0]);
|
||||
@@ -43,34 +18,14 @@ module.exports = function(source) {
|
||||
const config = this.exec(source, this.resourcePath);
|
||||
const plugins = getPluginList(config);
|
||||
|
||||
const exports = {
|
||||
configs: [],
|
||||
actions: [],
|
||||
reducers: [],
|
||||
components: [],
|
||||
queries: [],
|
||||
mutators: [],
|
||||
};
|
||||
|
||||
const list = [];
|
||||
plugins.forEach(plugin => {
|
||||
addIfExists(exports.components, plugin, 'index.js');
|
||||
addIfExists(exports.configs, plugin, 'config.json');
|
||||
addIfExists(exports.actions, plugin, 'actions');
|
||||
addIfExists(exports.reducers, plugin, 'reducer');
|
||||
addIfExists(exports.mutators, plugin, 'mutations');
|
||||
addIfExists(exports.queries, plugin, 'queries');
|
||||
list.push(`{module: require('plugins/${plugin}/client/index.js'), plugin: '${plugin}'}`);
|
||||
});
|
||||
|
||||
let result = '';
|
||||
|
||||
Object.keys(exports).forEach(key => {
|
||||
if (result) { result += '\n\n'; }
|
||||
result += stripIndent`
|
||||
module.exports.${key} = [
|
||||
${exports[key].join(',')}
|
||||
];
|
||||
`;
|
||||
});
|
||||
|
||||
return result;
|
||||
return stripIndent`
|
||||
module.exports = [
|
||||
${list.join(',')}
|
||||
];
|
||||
`;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import auth from './auth';
|
||||
import user from './user';
|
||||
import asset from './asset';
|
||||
import {getPluginReducers} from '../helpers/plugins';
|
||||
import {pluginReducers} from '../helpers/plugins';
|
||||
|
||||
export default {
|
||||
auth,
|
||||
user,
|
||||
asset,
|
||||
...getPluginReducers()
|
||||
...pluginReducers
|
||||
};
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import React from 'react';
|
||||
import styles from './style.css';
|
||||
import Icon from './Icon';
|
||||
|
||||
import {compose, gql, graphql} from 'react-apollo';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {I18n} from 'coral-framework';
|
||||
import cn from 'classnames';
|
||||
import translations from '../translations.json';
|
||||
import {withPostRespect} from '../mutations';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
import {getActionSummary} from 'coral-framework/utils';
|
||||
import {showSignInDialog} from 'coral-framework/actions/auth';
|
||||
import {deleteAction} from 'coral-framework/graphql/mutations';
|
||||
|
||||
class RespectButton extends React.Component {
|
||||
|
||||
static slot = 'Comment.Detail';
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
localPost: null, // Set to the ID of an action if one is posted
|
||||
localDelete: false // Set to true is the user deletes an action, unless localPost is already set.
|
||||
};
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
const {postRespect, showSignInDialog, deleteAction, commentId} = this.props;
|
||||
const {me, comment} = this.props.data;
|
||||
|
||||
const {localPost, localDelete} = this.state;
|
||||
const respect = getActionSummary('RespectActionSummary', comment);
|
||||
const respected = (respect && respect.current_user && !localDelete) || localPost;
|
||||
|
||||
/**
|
||||
* If the current user does not exist, trigger signIn Box
|
||||
*/
|
||||
|
||||
if (!me) {
|
||||
const offset = document.getElementById(`c_${commentId}`).getBoundingClientRect().top - 75;
|
||||
showSignInDialog(offset);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current user is banned, do nothing
|
||||
*/
|
||||
|
||||
if (me.status === 'BANNED') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!respected) {
|
||||
this.setState({
|
||||
localPost: 'temp'
|
||||
});
|
||||
|
||||
postRespect({
|
||||
item_id: commentId,
|
||||
item_type: 'COMMENTS'
|
||||
}).then(({data}) => {
|
||||
this.setState({
|
||||
localPost: data.createRespect.respect.id
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
this.setState((prev) => prev.localPost ? {...prev, localPost: null} : {...prev, localDelete: true});
|
||||
deleteAction(localPost || respect.current_user.id);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {comment} = this.props.data;
|
||||
const {localPost, localDelete} = this.state;
|
||||
const respect = comment && getActionSummary('RespectActionSummary', comment);
|
||||
const respected = (respect && respect.current_user && !localDelete) || localPost;
|
||||
let count = respect ? respect.count : 0;
|
||||
|
||||
if (localPost) {count += 1;}
|
||||
if (localDelete) {count -= 1;}
|
||||
|
||||
return (
|
||||
<div className={styles.respect}>
|
||||
<button
|
||||
className={cn(styles.button, {[styles.respected]: respected})}
|
||||
onClick={this.handleClick} >
|
||||
<span>{lang.t(respected ? 'respected' : 'respect')}</span>
|
||||
<Icon className={cn(styles.icon, {[styles.respected]: respected})} />
|
||||
{count > 0 && count}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const withQuery = graphql(gql`
|
||||
query CommentQuery($commentId: ID!) {
|
||||
comment(id: $commentId) {
|
||||
action_summaries {
|
||||
... on RespectActionSummary {
|
||||
count
|
||||
current_user {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
me {
|
||||
status
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({showSignInDialog}, dispatch);
|
||||
|
||||
const enhance = compose(
|
||||
connect(null, mapDispatchToProps),
|
||||
deleteAction,
|
||||
withPostRespect,
|
||||
withQuery,
|
||||
);
|
||||
|
||||
export default enhance(RespectButton);
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "Coral Plugin Respect",
|
||||
"slot": "Comment.Detail"
|
||||
}
|
||||
@@ -1,128 +1,7 @@
|
||||
import React from 'react';
|
||||
import styles from './style.css';
|
||||
import Icon from './components/Icon';
|
||||
|
||||
import {compose, gql, graphql} from 'react-apollo';
|
||||
import {connect} from 'react-redux';
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {I18n} from 'coral-framework';
|
||||
import cn from 'classnames';
|
||||
import translations from './translations.json';
|
||||
import {withPostRespect} from './mutations';
|
||||
|
||||
const lang = new I18n(translations);
|
||||
|
||||
import {getActionSummary} from 'coral-framework/utils';
|
||||
import {showSignInDialog} from 'coral-framework/actions/auth';
|
||||
import {deleteAction} from 'coral-framework/graphql/mutations';
|
||||
|
||||
class RespectButton extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
localPost: null, // Set to the ID of an action if one is posted
|
||||
localDelete: false // Set to true is the user deletes an action, unless localPost is already set.
|
||||
};
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
const {postRespect, showSignInDialog, deleteAction, commentId} = this.props;
|
||||
const {me, comment} = this.props.data;
|
||||
|
||||
const {localPost, localDelete} = this.state;
|
||||
const respect = getActionSummary('RespectActionSummary', comment);
|
||||
const respected = (respect && respect.current_user && !localDelete) || localPost;
|
||||
|
||||
/**
|
||||
* If the current user does not exist, trigger signIn Box
|
||||
*/
|
||||
|
||||
if (!me) {
|
||||
const offset = document.getElementById(`c_${commentId}`).getBoundingClientRect().top - 75;
|
||||
showSignInDialog(offset);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current user is banned, do nothing
|
||||
*/
|
||||
|
||||
if (me.status === 'BANNED') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!respected) {
|
||||
this.setState({
|
||||
localPost: 'temp'
|
||||
});
|
||||
|
||||
postRespect({
|
||||
item_id: commentId,
|
||||
item_type: 'COMMENTS'
|
||||
}).then(({data}) => {
|
||||
this.setState({
|
||||
localPost: data.createRespect.respect.id
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
this.setState((prev) => prev.localPost ? {...prev, localPost: null} : {...prev, localDelete: true});
|
||||
deleteAction(localPost || respect.current_user.id);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {comment} = this.props.data;
|
||||
const {localPost, localDelete} = this.state;
|
||||
const respect = comment && getActionSummary('RespectActionSummary', comment);
|
||||
const respected = (respect && respect.current_user && !localDelete) || localPost;
|
||||
let count = respect ? respect.count : 0;
|
||||
|
||||
if (localPost) {count += 1;}
|
||||
if (localDelete) {count -= 1;}
|
||||
|
||||
return (
|
||||
<div className={styles.respect}>
|
||||
<button
|
||||
className={cn(styles.button, {[styles.respected]: respected})}
|
||||
onClick={this.handleClick} >
|
||||
<span>{lang.t(respected ? 'respected' : 'respect')}</span>
|
||||
<Icon className={cn(styles.icon, {[styles.respected]: respected})} />
|
||||
{count > 0 && count}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const withQuery = graphql(gql`
|
||||
query CommentQuery($commentId: ID!) {
|
||||
comment(id: $commentId) {
|
||||
action_summaries {
|
||||
... on RespectActionSummary {
|
||||
count
|
||||
current_user {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
me {
|
||||
status
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({showSignInDialog}, dispatch);
|
||||
|
||||
const enhance = compose(
|
||||
connect(null, mapDispatchToProps),
|
||||
deleteAction,
|
||||
withPostRespect,
|
||||
withQuery,
|
||||
);
|
||||
|
||||
export default enhance(RespectButton);
|
||||
import RespectButton from './components/RespectButton';
|
||||
import * as reducer from './reducer';
|
||||
|
||||
export default {
|
||||
components: [RespectButton],
|
||||
reducer,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user