You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

282 lines
6.7 KiB
JavaScript

'use strict';
const React = require('react');
const create = require('create-react-class');
const Promise = require('bluebird');
const colorConvert = require('color-convert');
const sanitize = require('sanitize-html');
const riot = require('../lib/riot-utils.js');
const FileUpload = require('./fileUpload.js');
let input = create({
displayName: "Input",
getInitialState: function() {
return {
uploads: []
};
},
setRef: function(ref) {
if (ref !=null) {
ref.addEventListener("keydown", (e) => {
// only send on plain 'enter'
if (e.key == "Enter") {
if (!e.shiftKey && !e.altKey && !e.ctrlKey) {
this.send(e);
return;
}
}
});
ref.addEventListener('change', this.resize_textarea);
ref.addEventListener('cut', this.resize_textarea_delayed);
ref.addEventListener('paste', this.resize_textarea_delayed);
ref.addEventListener('drop', this.resize_textarea_delayed);
ref.addEventListener('keydown', this.resize_textarea_delayed);
this.setState({ref: ref});
}
},
addUpload: function(upload) {
let uploads = this.state.uploads;
uploads.push(upload);
this.setState({uploads: uploads});
},
removeUpload: function(index) {
let uploads = this.state.uploads;
uploads.splice(index, 1);
this.setState({uploads: uploads});
},
resize_textarea: function(element) {
if (element == undefined) {
return;
}
let ref = element.target;
if (ref != undefined) {
ref.style.height = 'auto';
ref.style.height = ref.scrollHeight+'px';
}
},
resize_textarea_delayed: function(e) {
setTimeout(() => this.resize_textarea(e), 5);
},
send: function(e) {
let msg = e.target.value;
if (msg.trim().length != 0) {
//TODO: parse markdown (commonmark?)
if (msg.startsWith('/')) {
// Handle other commands
let parts = msg.split(' ');
let command = parts[0];
let result = handleCommands(command, parts);
if (result != null) {
if (result.type == "html") {
this.sendHTML(result.content);
} else {
this.sendPlain(result.content);
}
}
} else {
this.sendPlain(msg);
}
}
if (this.state.uploads.length > 0) {
this.uploadFiles(this.state.uploads);
this.setState({uploads: []});
}
e.target.value = "";
e.preventDefault();
this.resize_textarea_delayed(e);
},
uploadFiles: function(uploads) {
let client = this.props.client;
return Promise.map(uploads, (upload) => {
let fileUploadPromise = client.uploadContent(upload.file,
{onlyContentUri: false}).then((response) => {
return response.content_uri;
});
let mimeType = upload.file.type;
let eventType = "m.file";
let additionalPromise;
if (mimeType.startsWith("image/") || mimeType.startsWith("video/")) {
function elementToThumbnail(element) {
return Promise.try(() => {
return riot.createThumbnail(element,
element.width,
element.height,
thumbnailType
);
}).catch((error) => {
console.error("neo: error getting thumbnail", error);
throw error;
}).then((thumbResult) => {
return client.uploadContent(thumbResult.thumbnail, {onlyContentUri: false});
}).then((response) => {
return {
thumbnail_url: response.content_uri,
thumbnail_info: {
mimetype: thumbnailType
}
};
});
}
if (mimeType.startsWith("image/")) {
eventType = "m.image";
additionalPromise = riot.loadImageElement(upload.file)
.then((element) => {return elementToThumbnail(element);});
} else if (mimeType.startsWith("video/")) {
eventType = "m.video";
additionalPromise = riot.loadVideoElement(upload.file)
.then((element) => {return elementToThumbnail(element);});
}
// create and upload thumbnail
let thumbnailType = "image/png";
if (mimeType == "image/jpeg") {
thumbnailType = mimeType;
}
} else if (mimeType.startsWith("audio/")) {
eventType = "m.audio";
} else {
// m.file
}
return Promise.all([fileUploadPromise, additionalPromise]).then((result) => {
console.log(result);
let info = {
mimetype: mimeType
};
if (result[1] != undefined) {
info = Object.assign(info, result[1]);
}
return client.sendEvent(this.props.roomId, "m.room.message", {
body: upload.file.name,
msgtype: eventType,
info: info,
url: result[0]
});
});
});
},
sendPlain: function(string) {
let content = {
body: string,
msgtype: "m.text"
};
content = this.sendReply(content);
/* FIXME: Promisify */
this.props.client.sendEvent(this.props.roomId, "m.room.message", content, (err, _res) => {
if (err != null) {
console.log(err);
}
});
},
sendHTML: function(html) {
let content = {
body: sanitize(html, {allowedTags: []}),
formatted_body: html,
format: "org.matrix.custom.html",
msgtype: "m.text"
};
content = this.sendReply(content);
/* FIXME: Promisify */
this.props.client.sendEvent(this.props.roomId, "m.room.message", content, (err, _res) => {
console.log(err);
});
},
sendReply: function(content) {
if (this.props.replyEvent != undefined) {
content['m.relates_to'] = {
'm.in_reply_to': {
event_id: this.props.replyEvent.event_id
}
};
this.props.onReplyClick();
}
return content;
},
render: function() {
return <div className="input">
{this.props.replyEvent &&
<div className="replyEvent" onClick={() => this.props.onReplyClick()}>
{this.props.replyEvent.plaintext()}
</div>
}
{this.state.uploads.length > 0 &&
<div className="imgPreview">
{this.state.uploads.map((upload, key) => {
return (
<div key={key}>
<img src={upload.preview}/>
<span onClick={() => this.removeUpload(key)}>X</span>
</div>
);
})}
</div>
}
<div className="content">
<textarea ref={this.setRef} rows="1" spellCheck="false" placeholder="unencrypted message"></textarea>
<FileUpload addUpload={this.addUpload}/>
</div>
</div>;
}
});
function handleCommands(command, parts) {
if (command == "/rainbow") {
if (parts.length < 2) {
return;
}
let string = parts[1];
for(let i=2; i < parts.length; i++) {
string += " " + parts[i];
}
let html = rainbowTransform(string);
return {
type: 'html',
content: html
};
}
return null;
}
function rainbowTransform(text) {
let array = text.split("");
let delta = 360/text.length;
if (delta < 10) {
delta = 10;
} else if (delta > 20) {
delta = 20;
}
let h = -1 * delta; // start at beginning
let rainbowArray = array.map((char) => {
h = h + delta;
if (h > 360) {
h = 0;
}
return `<font color="${colorConvert.hsl.hex(h, 100, 50)}">${char}</font>`;
});
let rainbow = rainbowArray.join("");
return rainbow;
}
module.exports = input;