Merge pull request #356 from matrix-org/matthew/disable-url-previews

Support for disabling/enabling URL previews per-user, per-room and per-user-per-room
This commit is contained in:
David Baker 2016-07-20 16:43:08 +01:00 committed by GitHub
commit fb964be072
13 changed files with 365 additions and 24 deletions

View file

@ -186,7 +186,7 @@ module.exports = {
* *
* highlights: optional list of words to highlight, ordered by longest word first * highlights: optional list of words to highlight, ordered by longest word first
* *
* opts.highlightLink: optional href to add to highlights * opts.highlightLink: optional href to add to highlighted words
*/ */
bodyToHtml: function(content, highlights, opts) { bodyToHtml: function(content, highlights, opts) {
opts = opts || {}; opts = opts || {};

View file

@ -113,6 +113,35 @@ module.exports = {
}); });
}, },
getUrlPreviewsDisabled: function() {
var event = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls");
return (event && event.getContent().disable);
},
setUrlPreviewsDisabled: function(disabled) {
// FIXME: handle errors
MatrixClientPeg.get().setAccountData("org.matrix.preview_urls", {
disable: disabled
});
},
getSyncedSettings: function() {
var event = MatrixClientPeg.get().getAccountData("im.vector.web.settings");
return event ? event.getContent() : {};
},
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 { isFeatureEnabled: function(feature: string): boolean {
return localStorage.getItem(`mx_labs_feature_${feature}`) === 'true'; return localStorage.getItem(`mx_labs_feature_${feature}`) === 'true';
}, },

View file

