From bcd1c7e0997aab09c91f51463ddb7b536938d1d4 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 18 Jul 2016 01:34:26 +0100 Subject: [PATCH 1/6] improve comment --- src/HtmlUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 8b3a368f4d..2ab635081f 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -186,7 +186,7 @@ module.exports = { * * 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) { opts = opts || {}; From ebdac4ee50a408b6876a94982475ca2f73466fb3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 18 Jul 2016 01:35:42 +0100 Subject: [PATCH 2/6] 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

From f13bb5f6562080250c5947637730e23fdf503879 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 18 Jul 2016 01:39:24 +0100 Subject: [PATCH 3/6] typos --- src/components/structures/RoomView.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 7a27340cc6..35217ab2f4 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -413,14 +413,14 @@ module.exports = React.createClass({ var roomStatePreviewUrls = room.currentState.getStateEvents('org.matrix.room.preview_urls', ''); if (roomStatePreviewUrls && roomStatePreviewUrls.disabled) { this.setState({ - showUrlPreview: false; + showUrlPreview: false }); return; } // otherwise, we assume they're on. this.setState({ - showUrlPreview: true; + showUrlPreview: true }); }, From e92024f7a9ecdfc06d83a05bab0b14ff1693386f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 18 Jul 2016 10:42:18 +0100 Subject: [PATCH 4/6] reskindex --- src/component-index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/component-index.js b/src/component-index.js index 4aa0efe21f..5fadb18b6a 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -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.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.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.EntityTile'] = require('./components/views/rooms/EntityTile'); module.exports.components['views.rooms.EventTile'] = require('./components/views/rooms/EventTile'); From 6bf54992895a0e768e4a453a25b5d5438e9d3317 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 18 Jul 2016 10:47:03 +0100 Subject: [PATCH 5/6] typos --- src/components/structures/RoomView.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 35217ab2f4..ea73193b03 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -240,7 +240,7 @@ module.exports = React.createClass({ this._onRoomLoaded(this.state.room); } - _updatePreviewUrlVisibility(this.state.room); + this._updatePreviewUrlVisibility(this.state.room); }, shouldComponentUpdate: function(nextProps, nextState) { @@ -343,8 +343,8 @@ 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); + if (ev.getType() === "org.matrix.room.preview_urls") { + this._updatePreviewUrlVisibility(room); } // ignore anything but real-time updates at the end of the room: From 1365f188294b67ed88c01faa43a8ce0640d6bfd4 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 20 Jul 2016 12:03:13 +0100 Subject: [PATCH 6/6] many stupid thinkos and bugfixes; make it work --- src/UserSettingsStore.js | 5 +- src/components/structures/RoomView.js | 23 +-- src/components/structures/TimelinePanel.js | 2 +- src/components/views/messages/TextualBody.js | 41 +++-- .../views/room_settings/UrlPreviewSettings.js | 157 ++++++++++++++++++ src/components/views/rooms/EventTile.js | 2 + src/components/views/rooms/RoomSettings.js | 10 +- 7 files changed, 211 insertions(+), 29 deletions(-) create mode 100644 src/components/views/room_settings/UrlPreviewSettings.js diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index 990fa8b7a9..39f393b242 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -115,7 +115,7 @@ module.exports = { getUrlPreviewsDisabled: function() { var event = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls"); - return (event && event.disable); + return (event && event.getContent().disable); }, setUrlPreviewsDisabled: function(disabled) { @@ -126,7 +126,8 @@ module.exports = { }, getSyncedSettings: function() { - return MatrixClientPeg.get().getAccountData("im.vector.web.settings") || {}; + var event = MatrixClientPeg.get().getAccountData("im.vector.web.settings"); + return event ? event.getContent() : {}; }, getSyncedSetting: function(type) { diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index ea73193b03..7b5b3f7c7f 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -239,8 +239,6 @@ module.exports = React.createClass({ MatrixClientPeg.get().stopPeeking(); this._onRoomLoaded(this.state.room); } - - this._updatePreviewUrlVisibility(this.state.room); }, shouldComponentUpdate: function(nextProps, nextState) { @@ -372,6 +370,7 @@ module.exports = React.createClass({ // after a successful peek, or after we join the room). _onRoomLoaded: function(room) { this._calculatePeekRules(room); + this._updatePreviewUrlVisibility(room); }, _calculatePeekRules: function(room) { @@ -391,18 +390,20 @@ 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.disabled !== undefined) { + if (roomPreviewUrls && roomPreviewUrls.getContent().disable !== undefined) { this.setState({ - showUrlPreview: !roomPreviewUrls.disabled + showUrlPreview: !roomPreviewUrls.getContent().disable }); return; } // check our global disable override - var userRoomPreviewUrls = MatrixClientPeg().get().getAccountData("org.matrix.preview_urls"); - if (userRoomPreviewUrls && userRoomPreviewUrls.disabled) { + var userRoomPreviewUrls = MatrixClientPeg.get().getAccountData("org.matrix.preview_urls"); + if (userRoomPreviewUrls && userRoomPreviewUrls.getContent().disable) { this.setState({ showUrlPreview: false }); @@ -411,7 +412,7 @@ module.exports = React.createClass({ // check the room state event var roomStatePreviewUrls = room.currentState.getStateEvents('org.matrix.room.preview_urls', ''); - if (roomStatePreviewUrls && roomStatePreviewUrls.disabled) { + if (roomStatePreviewUrls && roomStatePreviewUrls.getContent().disable) { this.setState({ showUrlPreview: false }); @@ -454,8 +455,8 @@ module.exports = React.createClass({ Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color); }, - onRoomAccountData: function(room, event) { - if (room.roomId == this.props.roomId) { + onRoomAccountData: function(event, room) { + if (room.roomId == this.state.roomId) { if (event.getType() === "org.matrix.room.color_scheme") { var color_scheme = event.getContent(); // XXX: we should validate the event @@ -463,7 +464,7 @@ module.exports = React.createClass({ Tinter.tint(color_scheme.primary_color, color_scheme.secondary_color); } else if (event.getType() === "org.matrix.room.preview_urls") { - _updatePreviewUrlVisibility(room); + this._updatePreviewUrlVisibility(room); } } }, @@ -1557,6 +1558,8 @@ module.exports = React.createClass({ hideMessagePanel = true; } + console.log("ShowUrlPreview for %s is %s", this.state.room.roomId, this.state.showUrlPreview); + var messagePanel = ( { @@ -74,19 +98,6 @@ module.exports = React.createClass({ } } } - - if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") - HtmlUtils.highlightDom(ReactDOM.findDOMNode(this)); - }, - - shouldComponentUpdate: function(nextProps, nextState) { - // 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() || - nextProps.highlights !== this.props.highlights || - nextProps.highlightLink !== this.props.highlightLink || - nextState.links !== this.state.links || - nextState.widgetHidden !== this.state.widgetHidden); }, findLinks: function(nodes) { @@ -175,7 +186,7 @@ module.exports = React.createClass({ } 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'); widgets = this.state.links.map((link)=>{ return + + Disable URL previews by default for participants in this room + + } + else { + disableRoomPreviewUrls = + + } + + return ( +
+

URL Previews

+ + + { disableRoomPreviewUrls } + + +
+ ); + + } +}); \ No newline at end of file diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index a914d513ac..77be8226a2 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -362,6 +362,8 @@ module.exports = React.createClass({ var SenderProfile = sdk.getComponent('messages.SenderProfile'); 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 msgtype = content.msgtype; diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index ee8b716e02..fda29a38f0 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -211,10 +211,13 @@ module.exports = React.createClass({ // color scheme promises.push(this.saveColor()); + // url preview settings + promises.push(this.saveUrlPreviewSettings()); + // encryption 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); }, @@ -228,6 +231,11 @@ module.exports = React.createClass({ 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 () { if (!this.refs.encrypt) { return q(); }