diff --git a/bin/cli-plugins b/bin/cli-plugins index b1dee381b..942036d29 100755 --- a/bin/cli-plugins +++ b/bin/cli-plugins @@ -8,13 +8,14 @@ // https://yarnpkg.com/ const program = require('./commander'); +const inquirer = require('inquirer'); // Make things colorful! require('colors'); const emoji = require('node-emoji'); const dir = process.cwd(); -const fs = require('fs'); +const fs = require('fs-extra'); const path = require('path'); const spawn = require('cross-spawn'); const semver = require('semver'); @@ -272,10 +273,128 @@ async function reconcilePluginDeps({skipLocal, skipRemote, dryRun, upgradeRemote console.log(`✨ Done in ${totalTime}s.`); } +async function createSeedPlugin() { + const pluginsDir = path.join(__dirname, 'plugins'); + + function pluginNameExists(pluginName) { + const pluginNames = fs.readdirSync(pluginsDir); + return !!pluginNames + .filter((pn) => pn === pluginName).length; + } + + let answers = await inquirer.prompt([ + { + type: 'input', + name: 'pluginName', + message: 'Plugin Name:', + validate: (input) => { + + if (pluginNameExists(input)) { + return 'Please, choose another name. That name already exists'; + } + + if (input && input.length > 0) { + return true; + } + + return 'Plugin Name is required.'; + } + }, + { + type: 'confirm', + name: 'server', + message: 'Is this plugin extending the server capabilities?' + }, + { + type: 'confirm', + name: 'client', + message: 'Is this plugin extending the client capabilities?' + }, + { + type: 'confirm', + name: 'addPluginsJson', + message: 'Should we add it to the plugins.json?' + } + ]); + + //============================================================================== + // Creating plugin seed + //============================================================================== + + const seedPlugin = path.join(__dirname, 'bin/templates/plugin'); + const newPluginPath = path.join(pluginsDir, answers.pluginName); + + if (fs.existsSync(seedPlugin)) { + + if (answers.server && answers.client) { + + // This is a server-side and client-side plugin!, let's copy the template + fs.copySync(seedPlugin, newPluginPath); + } else { + + fs.copySync(seedPlugin, newPluginPath, {filter: (p) => { + + // Allowing plugin folder and files with no subfolders + const rootRx = /plugin$|plugin\/[^/]*(\.).{2,3}/igm; + if (rootRx.test(p) && (fs.lstatSync(p).isDirectory() || fs.lstatSync(p).isFile())) { + return true; + } + + // If it's a client-side plugin, copying client folder + if (answers.client) { + return /client/.test(p); + } + + // If it's a server-side plugin, copying server folder + if (answers.server) { + return /server/.test(p); + } + + }}); + } + + // Let's add this to the plugins.json + if (answers.addPluginsJson) { + const pluginsJson = path.join(dir, 'plugins.json'); + + fs.readJson(pluginsJson) + .then((j) => { + + // This is a client-side plugin, let's push this. + if (answers.client) { + j.client.push(answers.pluginName); + + const output = JSON.stringify(j, null, 2); + fs.writeFileSync(pluginsJson, output); + } + + // This is a server-side plugin, let's push this. + if (answers.server) { + j.server.push(answers.pluginName); + + const output = JSON.stringify(j, null, 2); + fs.writeFileSync(pluginsJson, output); + } + }) + .catch((err) => { + console.error(err); + }); + } + + console.log(`✨ Yay! Plugin created! Find your plugin: ${answers.pluginName} in the ./plugins folder`); + } + +} + //============================================================================== // Setting up the program command line arguments. //============================================================================== +program + .command('create') + .description('creates a seed plugin') + .action(createSeedPlugin); + program .command('list') .description('') diff --git a/bin/templates/plugin/client/.babelrc b/bin/templates/plugin/client/.babelrc new file mode 100644 index 000000000..60be246eb --- /dev/null +++ b/bin/templates/plugin/client/.babelrc @@ -0,0 +1,14 @@ +{ + "presets": [ + "es2015" + ], + "plugins": [ + "add-module-exports", + "transform-class-properties", + "transform-decorators-legacy", + "transform-object-assign", + "transform-object-rest-spread", + "transform-async-to-generator", + "transform-react-jsx" + ] +} \ No newline at end of file diff --git a/bin/templates/plugin/client/.eslintrc.json b/bin/templates/plugin/client/.eslintrc.json new file mode 100644 index 000000000..9fe56bd14 --- /dev/null +++ b/bin/templates/plugin/client/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "env": { + "browser": true, + "es6": true, + "mocha": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + "jsx": true + } + }, + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "rules": { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + "no-console": ["warn", { "allow": ["warn", "error"] }] + } +} diff --git a/bin/templates/plugin/client/components/MyPluginComponent.css b/bin/templates/plugin/client/components/MyPluginComponent.css new file mode 100644 index 000000000..187e68750 --- /dev/null +++ b/bin/templates/plugin/client/components/MyPluginComponent.css @@ -0,0 +1,27 @@ +.myPluginContainer { + padding: 10px; + background: #f0f0f0; + border: 1px solid #d6d6d6; + margin: 10px 0; + text-align: center; + border-radius: 3px; +} + +.logo { + position: block; + animation: spin 2s infinite ease; + animation-delay: 1s; +} + +@keyframes spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.description { + color: #444444; +} diff --git a/bin/templates/plugin/client/components/MyPluginComponent.js b/bin/templates/plugin/client/components/MyPluginComponent.js new file mode 100644 index 000000000..89e274e1a --- /dev/null +++ b/bin/templates/plugin/client/components/MyPluginComponent.js @@ -0,0 +1,25 @@ +import React from 'react'; +import {CoralLogo} from 'plugin-api/beta/client/components/ui'; +import styles from './MyPluginComponent.css'; + +class MyPluginComponent extends React.Component { + render() { + return ( +
+ +
+

