From ebdac4ee50a408b6876a94982475ca2f73466fb3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 18 Jul 2016 01:35:42 +0100 Subject: [PATCH] first cut (untested) --- src/UserSettingsStore.js | 28 +++++++++ src/components/structures/MessagePanel.js | 4 ++ src/components/structures/RoomView.js | 46 +++++++++++++- src/components/structures/TimelinePanel.js | 4 ++ src/components/structures/UserSettings.js | 61 ++++++++++++++++++- src/components/views/messages/MessageEvent.js | 4 ++ src/components/views/messages/TextualBody.js | 29 +++++---- .../views/room_settings/ColorSettings.js | 2 +- src/components/views/rooms/EventTile.js | 4 ++ src/components/views/rooms/RoomSettings.js | 3 + 10 files changed, 171 insertions(+), 14 deletions(-) diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index 305994aa0e..990fa8b7a9 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -113,6 +113,34 @@ module.exports = { }); }, + getUrlPreviewsDisabled: function() { + var event = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls"); + return (event && event.disable); + }, + + setUrlPreviewsDisabled: function(disabled) { + // FIXME: handle errors + MatrixClientPeg.get().setAccountData("org.matrix.preview_urls", { + disable: disabled + }); + }, + + getSyncedSettings: function() { + return MatrixClientPeg.get().getAccountData("im.vector.web.settings") || {}; + }, + + getSyncedSetting: function(type) { + var settings = this.getSyncedSettings(); + return settings[type]; + }, + + setSyncedSetting: function(type, value) { + var settings = this.getSyncedSettings(); + settings[type] = value; + // FIXME: handle errors + MatrixClientPeg.get().setAccountData("im.vector.web.settings", settings); + }, + isFeatureEnabled: function(feature: string): boolean { return localStorage.getItem(`mx_labs_feature_${feature}`) === 'true'; }, diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index c8e878118b..53efac6406 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -44,6 +44,9 @@ module.exports = React.createClass({ // ID of an event to highlight. If undefined, no event will be highlighted. highlightedEventId: React.PropTypes.string, + // Should we show URL Previews + showUrlPreview: React.PropTypes.bool, + // event after which we should show a read marker readMarkerEventId: React.PropTypes.string, @@ -365,6 +368,7 @@ module.exports = React.createClass({ onWidgetLoad={this._onWidgetLoad} readReceipts={readReceipts} readReceiptMap={this._readReceiptMap} + showUrlPreview={this.props.showUrlPreview} checkUnmounting={this._isUnmounting} eventSendStatus={mxEv.status} last={last} isSelectedEvent={highlight}/> diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index cfd359ea01..7a27340cc6 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -239,6 +239,8 @@ module.exports = React.createClass({ MatrixClientPeg.get().stopPeeking(); this._onRoomLoaded(this.state.room); } + + _updatePreviewUrlVisibility(this.state.room); }, shouldComponentUpdate: function(nextProps, nextState) { @@ -341,6 +343,10 @@ module.exports = React.createClass({ // ignore events for other rooms if (!this.state.room || room.roomId != this.state.room.roomId) return; + if (event.getType() === "org.matrix.room.preview_urls") { + _updatePreviewUrlVisibility(room); + } + // ignore anything but real-time updates at the end of the room: // updates from pagination will happen when the paginate completes. if (toStartOfTimeline || !data || !data.liveEvent) return; @@ -384,6 +390,40 @@ module.exports = React.createClass({ } }, + _updatePreviewUrlVisibility: function(room) { + // check our per-room overrides + var roomPreviewUrls = room.getAccountData("org.matrix.room.preview_urls"); + if (roomPreviewUrls && roomPreviewUrls.disabled !== undefined) { + this.setState({ + showUrlPreview: !roomPreviewUrls.disabled + }); + return; + } + + // check our global disable override + var userRoomPreviewUrls = MatrixClientPeg().get().getAccountData("org.matrix.preview_urls"); + if (userRoomPreviewUrls && userRoomPreviewUrls.disabled) { + this.setState({ + showUrlPreview: false + }); + return; + } + + // check the room state event + var roomStatePreviewUrls = room.currentState.getStateEvents('org.matrix.room.preview_urls', ''); + if (roomStatePreviewUrls && roomStatePreviewUrls.disabled) { + this.setState({ + showUrlPreview: false; + }); + return; + } + + // otherwise, we assume they're on. + this.setState({ + showUrlPreview: true; + }); + }, + onRoom: function(room) { // This event is fired when the room is 'stored' by the JS SDK, which // means it's now a fully-fledged room object ready to be used, so @@ -416,12 +456,15 @@ module.exports = React.createClass({ onRoomAccountData: function(room, event) { if (room.roomId == this.props.roomId) { - if (event.getType === "org.matrix.room.color_scheme") { + if (event.getType() === "org.matrix.room.color_scheme") { var color_scheme = event.getContent(); // XXX: we should validate the event console.log("Tinter.tint from onRoomAccountData"); Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color); } + else if (event.getType() === "org.matrix.room.preview_urls") { + _updatePreviewUrlVisibility(room); + } } }, @@ -1523,6 +1566,7 @@ module.exports = React.createClass({ eventPixelOffset={this.props.eventPixelOffset} onScroll={ this.onMessageListScroll } onReadMarkerUpdated={ this._updateTopUnreadMessagesBar } + showUrlPreview = { this.state.showUrlPreview } opacity={ this.props.opacity } />); diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index d804dfd6b9..52225c7c09 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -71,6 +71,9 @@ var TimelinePanel = React.createClass({ // half way down the viewport. eventPixelOffset: React.PropTypes.number, + // Should we show URL Previews + showUrlPreview: React.PropTypes.bool, + // callback which is called when the panel is scrolled. onScroll: React.PropTypes.func, @@ -934,6 +937,7 @@ var TimelinePanel = React.createClass({ readMarkerEventId={ this.state.readMarkerEventId } readMarkerVisible={ this.state.readMarkerVisible } suppressFirstDateSeparator={ this.state.canBackPaginate } + showUrlPreview = { this.state.showUrlPreview } ourUserId={ MatrixClientPeg.get().credentials.userId } stickyBottom={ stickyBottom } onScroll={ this.onMessageListScroll } diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 7fcb81a60c..41ac03725b 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -261,6 +261,63 @@ module.exports = React.createClass({ }); }, + _renderUserInterfaceSettings: function() { + var client = MatrixClientPeg.get(); + + var settingsLabels = [ + /* + { + id: 'alwaysShowTimestamps', + label: 'Always show message timestamps', + }, + { + id: 'showTwelveHourTimestamps', + label: 'Show timestamps in 12 hour format (e.g. 2:30pm)', + }, + { + id: 'useCompactLayout', + label: 'Use compact timeline layout', + }, + { + id: 'useFixedWidthFont', + label: 'Use fixed width font', + }, + */ + ]; + + var syncedSettings = UserSettingsStore.getSyncedSettings(); + + return ( +
+

User Interface

+
+
+ UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) } + /> + +
+
+ { settingsLabels.forEach( setting => { +
+ UserSettingsStore.setSyncedSetting(setting.id, e.target.checked) } + /> + +
+ })} +
+ ); + }, + _renderDeviceInfo: function() { if (!UserSettingsStore.isFeatureEnabled("e2e_encryption")) { return null; @@ -378,7 +435,7 @@ module.exports = React.createClass({ this._renderLabs = function () { let features = LABS_FEATURES.map(feature => ( -
+
; }, }); diff --git a/src/components/views/messages/TextualBody.js b/src/components/views/messages/TextualBody.js index 310da598fa..2268affbe2 100644 --- a/src/components/views/messages/TextualBody.js +++ b/src/components/views/messages/TextualBody.js @@ -39,6 +39,9 @@ module.exports = React.createClass({ /* link URL for the highlights */ highlightLink: React.PropTypes.string, + /* should show URL previews for this event */ + showUrlPreview: React.PropTypes.bool, + /* callback for when our widget has loaded */ onWidgetLoad: React.PropTypes.func, }, @@ -57,16 +60,18 @@ module.exports = React.createClass({ componentDidMount: function() { linkifyElement(this.refs.content, linkifyMatrix.options); - var links = this.findLinks(this.refs.content.children); - if (links.length) { - this.setState({ links: links.map((link)=>{ - return link.getAttribute("href"); - })}); + if (this.props.showUrlPreview) { + var links = this.findLinks(this.refs.content.children); + if (links.length) { + this.setState({ links: links.map((link)=>{ + return link.getAttribute("href"); + })}); - // lazy-load the hidden state of the preview widget from localstorage - if (global.localStorage) { - var hidden = global.localStorage.getItem("hide_preview_" + this.props.mxEvent.getId()); - this.setState({ widgetHidden: hidden }); + // lazy-load the hidden state of the preview widget from localstorage + if (global.localStorage) { + var hidden = global.localStorage.getItem("hide_preview_" + this.props.mxEvent.getId()); + this.setState({ widgetHidden: hidden }); + } } } @@ -163,9 +168,11 @@ module.exports = React.createClass({ render: function() { var mxEvent = this.props.mxEvent; var content = mxEvent.getContent(); - var body = HtmlUtils.bodyToHtml(content, this.props.highlights, - {highlightLink: this.props.highlightLink}); + var body = HtmlUtils.bodyToHtml(content, this.props.highlights, {}); + if (this.props.highlightLink) { + body = { body }; + } var widgets; if (this.state.links.length && !this.state.widgetHidden) { diff --git a/src/components/views/room_settings/ColorSettings.js b/src/components/views/room_settings/ColorSettings.js index fff97ea817..6d147b1f63 100644 --- a/src/components/views/room_settings/ColorSettings.js +++ b/src/components/views/room_settings/ColorSettings.js @@ -57,7 +57,7 @@ module.exports = React.createClass({ data.primary_color = scheme.primary_color; data.secondary_color = scheme.secondary_color; data.index = this._getColorIndex(data); - + if (data.index === -1) { // append the unrecognised colours to our palette data.index = ROOM_COLORS.length; diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 70dfe8ac33..a914d513ac 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -101,6 +101,9 @@ module.exports = React.createClass({ /* link URL for the highlights */ highlightLink: React.PropTypes.string, + /* should show URL previews for this event */ + showUrlPreview: React.PropTypes.bool, + /* is this the focused event */ isSelectedEvent: React.PropTypes.bool, @@ -420,6 +423,7 @@ module.exports = React.createClass({
diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 1f50a9241b..ee8b716e02 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -422,6 +422,7 @@ module.exports = React.createClass({ var AliasSettings = sdk.getComponent("room_settings.AliasSettings"); var ColorSettings = sdk.getComponent("room_settings.ColorSettings"); + var UrlPreviewSettings = sdk.getComponent("room_settings.UrlPreviewSettings"); var EditableText = sdk.getComponent('elements.EditableText'); var PowerSelector = sdk.getComponent('elements.PowerSelector'); @@ -654,6 +655,8 @@ module.exports = React.createClass({ canonicalAliasEvent={this.props.room.currentState.getStateEvents('m.room.canonical_alias', '')} aliasEvents={this.props.room.currentState.getStateEvents('m.room.aliases')} /> + +

Permissions