post: refactored post-input into input and editor

This commit is contained in:
Logan Allen 2020-06-24 14:00:44 -04:00
parent 5b68df5e3d
commit 8707816d25
3 changed files with 136 additions and 159 deletions

View File

@ -0,0 +1,109 @@
import React, { Component } from 'react';
import { UnControlled as CodeEditor } from 'react-codemirror2';
import CodeMirror from 'codemirror';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/addon/display/placeholder';
import 'codemirror/lib/codemirror.css';
const BROWSER_REGEX =
new RegExp(String(!/Android|webOS|iPhone|iPad|iPod|BlackBerry/i));
const MARKDOWN_CONFIG = {
name: 'markdown',
tokenTypeOverrides: {
header: 'presentation',
quote: 'presentation',
list1: 'presentation',
list2: 'presentation',
list3: 'presentation',
hr: 'presentation',
image: 'presentation',
imageAltText: 'presentation',
imageMarker: 'presentation',
formatting: 'presentation',
linkInline: 'presentation',
linkEmail: 'presentation',
linkText: 'presentation',
linkHref: 'presentation'
}
};
export default class PostEditor extends Component {
constructor(props) {
super(props);
this.editor = null;
}
componentDidUpdate(prevProps) {
const { props } = this;
if (!props.inCodeMode) {
this.editor.setOption('mode', MARKDOWN_CONFIG);
this.editor.setOption('placeholder', this.props.placeholder);
} else {
this.editor.setOption('mode', null);
this.editor.setOption('placeholder', 'Code...');
}
const value = this.editor.getValue();
// Force redraw of placeholder
if(value.length === 0) {
this.editor.setValue(' ');
this.editor.setValue('');
}
}
submit() {
if(!this.editor) {
return;
}
let editorMessage = this.editor.getValue();
if (editorMessage === '') {
return;
}
this.props.submit(editorMessage);
this.editor.setValue('');
}
render() {
const { props } = this;
const codeTheme = props.inCodeMode ? ' code' : '';
const options = {
mode: MARKDOWN_CONFIG,
theme: 'tlon' + codeTheme,
lineNumbers: false,
lineWrapping: true,
scrollbarStyle: 'native',
cursorHeight: 0.85,
placeholder: props.inCodeMode ? 'Code...' : props.placeholder,
extraKeys: {
'Enter': () => {
this.submit();
}
}
};
return (
<div
className="chat fr h-100 flex bg-gray0-d lh-copy pl2 w-100 items-center"
style={{ flexGrow: 1, maxHeight: '224px', width: 'calc(100% - 72px)' }}
>
<CodeEditor
options={options}
editorDidMount={(editor) => {
this.editor = editor;
if (BROWSER_REGEX.test(navigator.userAgent)) {
editor.focus();
}
}}
/>
</div>
);
}
}

View File

