mirror of
https://github.com/wassname/talk.git
synced 2026-07-02 08:13:45 +08:00
Merge branch 'master' into slots-debug
This commit is contained in:
@@ -62,7 +62,6 @@ plugins/*
|
||||
!plugins/talk-plugin-toxic-comments
|
||||
!plugins/talk-plugin-viewing-options
|
||||
!plugins/talk-plugin-rich-text
|
||||
!plugins/talk-plugin-rich-text-pell
|
||||
|
||||
**/node_modules/*
|
||||
yarn-error.log
|
||||
|
||||
@@ -16,15 +16,19 @@ From getting up and running, to advanced configuration, to how to scale Talk, ou
|
||||
|
||||
## Product Guide
|
||||
|
||||
Learn more about Talk, including a deep dive into features for commenters and moderators, and FAQs in our [Talk Product Guide](https:/docs.coralproject.net/talk/how-talk-works).
|
||||
Learn more about Talk, including a deep dive into features for commenters and moderators, and FAQs in our [Talk Product Guide](https://docs.coralproject.net/talk/how-talk-works).
|
||||
|
||||
## Relevant Links
|
||||
## Pre-Launch Guide
|
||||
|
||||
You’ve installed Talk on your server, and you’re preparing to launch it on your site. The real community work starts now, before you go live. You have a unique opportunity pre-launch to set your community up for success. Read our [Talk Community Guide](https://blog.coralproject.net/youve-installed-talk-now-what/).
|
||||
|
||||
## More Resources
|
||||
|
||||
- [Talk Product Roadmap](https://www.pivotaltracker.com/n/projects/1863625)
|
||||
- [Our Blog](https://blog.coralproject.net/)
|
||||
- [Community Forums](https://community.coralproject.net/)
|
||||
- [Community Guides for Journalism](https://guides.coralproject.net/)
|
||||
- [More About Us](https://coralproject.net/)
|
||||
- [Talk Roadmap](https://www.pivotaltracker.com/n/projects/1863625)
|
||||
|
||||
## End-to-End Testing
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
---
|
||||
title: talk-plugin-rich-text-pell
|
||||
permalink: /plugin/talk-plugin-rich-text-pell/
|
||||
layout: plugin
|
||||
plugin:
|
||||
name: talk-plugin-rich-text-pell
|
||||
depends:
|
||||
- name: talk-plugin-rich-text
|
||||
provides:
|
||||
- Client
|
||||
---
|
||||
|
||||
Enables rich text support client-side by using [Pell](https://github.com/jaredreich/pell).
|
||||
|
||||
## Installation
|
||||
|
||||
Add `"talk-plugin-rich-text-pell"` to the `plugins.json` in your Talk
|
||||
installation. Remember to add this in the `client` property since this plugin
|
||||
only covers the client side. To add server support, please use
|
||||
[talk-plugin-rich-text](/talk/plugin/talk-plugin-rich-text).
|
||||
|
||||
_Note: Ensure that you don't have any other plugins utilizing the
|
||||
`commentContent` slot, as it would result in duplicate comments._
|
||||
|
||||
## How does this work?
|
||||
|
||||
This plugin contains 2 important components:
|
||||
|
||||
- The Editor (`./components/Editor.js`)
|
||||
- The Comment Content Renderer (`./components/CommentContent.js`)
|
||||
|
||||
The editor component contains the rich text editor. For this particular plugin
|
||||
we chose [Pell](https://github.com/jaredreich/pell). Pell is the simplest and
|
||||
smallest WYSIWYG text editor with no dependencies that we could find.
|
||||
|
||||
If you check our `index.js` you will notice that we inject this editor in the
|
||||
`commentBox` slot. We do this to replace the core comment box with this one.
|
||||
|
||||
Now, in order to render the new styled comments we need a comment renderer. For
|
||||
this task we will have to replace our core comment renderer by using the
|
||||
`commentContent` slot.
|
||||
|
||||
If you are not familiar with GraphQL `client/index.js` will look complicated,
|
||||
but fear not! With those functions we specify what to expect from the server
|
||||
schema, how to perform optimistic updates and how keep the client store updated
|
||||
with the latest changes.
|
||||
|
||||
We encourage you to see the files and check how easy is to build plugins! If you
|
||||
have any feedback, please let us know.
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "@coralproject/eslint-config-talk/client"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { pluginName } from '../../package.json';
|
||||
|
||||
class CommentContent extends React.Component {
|
||||
render() {
|
||||
const { comment } = this.props;
|
||||
return comment.richTextBody ? (
|
||||
<div
|
||||
className={`${pluginName}-text`}
|
||||
dangerouslySetInnerHTML={{ __html: comment.richTextBody }}
|
||||
/>
|
||||
) : (
|
||||
<div className={`${pluginName}-text`}>{comment.body}</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CommentContent.propTypes = {
|
||||
comment: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default CommentContent;
|
||||
@@ -1,41 +0,0 @@
|
||||
.content {
|
||||
background: #fff;
|
||||
border: solid 1px #bbb;
|
||||
min-height: 120px;
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
font-style: unset;
|
||||
}
|
||||
|
||||
.button > i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: transparent;
|
||||
padding: 3px;
|
||||
border: none;
|
||||
color: #4e4e4e;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.button:hover{
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
background-color: #eae8e8;
|
||||
}
|
||||
|
||||
.actionBar {
|
||||
user-select: none;
|
||||
padding: 5px 10px;
|
||||
border-top: 1px solid #bbb;
|
||||
border-left: 1px solid #bbb;
|
||||
border-right: 1px solid #bbb;
|
||||
}
|
||||
|
||||
.container {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { init } from 'pell';
|
||||
import styles from './Editor.css';
|
||||
import cn from 'classnames';
|
||||
import { pluginName } from '../../package.json';
|
||||
import { htmlNormalizer } from '../utils';
|
||||
|
||||
class Editor extends React.Component {
|
||||
ref = null;
|
||||
|
||||
handleRef = ref => (this.ref = ref);
|
||||
|
||||
componentDidMount() {
|
||||
const { onChange, actions, classNames, isReply } = this.props;
|
||||
|
||||
init({
|
||||
element: this.ref,
|
||||
onChange: richTextBody => {
|
||||
// We want to save the original comment body
|
||||
const originalBody = this.ref.childNodes[1].innerText;
|
||||
onChange(originalBody, { richTextBody: htmlNormalizer(richTextBody) });
|
||||
},
|
||||
actions,
|
||||
classes: {
|
||||
actionbar: cn(
|
||||
styles.actionBar,
|
||||
classNames.actionbar,
|
||||
`${pluginName}-action-bar`
|
||||
),
|
||||
content: cn(
|
||||
styles.content,
|
||||
classNames.content,
|
||||
`${pluginName}-content`
|
||||
),
|
||||
button: cn(styles.button, classNames.button, `${pluginName}-button`),
|
||||
},
|
||||
});
|
||||
|
||||
// To edit comments and have the previous html comment
|
||||
if (this.props.comment && this.props.comment.richTextBody && !isReply) {
|
||||
this.ref.content.innerHTML = this.props.comment.richTextBody;
|
||||
}
|
||||
|
||||
if (this.props.registerHook) {
|
||||
this.clearInputHook = this.props.registerHook(
|
||||
'postSubmit',
|
||||
(res, handleBodyChange) => {
|
||||
this.ref.content.innerHTML = '';
|
||||
handleBodyChange('', { richTextBody: '' });
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.unregisterHook(this.clearInputHook);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { id, classNames } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
id={id}
|
||||
ref={this.handleRef}
|
||||
className={cn(
|
||||
styles.container,
|
||||
classNames.container,
|
||||
`${pluginName}-container`
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Editor.defaultProps = {
|
||||
defaultContent: '',
|
||||
styleWithCSS: false,
|
||||
actions: [
|
||||
{ name: 'bold', icon: '<i class="material-icons">format_bold</i>' },
|
||||
{ name: 'italic', icon: '<i class="material-icons">format_italic</i>' },
|
||||
{ name: 'quote', icon: '<i class="material-icons">format_quote</i>' },
|
||||
],
|
||||
classNames: {
|
||||
button: '',
|
||||
content: '',
|
||||
actionbar: '',
|
||||
container: '',
|
||||
},
|
||||
};
|
||||
|
||||
Editor.propTypes = {
|
||||
id: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
rows: PropTypes.number,
|
||||
comment: PropTypes.object,
|
||||
classNames: PropTypes.object,
|
||||
actions: PropTypes.array,
|
||||
registerHook: PropTypes.func,
|
||||
unregisterHook: PropTypes.func,
|
||||
isReply: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Editor;
|
||||
@@ -1,12 +0,0 @@
|
||||
import { gql } from 'react-apollo';
|
||||
import { withFragments } from 'plugin-api/beta/client/hocs';
|
||||
import CommentContent from '../components/CommentContent';
|
||||
|
||||
export default withFragments({
|
||||
comment: gql`
|
||||
fragment TalkPluginRTE_CommentContent_comment on Comment {
|
||||
body
|
||||
richTextBody
|
||||
}
|
||||
`,
|
||||
})(CommentContent);
|
||||
@@ -1,12 +0,0 @@
|
||||
import { gql } from 'react-apollo';
|
||||
import { withFragments } from 'plugin-api/beta/client/hocs';
|
||||
import Editor from '../components/Editor';
|
||||
|
||||
export default withFragments({
|
||||
comment: gql`
|
||||
fragment TalkPluginRTE_Editor_comment on Comment {
|
||||
body
|
||||
richTextBody
|
||||
}
|
||||
`,
|
||||
})(Editor);
|
||||
@@ -1,70 +0,0 @@
|
||||
import Editor from './containers/Editor';
|
||||
import CommentContent from './containers/CommentContent';
|
||||
import { gql } from 'react-apollo';
|
||||
|
||||
export default {
|
||||
slots: {
|
||||
draftArea: [Editor],
|
||||
commentContent: [CommentContent],
|
||||
adminCommentContent: [CommentContent],
|
||||
userDetailCommentContent: [CommentContent],
|
||||
},
|
||||
fragments: {
|
||||
CreateCommentResponse: gql`
|
||||
fragment TalkRTE_CreateCommentResponse on CreateCommentResponse {
|
||||
comment {
|
||||
richTextBody
|
||||
}
|
||||
}
|
||||
`,
|
||||
EditCommentResponse: gql`
|
||||
fragment TalkRTE_EditCommentResponse on EditCommentResponse {
|
||||
comment {
|
||||
richTextBody
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
mutations: {
|
||||
PostComment: ({ variables: { input } }) => {
|
||||
return {
|
||||
optimisticResponse: {
|
||||
createComment: {
|
||||
comment: {
|
||||
richTextBody: input.richTextBody,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
EditComment: ({ variables: { id, edit } }) => {
|
||||
return {
|
||||
optimisticResponse: {
|
||||
editComment: {
|
||||
comment: {
|
||||
richTextBody: edit.richTextBody,
|
||||
},
|
||||
},
|
||||
},
|
||||
update: proxy => {
|
||||
const editCommentFragment = gql`
|
||||
fragment Talk_EditComment on Comment {
|
||||
richTextBody
|
||||
}
|
||||
`;
|
||||
|
||||
const fragmentId = `Comment_${id}`;
|
||||
|
||||
proxy.writeFragment({
|
||||
fragment: editCommentFragment,
|
||||
id: fragmentId,
|
||||
data: {
|
||||
__typename: 'Comment',
|
||||
richTextBody: edit.richTextBody,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
export function htmlNormalizer(htmlInput) {
|
||||
let str = htmlInput;
|
||||
// We are normalizing the input from contenteditable of each browser, also removing unnecesary html tags
|
||||
// https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Editable_content#Differences_in_markup_generation
|
||||
|
||||
// Old browsers uses `p` normalize to `div` instead.
|
||||
str = str
|
||||
.replace(/<p>/g, '<div>') // IE and old browsers outputs <p> instead of <div>s
|
||||
.replace(/<\/p>/g, '</div>'); // IE and old browsers outputs <p> instead of <div>s
|
||||
|
||||
// Remove first opening tag, otherwise
|
||||
// with the following transformation below
|
||||
// we might add an unintended first empty line.
|
||||
if (str.startsWith('<div>')) {
|
||||
str = str.replace('<div>', '');
|
||||
}
|
||||
|
||||
// Normalize <div>s to <br>.
|
||||
return str.replace(/<div>/g, '<br>').replace(/<\/div>/g, '');
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
module.exports = {};
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "@coralproject/talk-plugin-rich-text-pell",
|
||||
"pluginName": "talk-plugin-rich-text-pell",
|
||||
"version": "0.0.1",
|
||||
"description": "Pell's Rich Text Editor for Talk",
|
||||
"main": "index.js",
|
||||
"author": "The Coral Project Team <coral@mozillafoundation.org>",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"pell": "^1.0.1"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user