mirror of
https://github.com/urbit/shrub.git
synced 2024-12-20 09:21:42 +03:00
post: refactored post-input into input and editor
This commit is contained in:
parent
5b68df5e3d
commit
8707816d25
109
pkg/interface/src/apps/post/components/lib/post-editor.js
Normal file
109
pkg/interface/src/apps/post/components/lib/post-editor.js
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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"
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user