@ -1,91 +1,30 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import _ from 'lodash';
import moment from 'moment';
import { UnControlled as CodeEditor } from 'react-codemirror2';
import CodeMirror from 'codemirror';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/addon/display/placeholder';
import 'codemirror/lib/codemirror.css';
import { Sigil } from '../../../../lib/sigil'; import { Sigil } from '../../../../lib/sigil';
import PostEditor from './post-editor';
import { uxToHex } from '../../../../lib/util';
const MARKDOWN_CONFIG = { const URL_REGEX = new RegExp(String(/^((\w+:\/\/)[-a-zA-Z0-9:@;?&=\/%\+\.\*!'\(\),\$_\{\}\^~\[\]`#|]+)/.source));
name: 'markdown',
tokenTypeOverrides: {
header: 'presentation',
quote: 'presentation',
list1: 'presentation',
list2: 'presentation',
list3: 'presentation',
hr: 'presentation',
image: 'presentation',
imageAltText: 'presentation',
imageMarker: 'presentation',
formatting: 'presentation',
linkInline: 'presentation',
linkEmail: 'presentation',
linkText: 'presentation',
linkHref: 'presentation'
}
};
export class PostInput extends Component { export class PostInput extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
message: '', inCodeMode: false
}; };
this.textareaRef = React.createRef(); this.submit = this.submit.bind(this);
this.messageSubmit = this.messageSubmit.bind(this);
this.toggleCode = this.toggleCode.bind(this); this.toggleCode = this.toggleCode.bind(this);
}
this.editor = null; toggleCode() {
this.setState({
moment.updateLocale('en', { inCodeMode: !this.state.inCodeMode
relativeTime : {
past: function(input) {
return input === 'just now'
? input
: input + ' ago';
},
s : 'just now',
future: 'in %s',
ss : '%d sec',
m: 'a minute',
mm: '%d min',
h: 'an hr',
hh: '%d hrs',
d: 'a day',
dd: '%d days',
M: 'a month',
MM: '%d months',
y: 'a year',
yy: '%d years'
}
}); });
} }
getLetterType(letter) { getLetterType(letter) {
if (letter.startsWith('/me ')) { if (this.isUrl(letter)) {
letter = letter.slice(4);
// remove insignificant leading whitespace.
// aces might be relevant to style.
while (letter[0] === '\n') {
letter = letter.slice(1);
}
return {
me: letter
};
} else if (this.isUrl(letter)) {
return { return {
url: letter url: letter
}; };
@ -98,45 +37,33 @@ export class PostInput extends Component {
isUrl(string) { isUrl(string) {
try { try {
const websiteTest = new RegExp(String(/^((\w+:\/\/)[-a-zA-Z0-9:@;?&=\/%\+\.\*!'\(\),\$_\{\}\^~\[\]`#|]+)/.source) return URL_REGEX.test(string);
);
return websiteTest.test(string);
} catch (e) { } catch (e) {
return false; return false;
} }
} }
messageSubmit() { submit(text) {
if(!this.editor) {
return;
}
const { props, state } = this; const { props, state } = this;
const editorMessage = this.editor.getValue(); if (state.inCodeMode) {
console.log(editorMessage);
if (editorMessage === '') {
return;
}
if(state.code) {
let post = props.api.createPost([ let post = props.api.createPost([
{ {
code: { code: {
expression: editorMessage, expression: text,
output: null output: null
} }
} }
], props.parentIndex); ], props.parentIndex);
props.api.addPost(props.resource.ship, props.resource.name, post); this.setState({
this.editor.setValue(''); inCodeMode: false
}, () => {
props.api.addPost(props.resource.ship, props.resource.name, post);
});
return; return;
} }
let message = this.getLetterType(editorMessage); let message = this.getLetterType(text);
let post = props.api.createPost([message], props.parentIndex); let post = props.api.createPost([message], props.parentIndex);
props.api.addPost(props.resource.ship, props.resource.name, post); props.api.addPost(props.resource.ship, props.resource.name, post);
@ -156,59 +83,12 @@ export class PostInput extends Component {
setTimeout(closure, 1000); setTimeout(closure, 1000);
}; };
setTimeout(closure, 2000);*/ setTimeout(closure, 2000);*/
this.editor.setValue('');
}
toggleCode() {
if(this.state.code) {
this.setState({ code: false });
this.editor.setOption('mode', MARKDOWN_CONFIG);
this.editor.setOption('placeholder', this.props.placeholder);
} else {
this.setState({ code: true });
this.editor.setOption('mode', null);
this.editor.setOption('placeholder', 'Code...');
}
const value = this.editor.getValue();
// Force redraw of placeholder
if(value.length === 0) {
this.editor.setValue(' ');
this.editor.setValue('');
}
} }
render() { render() {
const { props, state } = this; const { props, state } = this;
const codeTheme = state.code ? ' code' : '';
const options = {
mode: MARKDOWN_CONFIG,
theme: 'tlon' + codeTheme,
lineNumbers: false,
lineWrapping: true,
scrollbarStyle: 'native',
cursorHeight: 0.85,
placeholder: state.code ? 'Code...' : props.placeholder,
extraKeys: {
'Enter': () => {
this.messageSubmit();
if (this.state.code) {
this.toggleCode();
}
},
'Shift-3': cm =>
cm.getValue().length === 0
? this.toggleCode()
: CodeMirror.Pass
}
};
return ( return (
<div className="chat pa3 cf flex black white-d bt b--gray4 b--gray1-d bg-white bg-gray0-d relative" <div className="pa3 cf flex black white-d bt b--gray4 b--gray1-d bg-white bg-gray0-d relative"
style={{ flexGrow: 1 }} style={{ flexGrow: 1 }}
> >
<div <div
@ -225,28 +105,16 @@ export class PostInput extends Component {
color={`#000`} color={`#000`}
/> />
</div> </div>
<div <PostEditor
className="fr h-100 flex bg-gray0-d lh-copy pl2 w-100 items-center" inCodeMode={state.inCodeMode}
style={{ flexGrow: 1, maxHeight: '224px', width: 'calc(100% - 72px)' }} submit={this.submit}
> placeholder='Post...' />
<CodeEditor
options={options}
editorDidMount={(editor) => {
this.editor = editor;
if (!/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(
navigator.userAgent
)) {
editor.focus();
}
}}
/>
</div>
<div className="ml2 mr2" <div className="ml2 mr2"
style={{ height: '16px', width: '16px', flexBasis: 16, marginTop: 10 }}> style={{ height: '16px', width: '16px', flexBasis: 16, marginTop: 10 }}>
</div> </div>
<div style={{ height: '16px', width: '16px', flexBasis: 16, marginTop: 10 }}> <div style={{ height: '16px', width: '16px', flexBasis: 16, marginTop: 10 }}>
<img <img
style={{ filter: state.code && 'invert(100%)', height: '100%', width: '100%' }} style={{ filter: state.inCodeMode && 'invert(100%)', height: '100%', width: '100%' }}
onClick={this.toggleCode} onClick={this.toggleCode}
src="/~chat/img/CodeEval.png" src="/~chat/img/CodeEval.png"
className="contrast-10-d bg-white bg-none-d ba b--gray1-d br1" className="contrast-10-d bg-white bg-none-d ba b--gray1-d br1"

View File

@ -23,7 +23,7 @@ export class NewScreen extends Component {
if (prevProps !== props) { if (prevProps !== props) {
const resource = `${window.ship}/${state.idName}`; const resource = `${window.ship}/${state.idName}`;
if (!!props.keys && props.keys.has(resource)) { if (!!props.keys && props.keys.has(resource)) {
props.history.push('/~chat/room/' + resource); props.history.push('/~post/room/' + resource);
} }
} }
} }