From e6842eab94b7dc6abbae94b45f3bf7cdb19b496d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 31 Mar 2016 18:38:01 +0100 Subject: [PATCH] WIP url previewing --- src/component-index.js | 13 ++-- src/components/views/messages/TextualBody.js | 45 +++++++++++--- .../views/rooms/LinkPreviewWidget.js | 60 +++++++++++++++++++ 3 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 src/components/views/rooms/LinkPreviewWidget.js diff --git a/src/component-index.js b/src/component-index.js index 8a4035811a..b5f5dd0a53 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -26,10 +26,6 @@ limitations under the License. module.exports.components = {}; module.exports.components['structures.CreateRoom'] = require('./components/structures/CreateRoom'); -module.exports.components['structures.login.ForgotPassword'] = require('./components/structures/login/ForgotPassword'); -module.exports.components['structures.login.Login'] = require('./components/structures/login/Login'); -module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration'); -module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); module.exports.components['structures.MatrixChat'] = require('./components/structures/MatrixChat'); module.exports.components['structures.MessagePanel'] = require('./components/structures/MessagePanel'); module.exports.components['structures.RoomStatusBar'] = require('./components/structures/RoomStatusBar'); @@ -38,6 +34,10 @@ module.exports.components['structures.ScrollPanel'] = require('./components/stru module.exports.components['structures.TimelinePanel'] = require('./components/structures/TimelinePanel'); module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar'); module.exports.components['structures.UserSettings'] = require('./components/structures/UserSettings'); +module.exports.components['structures.login.ForgotPassword'] = require('./components/structures/login/ForgotPassword'); +module.exports.components['structures.login.Login'] = require('./components/structures/login/Login'); +module.exports.components['structures.login.PostRegistration'] = require('./components/structures/login/PostRegistration'); +module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); module.exports.components['views.avatars.BaseAvatar'] = require('./components/views/avatars/BaseAvatar'); module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar'); module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar'); @@ -64,10 +64,10 @@ module.exports.components['views.login.LoginHeader'] = require('./components/vie module.exports.components['views.login.PasswordLogin'] = require('./components/views/login/PasswordLogin'); module.exports.components['views.login.RegistrationForm'] = require('./components/views/login/RegistrationForm'); module.exports.components['views.login.ServerConfig'] = require('./components/views/login/ServerConfig'); -module.exports.components['views.messages.MessageEvent'] = require('./components/views/messages/MessageEvent'); module.exports.components['views.messages.MFileBody'] = require('./components/views/messages/MFileBody'); module.exports.components['views.messages.MImageBody'] = require('./components/views/messages/MImageBody'); module.exports.components['views.messages.MVideoBody'] = require('./components/views/messages/MVideoBody'); +module.exports.components['views.messages.MessageEvent'] = require('./components/views/messages/MessageEvent'); module.exports.components['views.messages.TextualBody'] = require('./components/views/messages/TextualBody'); module.exports.components['views.messages.TextualEvent'] = require('./components/views/messages/TextualEvent'); module.exports.components['views.messages.UnknownBody'] = require('./components/views/messages/UnknownBody'); @@ -77,6 +77,7 @@ module.exports.components['views.rooms.AuxPanel'] = require('./components/views/ module.exports.components['views.rooms.EntityTile'] = require('./components/views/rooms/EntityTile'); module.exports.components['views.rooms.EventTile'] = require('./components/views/rooms/EventTile'); module.exports.components['views.rooms.InviteMemberList'] = require('./components/views/rooms/InviteMemberList'); +module.exports.components['views.rooms.LinkPreviewWidget'] = require('./components/views/rooms/LinkPreviewWidget'); module.exports.components['views.rooms.MemberInfo'] = require('./components/views/rooms/MemberInfo'); module.exports.components['views.rooms.MemberList'] = require('./components/views/rooms/MemberList'); module.exports.components['views.rooms.MemberTile'] = require('./components/views/rooms/MemberTile'); @@ -90,8 +91,8 @@ module.exports.components['views.rooms.RoomPreviewBar'] = require('./components/ module.exports.components['views.rooms.RoomSettings'] = require('./components/views/rooms/RoomSettings'); module.exports.components['views.rooms.RoomTile'] = require('./components/views/rooms/RoomTile'); module.exports.components['views.rooms.RoomTopicEditor'] = require('./components/views/rooms/RoomTopicEditor'); -module.exports.components['views.rooms.SearchableEntityList'] = require('./components/views/rooms/SearchableEntityList'); module.exports.components['views.rooms.SearchResultTile'] = require('./components/views/rooms/SearchResultTile'); +module.exports.components['views.rooms.SearchableEntityList'] = require('./components/views/rooms/SearchableEntityList'); module.exports.components['views.rooms.SimpleRoomHeader'] = require('./components/views/rooms/SimpleRoomHeader'); module.exports.components['views.rooms.TabCompleteBar'] = require('./components/views/rooms/TabCompleteBar'); module.exports.components['views.rooms.TopUnreadMessagesBar'] = require('./components/views/rooms/TopUnreadMessagesBar'); diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 92447dd1da..d93b4bac9b 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -22,6 +22,7 @@ var HtmlUtils = require('../../../HtmlUtils'); var linkify = require('linkifyjs'); var linkifyElement = require('linkifyjs/element'); var linkifyMatrix = require('../../../linkify-matrix'); +var sdk = require('../../../index'); linkifyMatrix(linkify); @@ -39,26 +40,42 @@ module.exports = React.createClass({ highlightLink: React.PropTypes.string, }, + getInitialState: function() { + return { + link: null, + }; + }, + componentDidMount: function() { linkifyElement(this.refs.content, linkifyMatrix.options); - if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") - HtmlUtils.highlightDom(ReactDOM.findDOMNode(this)); - }, - - componentDidUpdate: function() { - // XXX: why don't we linkify here? - // XXX: why do we bother doing this on update at all, given events are immutable? + var link = this.findLink(this.refs.content.children); + if (link) { + this.setState({ link: link.getAttribute("href") }); + } if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") HtmlUtils.highlightDom(ReactDOM.findDOMNode(this)); }, - shouldComponentUpdate: function(nextProps) { + findLink: function(nodes) { + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i]; + if (node.tagName === "A" && node.getAttribute("href")) { + return node; + } + else if (node.children && node.children.length) { + return this.findLink(node.children) + } + } + }, + + shouldComponentUpdate: function(nextProps, nextState) { // exploit that events are immutable :) return (nextProps.mxEvent.getId() !== this.props.mxEvent.getId() || nextProps.highlights !== this.props.highlights || - nextProps.highlightLink !== this.props.highlightLink); + nextProps.highlightLink !== this.props.highlightLink || + nextState.link !== this.state.link); }, render: function() { @@ -67,24 +84,34 @@ module.exports = React.createClass({ var body = HtmlUtils.bodyToHtml(content, this.props.highlights, {highlightLink: this.props.highlightLink}); + + var widget; + if (this.state.link) { + var LinkPreviewWidget = sdk.getComponent('rooms.LinkPreviewWidget'); + widget = ; + } + switch (content.msgtype) { case "m.emote": var name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); return ( * { name } { body } + { widget } ); case "m.notice": return ( { body } + { widget } ); default: // including "m.text" return ( { body } + { widget } ); } diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.js new file mode 100644 index 0000000000..f474776713 --- /dev/null +++ b/src/components/views/rooms/LinkPreviewWidget.js @@ -0,0 +1,60 @@ +/* +Copyright 2016 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 MatrixClientPeg = require('../../../MatrixClientPeg'); + +module.exports = React.createClass({ + displayName: 'LinkPreviewWidget', + + propTypes: { + link: React.PropTypes.string.isRequired + }, + + getInitialState: function() { + return { + preview: {} + }; + }, + + componentWillMount: function() { + MatrixClientPeg.get().getUrlPreview(this.props.link).then((res)=>{ + this.setState({ preview: res }); + }, (error)=>{ + console.error("Failed to get preview for URL: " + error); + }); + }, + + render: function() { + var p = this.state.preview; + return ( +
+
{ p["og:title"] }
+
{ p["og:site_name"] ? (" &emdash; " + p["og:site_name"]) : null }
+
+ +
+
+ { p["og:description"] } +
+ +
+ ); + } +});