Plugin created by Talk CLI

+ + + To read more about plugins check{' '} + + our docs and guides! + + +
+
+ ); + } +} + +export default MyPluginComponent; diff --git a/bin/templates/plugin/client/index.js b/bin/templates/plugin/client/index.js new file mode 100644 index 000000000..acd0910be --- /dev/null +++ b/bin/templates/plugin/client/index.js @@ -0,0 +1,26 @@ + +/** + This is a client index example file and it could look like this: + + ``` + import LoveButton from './components/LoveButton'; + + export default { + slots: { + commentReactions: [LoveButton] + }, + reducer, + translations + }; + ``` + + To read more info on how to build client plugins. Please, go to: https://coralproject.github.io/talk/plugins-client.html + */ + +import MyPluginComponent from './components/MyPluginComponent'; + +export default { + slots: { + stream: [MyPluginComponent] + } +}; diff --git a/bin/templates/plugin/client/translations.yml b/bin/templates/plugin/client/translations.yml new file mode 100644 index 000000000..d8407b801 --- /dev/null +++ b/bin/templates/plugin/client/translations.yml @@ -0,0 +1,15 @@ +# +# This file is for translations and they should look like this: +# +# +# ``` +# en: +# coral-plugin-respect: +# respect: Respect +# respected: Respected +# es: +# coral-plugin-respect: +# respect: Respetar +# respected: Respetado +# ``` +# diff --git a/bin/templates/plugin/index.js b/bin/templates/plugin/index.js new file mode 100644 index 000000000..f053ebf79 --- /dev/null +++ b/bin/templates/plugin/index.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index 145e8340a..98137624f 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -9,6 +9,7 @@ import Count from 'coral-plugin-comment-count/CommentCount'; import ProfileContainer from 'coral-settings/containers/ProfileContainer'; import ConfigureStreamContainer from 'coral-configure/containers/ConfigureStreamContainer'; +import cn from 'classnames'; export default class Embed extends React.Component { changeTab = (tab) => { @@ -37,34 +38,36 @@ export default class Embed extends React.Component { const {activeTab, viewAllComments, commentId} = this.props; const {asset: {totalCommentCount}} = this.props.root; const {user} = this.props.auth; + const hasHighlightedComment = !!commentId; return ( -
-
- - - {t('framework.my_profile')} - {t('framework.configure_stream')} - - {commentId && - } - - - - - - - - - - -
+
+ + + {t('framework.my_profile')} + + {t('framework.configure_stream')} + + + {commentId && + } + + + + + + + + + +
); } diff --git a/client/coral-embed-stream/src/index.js b/client/coral-embed-stream/src/index.js index f5be3922d..9020a8457 100644 --- a/client/coral-embed-stream/src/index.js +++ b/client/coral-embed-stream/src/index.js @@ -48,5 +48,5 @@ render( - , document.querySelector('#coralStream') + , document.querySelector('#talk-embed-stream-container') ); diff --git a/client/coral-framework/helpers/plugins.js b/client/coral-framework/helpers/plugins.js index ab5e3080e..2e5934bc4 100644 --- a/client/coral-framework/helpers/plugins.js +++ b/client/coral-framework/helpers/plugins.js @@ -15,9 +15,9 @@ function getSlotComponents(slot) { // Filter out components that have been disabled in `plugin_config` return flatten(plugins - - // Filter out components that have been disabled in `plugin_config` - .filter((o) => !pluginConfig || !pluginConfig[o.plugin] || !pluginConfig[o.plugin].disable_components) + + // Filter out components that have slots and have been disabled in `plugin_config` + .filter((o) => o.module.slots && (!pluginConfig || !pluginConfig[o.plugin] || !pluginConfig[o.plugin].disable_components)) .filter((o) => o.module.slots[slot]) .map((o) => o.module.slots[slot])); @@ -78,7 +78,7 @@ export function getSlotsFragments(slots) { } const components = uniq(flattenDeep(slots.map((slot) => { return plugins - .filter((o) => o.module.slots[slot]) + .filter((o) => o.module.slots ? o.module.slots[slot] : false) .map((o) => o.module.slots[slot]); }))); diff --git a/client/coral-plugin-flags/components/FlagButton.js b/client/coral-plugin-flags/components/FlagButton.js index 25fcf21a9..33499e83d 100644 --- a/client/coral-plugin-flags/components/FlagButton.js +++ b/client/coral-plugin-flags/components/FlagButton.js @@ -148,7 +148,7 @@ export default class FlagButton extends Component {