first cut (untested)

This commit is contained in:
Matthew Hodgson 2016-07-18 01:35:42 +01:00
parent bcd1c7e099
commit ebdac4ee50
10 changed files with 171 additions and 14 deletions

View file

@ -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 { isFeatureEnabled: function(feature: string): boolean {
return localStorage.getItem(`mx_labs_feature_${feature}`) === 'true'; return localStorage.getItem(`mx_labs_feature_${feature}`) === 'true';
}, },

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

@ -239,6 +239,8 @@ module.exports = React.createClass({
MatrixClientPeg.get().stopPeeking(); MatrixClientPeg.get().stopPeeking();
this._onRoomLoaded(this.state.room); this._onRoomLoaded(this.state.room);
} }
_updatePreviewUrlVisibility(this.state.room);
}, },
shouldComponentUpdate: function(nextProps, nextState) { shouldComponentUpdate: function(nextProps, nextState) {
@ -341,6 +343,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 (event.getType() === "org.matrix.room.preview_urls") {
_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;
@ -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) { 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
@ -416,12 +456,15 @@ module.exports = React.createClass({
onRoomAccountData: function(room, event) { onRoomAccountData: function(room, event) {
if (room.roomId == this.props.roomId) { 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(); 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") {
_updatePreviewUrlVisibility(room);
}
} }
}, },
@ -1523,6 +1566,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.state.showUrlPreview }
ourUserId={ MatrixClientPeg.get().credentials.userId } ourUserId={ MatrixClientPeg.get().credentials.userId }
stickyBottom={ stickyBottom } stickyBottom={ stickyBottom }
onScroll={ this.onMessageListScroll } onScroll={ this.onMessageListScroll }

View file

@ -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 (
<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;
@ -378,7 +435,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}
@ -452,6 +509,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,
}, },
@ -57,6 +60,7 @@ module.exports = React.createClass({
componentDidMount: function() { componentDidMount: function() {
linkifyElement(this.refs.content, linkifyMatrix.options); linkifyElement(this.refs.content, linkifyMatrix.options);
if (this.props.showUrlPreview) {
var links = this.findLinks(this.refs.content.children); var links = this.findLinks(this.refs.content.children);
if (links.length) { if (links.length) {
this.setState({ links: links.map((link)=>{ this.setState({ links: links.map((link)=>{
@ -69,6 +73,7 @@ module.exports = React.createClass({
this.setState({ widgetHidden: hidden }); 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));
@ -163,9 +168,11 @@ 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) {

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,
@ -420,6 +423,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

@ -422,6 +422,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');
@ -654,6 +655,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">