chat-js: lint changed files

This commit is contained in:
Liam Fitzgerald 2020-04-22 13:46:03 +10:00
parent 8e028ad255
commit 8277802027
2 changed files with 117 additions and 151 deletions

View File

@ -1,10 +1,7 @@
import React, { Component } from 'react';
import _ from 'lodash';
import moment from 'moment';
import Mousetrap from 'mousetrap';
import cn from 'classnames';
import { UnControlled as CodeEditor } from 'react-codemirror2'
import CodeMirror from 'codemirror';
import { UnControlled as CodeEditor } from 'react-codemirror2';
import 'codemirror/mode/markdown/markdown';
import 'codemirror/addon/display/placeholder';
@ -12,48 +9,27 @@ import 'codemirror/addon/display/placeholder';
import { Sigil } from '/components/lib/icons/sigil';
import { ShipSearch } from '/components/lib/ship-search';
import { uuid, uxToHex, hexToRgba } from '/lib/util';
import { uxToHex } from '/lib/util';
const MARKDOWN_CONFIG = {
name: "markdown",
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",
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'
}
}
// line height
const INPUT_LINE_HEIGHT = 28;
const INPUT_TOP_PADDING = 3;
function getAdvance(a, b) {
let res = '';
if(!a) {
return b;
}
for (let i = 0; i < Math.min(a.length, b.length); i++) {
if (a[i] !== b[i]) {
return res;
}
res = res.concat(a[i]);
}
return res;
}
};
export class ChatInput extends Component {
constructor(props) {
@ -73,14 +49,12 @@ export class ChatInput extends Component {
this.completePatp = this.completePatp.bind(this);
this.clearSearch = this.clearSearch.bind(this);
this.toggleCode = this.toggleCode.bind(this);
this.editor = null;
// perf testing:
/*let closure = () => {
/* let closure = () => {
let x = 0;
for (var i = 0; i < 30; i++) {
x++;
@ -102,26 +76,25 @@ export class ChatInput extends Component {
past: function(input) {
return input === 'just now'
? input
: input + ' ago'
: input + ' ago';
},
s : 'just now',
future: "in %s",
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"
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'
}
});
}
nextAutocompleteSuggestion(backward = false) {
const { patpSuggestions } = this.state;
let idx = patpSuggestions.findIndex(s => s === this.state.selectedSuggestion);
@ -135,12 +108,11 @@ export class ChatInput extends Component {
this.setState({ selectedSuggestion: patpSuggestions[idx] });
}
patpAutocomplete(message, fresh = false) {
patpAutocomplete(message) {
const match = /~([a-zA-Z\-]*)$/.exec(message);
if (!match ) {
this.setState({ patpSearch: null })
this.setState({ patpSearch: null });
return;
}
this.setState({ patpSearch: match[1].toLowerCase() });
@ -149,7 +121,7 @@ export class ChatInput extends Component {
clearSearch() {
this.setState({
patpSearch: null
})
});
}
completePatp(suggestion) {
@ -170,15 +142,12 @@ export class ChatInput extends Component {
}
messageChange(editor, data, value) {
const { patpSearch } = this.state;
if(patpSearch !== null) {
this.patpAutocomplete(value, false);
}
}
getLetterType(letter) {
if (letter.startsWith('/me')) {
letter = letter.slice(3);
@ -190,22 +159,21 @@ export class ChatInput extends Component {
return {
me: letter
}
};
} else if (this.isUrl(letter)) {
return {
url: letter
}
};
} else {
return {
text: letter
}
};
}
}
isUrl(string) {
try {
let websiteTest = new RegExp(''
+ /((\w+:\/\/)[-a-zA-Z0-9:@;?&=\/%\+\.\*!'\(\),\$_\{\}\^~\[\]`#|]+)/.source
const websiteTest = new RegExp(String(/((\w+:\/\/)[-a-zA-Z0-9:@;?&=\/%\+\.\*!'\(\),\$_\{\}\^~\[\]`#|]+)/.source)
);
return websiteTest.test(string);
} catch (e) {
@ -235,10 +203,10 @@ export class ChatInput extends Component {
return;
}
let message = [];
editorMessage.split(" ").map((each) => {
editorMessage.split(' ').map((each) => {
if (this.isUrl(each)) {
if (message.length > 0) {
message = message.join(" ");
message = message.join(' ');
message = this.getLetterType(message);
props.api.chat.message(
props.station,
@ -248,22 +216,20 @@ export class ChatInput extends Component {
);
message = [];
}
let URL = this.getLetterType(each);
const URL = this.getLetterType(each);
props.api.chat.message(
props.station,
`~${window.ship}`,
Date.now(),
URL
);
}
else {
} else {
return message.push(each);
}
})
});
if (message.length > 0) {
message = message.join(" ");
message = message.join(' ');
message = this.getLetterType(message);
props.api.chat.message(
props.station,
@ -275,10 +241,9 @@ export class ChatInput extends Component {
}
// perf:
//setTimeout(this.closure, 2000);
// setTimeout(this.closure, 2000);
this.editor.setValue('');
}
toggleCode() {
@ -289,7 +254,7 @@ export class ChatInput extends Component {
} else {
this.setState({ code: true });
this.editor.setOption('mode', null);
this.editor.setOption('placeholder', "Code...");
this.editor.setOption('placeholder', 'Code...');
}
const value = this.editor.getValue();
@ -298,29 +263,26 @@ export class ChatInput extends Component {
this.editor.setValue(' ');
this.editor.setValue('');
}
}
render() {
const { props, state } = this;
let color = !!props.ownerContact
const color = props.ownerContact
? uxToHex(props.ownerContact.color) : '000000';
let sigilClass = !!props.ownerContact
? "" : "mix-blend-diff";
const sigilClass = props.ownerContact
? '' : 'mix-blend-diff';
const candidates = _.chain(this.props.envelopes)
.defaultTo([])
.map("author")
.map('author')
.uniq()
.reverse()
.value();
const codeTheme = state.code ? ' code' : '';
const completeActive = state.patpSearch !== null;
const options = {
mode: MARKDOWN_CONFIG,
theme: 'tlon' + codeTheme,
@ -328,20 +290,21 @@ export class ChatInput extends Component {
lineWrapping: true,
scrollbarStyle: 'native',
cursorHeight: 0.85,
placeholder: state.code ? "Code..." : props.placeholder,
placeholder: state.code ? 'Code...' : props.placeholder,
extraKeys: {
Tab: (cm) =>
Tab: cm =>
this.patpAutocomplete(cm.getValue(), true),
'Enter': (cm) =>
'Enter': cm =>
this.messageSubmit(),
'Shift-3': (cm) =>
'Shift-3': cm =>
this.toggleCode()
}
};
return (
<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 }}
>
<ShipSearch
popover
onSelect={this.completePatp}
@ -358,7 +321,8 @@ export class ChatInput extends Component {
marginTop: 6,
flexBasis: 24,
height: 24
}}>
}}
>
<Sigil
ship={window.ship}
size={24}
@ -368,10 +332,13 @@ export class ChatInput extends Component {
</div>
<div
className="fr h-100 flex bg-gray0-d lh-copy pl2 w-100 items-center"
style={{ flexGrow: 1, maxHeight: '224px', width: 'calc(100% - 48px)' }}>
style={{ flexGrow: 1, maxHeight: '224px', width: 'calc(100% - 48px)' }}
>
<CodeEditor
options={options}
editorDidMount={editor => { this.editor = editor; }}
editorDidMount={(editor) => {
this.editor = editor;
}}
onChange={(e, d, v) => this.messageChange(e, d, v)}
/>
</div>

View File

@ -1,25 +1,26 @@
import React, { Component } from "react";
import _ from "lodash";
import urbitOb from "urbit-ob";
import React, { Component } from 'react';
import _ from 'lodash';
import urbitOb from 'urbit-ob';
import Mousetrap from 'mousetrap';
import cn from "classnames";
import { Sigil } from "/components/lib/icons/sigil";
import { hexToRgba, uxToHex, deSig } from "/lib/util";
import cn from 'classnames';
import { Sigil } from '/components/lib/icons/sigil';
import { hexToRgba, uxToHex, deSig } from '/lib/util';
function ShipSearchItem({ ship, contacts, selected, onSelect }) {
let contact = contacts[ship];
let color = "#000000";
let sigilClass = "v-mid mix-blend-diff";
const contact = contacts[ship];
let color = '#000000';
let sigilClass = 'v-mid mix-blend-diff';
let nickname;
let nameStyle = {};
const nameStyle = {};
const isSelected = ship === selected;
if (contact) {
const hex = uxToHex(contact.color);
color = `#${hex}`;
nameStyle.color = hexToRgba(hex, 0.7);
nameStyle.textShadow = "0px 0px 0px #000";
nameStyle.filter = "contrast(1.3) saturate(1.5)";
sigilClass = "v-mid";
nameStyle.textShadow = '0px 0px 0px #000';
nameStyle.filter = 'contrast(1.3) saturate(1.5)';
sigilClass = 'v-mid';
nickname = contact.nickname;
}
@ -27,21 +28,21 @@ function ShipSearchItem({ ship, contacts, selected, onSelect }) {
<div
onClick={() => onSelect(ship)}
className={cn(
"f8 pv1 ph3 pointer hover-bg-gray1-d hover-bg-gray4 relative flex items-center",
'f8 pv1 ph3 pointer hover-bg-gray1-d hover-bg-gray4 relative flex items-center',
{
"white-d bg-gray0-d bg-white": !isSelected,
"black-d bg-gray1-d bg-gray4": isSelected
'white-d bg-gray0-d bg-white': !isSelected,
'black-d bg-gray1-d bg-gray4': isSelected
}
)}
key={ship}
>
<Sigil ship={"~" + ship} size={24} color={color} classes={sigilClass} />
<Sigil ship={'~' + ship} size={24} color={color} classes={sigilClass} />
{nickname && (
<p style={nameStyle} className="dib ml4 b">
{nickname}
</p>
)}
<div className="mono gray2 gray4-d ml4">{"~" + ship}</div>
<div className="mono gray2 gray4-d ml4">{'~' + ship}</div>
<p className="nowrap ml4">{status}</p>
</div>
);
@ -58,31 +59,30 @@ export class ShipSearch extends Component {
};
this.keymap = {
Tab: (cm) =>
Tab: cm =>
this.nextAutocompleteSuggestion(),
'Shift-Tab': (cm) =>
'Shift-Tab': cm =>
this.nextAutocompleteSuggestion(true),
'Up': (cm) =>
'Up': cm =>
this.nextAutocompleteSuggestion(true),
'Escape': (cm) =>
'Escape': cm =>
this.props.onClear(),
'Down': (cm) =>
'Down': cm =>
this.nextAutocompleteSuggestion(),
'Enter': (cm) => {
if(this.props.searchTerm !== null) {
this.props.onSelect(this.state.selected);
}
},
'Shift-3': (cm) =>
'Shift-3': cm =>
this.toggleCode()
}
};
}
componentDidMount() {
if(this.props.searchTerm !== null) {
this.updateSuggestions(true);
}
}
componentDidUpdate(prevProps) {
@ -109,12 +109,11 @@ export class ShipSearch extends Component {
} else if (prevProps.searchTerm !== props.searchTerm) {
this.updateSuggestions(true);
}
}
updateSuggestions(isStale = false) {
const needle = this.props.searchTerm;
const matchString = hay => {
const matchString = (hay) => {
hay = hay.toLowerCase();
return (
@ -132,7 +131,7 @@ export class ShipSearch extends Component {
.filter(
({ nickname, ship }) => matchString(nickname) || matchString(ship)
)
.map("ship")
.map('ship')
.value();
const exactMatch = urbitOb.isValidPatp(`~${needle}`) ? [needle] : [];
@ -179,7 +178,7 @@ export class ShipSearch extends Component {
this.mousetrap = new Mousetrap(this.props.inputRef);
}
this.mousetrap.bind("enter", e => {
this.mousetrap.bind('enter', (e) => {
e.preventDefault();
e.stopPropagation();
@ -189,22 +188,22 @@ export class ShipSearch extends Component {
}
});
this.mousetrap.bind("tab", e => {
this.mousetrap.bind('tab', (e) => {
e.preventDefault();
e.stopPropagation();
this.nextAutocompleteSuggestion(false);
});
this.mousetrap.bind(["up", "shift+tab"], e => {
this.mousetrap.bind(['up', 'shift+tab'], (e) => {
e.preventDefault();
e.stopPropagation();
this.nextAutocompleteSuggestion(true);
});
this.mousetrap.bind("down", e => {
this.mousetrap.bind('down', (e) => {
e.preventDefault();
e.stopPropagation();
this.nextAutocompleteSuggestion(false);
});
this.mousetrap.bind("esc", e => {
this.mousetrap.bind('esc', (e) => {
e.preventDefault();
e.stopPropagation();
this.props.onClear();
@ -213,7 +212,7 @@ export class ShipSearch extends Component {
unbindShortcuts() {
if(!this.props.inputRef) {
this.unbindCmShortcuts()
this.unbindCmShortcuts();
}
if (!this.state.bound) {
@ -221,11 +220,11 @@ export class ShipSearch extends Component {
}
this.setState({ bound: false });
this.mousetrap.unbind("enter");
this.mousetrap.unbind("tab");
this.mousetrap.unbind(["up", "shift+tab"]);
this.mousetrap.unbind("down");
this.mousetrap.unbind("esc");
this.mousetrap.unbind('enter');
this.mousetrap.unbind('tab');
this.mousetrap.unbind(['up', 'shift+tab']);
this.mousetrap.unbind('down');
this.mousetrap.unbind('esc');
}
nextAutocompleteSuggestion(backward = false) {
@ -249,22 +248,22 @@ export class ShipSearch extends Component {
return null;
}
const popoverClasses = (popover && " absolute ") || " ";
const popoverClasses = (popover && ' absolute ') || ' ';
return (
<div
style={
popover
? {
bottom: "90%",
left: "48px"
bottom: '90%',
left: '48px'
}
: {}
}
className={
"black white-d bg-white bg-gray0-d " +
"w7 pv3 z-1 mt1 ba b--gray1-d b--gray4" +
'black white-d bg-white bg-gray0-d ' +
'w7 pv3 z-1 mt1 ba b--gray1-d b--gray4' +
popoverClasses +
className || ""
className || ''
}
>
{suggestions.slice(0, 5).map(ship => (
@ -285,7 +284,7 @@ export class ShipSearchInput extends Component {
constructor() {
super();
this.state = {
searchTerm: ""
searchTerm: ''
};
this.inputRef = null;
@ -308,13 +307,13 @@ export class ShipSearchInput extends Component {
}
componentDidMount() {
document.addEventListener("mousedown", this.onClick);
document.addEventListener("touchstart", this.onClick);
document.addEventListener('mousedown', this.onClick);
document.addEventListener('touchstart', this.onClick);
}
componentWillUnmount() {
document.removeEventListener("mousedown", this.onClick);
document.removeEventListener("touchstart", this.onClick);
document.removeEventListener('mousedown', this.onClick);
document.removeEventListener('touchstart', this.onClick);
}
setInputRef(ref) {
@ -337,11 +336,11 @@ export class ShipSearchInput extends Component {
return (
<div
ref={ref => (this.popoverRef = ref)}
style={{ top: "150%", left: "-80px" }}
style={{ top: '150%', left: '-80px' }}
className="b--gray2 b--solid ba absolute bg-white bg-gray0-d shadow-5"
>
<textarea
style={{ resize: "none", maxWidth: '200px' }}
style={{ resize: 'none', maxWidth: '200px' }}
className="ma2 pa2 b--gray4 ba b--solid w7 db bg-gray0-d white-d"
rows={1}
autocapitalise="none"