@ -74,6 +74,8 @@ module.exports.components['views.messages.TextualEvent'] = require('./components
module.exports.components['views.messages.UnknownBody'] = require('./components/views/messages/UnknownBody'); module.exports.components['views.messages.UnknownBody'] = require('./components/views/messages/UnknownBody');
module.exports.components['views.room_settings.AliasSettings'] = require('./components/views/room_settings/AliasSettings'); module.exports.components['views.room_settings.AliasSettings'] = require('./components/views/room_settings/AliasSettings');
module.exports.components['views.room_settings.ColorSettings'] = require('./components/views/room_settings/ColorSettings'); module.exports.components['views.room_settings.ColorSettings'] = require('./components/views/room_settings/ColorSettings');
module.exports.components['views.room_settings.UrlPreviewSettings'] = require('./components/views/room_settings/UrlPreviewSettings');
module.exports.components['views.rooms.Autocomplete'] = require('./components/views/rooms/Autocomplete');
module.exports.components['views.rooms.AuxPanel'] = require('./components/views/rooms/AuxPanel'); module.exports.components['views.rooms.AuxPanel'] = require('./components/views/rooms/AuxPanel');
module.exports.components['views.rooms.EntityTile'] = require('./components/views/rooms/EntityTile'); 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.EventTile'] = require('./components/views/rooms/EventTile');

View file

@ -44,6 +44,9 @@ module.exports = React.createClass({
// ID of an event to highlight. If undefined, no event will be highlighted. // ID of an event to highlight. If undefined, no event will be highlighted.
highlightedEventId: React.PropTypes.string, highlightedEventId: React.PropTypes.string,
// Should we show URL Previews
showUrlPreview: React.PropTypes.bool,
// event after which we should show a read marker // event after which we should show a read marker
readMarkerEventId: React.PropTypes.string, readMarkerEventId: React.PropTypes.string,
@ -365,6 +368,7 @@ module.exports = React.createClass({
onWidgetLoad={this._onWidgetLoad} onWidgetLoad={this._onWidgetLoad}
readReceipts={readReceipts} readReceipts={readReceipts}
readReceiptMap={this._readReceiptMap} readReceiptMap={this._readReceiptMap}
showUrlPreview={this.props.showUrlPreview}
checkUnmounting={this._isUnmounting} checkUnmounting={this._isUnmounting}
eventSendStatus={mxEv.status} eventSendStatus={mxEv.status}
last={last} isSelectedEvent={highlight}/> last={last} isSelectedEvent={highlight}/>

View file

@ -338,6 +338,10 @@ module.exports = React.createClass({
// ignore events for other rooms // ignore events for other rooms
if (!this.state.room || room.roomId != this.state.room.roomId) return; if (!this.state.room || room.roomId != this.state.room.roomId) return;
if (ev.getType() === "org.matrix.room.preview_urls") {
this._updatePreviewUrlVisibility(room);
}
// ignore anything but real-time updates at the end of the room: // ignore anything but real-time updates at the end of the room:
// updates from pagination will happen when the paginate completes. // updates from pagination will happen when the paginate completes.
if (toStartOfTimeline || !data || !data.liveEvent) return; if (toStartOfTimeline || !data || !data.liveEvent) return;
@ -371,6 +375,7 @@ module.exports = React.createClass({
// after a successful peek, or after we join the room). // after a successful peek, or after we join the room).
_onRoomLoaded: function(room) { _onRoomLoaded: function(room) {
this._calculatePeekRules(room); this._calculatePeekRules(room);
this._updatePreviewUrlVisibility(room);
}, },
_calculatePeekRules: function(room) { _calculatePeekRules: function(room) {
@ -389,6 +394,42 @@ module.exports = React.createClass({
} }
}, },
_updatePreviewUrlVisibility: function(room) {
console.log("_updatePreviewUrlVisibility");
// check our per-room overrides
var roomPreviewUrls = room.getAccountData("org.matrix.room.preview_urls");
if (roomPreviewUrls && roomPreviewUrls.getContent().disable !== undefined) {
this.setState({
showUrlPreview: !roomPreviewUrls.getContent().disable
});
return;
}
// check our global disable override
var userRoomPreviewUrls = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls");
if (userRoomPreviewUrls && userRoomPreviewUrls.getContent().disable) {
this.setState({
showUrlPreview: false
});
return;
}
// check the room state event
var roomStatePreviewUrls = room.currentState.getStateEvents('org.matrix.room.preview_urls', '');
if (roomStatePreviewUrls && roomStatePreviewUrls.getContent().disable) {
this.setState({
showUrlPreview: false
});
return;
}
// otherwise, we assume they're on.
this.setState({
showUrlPreview: true
});
},
onRoom: function(room) { onRoom: function(room) {
// This event is fired when the room is 'stored' by the JS SDK, which // 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 // means it's now a fully-fledged room object ready to be used, so
@ -419,14 +460,17 @@ module.exports = React.createClass({
Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color); Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
}, },
onRoomAccountData: function(room, event) { onRoomAccountData: function(event, room) {
if (room.roomId == this.props.roomId) { if (room.roomId == this.state.roomId) {
if (event.getType === "org.matrix.room.color_scheme") { if (event.getType() === "org.matrix.room.color_scheme") {
var color_scheme = event.getContent(); var color_scheme = event.getContent();
// XXX: we should validate the event // XXX: we should validate the event
console.log("Tinter.tint from onRoomAccountData"); console.log("Tinter.tint from onRoomAccountData");
Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color); Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color);
} }
else if (event.getType() === "org.matrix.room.preview_urls") {
this._updatePreviewUrlVisibility(room);
}
} }
}, },
@ -1506,6 +1550,8 @@ module.exports = React.createClass({
hideMessagePanel = true; hideMessagePanel = true;
} }
console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview);
var messagePanel = ( var messagePanel = (
<TimelinePanel ref={this._gatherTimelinePanelRef} <TimelinePanel ref={this._gatherTimelinePanelRef}
room={this.state.room} room={this.state.room}
@ -1515,6 +1561,7 @@ module.exports = React.createClass({
eventPixelOffset={this.props.eventPixelOffset} eventPixelOffset={this.props.eventPixelOffset}
onScroll={ this.onMessageListScroll } onScroll={ this.onMessageListScroll }
onReadMarkerUpdated={ this._updateTopUnreadMessagesBar } onReadMarkerUpdated={ this._updateTopUnreadMessagesBar }
showUrlPreview = { this.state.showUrlPreview }
opacity={ this.props.opacity } opacity={ this.props.opacity }
/>); />);

View file

@ -71,6 +71,9 @@ var TimelinePanel = React.createClass({
// half way down the viewport. // half way down the viewport.
eventPixelOffset: React.PropTypes.number, eventPixelOffset: React.PropTypes.number,
// Should we show URL Previews
showUrlPreview: React.PropTypes.bool,
// callback which is called when the panel is scrolled. // callback which is called when the panel is scrolled.
onScroll: React.PropTypes.func, onScroll: React.PropTypes.func,
@ -934,6 +937,7 @@ var TimelinePanel = React.createClass({
readMarkerEventId={ this.state.readMarkerEventId } readMarkerEventId={ this.state.readMarkerEventId }
readMarkerVisible={ this.state.readMarkerVisible } readMarkerVisible={ this.state.readMarkerVisible }
suppressFirstDateSeparator={ this.state.canBackPaginate } suppressFirstDateSeparator={ this.state.canBackPaginate }
showUrlPreview = { this.props.showUrlPreview }
ourUserId={ MatrixClientPeg.get().credentials.userId } ourUserId={ MatrixClientPeg.get().credentials.userId }
stickyBottom={ stickyBottom } stickyBottom={ stickyBottom }
onScroll={ this.onMessageListScroll } onScroll={ this.onMessageListScroll }

View file

@ -262,6 +262,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 (
<div>
<h3>User Interface</h3>
<div className="mx_UserSettings_section">
<div className="mx_UserSettings_toggle">
<input id="urlPreviewsDisabled"
type="checkbox"
defaultChecked={ UserSettingsStore.getUrlPreviewsDisabled() }
onChange={ e => UserSettingsStore.setUrlPreviewsDisabled(e.target.checked) }
/>
<label htmlFor="urlPreviewsDisabled">
Disable inline URL previews by default
</label>
</div>
</div>
{ settingsLabels.forEach( setting => {
<div className="mx_UserSettings_toggle">
<input id={ setting.id }
type="checkbox"
defaultChecked={ syncedSettings[setting.id] }
onChange={ e => UserSettingsStore.setSyncedSetting(setting.id, e.target.checked) }
/>
<label htmlFor={ setting.id }>
{ settings.label }
</label>
</div>
})}
</div>
);
},
_renderDeviceInfo: function() { _renderDeviceInfo: function() {
if (!UserSettingsStore.isFeatureEnabled("e2e_encryption")) { if (!UserSettingsStore.isFeatureEnabled("e2e_encryption")) {
return null; return null;
@ -379,7 +436,7 @@ module.exports = React.createClass({
this._renderLabs = function () { this._renderLabs = function () {
let features = LABS_FEATURES.map(feature => ( let features = LABS_FEATURES.map(feature => (
<div key={feature.id}> <div key={feature.id} className="mx_UserSettings_toggle">
<input <input
type="checkbox" type="checkbox"
id={feature.id} id={feature.id}
@ -453,6 +510,8 @@ module.exports = React.createClass({
{notification_area} {notification_area}
{this._renderUserInterfaceSettings()}
{this._renderDeviceInfo()} {this._renderDeviceInfo()}
{this._renderLabs()} {this._renderLabs()}

View file

@ -38,6 +38,9 @@ module.exports = React.createClass({
/* link URL for the highlights */ /* link URL for the highlights */
highlightLink: React.PropTypes.string, highlightLink: React.PropTypes.string,
/* should show URL previews for this event */
showUrlPreview: React.PropTypes.bool,
/* callback called when dynamic content in events are loaded */ /* callback called when dynamic content in events are loaded */
onWidgetLoad: React.PropTypes.func, onWidgetLoad: React.PropTypes.func,
}, },
@ -71,6 +74,7 @@ module.exports = React.createClass({
return <BodyType ref="body" mxEvent={this.props.mxEvent} highlights={this.props.highlights} return <BodyType ref="body" mxEvent={this.props.mxEvent} highlights={this.props.highlights}
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview}
onWidgetLoad={this.props.onWidgetLoad} />; onWidgetLoad={this.props.onWidgetLoad} />;
}, },
}); });

View file

@ -39,6 +39,9 @@ module.exports = React.createClass({
/* link URL for the highlights */ /* link URL for the highlights */
highlightLink: React.PropTypes.string, highlightLink: React.PropTypes.string,
/* should show URL previews for this event */
showUrlPreview: React.PropTypes.bool,
/* callback for when our widget has loaded */ /* callback for when our widget has loaded */
onWidgetLoad: React.PropTypes.func, onWidgetLoad: React.PropTypes.func,
}, },
@ -56,34 +59,47 @@ module.exports = React.createClass({
componentDidMount: function() { componentDidMount: function() {
linkifyElement(this.refs.content, linkifyMatrix.options); linkifyElement(this.refs.content, linkifyMatrix.options);
this.calculateUrlPreview();
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 });
}
}
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") if (this.props.mxEvent.getContent().format === "org.matrix.custom.html")
HtmlUtils.highlightDom(ReactDOM.findDOMNode(this)); HtmlUtils.highlightDom(ReactDOM.findDOMNode(this));
}, },
componentDidUpdate: function() {
this.calculateUrlPreview();
},
shouldComponentUpdate: function(nextProps, nextState) { shouldComponentUpdate: function(nextProps, nextState) {
//console.log("shouldComponentUpdate: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
// exploit that events are immutable :) // exploit that events are immutable :)
// ...and that .links is only ever set in componentDidMount and never changes
return (nextProps.mxEvent.getId() !== this.props.mxEvent.getId() || return (nextProps.mxEvent.getId() !== this.props.mxEvent.getId() ||
nextProps.highlights !== this.props.highlights || nextProps.highlights !== this.props.highlights ||
nextProps.highlightLink !== this.props.highlightLink || nextProps.highlightLink !== this.props.highlightLink ||
nextProps.showUrlPreview !== this.props.showUrlPreview ||
nextState.links !== this.state.links || nextState.links !== this.state.links ||
nextState.widgetHidden !== this.state.widgetHidden); nextState.widgetHidden !== this.state.widgetHidden);
}, },
calculateUrlPreview: function() {
//console.log("calculateUrlPreview: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
if (this.props.showUrlPreview && !this.state.links.length) {
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 });
}
}
}
},
findLinks: function(nodes) { findLinks: function(nodes) {
var links = []; var links = [];
for (var i = 0; i < nodes.length; i++) { for (var i = 0; i < nodes.length; i++) {
@ -163,12 +179,14 @@ module.exports = React.createClass({
render: function() { render: function() {
var mxEvent = this.props.mxEvent; var mxEvent = this.props.mxEvent;
var content = mxEvent.getContent(); var content = mxEvent.getContent();
var body = HtmlUtils.bodyToHtml(content, this.props.highlights, var body = HtmlUtils.bodyToHtml(content, this.props.highlights, {});
{highlightLink: this.props.highlightLink});
if (this.props.highlightLink) {
body = <a href={ this.props.highlightLink }>{ body }</a>;
}
var widgets; var widgets;
if (this.state.links.length && !this.state.widgetHidden) { if (this.state.links.length && !this.state.widgetHidden && this.props.showUrlPreview) {
var LinkPreviewWidget = sdk.getComponent('rooms.LinkPreviewWidget'); var LinkPreviewWidget = sdk.getComponent('rooms.LinkPreviewWidget');
widgets = this.state.links.map((link)=>{ widgets = this.state.links.map((link)=>{
return <LinkPreviewWidget return <LinkPreviewWidget

View file

@ -57,7 +57,7 @@ module.exports = React.createClass({
data.primary_color = scheme.primary_color; data.primary_color = scheme.primary_color;
data.secondary_color = scheme.secondary_color; data.secondary_color = scheme.secondary_color;
data.index = this._getColorIndex(data); data.index = this._getColorIndex(data);
if (data.index === -1) { if (data.index === -1) {
// append the unrecognised colours to our palette // append the unrecognised colours to our palette
data.index = ROOM_COLORS.length; data.index = ROOM_COLORS.length;

View file

@ -0,0 +1,157 @@
/*
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.
*/
var q = require("q");
var React = require('react');
var MatrixClientPeg = require('../../../MatrixClientPeg');
var sdk = require("../../../index");
var Modal = require("../../../Modal");
var UserSettingsStore = require('../../../UserSettingsStore');
module.exports = React.createClass({
displayName: 'UrlPreviewSettings',
propTypes: {
room: React.PropTypes.object,
},
getInitialState: function() {
var cli = MatrixClientPeg.get();
var roomState = this.props.room.currentState;
var roomPreviewUrls = this.props.room.currentState.getStateEvents('org.matrix.room.preview_urls', '');
var userPreviewUrls = this.props.room.getAccountData("org.matrix.room.preview_urls");
return {
globalDisableUrlPreview: (roomPreviewUrls && roomPreviewUrls.getContent().disable) || false,
userDisableUrlPreview: (userPreviewUrls && (userPreviewUrls.getContent().disable === true)) || false,
userEnableUrlPreview: (userPreviewUrls && (userPreviewUrls.getContent().disable === false)) || false,
};
},
componentDidMount: function() {
this.originalState = Object.assign({}, this.state);
},
saveSettings: function() {
var promises = [];
if (this.state.globalDisableUrlPreview !== this.originalState.globalDisableUrlPreview) {
console.log("UrlPreviewSettings: Updating room's preview_urls state event");
promises.push(
MatrixClientPeg.get().sendStateEvent(
this.props.room.roomId, "org.matrix.room.preview_urls", {
disable: this.state.globalDisableUrlPreview
}, ""
)
);
}
var content = undefined;
if (this.state.userDisableUrlPreview !== this.originalState.userDisableUrlPreview) {
console.log("UrlPreviewSettings: Disabling user's per-room preview_urls");
content = this.state.userDisableUrlPreview ? { disable : true } : {};
}
if (this.state.userEnableUrlPreview !== this.originalState.userEnableUrlPreview) {
console.log("UrlPreviewSettings: Enabling user's per-room preview_urls");
if (!content || content.disable === undefined) {
content = this.state.userEnableUrlPreview ? { disable : false } : {};
}
}
if (content) {
promises.push(
MatrixClientPeg.get().setRoomAccountData(
this.props.room.roomId, "org.matrix.room.preview_urls", content
)
);
}
console.log("UrlPreviewSettings: saveSettings: " + JSON.stringify(promises));
return promises;
},
onGlobalDisableUrlPreviewChange: function() {
this.setState({
globalDisableUrlPreview: this.refs.globalDisableUrlPreview.checked ? true : false,
});
},
onUserEnableUrlPreviewChange: function() {
this.setState({
userDisableUrlPreview: false,
userEnableUrlPreview: this.refs.userEnableUrlPreview.checked ? true : false,
});
},
onUserDisableUrlPreviewChange: function() {
this.setState({
userDisableUrlPreview: this.refs.userDisableUrlPreview.checked ? true : false,
userEnableUrlPreview: false,
});
},
render: function() {
var self = this;
var roomState = this.props.room.currentState;
var cli = MatrixClientPeg.get();
var maySetRoomPreviewUrls = roomState.mayClientSendStateEvent('org.matrix.room.preview_urls', cli);
var disableRoomPreviewUrls;
if (maySetRoomPreviewUrls) {
disableRoomPreviewUrls =
<label>
<input type="checkbox" ref="globalDisableUrlPreview"
onChange={ this.onGlobalDisableUrlPreviewChange }
checked={ this.state.globalDisableUrlPreview } />
Disable URL previews by default for participants in this room
</label>
}
else {
disableRoomPreviewUrls =
<label>
URL previews are { this.state.globalDisableUrlPreview ? "disabled" : "enabled" } by default for participants in this room.
</label>
}
return (
<div className="mx_RoomSettings_toggles">
<h3>URL Previews</h3>
<label>
You have <a href="#/settings">{ UserSettingsStore.getUrlPreviewsDisabled() ? 'disabled' : 'enabled' }</a> URL previews by default.
</label>
{ disableRoomPreviewUrls }
<label>
<input type="checkbox" ref="userEnableUrlPreview"
onChange={ this.onUserEnableUrlPreviewChange }
checked={ this.state.userEnableUrlPreview } />
Enable URL previews for this room (affects only you)
</label>
<label>
<input type="checkbox" ref="userDisableUrlPreview"
onChange={ this.onUserDisableUrlPreviewChange }
checked={ this.state.userDisableUrlPreview } />
Disable URL previews for this room (affects only you)
</label>
</div>
);
}
});

View file

@ -101,6 +101,9 @@ module.exports = React.createClass({
/* link URL for the highlights */ /* link URL for the highlights */
highlightLink: React.PropTypes.string, highlightLink: React.PropTypes.string,
/* should show URL previews for this event */
showUrlPreview: React.PropTypes.bool,
/* is this the focused event */ /* is this the focused event */
isSelectedEvent: React.PropTypes.bool, isSelectedEvent: React.PropTypes.bool,
@ -359,6 +362,8 @@ module.exports = React.createClass({
var SenderProfile = sdk.getComponent('messages.SenderProfile'); var SenderProfile = sdk.getComponent('messages.SenderProfile');
var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); var MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
//console.log("EventTile showUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
var content = this.props.mxEvent.getContent(); var content = this.props.mxEvent.getContent();
var msgtype = content.msgtype; var msgtype = content.msgtype;
@ -420,6 +425,7 @@ module.exports = React.createClass({
<div className="mx_EventTile_line"> <div className="mx_EventTile_line">
<EventTileType ref="tile" mxEvent={this.props.mxEvent} highlights={this.props.highlights} <EventTileType ref="tile" mxEvent={this.props.mxEvent} highlights={this.props.highlights}
highlightLink={this.props.highlightLink} highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview}
onWidgetLoad={this.props.onWidgetLoad} /> onWidgetLoad={this.props.onWidgetLoad} />
</div> </div>
</div> </div>

View file

@ -216,10 +216,13 @@ module.exports = React.createClass({
// color scheme // color scheme
promises.push(this.saveColor()); promises.push(this.saveColor());
// url preview settings
promises.push(this.saveUrlPreviewSettings());
// encryption // encryption
promises.push(this.saveEncryption()); promises.push(this.saveEncryption());
console.log("Performing %s operations", promises.length); console.log("Performing %s operations: %s", promises.length, JSON.stringify(promises));
return q.allSettled(promises); return q.allSettled(promises);
}, },
@ -233,6 +236,11 @@ module.exports = React.createClass({
return this.refs.color_settings.saveSettings(); return this.refs.color_settings.saveSettings();
}, },
saveUrlPreviewSettings: function() {
if (!this.refs.url_preview_settings) { return q(); }
return this.refs.url_preview_settings.saveSettings();
},
saveEncryption: function () { saveEncryption: function () {
if (!this.refs.encrypt) { return q(); } if (!this.refs.encrypt) { return q(); }
@ -427,6 +435,7 @@ module.exports = React.createClass({
var AliasSettings = sdk.getComponent("room_settings.AliasSettings"); var AliasSettings = sdk.getComponent("room_settings.AliasSettings");
var ColorSettings = sdk.getComponent("room_settings.ColorSettings"); var ColorSettings = sdk.getComponent("room_settings.ColorSettings");
var UrlPreviewSettings = sdk.getComponent("room_settings.UrlPreviewSettings");
var EditableText = sdk.getComponent('elements.EditableText'); var EditableText = sdk.getComponent('elements.EditableText');
var PowerSelector = sdk.getComponent('elements.PowerSelector'); var PowerSelector = sdk.getComponent('elements.PowerSelector');
@ -659,6 +668,8 @@ module.exports = React.createClass({
canonicalAliasEvent={this.props.room.currentState.getStateEvents('m.room.canonical_alias', '')} canonicalAliasEvent={this.props.room.currentState.getStateEvents('m.room.canonical_alias', '')}
aliasEvents={this.props.room.currentState.getStateEvents('m.room.aliases')} /> aliasEvents={this.props.room.currentState.getStateEvents('m.room.aliases')} />
<UrlPreviewSettings ref="url_preview_settings" room={this.props.room} />
<h3>Permissions</h3> <h3>Permissions</h3>
<div className="mx_RoomSettings_powerLevels mx_RoomSettings_settings"> <div className="mx_RoomSettings_powerLevels mx_RoomSettings_settings">
<div className="mx_RoomSettings_powerLevel"> <div className="mx_RoomSettings_powerLevel">