From 99923b7b8f67b3d82df069db032611430443262d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 3 Apr 2017 20:30:05 +0100 Subject: [PATCH 01/56] Escape HTML tags in Notifications (Linux) Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/vector/platform/ElectronPlatform.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js index c10f2f83c03..ce14a22e32d 100644 --- a/src/vector/platform/ElectronPlatform.js +++ b/src/vector/platform/ElectronPlatform.js @@ -81,6 +81,15 @@ export default class ElectronPlatform extends VectorBasePlatform { } displayNotification(title: string, msg: string, avatarUrl: string, room: Object): Notification { + + // GNOME notification spec parses HTML tags for styling... + // Electron Docs state all supported linux notification systems follow this markup spec + // https://github.com/electron/electron/blob/master/docs/tutorial/desktop-environment-integration.md#linux + // maybe we should pass basic styling (italics, bold, underline) through from MD + if (window.process.platform === 'linux') { + msg = msg.replace(//g, ">"); + } + // Notifications in Electron use the HTML5 notification API const notification = new global.Notification( title, From ab1b377a1db736f98960d17205c9a0f89c059f53 Mon Sep 17 00:00:00 2001 From: daniel tygel Date: Mon, 10 Apr 2017 14:11:26 -0300 Subject: [PATCH 02/56] add config.json to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c28df64c650..0978bc5676f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ npm-debug.log electron/dist electron/pub +config.json From cbfa4dd1abc6f8081db32ca3b1b4941d8c9c41b3 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 11 Apr 2017 18:46:48 +0100 Subject: [PATCH 03/56] Get rageshake endpoint from SdkConfig instead of storing in rageshake - in preparation for factoring out the sending of the rageshake --- src/components/views/dialogs/BugReportDialog.js | 5 ++++- src/vector/index.js | 1 - src/vector/rageshake.js | 8 ++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js index dcc0850ef76..c03c9b43ec7 100644 --- a/src/components/views/dialogs/BugReportDialog.js +++ b/src/components/views/dialogs/BugReportDialog.js @@ -17,6 +17,7 @@ limitations under the License. import React from 'react'; import sdk from 'matrix-react-sdk'; import rageshake from '../../../vector/rageshake'; +import SdkConfig from 'matrix-react-sdk/lib/SdkConfig'; export default class BugReportDialog extends React.Component { constructor(props, context) { @@ -47,7 +48,9 @@ export default class BugReportDialog extends React.Component { return; } this.setState({ busy: true, err: null }); - rageshake.sendBugReport(userText, sendLogs).then(() => { + rageshake.sendBugReport( + SdkConfig.get().bug_report_endpoint_url, userText, sendLogs, + ).then(() => { this.setState({ busy: false }); this.props.onFinished(false); }, (err) => { diff --git a/src/vector/index.js b/src/vector/index.js index e08b717450f..42a60e455cc 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -259,7 +259,6 @@ async function loadApp() { let configError; try { configJson = await getConfig(); - rageshake.setBugReportEndpoint(configJson.bug_report_endpoint_url); } catch (e) { configError = e; } diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 0e5b5dace2c..c519f8b3ea4 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -396,7 +396,6 @@ function selectQuery(store, keyRange, resultMapper) { let store = null; let logger = null; let initPromise = null; -let bugReportEndpoint = null; module.exports = { /** @@ -430,17 +429,14 @@ module.exports = { await store.consume(); }, - setBugReportEndpoint: function(url) { - bugReportEndpoint = url; - }, - /** * Send a bug report. + * @param {string} bugReportEndpoint HTTP url to send the report to * @param {string} userText Any additional user input. * @param {boolean} sendLogs True to send logs * @return {Promise} Resolved when the bug report is sent. */ - sendBugReport: async function(userText, sendLogs) { + sendBugReport: async function(bugReportEndpoint, userText, sendLogs) { if (!logger) { throw new Error( "No console logger, did you forget to call init()?" From 4efb2b6750c62646f427f204d6c67c54502db8c9 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 11 Apr 2017 18:47:55 +0100 Subject: [PATCH 04/56] Rageshake: Factor out `getLogsForReport` ... in preparation for factoring out sending the report --- src/vector/rageshake.js | 46 +++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index c519f8b3ea4..711cc298c6e 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -429,6 +429,32 @@ module.exports = { await store.consume(); }, + /** + * Get a recent snapshot of the logs, ready for attaching to a bug report + * + * @return {Array<{lines: string, id, string}>} list of log data + */ + getLogsForReport: async function() { + if (!logger) { + throw new Error( + "No console logger, did you forget to call init()?" + ); + } + // If in incognito mode, store is null, but we still want bug report + // sending to work going off the in-memory console logs. + if (store) { + // flush most recent logs + await store.flush(); + return await store.consume(); + } + else { + return [{ + lines: logger.flush(true), + id: "-", + }]; + } + }, + /** * Send a bug report. * @param {string} bugReportEndpoint HTTP url to send the report to @@ -437,11 +463,6 @@ module.exports = { * @return {Promise} Resolved when the bug report is sent. */ sendBugReport: async function(bugReportEndpoint, userText, sendLogs) { - if (!logger) { - throw new Error( - "No console logger, did you forget to call init()?" - ); - } if (!bugReportEndpoint) { throw new Error("No bug report endpoint has been set."); } @@ -457,22 +478,11 @@ module.exports = { userAgent = window.navigator.userAgent; } - // If in incognito mode, store is null, but we still want bug report - // sending to work going off the in-memory console logs. console.log("Sending bug report."); + let logs = []; if (sendLogs) { - if (store) { - // flush most recent logs - await store.flush(); - logs = await store.consume(); - } - else { - logs.push({ - lines: logger.flush(true), - id: "-", - }); - } + logs = await this.getLogsForReport(); } await q.Promise((resolve, reject) => { From 6423f7ce03b68fd219fbd226def68a809e1c5494 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 11 Apr 2017 18:59:22 +0100 Subject: [PATCH 05/56] rageshake: factor out submission to a separate file This will mean we can load it asyncronously in future, if we want. --- .../views/dialogs/BugReportDialog.js | 4 +- src/vector/rageshake.js | 61 -------------- src/vector/submit-rageshake.js | 81 +++++++++++++++++++ 3 files changed, 83 insertions(+), 63 deletions(-) create mode 100644 src/vector/submit-rageshake.js diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js index c03c9b43ec7..62424cb1bc4 100644 --- a/src/components/views/dialogs/BugReportDialog.js +++ b/src/components/views/dialogs/BugReportDialog.js @@ -16,7 +16,7 @@ limitations under the License. import React from 'react'; import sdk from 'matrix-react-sdk'; -import rageshake from '../../../vector/rageshake'; +import submit_rageshake from '../../../vector/submit-rageshake'; import SdkConfig from 'matrix-react-sdk/lib/SdkConfig'; export default class BugReportDialog extends React.Component { @@ -48,7 +48,7 @@ export default class BugReportDialog extends React.Component { return; } this.setState({ busy: true, err: null }); - rageshake.sendBugReport( + submit_rageshake( SdkConfig.get().bug_report_endpoint_url, userText, sendLogs, ).then(() => { this.setState({ busy: false }); diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 711cc298c6e..feaec26dec4 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg'; -import request from "browser-request"; import q from "q"; // This module contains all the code needed to log the console, persist it to @@ -454,63 +452,4 @@ module.exports = { }]; } }, - - /** - * Send a bug report. - * @param {string} bugReportEndpoint HTTP url to send the report to - * @param {string} userText Any additional user input. - * @param {boolean} sendLogs True to send logs - * @return {Promise} Resolved when the bug report is sent. - */ - sendBugReport: async function(bugReportEndpoint, userText, sendLogs) { - if (!bugReportEndpoint) { - throw new Error("No bug report endpoint has been set."); - } - - let version = "UNKNOWN"; - try { - version = await PlatformPeg.get().getAppVersion(); - } - catch (err) {} // PlatformPeg already logs this. - - let userAgent = "UNKNOWN"; - if (window.navigator && window.navigator.userAgent) { - userAgent = window.navigator.userAgent; - } - - console.log("Sending bug report."); - - let logs = []; - if (sendLogs) { - logs = await this.getLogsForReport(); - } - - await q.Promise((resolve, reject) => { - request({ - method: "POST", - url: bugReportEndpoint, - body: { - logs: logs, - text: ( - userText || "User did not supply any additional text." - ), - app: 'riot-web', - version: version, - user_agent: userAgent, - }, - json: true, - timeout: 5 * 60 * 1000, - }, (err, res) => { - if (err) { - reject(err); - return; - } - if (res.status < 200 || res.status >= 400) { - reject(new Error(`HTTP ${res.status}`)); - return; - } - resolve(); - }) - }); - } }; diff --git a/src/vector/submit-rageshake.js b/src/vector/submit-rageshake.js new file mode 100644 index 00000000000..871888211c3 --- /dev/null +++ b/src/vector/submit-rageshake.js @@ -0,0 +1,81 @@ +/* +Copyright 2017 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. +*/ + +import q from "q"; +import request from "browser-request"; + +import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg'; + +import rageshake from './rageshake' + +/** + * Send a bug report. + * @param {string} bugReportEndpoint HTTP url to send the report to + * @param {string} userText Any additional user input. + * @param {boolean} sendLogs True to send logs + * @return {Promise} Resolved when the bug report is sent. + */ +export default async function sendBugReport(bugReportEndpoint, userText, sendLogs) { + if (!bugReportEndpoint) { + throw new Error("No bug report endpoint has been set."); + } + + let version = "UNKNOWN"; + try { + version = await PlatformPeg.get().getAppVersion(); + } + catch (err) {} // PlatformPeg already logs this. + + let userAgent = "UNKNOWN"; + if (window.navigator && window.navigator.userAgent) { + userAgent = window.navigator.userAgent; + } + + console.log("Sending bug report."); + + let logs = []; + if (sendLogs) { + logs = await rageshake.getLogsForReport(); + } + + await q.Promise((resolve, reject) => { + request({ + method: "POST", + url: bugReportEndpoint, + body: { + logs: logs, + text: ( + userText || "User did not supply any additional text." + ), + app: 'riot-web', + version: version, + user_agent: userAgent, + }, + json: true, + timeout: 5 * 60 * 1000, + }, (err, res) => { + if (err) { + reject(err); + return; + } + if (res.status < 200 || res.status >= 400) { + reject(new Error(`HTTP ${res.status}`)); + return; + } + resolve(); + }) + }); +} From 3f291aae5b01fe842757e1cce33dcc67025c3f68 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 12 Apr 2017 11:26:53 +0100 Subject: [PATCH 06/56] Use an opts arg for submit-rageshake --- src/components/views/dialogs/BugReportDialog.js | 7 ++++--- src/vector/submit-rageshake.js | 13 ++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js index 62424cb1bc4..badc994bb97 100644 --- a/src/components/views/dialogs/BugReportDialog.js +++ b/src/components/views/dialogs/BugReportDialog.js @@ -48,9 +48,10 @@ export default class BugReportDialog extends React.Component { return; } this.setState({ busy: true, err: null }); - submit_rageshake( - SdkConfig.get().bug_report_endpoint_url, userText, sendLogs, - ).then(() => { + submit_rageshake(SdkConfig.get().bug_report_endpoint_url, { + userText: userText, + sendLogs: sendLogs, + }).then(() => { this.setState({ busy: false }); this.props.onFinished(false); }, (err) => { diff --git a/src/vector/submit-rageshake.js b/src/vector/submit-rageshake.js index 871888211c3..8c070076980 100644 --- a/src/vector/submit-rageshake.js +++ b/src/vector/submit-rageshake.js @@ -24,15 +24,18 @@ import rageshake from './rageshake' /** * Send a bug report. * @param {string} bugReportEndpoint HTTP url to send the report to - * @param {string} userText Any additional user input. - * @param {boolean} sendLogs True to send logs + * @param {object} opts optional dictionary of options + * @param {string} opts.userText Any additional user input. + * @param {boolean} opts.sendLogs True to send logs * @return {Promise} Resolved when the bug report is sent. */ -export default async function sendBugReport(bugReportEndpoint, userText, sendLogs) { +export default async function sendBugReport(bugReportEndpoint, opts) { if (!bugReportEndpoint) { throw new Error("No bug report endpoint has been set."); } + opts = opts || {}; + let version = "UNKNOWN"; try { version = await PlatformPeg.get().getAppVersion(); @@ -47,7 +50,7 @@ export default async function sendBugReport(bugReportEndpoint, userText, sendLog console.log("Sending bug report."); let logs = []; - if (sendLogs) { + if (opts.sendLogs) { logs = await rageshake.getLogsForReport(); } @@ -58,7 +61,7 @@ export default async function sendBugReport(bugReportEndpoint, userText, sendLog body: { logs: logs, text: ( - userText || "User did not supply any additional text." + opts.userText || "User did not supply any additional text." ), app: 'riot-web', version: version, From a74bbb424c7c07494d71329e8d4c6ef9fc37a7b9 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 15 Apr 2017 11:37:09 +0100 Subject: [PATCH 07/56] cmd-k shortcut to the searchbox --- src/components/structures/SearchBox.js | 30 ++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/components/structures/SearchBox.js b/src/components/structures/SearchBox.js index 729e7ef772e..fb5beab1cd7 100644 --- a/src/components/structures/SearchBox.js +++ b/src/components/structures/SearchBox.js @@ -21,6 +21,7 @@ var sdk = require('matrix-react-sdk') var dis = require('matrix-react-sdk/lib/dispatcher'); var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc'); var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); +var KeyCode = require('matrix-react-sdk/lib/KeyCode'); module.exports = React.createClass({ displayName: 'SearchBox', @@ -38,10 +39,12 @@ module.exports = React.createClass({ componentDidMount: function() { this.dispatcherRef = dis.register(this.onAction); + document.addEventListener('keydown', this._onKeyDown); }, componentWillUnmount: function() { dis.unregister(this.dispatcherRef); + document.removeEventListener('keydown', this._onKeyDown); }, onAction: function(payload) { @@ -90,6 +93,33 @@ module.exports = React.createClass({ this.onChange(); }, + _onKeyDown: function(ev) { + let handled = false; + const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0; + let ctrlCmdOnly; + if (isMac) { + ctrlCmdOnly = ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey; + } else { + ctrlCmdOnly = ev.ctrlKey && !ev.altKey && !ev.metaKey && !ev.shiftKey; + } + + switch (ev.keyCode) { + case KeyCode.KEY_K: + if (ctrlCmdOnly) { + if (this.refs.search) { + this.refs.search.focus(); + } + handled = true; + } + break; + } + + if (handled) { + ev.stopPropagation(); + ev.preventDefault(); + } + }, + render: function() { var TintableSvg = sdk.getComponent('elements.TintableSvg'); From e5e259e1f8e403383f5051e148c76229b59e0440 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 15 Apr 2017 12:02:16 +0100 Subject: [PATCH 08/56] put a ! on invite sublists --- src/components/structures/RoomSubList.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 4aa9ff00521..de883932bb7 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -248,10 +248,9 @@ var RoomSubList = React.createClass({ if (badges) { result[0] += notificationCount; - if (highlight) { - result[1] = true; - } } + + result[1] |= highlight; } return result; }, [0, false]); @@ -403,6 +402,9 @@ var RoomSubList = React.createClass({ if (subListNotifCount > 0) { badge =
{ FormattingUtils.formatCount(subListNotifCount) }
; } + else if (subListNotifHighlight) { + badge =
{ ! }
; + } // When collapsed, allow a long hover on the header to show user // the full tag name and room count From 27de972bfbba85a853c69c800519404826595f68 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 15 Apr 2017 12:02:50 +0100 Subject: [PATCH 09/56] oops --- src/components/structures/RoomSubList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index de883932bb7..241a1efa3c0 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -403,7 +403,7 @@ var RoomSubList = React.createClass({ badge =
{ FormattingUtils.formatCount(subListNotifCount) }
; } else if (subListNotifHighlight) { - badge =
{ ! }
; + badge =
!
; } // When collapsed, allow a long hover on the header to show user From 8351ec97380350b6c8ca8c63b920120b33468fa9 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 15 Apr 2017 13:23:11 +0100 Subject: [PATCH 10/56] thread RoomTile focus events through RoomSubList up to RoomList --- src/components/structures/RoomSubList.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 241a1efa3c0..a0c9a5e16be 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -78,6 +78,7 @@ var RoomSubList = React.createClass({ showSpinner: React.PropTypes.bool, // true to show a spinner if 0 elements when expanded collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed? onHeaderClick: React.PropTypes.func, + onRoomTileFocus: React.PropTypes.func, alwaysShowHeader: React.PropTypes.bool, incomingCall: React.PropTypes.object, onShowMoreRooms: React.PropTypes.func, @@ -373,6 +374,7 @@ var RoomSubList = React.createClass({ refreshSubList={ self._updateSubListCount } incomingCall={ null } onClick={ self.onRoomTileClick } + onFocus={ self.props.onRoomTileFocus } /> ); }); From c6ee221ae4aaac5d37be994cf5afcf97de2b854e Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 16 Apr 2017 15:58:00 +0100 Subject: [PATCH 11/56] typos --- src/components/structures/RoomSubList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index a0c9a5e16be..a2d05d8a99c 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -28,7 +28,7 @@ var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs'); var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils'); var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); -// turn this on for drop & drag console debugging galore +// turn this on for drag & drop console debugging galore var debug = false; const TRUNCATE_AT = 10; @@ -502,7 +502,7 @@ var RoomSubList = React.createClass({ // gets triggered and another list is passed in. Doing it one at a time means that // we always correctly calculate the highest order for the list - stops multiple // rooms getting the same order. This is only really relevant for the first time this - // is run with historical room tag data, after that there should only be undefined + // is run with historical room tag data, after that there should only be one undefined // in the list at a time anyway. for (let i = 0; i < list.length; i++) { if (list[i].tags[self.props.tagName] && list[i].tags[self.props.tagName].order === undefined) { From cc7a58512639e78444f655039f7178ef02161614 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 17 Apr 2017 14:10:51 +0100 Subject: [PATCH 12/56] make ImageView Download work, based on props.name Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/components/views/elements/ImageView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 42730aca79c..ab3e9ee8f0d 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -176,7 +176,7 @@ module.exports = React.createClass({ { this.getName() } { eventMeta } - +
Download this file
{ size_res } From 5ff49f400000f05912b6b701f315885eb08768ca Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 17 Apr 2017 20:53:46 +0100 Subject: [PATCH 13/56] split out header from RoomSubList and let it update separately By moving the header into its own RoomSubListHeader, we can refresh it explicitly by poking it by the new constantTimeDispatcher without re-rendering the whole stack of room tiles *UNTESTED* --- src/component-index.js | 2 + src/components/structures/RoomSubList.js | 115 +++++++---------- .../structures/RoomSubListHeader.js | 116 ++++++++++++++++++ 3 files changed, 161 insertions(+), 72 deletions(-) create mode 100644 src/components/structures/RoomSubListHeader.js diff --git a/src/component-index.js b/src/component-index.js index 2b67aa15e5f..4bf0b0f9847 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -40,6 +40,8 @@ import structures$RoomDirectory from './components/structures/RoomDirectory'; structures$RoomDirectory && (module.exports.components['structures.RoomDirectory'] = structures$RoomDirectory); import structures$RoomSubList from './components/structures/RoomSubList'; structures$RoomSubList && (module.exports.components['structures.RoomSubList'] = structures$RoomSubList); +import structures$RoomSubListHeader from './components/structures/RoomSubListHeader'; +structures$RoomSubListHeader && (module.exports.components['structures.RoomSubListHeader'] = structures$RoomSubListHeader); import structures$SearchBox from './components/structures/SearchBox'; structures$SearchBox && (module.exports.components['structures.SearchBox'] = structures$SearchBox); import structures$ViewSource from './components/structures/ViewSource'; diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index a2d05d8a99c..1c02b416242 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -27,6 +27,7 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs'); var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils'); var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); +var ConstantTimeDispatcher = require('matrix-react-sdk/lib/ConstantTimeDispatcher'); // turn this on for drag & drop console debugging galore var debug = false; @@ -101,13 +102,25 @@ var RoomSubList = React.createClass({ }, componentWillMount: function() { + constantTimeDispatcher.register("RoomSubList.sort", this.props.tagName, this.onSort); this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order); + this._fixUndefinedOrder(list); + }, + + componentWillUnmount: function() { + constantTimeDispatcher.unregister("RoomSubList.sort", this.props.tagName, this.onSort); }, componentWillReceiveProps: function(newProps) { // order the room list appropriately before we re-render //if (debug) console.log("received new props, list = " + newProps.list); this.sortList(this.applySearchFilter(newProps.list, newProps.searchFilter), newProps.order); + this._fixUndefinedOrder(list); + }, + + onSort: function() { + this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order); + // we deliberately don't waste time trying to fix undefined ordering here }, applySearchFilter: function(list, filter) { @@ -212,9 +225,6 @@ var RoomSubList = React.createClass({ if (order === "manual") comparator = this.manualComparator; if (order === "recent") comparator = this.recentsComparator; - // Fix undefined orders here, and make sure the backend gets updated as well - this._fixUndefinedOrder(list); - //if (debug) console.log("sorting list for sublist " + this.props.label + " with length " + list.length + ", this.props.list = " + this.props.list); this.setState({ sortedList: list.sort(comparator) }); }, @@ -380,73 +390,6 @@ var RoomSubList = React.createClass({ }); }, - _getHeaderJsx: function() { - var TintableSvg = sdk.getComponent("elements.TintableSvg"); - - var subListNotifications = this.roomNotificationCount(); - var subListNotifCount = subListNotifications[0]; - var subListNotifHighlight = subListNotifications[1]; - - var roomCount = this.props.list.length > 0 ? this.props.list.length : ''; - - var chevronClasses = classNames({ - 'mx_RoomSubList_chevron': true, - 'mx_RoomSubList_chevronRight': this.state.hidden, - 'mx_RoomSubList_chevronDown': !this.state.hidden, - }); - - var badgeClasses = classNames({ - 'mx_RoomSubList_badge': true, - 'mx_RoomSubList_badgeHighlight': subListNotifHighlight, - }); - - var badge; - if (subListNotifCount > 0) { - badge =
{ FormattingUtils.formatCount(subListNotifCount) }
; - } - else if (subListNotifHighlight) { - badge =
!
; - } - - // When collapsed, allow a long hover on the header to show user - // the full tag name and room count - var title; - if (this.props.collapsed) { - title = this.props.label; - if (roomCount !== '') { - title += " [" + roomCount + "]"; - } - } - - var incomingCall; - if (this.props.incomingCall) { - var self = this; - // Check if the incoming call is for this section - var incomingCallRoom = this.props.list.filter(function(room) { - return self.props.incomingCall.roomId === room.roomId; - }); - - if (incomingCallRoom.length === 1) { - var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox"); - incomingCall = ; - } - } - - var tabindex = this.props.searchFilter === "" ? "0" : "-1"; - - return ( -
- - { this.props.collapsed ? '' : this.props.label } -
{ roomCount }
-
- { badge } - { incomingCall } -
-
- ); - }, - _createOverflowTile: function(overflowCount, totalCount) { var content =
; @@ -536,6 +479,16 @@ var RoomSubList = React.createClass({ target = ; } + var roomCount = this.props.list.length > 0 ? this.props.list.length : ''; + + var isIncomingCallRoom; + if (this.props.incomingCall) { + // Check if the incoming call is for this section + isIncomingCallRoom = this.props.list.find(room=>{ + return this.props.incomingCall.roomId === room.roomId; + }) ? true : false; + } + if (this.state.sortedList.length > 0 || this.props.editable) { var subList; var classes = "mx_RoomSubList"; @@ -554,7 +507,15 @@ var RoomSubList = React.createClass({ return connectDropTarget(
- { this._getHeaderJsx() } +
); @@ -563,7 +524,17 @@ var RoomSubList = React.createClass({ var Loader = sdk.getComponent("elements.Spinner"); return (
- { this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined } + { this.props.alwaysShowHeader ? +
); diff --git a/src/components/structures/RoomSubListHeader.js b/src/components/structures/RoomSubListHeader.js new file mode 100644 index 00000000000..f97f9f47dcf --- /dev/null +++ b/src/components/structures/RoomSubListHeader.js @@ -0,0 +1,116 @@ +/* +Copyright 2017 Vector Creations 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 ReactDOM = require('react-dom'); +var classNames = require('classnames'); +var sdk = require('matrix-react-sdk') +var dis = require('matrix-react-sdk/lib/dispatcher'); +var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); +var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs'); +var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); +var ConstantTimeDispatcher = require('matrix-react-sdk/lib/ConstantTimeDispatcher'); + +module.exports = React.createClass({ + displayName: 'RoomSubListHeader', + + propTypes: { + label: React.PropTypes.string.isRequired, + tagName: React.PropTypes.string, + roomCount: React.PropTypes.string, + collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed? + isIncomingCallRoom: React.PropTypes.bool, + hidden: React.PropTypes.bool, + onHeaderClick: React.PropTypes.func, + }, + + getDefaultProps: function() { + return { + onHeaderClick: function() {}, // NOP + }; + }, + + componentWillMount: function() { + constantTimeDispatcher.register("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh); + }, + + componentWillUnmount: function() { + constantTimeDispatcher.unregister("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh); + }, + + onRefresh: function() { + this.forceUpdate(); + }, + + render: function() { + var TintableSvg = sdk.getComponent("elements.TintableSvg"); + + var subListNotifications = this.roomNotificationCount(); + var subListNotifCount = subListNotifications[0]; + var subListNotifHighlight = subListNotifications[1]; + + var chevronClasses = classNames({ + 'mx_RoomSubList_chevron': true, + 'mx_RoomSubList_chevronRight': this.props.hidden, + 'mx_RoomSubList_chevronDown': !this.props.hidden, + }); + + var badgeClasses = classNames({ + 'mx_RoomSubList_badge': true, + 'mx_RoomSubList_badgeHighlight': subListNotifHighlight, + }); + + var badge; + if (subListNotifCount > 0) { + badge =
{ FormattingUtils.formatCount(subListNotifCount) }
; + } + else if (subListNotifHighlight) { + badge =
!
; + } + + // When collapsed, allow a long hover on the header to show user + // the full tag name and room count + var title; + if (this.props.collapsed) { + title = this.props.label; + if (roomCount !== '') { + title += " [" + roomCount + "]"; + } + } + + if (this.props.isIncomingCallRoom) { + var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox"); + incomingCall = ; + } + + var tabindex = this.props.searchFilter === "" ? "0" : "-1"; + + return ( +
+ + { this.props.collapsed ? '' : this.props.label } +
{ roomCount }
+
+ { badge } + { incomingCall } +
+
+ ); + }, +}); + From 44479f24dd26950462ef401cbe955cb69d37c7b1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 17 Apr 2017 22:10:56 +0100 Subject: [PATCH 14/56] add command line arg (--hidden) for electron app Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- electron/src/electron-main.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/electron/src/electron-main.js b/electron/src/electron-main.js index 33b44ce9d14..e6ffffac9b7 100644 --- a/electron/src/electron-main.js +++ b/electron/src/electron-main.js @@ -201,9 +201,12 @@ electron.app.on('ready', () => { brand: vectorConfig.brand || 'Riot' }); - mainWindow.once('ready-to-show', () => { - mainWindow.show(); - }); + if (!process.argv.includes('--hidden')) { + mainWindow.once('ready-to-show', () => { + mainWindow.show(); + }); + } + mainWindow.on('closed', () => { mainWindow = null; }); From f8aa2c3487061881c1f4ca0cbe02af11981d6dfd Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 18 Apr 2017 02:43:06 +0100 Subject: [PATCH 15/56] fix bugs in RoomSubListHeader splitout --- src/components/structures/RoomSubList.js | 15 +++++++++++---- src/components/structures/RoomSubListHeader.js | 16 +++++++++++----- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 1c02b416242..7656149fc6b 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -28,6 +28,7 @@ var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs'); var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils'); var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); var ConstantTimeDispatcher = require('matrix-react-sdk/lib/ConstantTimeDispatcher'); +var RoomSubListHeader = require('./RoomSubListHeader.js'); // turn this on for drag & drop console debugging galore var debug = false; @@ -104,7 +105,7 @@ var RoomSubList = React.createClass({ componentWillMount: function() { constantTimeDispatcher.register("RoomSubList.sort", this.props.tagName, this.onSort); this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order); - this._fixUndefinedOrder(list); + this._fixUndefinedOrder(this.props.list); }, componentWillUnmount: function() { @@ -115,7 +116,7 @@ var RoomSubList = React.createClass({ // order the room list appropriately before we re-render //if (debug) console.log("received new props, list = " + newProps.list); this.sortList(this.applySearchFilter(newProps.list, newProps.searchFilter), newProps.order); - this._fixUndefinedOrder(list); + this._fixUndefinedOrder(newProps.list); }, onSort: function() { @@ -133,7 +134,7 @@ var RoomSubList = React.createClass({ // The header is collapsable if it is hidden or not stuck // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method isCollapsableOnClick: function() { - var stuck = this.refs.header.dataset.stuck; + var stuck = this.refs.header.refs.header.dataset.stuck; if (this.state.hidden || stuck === undefined || stuck === "none") { return true; } else { @@ -156,7 +157,7 @@ var RoomSubList = React.createClass({ this.props.onHeaderClick(isHidden); } else { // The header is stuck, so the click is to be interpreted as a scroll to the header - this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition); + this.props.onHeaderClick(this.state.hidden, this.refs.header.refs.header.dataset.originalPosition); } }, @@ -508,12 +509,15 @@ var RoomSubList = React.createClass({ return connectDropTarget(