Merge pull request #177 from jorilallo/emoji

EMOJI 🌈 💁 🎉
This commit is contained in:
Tom Moor
2017-08-02 20:16:30 -07:00
committed by GitHub
12 changed files with 88 additions and 14 deletions

View File

@@ -1,5 +1,7 @@
[include]
.*/frontend/.*
.*/server/.*
.*/shared/.*
[ignore]
.*/node_modules/styled-components/.*

View File

@@ -29,6 +29,7 @@ type Props = {
onImageUploadStart: Function,
onImageUploadStop: Function,
starred: boolean,
emoji: string,
readOnly: boolean,
heading?: ?React.Element<*>,
};
@@ -213,6 +214,7 @@ type KeyData = {
className={cx(styles.editor, { readOnly: this.props.readOnly })}
schema={this.schema}
plugins={this.plugins}
emoji={this.props.emoji}
state={this.state.state}
onKeyDown={this.onKeyDown}
onChange={this.onChange}

View File

@@ -25,6 +25,10 @@ type Context = {
starred?: boolean,
};
const Wrapper = styled.div`
margin-left: ${props => (props.hasEmoji ? '-1.2em' : 0)}
`;
const StyledStar = styled(StarIcon)`
top: 3px;
position: relative;
@@ -61,10 +65,14 @@ function Heading(props: Props, { starred }: Context) {
const showStar = readOnly && !!onStar;
const showHash = readOnly && !!slugish && !showStar;
const Component = component;
const emoji = editor.props.emoji || '';
const title = node.text.trim();
const startsWithEmojiAndSpace =
emoji && title.match(new RegExp(`^${emoji}\\s`));
return (
<Component className={styles.title}>
{children}
<Wrapper hasEmoji={startsWithEmojiAndSpace}>{children}</Wrapper>
{showPlaceholder &&
<span className={styles.placeholder} contentEditable={false}>
{editor.props.placeholder}

View File

@@ -5,15 +5,11 @@ import invariant from 'invariant';
import { client } from 'utils/ApiClient';
import stores from 'stores';
import ErrorsStore from 'stores/ErrorsStore';
import parseTitle from '../../shared/parseTitle';
import type { User } from 'types';
import Collection from './Collection';
const parseHeader = text => {
const firstLine = text.trim().split(/\r?\n/)[0];
return firstLine.replace(/^#/, '').trim();
};
const DEFAULT_TITLE = 'Untitled document';
class Document {
@@ -31,6 +27,7 @@ class Document {
html: string;
id: string;
team: string;
emoji: string;
private: boolean = false;
starred: boolean = false;
text: string = '';
@@ -181,7 +178,11 @@ class Document {
};
updateData(data: Object = {}, dirty: boolean = false) {
if (data.text) data.title = parseHeader(data.text);
if (data.text) {
const { title, emoji } = parseTitle(data.text);
data.title = title;
data.emoji = emoji;
}
if (dirty) this.hasPendingChanges = true;
this.data = data;
extendObservable(this, data);

View File

@@ -203,6 +203,7 @@ type Props = {
<Editor
key={document.id}
text={document.text}
emoji={document.emoji}
onImageUploadStart={this.onImageUploadStart}
onImageUploadStop={this.onImageUploadStop}
onChange={this.onChange}

View File

@@ -22,19 +22,35 @@
"precommit": "lint-staged"
},
"lint-staged": {
"*.js": ["eslint --fix", "git add"]
"*.js": [
"eslint --fix",
"git add"
]
},
"jest": {
"verbose": false,
"roots": ["frontend"],
"roots": [
"frontend"
],
"moduleNameMapper": {
"^.*[.](s?css|css)$": "<rootDir>/__mocks__/styleMock.js",
"^.*[.](gif|ttf|eot|svg)$": "<rootDir>/__test__/fileMock.js"
},
"moduleFileExtensions": ["js", "jsx", "json"],
"moduleDirectories": ["node_modules"],
"modulePaths": ["frontend"],
"setupFiles": ["<rootDir>/setupJest.js", "<rootDir>/__mocks__/window.js"]
"moduleFileExtensions": [
"js",
"jsx",
"json"
],
"moduleDirectories": [
"node_modules"
],
"modulePaths": [
"frontend"
],
"setupFiles": [
"<rootDir>/setupJest.js",
"<rootDir>/__mocks__/window.js"
]
},
"engines": {
"node": ">= 7.6"
@@ -69,6 +85,7 @@
"debug": "2.2.0",
"dotenv": "^4.0.0",
"emoji-name-map": "1.1.2",
"emoji-regex": "^6.5.1",
"eslint": "^3.19.0",
"eslint-config-react-app": "^0.6.2",
"eslint-import-resolver-webpack": "^0.3.1",

View File

@@ -0,0 +1,12 @@
module.exports = {
up: (queryInterface, Sequelize) => {
queryInterface.addColumn('documents', 'emoji', {
type: Sequelize.STRING,
allowNull: true,
});
},
down: (queryInterface, _Sequelize) => {
queryInterface.removeColumn('documents', 'emoji');
},
};

View File

@@ -2,11 +2,13 @@
import slug from 'slug';
import _ from 'lodash';
import randomstring from 'randomstring';
import emojiRegex from 'emoji-regex';
import isUUID from 'validator/lib/isUUID';
import { DataTypes, sequelize } from '../sequelize';
import { convertToMarkdown } from '../../frontend/utils/markdown';
import { truncateMarkdown } from '../utils/truncate';
import parseTitle from '../../shared/parseTitle';
import Revision from './Revision';
const URL_REGEX = /^[a-zA-Z0-9-]*-([a-zA-Z0-9]{10,15})$/;
@@ -35,6 +37,9 @@ const createUrlId = doc => {
};
const beforeSave = async doc => {
const { emoji } = parseTitle(doc.text);
doc.emoji = emoji;
doc.html = convertToMarkdown(doc.text);
doc.preview = truncateMarkdown(doc.text, 160);
doc.revisionCount += 1;

View File

@@ -22,6 +22,7 @@ async function present(ctx: Object, document: Document, options: ?Options) {
text: document.text,
html: document.html,
preview: document.preview,
emoji: document.emoji,
createdAt: document.createdAt,
createdBy: presentUser(ctx, document.createdBy),
updatedAt: document.updatedAt,

18
shared/parseTitle.js Normal file
View File

@@ -0,0 +1,18 @@
// @flow
import emojiRegex from 'emoji-regex';
export default function parseTitle(text: string = '') {
const regex = emojiRegex();
// find and extract title
const firstLine = text.trim().split(/\r?\n/)[0];
const title = firstLine.replace(/^#/, '').trim();
// find and extract first emoji
const matches = regex.exec(title);
const firstEmoji = matches ? matches[0] : null;
const startsWithEmoji = firstEmoji && title.startsWith(firstEmoji);
const emoji = startsWithEmoji ? firstEmoji : undefined;
return { title, emoji };
}

View File

@@ -26,7 +26,10 @@ module.exports = {
{
test: /\.js$/,
loader: 'babel',
include: path.join(__dirname, 'frontend'),
include: [
path.join(__dirname, 'frontend'),
path.join(__dirname, 'shared'),
]
},
{ test: /\.json$/, loader: 'json-loader' },
// inline base64 URLs for <=8k images, direct URLs for the rest

View File

@@ -2499,6 +2499,10 @@ emoji-regex@^6.1.0:
version "6.4.2"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.4.2.tgz#a30b6fee353d406d96cfb9fa765bdc82897eff6e"
emoji-regex@^6.5.1:
version "6.5.1"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2"
emojilib@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/emojilib/-/emojilib-2.0.2.tgz#df91c45ede69f2d0ffd3d80acf8c72771b2a5ea1"