Add components which were previously in vector
This commit is contained in:
parent
4a2fe426bf
commit
1825b0317e
14 changed files with 910 additions and 75 deletions
108
src/HtmlUtils.js
Normal file
108
src/HtmlUtils.js
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var sanitizeHtml = require('sanitize-html');
|
||||||
|
var highlight = require('highlight.js');
|
||||||
|
|
||||||
|
var sanitizeHtmlParams = {
|
||||||
|
allowedTags: [
|
||||||
|
'font', // custom to matrix. deliberately no h1/h2 to stop people shouting.
|
||||||
|
'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol',
|
||||||
|
'nl', 'li', 'b', 'i', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div',
|
||||||
|
'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre'
|
||||||
|
],
|
||||||
|
allowedAttributes: {
|
||||||
|
// custom ones first:
|
||||||
|
font: [ 'color' ], // custom to matrix
|
||||||
|
a: [ 'href', 'name', 'target' ], // remote target: custom to matrix
|
||||||
|
// We don't currently allow img itself by default, but this
|
||||||
|
// would make sense if we did
|
||||||
|
img: [ 'src' ],
|
||||||
|
},
|
||||||
|
// Lots of these won't come up by default because we don't allow them
|
||||||
|
selfClosing: [ 'img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta' ],
|
||||||
|
// URL schemes we permit
|
||||||
|
allowedSchemes: [ 'http', 'https', 'ftp', 'mailto' ],
|
||||||
|
allowedSchemesByTag: {},
|
||||||
|
|
||||||
|
transformTags: { // custom to matrix
|
||||||
|
// add blank targets to all hyperlinks
|
||||||
|
'a': sanitizeHtml.simpleTransform('a', { target: '_blank'} )
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
bodyToHtml: function(content, searchTerm) {
|
||||||
|
var originalBody = content.body;
|
||||||
|
var body;
|
||||||
|
|
||||||
|
if (searchTerm) {
|
||||||
|
var lastOffset = 0;
|
||||||
|
var bodyList = [];
|
||||||
|
var k = 0;
|
||||||
|
var offset;
|
||||||
|
|
||||||
|
// XXX: rather than searching for the search term in the body,
|
||||||
|
// we should be looking at the match delimiters returned by the FTS engine
|
||||||
|
if (content.format === "org.matrix.custom.html") {
|
||||||
|
|
||||||
|
var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
|
||||||
|
var safeSearchTerm = sanitizeHtml(searchTerm, sanitizeHtmlParams);
|
||||||
|
while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) {
|
||||||
|
// FIXME: we need to apply the search highlighting to only the text elements of HTML, which means
|
||||||
|
// hooking into the sanitizer parser rather than treating it as a string. Otherwise
|
||||||
|
// the act of highlighting a <b/> or whatever will break the HTML badly.
|
||||||
|
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset, offset) }} />);
|
||||||
|
bodyList.push(<span key={ k++ } dangerouslySetInnerHTML={{ __html: safeSearchTerm }} className="mx_MessageTile_searchHighlight" />);
|
||||||
|
lastOffset = offset + safeSearchTerm.length;
|
||||||
|
}
|
||||||
|
bodyList.push(<span className="markdown-body" key={ k++ } dangerouslySetInnerHTML={{ __html: safeBody.substring(lastOffset) }} />);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while ((offset = originalBody.indexOf(searchTerm, lastOffset)) >= 0) {
|
||||||
|
bodyList.push(<span key={ k++ } >{ originalBody.substring(lastOffset, offset) }</span>);
|
||||||
|
bodyList.push(<span key={ k++ } className="mx_MessageTile_searchHighlight">{ searchTerm }</span>);
|
||||||
|
lastOffset = offset + searchTerm.length;
|
||||||
|
}
|
||||||
|
bodyList.push(<span key={ k++ }>{ originalBody.substring(lastOffset) }</span>);
|
||||||
|
}
|
||||||
|
body = bodyList;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (content.format === "org.matrix.custom.html") {
|
||||||
|
var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams);
|
||||||
|
body = <span className="markdown-body" dangerouslySetInnerHTML={{ __html: safeBody }} />;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
body = originalBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return body;
|
||||||
|
},
|
||||||
|
|
||||||
|
highlightDom: function(element) {
|
||||||
|
var blocks = element.getElementsByTagName("code");
|
||||||
|
for (var i = 0; i < blocks.length; i++) {
|
||||||
|
highlight.highlightBlock(blocks[i]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
277
src/components/views/messages/Event.js
Normal file
277
src/components/views/messages/Event.js
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var ReactDom = require('react-dom');
|
||||||
|
var classNames = require("classnames");
|
||||||
|
|
||||||
|
var sdk = require('../../../index');
|
||||||
|
var MatrixClientPeg = require('../../../MatrixClientPeg')
|
||||||
|
var TextForEvent = require('../../../TextForEvent');
|
||||||
|
|
||||||
|
// FIXME BROKEN IMPORTS
|
||||||
|
var ContextualMenu = require('../../../../ContextualMenu');
|
||||||
|
var Velociraptor = require('../../../../Velociraptor');
|
||||||
|
require('../../../../VelocityBounce');
|
||||||
|
|
||||||
|
var bounce = false;
|
||||||
|
try {
|
||||||
|
if (global.localStorage) {
|
||||||
|
bounce = global.localStorage.getItem('avatar_bounce') == 'true';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventTileTypes = {
|
||||||
|
'm.room.message': 'messages.Message',
|
||||||
|
'm.room.member' : 'messages.TextualEvent',
|
||||||
|
'm.call.invite' : 'messages.TextualEvent',
|
||||||
|
'm.call.answer' : 'messages.TextualEvent',
|
||||||
|
'm.call.hangup' : 'messages.TextualEvent',
|
||||||
|
'm.room.name' : 'messages.TextualEvent',
|
||||||
|
'm.room.topic' : 'messages.TextualEvent',
|
||||||
|
};
|
||||||
|
|
||||||
|
var MAX_READ_AVATARS = 5;
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'Event',
|
||||||
|
|
||||||
|
statics: {
|
||||||
|
haveTileForEvent: function(e) {
|
||||||
|
if (eventTileTypes[e.getType()] == undefined) return false;
|
||||||
|
if (eventTileTypes[e.getType()] == 'messages.TextualEvent') {
|
||||||
|
return TextForEvent.textForEvent(e) !== '';
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {menu: false, allReadAvatars: false};
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldHighlight: function() {
|
||||||
|
var actions = MatrixClientPeg.get().getPushActionsForEvent(this.props.mxEvent);
|
||||||
|
if (!actions || !actions.tweaks) { return false; }
|
||||||
|
return actions.tweaks.highlight;
|
||||||
|
},
|
||||||
|
|
||||||
|
onEditClicked: function(e) {
|
||||||
|
var MessageContextMenu = sdk.getComponent('molecules.MessageContextMenu');
|
||||||
|
var buttonRect = e.target.getBoundingClientRect()
|
||||||
|
var x = buttonRect.right;
|
||||||
|
var y = buttonRect.top + (e.target.height / 2);
|
||||||
|
var self = this;
|
||||||
|
ContextualMenu.createMenu(MessageContextMenu, {
|
||||||
|
mxEvent: this.props.mxEvent,
|
||||||
|
left: x,
|
||||||
|
top: y,
|
||||||
|
onFinished: function() {
|
||||||
|
self.setState({menu: false});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState({menu: true});
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleAllReadAvatars: function() {
|
||||||
|
this.setState({
|
||||||
|
allReadAvatars: !this.state.allReadAvatars
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getReadAvatars: function() {
|
||||||
|
var avatars = [];
|
||||||
|
|
||||||
|
var room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
||||||
|
|
||||||
|
if (!room) return [];
|
||||||
|
|
||||||
|
var myUserId = MatrixClientPeg.get().credentials.userId;
|
||||||
|
|
||||||
|
// get list of read receipts, sorted most recent first
|
||||||
|
var receipts = room.getReceiptsForEvent(this.props.mxEvent).filter(function(r) {
|
||||||
|
return r.type === "m.read" && r.userId != myUserId;
|
||||||
|
}).sort(function(r1, r2) {
|
||||||
|
return r2.data.ts - r1.data.ts;
|
||||||
|
});
|
||||||
|
|
||||||
|
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||||
|
|
||||||
|
var left = 0;
|
||||||
|
|
||||||
|
var reorderTransitionOpts = {
|
||||||
|
duration: 100,
|
||||||
|
easing: 'easeOut'
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < receipts.length; ++i) {
|
||||||
|
var member = room.getMember(receipts[i].userId);
|
||||||
|
|
||||||
|
// Using react refs here would mean both getting Velociraptor to expose
|
||||||
|
// them and making them scoped to the whole RoomView. Not impossible, but
|
||||||
|
// getElementById seems simpler at least for a first cut.
|
||||||
|
var oldAvatarDomNode = document.getElementById('mx_readAvatar'+member.userId);
|
||||||
|
var startStyles = [];
|
||||||
|
var enterTransitionOpts = [];
|
||||||
|
var oldNodeTop = -15; // For avatars that weren't on screen, act as if they were just off the top
|
||||||
|
if (oldAvatarDomNode) {
|
||||||
|
oldNodeTop = oldAvatarDomNode.getBoundingClientRect().top;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.readAvatarNode) {
|
||||||
|
var topOffset = oldNodeTop - this.readAvatarNode.getBoundingClientRect().top;
|
||||||
|
|
||||||
|
if (oldAvatarDomNode && oldAvatarDomNode.style.left !== '0px') {
|
||||||
|
var leftOffset = oldAvatarDomNode.style.left;
|
||||||
|
// start at the old height and in the old h pos
|
||||||
|
startStyles.push({ top: topOffset, left: leftOffset });
|
||||||
|
enterTransitionOpts.push(reorderTransitionOpts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// then shift to the rightmost column,
|
||||||
|
// and then it will drop down to its resting position
|
||||||
|
startStyles.push({ top: topOffset, left: '0px' });
|
||||||
|
enterTransitionOpts.push({
|
||||||
|
duration: bounce ? Math.min(Math.log(Math.abs(topOffset)) * 200, 3000) : 300,
|
||||||
|
easing: bounce ? 'easeOutBounce' : 'easeOutCubic',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var style = {
|
||||||
|
left: left+'px',
|
||||||
|
top: '0px',
|
||||||
|
visibility: ((i < MAX_READ_AVATARS) || this.state.allReadAvatars) ? 'visible' : 'hidden'
|
||||||
|
};
|
||||||
|
|
||||||
|
//console.log("i = " + i + ", MAX_READ_AVATARS = " + MAX_READ_AVATARS + ", allReadAvatars = " + this.state.allReadAvatars + " visibility = " + style.visibility);
|
||||||
|
|
||||||
|
// add to the start so the most recent is on the end (ie. ends up rightmost)
|
||||||
|
avatars.unshift(
|
||||||
|
<MemberAvatar key={member.userId} member={member}
|
||||||
|
width={14} height={14} resizeMethod="crop"
|
||||||
|
style={style}
|
||||||
|
startStyle={startStyles}
|
||||||
|
enterTransitionOpts={enterTransitionOpts}
|
||||||
|
id={'mx_readAvatar'+member.userId}
|
||||||
|
onClick={this.toggleAllReadAvatars}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
// TODO: we keep the extra read avatars in the dom to make animation simpler
|
||||||
|
// we could optimise this to reduce the dom size.
|
||||||
|
if (i < MAX_READ_AVATARS - 1 || this.state.allReadAvatars) { // XXX: where does this -1 come from? is it to make the max'th avatar animate properly?
|
||||||
|
left -= 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var editButton;
|
||||||
|
if (!this.state.allReadAvatars) {
|
||||||
|
var remainder = receipts.length - MAX_READ_AVATARS;
|
||||||
|
var remText;
|
||||||
|
if (i >= MAX_READ_AVATARS - 1) left -= 15;
|
||||||
|
if (remainder > 0) {
|
||||||
|
remText = <span className="mx_EventTile_readAvatarRemainder"
|
||||||
|
onClick={this.toggleAllReadAvatars}
|
||||||
|
style={{ left: left }}>{ remainder }+
|
||||||
|
</span>;
|
||||||
|
left -= 15;
|
||||||
|
}
|
||||||
|
editButton = (
|
||||||
|
<input style={{ left: left }}
|
||||||
|
type="image" src="img/edit.png" alt="Options" title="Options" width="14" height="14"
|
||||||
|
className="mx_EventTile_editButton" onClick={this.onEditClicked} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <span className="mx_EventTile_readAvatars" ref={this.collectReadAvatarNode}>
|
||||||
|
{ editButton }
|
||||||
|
{ remText }
|
||||||
|
<Velociraptor transition={ reorderTransitionOpts }>
|
||||||
|
{ avatars }
|
||||||
|
</Velociraptor>
|
||||||
|
</span>;
|
||||||
|
},
|
||||||
|
|
||||||
|
collectReadAvatarNode: function(node) {
|
||||||
|
this.readAvatarNode = ReactDom.findDOMNode(node);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var MessageTimestamp = sdk.getComponent('messages.MessageTimestamp');
|
||||||
|
var SenderProfile = sdk.getComponent('molecules.SenderProfile');
|
||||||
|
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||||
|
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var msgtype = content.msgtype;
|
||||||
|
|
||||||
|
var EventTileType = sdk.getComponent(eventTileTypes[this.props.mxEvent.getType()]);
|
||||||
|
// This shouldn't happen: the caller should check we support this type
|
||||||
|
// before trying to instantiate us
|
||||||
|
if (!EventTileType) {
|
||||||
|
throw new Error("Event type not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
var classes = classNames({
|
||||||
|
mx_EventTile: true,
|
||||||
|
mx_EventTile_sending: ['sending', 'queued'].indexOf(
|
||||||
|
this.props.mxEvent.status
|
||||||
|
) !== -1,
|
||||||
|
mx_EventTile_notSent: this.props.mxEvent.status == 'not_sent',
|
||||||
|
mx_EventTile_highlight: this.shouldHighlight(),
|
||||||
|
mx_EventTile_continuation: this.props.continuation,
|
||||||
|
mx_EventTile_last: this.props.last,
|
||||||
|
mx_EventTile_contextual: this.props.contextual,
|
||||||
|
menu: this.state.menu,
|
||||||
|
});
|
||||||
|
var timestamp = <MessageTimestamp ts={this.props.mxEvent.getTs()} />
|
||||||
|
|
||||||
|
var aux = null;
|
||||||
|
if (msgtype === 'm.image') aux = "sent an image";
|
||||||
|
else if (msgtype === 'm.video') aux = "sent a video";
|
||||||
|
else if (msgtype === 'm.file') aux = "uploaded a file";
|
||||||
|
|
||||||
|
var readAvatars = this.getReadAvatars();
|
||||||
|
|
||||||
|
var avatar, sender;
|
||||||
|
if (!this.props.continuation) {
|
||||||
|
if (this.props.mxEvent.sender) {
|
||||||
|
avatar = (
|
||||||
|
<div className="mx_EventTile_avatar">
|
||||||
|
<MemberAvatar member={this.props.mxEvent.sender} width={24} height={24} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (EventTileType.needsSenderProfile()) {
|
||||||
|
sender = <SenderProfile mxEvent={this.props.mxEvent} aux={aux} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={classes}>
|
||||||
|
<div className="mx_EventTile_msgOption">
|
||||||
|
{ timestamp }
|
||||||
|
{ readAvatars }
|
||||||
|
</div>
|
||||||
|
{ avatar }
|
||||||
|
{ sender }
|
||||||
|
<div className="mx_EventTile_line">
|
||||||
|
<EventTileType mxEvent={this.props.mxEvent} searchTerm={this.props.searchTerm} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -16,15 +16,29 @@ limitations under the License.
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
var linkify = require('linkifyjs');
|
var linkify = require('linkifyjs');
|
||||||
var linkifyElement = require('linkifyjs/element');
|
var linkifyElement = require('linkifyjs/element');
|
||||||
var linkifyMatrix = require('../../linkify-matrix');
|
var linkifyMatrix = require('../../../linkify-matrix');
|
||||||
|
|
||||||
linkifyMatrix(linkify);
|
linkifyMatrix(linkify);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = React.createClass({
|
||||||
|
displayName: 'MEmoteMessage',
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
linkifyElement(this.refs.content, linkifyMatrix.options);
|
linkifyElement(this.refs.content, linkifyMatrix.options);
|
||||||
}
|
},
|
||||||
};
|
|
||||||
|
render: function() {
|
||||||
|
var mxEvent = this.props.mxEvent;
|
||||||
|
var content = mxEvent.getContent();
|
||||||
|
var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
|
||||||
|
return (
|
||||||
|
<span ref="content" className="mx_MEmoteTile mx_MessageTile_content">
|
||||||
|
* {name} {content.body}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -16,9 +16,13 @@ limitations under the License.
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
var filesize = require('filesize');
|
var filesize = require('filesize');
|
||||||
|
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'MFileMessage',
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
presentableTextForFile: function(content) {
|
presentableTextForFile: function(content) {
|
||||||
var linkText = 'Attachment';
|
var linkText = 'Attachment';
|
||||||
if (content.body && content.body.length > 0) {
|
if (content.body && content.body.length > 0) {
|
||||||
|
@ -39,6 +43,31 @@ module.exports = {
|
||||||
linkText += ' (' + additionals.join(', ') + ')';
|
linkText += ' (' + additionals.join(', ') + ')';
|
||||||
}
|
}
|
||||||
return linkText;
|
return linkText;
|
||||||
}
|
},
|
||||||
};
|
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
|
var httpUrl = cli.mxcUrlToHttp(content.url);
|
||||||
|
var text = this.presentableTextForFile(content);
|
||||||
|
|
||||||
|
if (httpUrl) {
|
||||||
|
return (
|
||||||
|
<span className="mx_MFileTile">
|
||||||
|
<div className="mx_MImageTile_download">
|
||||||
|
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||||
|
<img src="img/download.png" width="10" height="12"/>
|
||||||
|
Download {text}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
var extra = text ? ': '+text : '';
|
||||||
|
return <span className="mx_MFileTile">
|
||||||
|
Invalid file{extra}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
136
src/components/views/messages/MImageMessage.js
Normal file
136
src/components/views/messages/MImageMessage.js
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var filesize = require('filesize');
|
||||||
|
|
||||||
|
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||||
|
var Modal = require('../../../Modal');
|
||||||
|
var sdk = require('../../../index');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'MImageMessage',
|
||||||
|
|
||||||
|
thumbHeight: function(fullWidth, fullHeight, thumbWidth, thumbHeight) {
|
||||||
|
if (!fullWidth || !fullHeight) {
|
||||||
|
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
|
||||||
|
// log this because it's spammy
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (fullWidth < thumbWidth && fullHeight < thumbHeight) {
|
||||||
|
// no scaling needs to be applied
|
||||||
|
return fullHeight;
|
||||||
|
}
|
||||||
|
var widthMulti = thumbWidth / fullWidth;
|
||||||
|
var heightMulti = thumbHeight / fullHeight;
|
||||||
|
if (widthMulti < heightMulti) {
|
||||||
|
// width is the dominant dimension so scaling will be fixed on that
|
||||||
|
return Math.floor(widthMulti * fullHeight);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// height is the dominant dimension so scaling will be fixed on that
|
||||||
|
return Math.floor(heightMulti * fullHeight);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onClick: function(ev) {
|
||||||
|
if (ev.button == 0 && !ev.metaKey) {
|
||||||
|
ev.preventDefault();
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var httpUrl = MatrixClientPeg.get().mxcUrlToHttp(content.url);
|
||||||
|
var ImageView = sdk.getComponent("elements.ImageView");
|
||||||
|
Modal.createDialog(ImageView, {
|
||||||
|
src: httpUrl,
|
||||||
|
width: content.info.w,
|
||||||
|
height: content.info.h,
|
||||||
|
mxEvent: this.props.mxEvent,
|
||||||
|
}, "mx_Dialog_lightbox");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_isGif: function() {
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
return (content && content.info && content.info.mimetype === "image/gif");
|
||||||
|
},
|
||||||
|
|
||||||
|
onImageEnter: function(e) {
|
||||||
|
if (!this._isGif()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var imgElement = e.target;
|
||||||
|
imgElement.src = MatrixClientPeg.get().mxcUrlToHttp(
|
||||||
|
this.props.mxEvent.getContent().url
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
onImageLeave: function(e) {
|
||||||
|
if (!this._isGif()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var imgElement = e.target;
|
||||||
|
imgElement.src = this._getThumbUrl();
|
||||||
|
},
|
||||||
|
|
||||||
|
_getThumbUrl: function() {
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
return MatrixClientPeg.get().mxcUrlToHttp(content.url, 480, 360);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
|
var thumbHeight = null;
|
||||||
|
if (content.info) thumbHeight = this.thumbHeight(content.info.w, content.info.h, 480, 360);
|
||||||
|
|
||||||
|
var imgStyle = {};
|
||||||
|
if (thumbHeight) imgStyle['height'] = thumbHeight;
|
||||||
|
|
||||||
|
var thumbUrl = this._getThumbUrl();
|
||||||
|
if (thumbUrl) {
|
||||||
|
return (
|
||||||
|
<span className="mx_MImageTile">
|
||||||
|
<a href={cli.mxcUrlToHttp(content.url)} onClick={ this.onClick }>
|
||||||
|
<img className="mx_MImageTile_thumbnail" src={thumbUrl}
|
||||||
|
alt={content.body} style={imgStyle}
|
||||||
|
onMouseEnter={this.onImageEnter}
|
||||||
|
onMouseLeave={this.onImageLeave} />
|
||||||
|
</a>
|
||||||
|
<div className="mx_MImageTile_download">
|
||||||
|
<a href={cli.mxcUrlToHttp(content.url)} target="_blank">
|
||||||
|
<img src="img/download.png" width="10" height="12"/>
|
||||||
|
Download {content.body} ({ content.info && content.info.size ? filesize(content.info.size) : "Unknown size" })
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else if (content.body) {
|
||||||
|
return (
|
||||||
|
<span className="mx_MImageTile">
|
||||||
|
Image '{content.body}' cannot be displayed.
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<span className="mx_MImageTile">
|
||||||
|
This image cannot be displayed.
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
59
src/components/views/messages/MNoticeMessage.js
Normal file
59
src/components/views/messages/MNoticeMessage.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var linkify = require('linkifyjs');
|
||||||
|
var linkifyElement = require('linkifyjs/element');
|
||||||
|
var linkifyMatrix = require('../../../linkify-matrix.js');
|
||||||
|
linkifyMatrix(linkify);
|
||||||
|
var HtmlUtils = require('../../../HtmlUtils');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'MNoticeMessage',
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
linkifyElement(this.refs.content, linkifyMatrix.options);
|
||||||
|
|
||||||
|
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html")
|
||||||
|
HtmlUtils.highlightDom(this.getDOMNode());
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidUpdate: function() {
|
||||||
|
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html")
|
||||||
|
HtmlUtils.highlightDom(this.getDOMNode());
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldComponentUpdate: function(nextProps) {
|
||||||
|
// exploit that events are immutable :)
|
||||||
|
return (nextProps.mxEvent.getId() !== this.props.mxEvent.getId() ||
|
||||||
|
nextProps.searchTerm !== this.props.searchTerm);
|
||||||
|
},
|
||||||
|
|
||||||
|
// XXX: fix horrible duplication with MTextTile
|
||||||
|
render: function() {
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var body = HtmlUtils.bodyToHtml(content, this.props.searchTerm);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span ref="content" className="mx_MNoticeTile mx_MessageTile_content">
|
||||||
|
{ body }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
52
src/components/views/messages/MRoomMemberEvent.js
Normal file
52
src/components/views/messages/MRoomMemberEvent.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
|
||||||
|
var sdk = require('../../../index');
|
||||||
|
var TextForEvent = require('../../../TextForEvent');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'MRoomMemberEvent',
|
||||||
|
|
||||||
|
getMemberEventText: function() {
|
||||||
|
return TextForEvent.textForEvent(this.props.mxEvent);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
// XXX: for now, just cheekily borrow the css from message tile...
|
||||||
|
var timestamp = this.props.last ? <MessageTimestamp ts={this.props.mxEvent.getTs()} /> : null;
|
||||||
|
var text = this.getMemberEventText();
|
||||||
|
if (!text) return <div/>;
|
||||||
|
var MessageTimestamp = sdk.getComponent('messages.MessageTimestamp');
|
||||||
|
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||||
|
return (
|
||||||
|
<div className="mx_MessageTile mx_MessageTile_notice">
|
||||||
|
<div className="mx_MessageTile_avatar">
|
||||||
|
<MemberAvatar member={this.props.mxEvent.sender} />
|
||||||
|
</div>
|
||||||
|
{ timestamp }
|
||||||
|
<span className="mx_SenderProfile"></span>
|
||||||
|
<span className="mx_MessageTile_content">
|
||||||
|
{ text }
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
59
src/components/views/messages/MTextMessage.js
Normal file
59
src/components/views/messages/MTextMessage.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var HtmlUtils = require('../../../HtmlUtils');
|
||||||
|
var linkify = require('linkifyjs');
|
||||||
|
var linkifyElement = require('linkifyjs/element');
|
||||||
|
var linkifyMatrix = require('../../../linkify-matrix');
|
||||||
|
|
||||||
|
linkifyMatrix(linkify);
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'MTextMessage',
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
linkifyElement(this.refs.content, linkifyMatrix.options);
|
||||||
|
|
||||||
|
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html")
|
||||||
|
HtmlUtils.highlightDom(this.getDOMNode());
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidUpdate: function() {
|
||||||
|
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html")
|
||||||
|
HtmlUtils.highlightDom(this.getDOMNode());
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldComponentUpdate: function(nextProps) {
|
||||||
|
// exploit that events are immutable :)
|
||||||
|
return (nextProps.mxEvent.getId() !== this.props.mxEvent.getId() ||
|
||||||
|
nextProps.searchTerm !== this.props.searchTerm);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var body = HtmlUtils.bodyToHtml(content, this.props.searchTerm);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span ref="content" className="mx_MTextTile mx_MessageTile_content">
|
||||||
|
{ body }
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
83
src/components/views/messages/MVideoMessage.js
Normal file
83
src/components/views/messages/MVideoMessage.js
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var filesize = require('filesize');
|
||||||
|
|
||||||
|
var MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||||
|
var Modal = require('../../../Modal');
|
||||||
|
var sdk = require('../../../index');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'MVideoMessage',
|
||||||
|
|
||||||
|
thumbScale: function(fullWidth, fullHeight, thumbWidth, thumbHeight) {
|
||||||
|
if (!fullWidth || !fullHeight) {
|
||||||
|
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
|
||||||
|
// log this because it's spammy
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (fullWidth < thumbWidth && fullHeight < thumbHeight) {
|
||||||
|
// no scaling needs to be applied
|
||||||
|
return fullHeight;
|
||||||
|
}
|
||||||
|
var widthMulti = thumbWidth / fullWidth;
|
||||||
|
var heightMulti = thumbHeight / fullHeight;
|
||||||
|
if (widthMulti < heightMulti) {
|
||||||
|
// width is the dominant dimension so scaling will be fixed on that
|
||||||
|
return widthMulti;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// height is the dominant dimension so scaling will be fixed on that
|
||||||
|
return heightMulti;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
|
var height = null;
|
||||||
|
var width = null;
|
||||||
|
var poster = null;
|
||||||
|
var preload = "metadata";
|
||||||
|
if (content.info) {
|
||||||
|
var scale = this.thumbScale(content.info.w, content.info.h, 480, 360);
|
||||||
|
if (scale) {
|
||||||
|
width = Math.floor(content.info.w * scale);
|
||||||
|
height = Math.floor(content.info.h * scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content.info.thumbnail_url) {
|
||||||
|
poster = cli.mxcUrlToHttp(content.info.thumbnail_url);
|
||||||
|
preload = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="mx_MVideoTile">
|
||||||
|
<video className="mx_MVideoTile" src={cli.mxcUrlToHttp(content.url)} alt={content.body}
|
||||||
|
controls preload={preload} autoplay="false" loop
|
||||||
|
height={height} width={width} poster={poster}>
|
||||||
|
</video>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
52
src/components/views/messages/Message.js
Normal file
52
src/components/views/messages/Message.js
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
Copyright 2015 OpenMarket Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var sdk = require('../../../index');
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'Message',
|
||||||
|
|
||||||
|
statics: {
|
||||||
|
needsSenderProfile: function() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var UnknownMessageTile = sdk.getComponent('messages.UnknownMessage');
|
||||||
|
|
||||||
|
var tileTypes = {
|
||||||
|
'm.text': sdk.getComponent('messages.MTextMessage'),
|
||||||
|
'm.notice': sdk.getComponent('messages.MNoticeMessage'),
|
||||||
|
'm.emote': sdk.getComponent('messages.MEmoteMessage'),
|
||||||
|
'm.image': sdk.getComponent('messages.MImageMessage'),
|
||||||
|
'm.file': sdk.getComponent('messages.MFileMessage'),
|
||||||
|
'm.video': sdk.getComponent('messages.MVideoMessage')
|
||||||
|
};
|
||||||
|
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
var msgtype = content.msgtype;
|
||||||
|
var TileType = UnknownMessageTile;
|
||||||
|
if (msgtype && tileTypes[msgtype]) {
|
||||||
|
TileType = tileTypes[msgtype];
|
||||||
|
}
|
||||||
|
|
||||||
|
return <TileType mxEvent={this.props.mxEvent} searchTerm={this.props.searchTerm} />;
|
||||||
|
},
|
||||||
|
});
|
|
@ -16,15 +16,28 @@ limitations under the License.
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var linkify = require('linkifyjs');
|
var React = require('react');
|
||||||
var linkifyElement = require('linkifyjs/element');
|
|
||||||
var linkifyMatrix = require('../../linkify-matrix');
|
|
||||||
|
|
||||||
linkifyMatrix(linkify);
|
var TextForEvent = require('../../../TextForEvent');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = React.createClass({
|
||||||
componentDidMount: function() {
|
displayName: 'TextualEvent',
|
||||||
linkifyElement(this.refs.content, linkifyMatrix.options);
|
|
||||||
|
statics: {
|
||||||
|
needsSenderProfile: function() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var text = TextForEvent.textForEvent(this.props.mxEvent);
|
||||||
|
if (text == null || text.length == 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_EventAsTextTile">
|
||||||
|
{TextForEvent.textForEvent(this.props.mxEvent)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
@ -16,13 +16,17 @@ limitations under the License.
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
var React = require('react');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = React.createClass({
|
||||||
shouldHighlight: function() {
|
displayName: 'UnknownMessage',
|
||||||
var actions = MatrixClientPeg.get().getPushActionsForEvent(this.props.mxEvent);
|
|
||||||
if (!actions || !actions.tweaks) { return false; }
|
|
||||||
return actions.tweaks.highlight;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
var content = this.props.mxEvent.getContent();
|
||||||
|
return (
|
||||||
|
<span className="mx_UnknownMessageTile">
|
||||||
|
{content.body}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2015 OpenMarket Ltd
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var linkify = require('linkifyjs');
|
|
||||||
var linkifyElement = require('linkifyjs/element');
|
|
||||||
var linkifyMatrix = require('../../linkify-matrix.js');
|
|
||||||
linkifyMatrix(linkify);
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
componentDidMount: function() {
|
|
||||||
linkifyElement(this.refs.content, linkifyMatrix.options);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,23 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2015 OpenMarket Ltd
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var MatrixClientPeg = require("../../MatrixClientPeg");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
};
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue