From e9132a873b870dea6f360e807ebe74b7365814d2 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 17 Nov 2015 02:13:42 +0000 Subject: [PATCH 001/123] experiment with turning the UserSettings controller into a UserSettingsStore logic class --- src/UserSettingsStore.js | 80 +++++++++++++++++++++++ src/controllers/organisms/UserSettings.js | 54 --------------- 2 files changed, 80 insertions(+), 54 deletions(-) create mode 100644 src/UserSettingsStore.js delete mode 100644 src/controllers/organisms/UserSettings.js diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js new file mode 100644 index 0000000000..50ee03a433 --- /dev/null +++ b/src/UserSettingsStore.js @@ -0,0 +1,80 @@ +/* +Copyright 2015 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. +*/ + +'use strict'; + +var MatrixClientPeg = require("./MatrixClientPeg"); +var sdk = require('./index'); + +// XXX: should we be doing something here to use this as a singleton rather than +// class methods? + +module.exports = { + + // we add these wrappers to the js-sdk here in case we want to do react-specific + // dispatches or similar in future, and to give us a place to clearly separate + // business logic specific from the 'thin' react component and parent wiring component + // which actually handles the UI. + // XXX: I'm not convinced this abstraction is worth it though. + + loadProfileInfo: function() { + var cli = MatrixClientPeg.get(); + return cli.getProfileInfo(cli.credentials.userId); + }, + + saveDisplayName: function(newDisplayname) { + return MatrixClientPeg.get().setDisplayName(newDisplayname); + }, + + loadThreePids: function() { + return MatrixClientPeg.get().getThreePids(); + }, + + saveThreePids: function(threePids) { + + }, + + getEnableNotifications: function() { + var Notifier = sdk.getComponent('organisms.Notifier'); + return Notifier.isEnabled(); + }, + + setEnableNotifications: function(enable) { + var Notifier = sdk.getComponent('organisms.Notifier'); + var self = this; + if (!Notifier.supportsDesktopNotifications()) { + return; + } + Notifier.setEnabled(enable); + }, + + changePassword: function(old_password, new_password) { + var cli = MatrixClientPeg.get(); + + var authDict = { + type: 'm.login.password', + user: cli.credentials.userId, + password: old_password + }; + + this.setState({ + phase: this.Phases.Uploading, + errorString: '', + }) + + return cli.setPassword(authDict, new_password); + }, +} diff --git a/src/controllers/organisms/UserSettings.js b/src/controllers/organisms/UserSettings.js deleted file mode 100644 index d7bb8d391a..0000000000 --- a/src/controllers/organisms/UserSettings.js +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2015 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 MatrixClientPeg = require("../../MatrixClientPeg"); -var q = require('q'); -var version = require('../../../package.json').version; - -module.exports = { - Phases: { - Loading: "loading", - Display: "display", - }, - - getInitialState: function() { - return { - avatarUrl: null, - threePids: [], - clientVersion: version, - phase: this.Phases.Loading, - }; - }, - - componentWillMount: function() { - var self = this; - var cli = MatrixClientPeg.get(); - - var profile_d = cli.getProfileInfo(cli.credentials.userId); - var threepid_d = cli.getThreePids(); - - q.all([profile_d, threepid_d]).then( - function(resps) { - self.setState({ - avatarUrl: resps[0].avatar_url, - threepids: resps[1].threepids, - phase: self.Phases.Display, - }); - }, - function(err) { console.err(err); } - ); - } -} From cd13041b31fb5e63133b450271d5872a3cd316b5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 2 Dec 2015 16:35:16 +0000 Subject: [PATCH 002/123] Make the X on the desktop notification bar work again See vector-im/vector-web #464 for more info. --- src/Notifier.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Notifier.js b/src/Notifier.js index 66e96fb15c..307c91f457 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -29,7 +29,7 @@ var dis = require("./dispatcher"); * } */ -module.exports = { +var Notifier = { notificationMessageForEvent: function(ev) { return TextForEvent.textForEvent(ev); @@ -190,3 +190,8 @@ module.exports = { } }; +if (!global.mxNotifier) { + global.mxNotifier = Notifier; +} + +module.exports = global.mxNotifier; \ No newline at end of file From aeb8e21b5d547648868ff78777c7ee3482510f3e Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 2 Dec 2015 17:37:53 +0000 Subject: [PATCH 003/123] better typing test :D --- src/components/structures/RoomView.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 5b7496e8b2..dda6da5834 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -918,7 +918,7 @@ module.exports = React.createClass({ ); } else { var typingString = this.getWhoIsTypingString(); - //typingString = "Testing typing..."; + // typingString = "S͚͍̭̪̤͙̱͙̖̥͙̥̤̻̙͕͓͂̌ͬ͐̂k̜̝͎̰̥̻̼̂̌͛͗͊̅̒͂̊̍̍͌̈̈́͌̋̊ͬa͉̯͚̺̗̳̩ͪ̋̑͌̓̆̍̂̉̏̅̆ͧ̌̑v̲̲̪̝ͥ̌ͨͮͭ̊͆̾ͮ̍ͮ͑̚e̮̙͈̱̘͕̼̮͒ͩͨͫ̃͗̇ͩ͒ͣͦ͒̄̍͐ͣ̿ͥṘ̗̺͇̺̺͔̄́̊̓͊̍̃ͨ̚ā̼͎̘̟̼͎̜̪̪͚̋ͨͨͧ̓ͦͯͤ̄͆̋͂ͩ͌ͧͅt̙̙̹̗̦͖̞ͫͪ͑̑̅ͪ̃̚ͅ is typing..."; var unreadMsgs = this.getUnreadMessagesString(); // no conn bar trumps unread count since you can't get unread messages // without a connection! (technically may already have some but meh) From e8f5131ed6f29951a4765992290e437a8a917180 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 2 Dec 2015 18:16:16 +0000 Subject: [PATCH 004/123] Make file uploads cancelable, make them not go weird if you change rooms, error if they fail and move upload status bar out into its own component. --- src/ContentMessages.js | 123 +++++++++++++++++++------ src/component-index.js | 1 + src/components/structures/RoomView.js | 51 ++-------- src/components/structures/UploadBar.js | 93 +++++++++++++++++++ 4 files changed, 196 insertions(+), 72 deletions(-) create mode 100644 src/components/structures/UploadBar.js diff --git a/src/ContentMessages.js b/src/ContentMessages.js index eba3011917..59fea7bff4 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -18,6 +18,10 @@ limitations under the License. var q = require('q'); var extend = require('./extend'); +var dis = require('./dispatcher'); +var MatrixClientPeg = require('./MatrixClientPeg'); +var sdk = require('./index'); +var Modal = require('./Modal'); function infoForImageFile(imageFile) { var deferred = q.defer(); @@ -48,39 +52,102 @@ function infoForImageFile(imageFile) { return deferred.promise; } -function sendContentToRoom(file, roomId, matrixClient) { - var content = { - body: file.name, - info: { - size: file.size, +class ContentMessages { + constructor() { + this.inprogress = []; + this.nextId = 0; + } + + sendContentToRoom(file, roomId, matrixClient) { + var content = { + body: file.name, + info: { + size: file.size, + } + }; + + // if we have a mime type for the file, add it to the message metadata + if (file.type) { + content.info.mimetype = file.type; } - }; - // if we have a mime type for the file, add it to the message metadata - if (file.type) { - content.info.mimetype = file.type; - } - - var def = q.defer(); - if (file.type.indexOf('image/') == 0) { - content.msgtype = 'm.image'; - infoForImageFile(file).then(function(imageInfo) { - extend(content.info, imageInfo); + var def = q.defer(); + if (file.type.indexOf('image/') == 0) { + content.msgtype = 'm.image'; + infoForImageFile(file).then(function(imageInfo) { + extend(content.info, imageInfo); + def.resolve(); + }); + } else { + content.msgtype = 'm.file'; def.resolve(); + } + + var upload = { + fileName: file.name, + roomId: roomId, + total: 0, + loaded: 0 + }; + this.inprogress.push(upload); + dis.dispatch({action: 'upload_started'}); + + var self = this; + return def.promise.then(function() { + upload.promise = matrixClient.uploadContent(file); + return upload.promise; + }).progress(function(ev) { + if (ev) { + upload.total = ev.total; + upload.loaded = ev.loaded; + dis.dispatch({action: 'upload_progress', upload: upload}); + } + }).then(function(url) { + dis.dispatch({action: 'upload_finished', upload: upload}); + content.url = url; + return matrixClient.sendMessage(roomId, content); + }, function(err) { + dis.dispatch({action: 'upload_failed', upload: upload}); + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Upload Failed", + description: "The file '"+upload.fileName+"' failed to upload." + }); + }).finally(function() { + var inprogressKeys = Object.keys(self.inprogress); + for (var i = 0; i < self.inprogress.length; ++i) { + var k = inprogressKeys[i]; + if (self.inprogress[k].promise === upload.promise) { + self.inprogress.splice(k, 1); + break; + } + } }); - } else { - content.msgtype = 'm.file'; - def.resolve(); } - return def.promise.then(function() { - return matrixClient.uploadContent(file); - }).then(function(url) { - content.url = url; - return matrixClient.sendMessage(roomId, content); - }); + getCurrentUploads() { + return this.inprogress; + } + + cancelUpload(promise) { + var inprogressKeys = Object.keys(this.inprogress); + var upload; + for (var i = 0; i < this.inprogress.length; ++i) { + var k = inprogressKeys[i]; + if (this.inprogress[k].promise === promise) { + upload = this.inprogress[k]; + break; + } + } + if (upload) { + upload.canceled = true; + MatrixClientPeg.get().cancelUpload(upload.promise); + } + } } -module.exports = { - sendContentToRoom: sendContentToRoom -}; +if (global.mx_ContentMessage === undefined) { + global.mx_ContentMessage = new ContentMessages(); +} + +module.exports = global.mx_ContentMessage; diff --git a/src/component-index.js b/src/component-index.js index b9821b6c56..2f89e6d94b 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -28,6 +28,7 @@ module.exports.components['structures.login.PostRegistration'] = require('./comp module.exports.components['structures.login.Registration'] = require('./components/structures/login/Registration'); module.exports.components['structures.MatrixChat'] = require('./components/structures/MatrixChat'); module.exports.components['structures.RoomView'] = require('./components/structures/RoomView'); +module.exports.components['structures.UploadBar'] = require('./components/structures/UploadBar'); module.exports.components['structures.UserSettings'] = require('./components/structures/UserSettings'); module.exports.components['views.avatars.MemberAvatar'] = require('./components/views/avatars/MemberAvatar'); module.exports.components['views.avatars.RoomAvatar'] = require('./components/views/avatars/RoomAvatar'); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 5b7496e8b2..821433863e 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -26,7 +26,6 @@ var ReactDOM = require("react-dom"); var GeminiScrollbar = require('react-gemini-scrollbar'); var q = require("q"); var classNames = require("classnames"); -var filesize = require('filesize'); var Matrix = require("matrix-js-sdk"); var MatrixClientPeg = require("../../MatrixClientPeg"); @@ -107,6 +106,9 @@ module.exports = React.createClass({ this.forceUpdate(); break; case 'notifier_enabled': + case 'upload_failed': + case 'upload_started': + case 'upload_finished': this.forceUpdate(); break; case 'call_state': @@ -408,30 +410,10 @@ module.exports = React.createClass({ }, uploadFile: function(file) { - this.setState({ - upload: { - fileName: file.name, - uploadedBytes: 0, - totalBytes: file.size - } - }); var self = this; ContentMessages.sendContentToRoom( file, this.props.roomId, MatrixClientPeg.get() - ).progress(function(ev) { - //console.log("Upload: "+ev.loaded+" / "+ev.total); - self.setState({ - upload: { - fileName: file.name, - uploadedBytes: ev.loaded, - totalBytes: ev.total - } - }); - }).finally(function() { - self.setState({ - upload: undefined - }); - }).done(undefined, function(error) { + ).done(undefined, function(error) { var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createDialog(ErrorDialog, { title: "Failed to upload file", @@ -894,28 +876,9 @@ module.exports = React.createClass({ // fileName: "testing_fooble.jpg", // } - if (this.state.upload) { - var innerProgressStyle = { - width: ((this.state.upload.uploadedBytes / this.state.upload.totalBytes) * 100) + '%' - }; - var uploadedSize = filesize(this.state.upload.uploadedBytes); - var totalSize = filesize(this.state.upload.totalBytes); - if (uploadedSize.replace(/^.* /,'') === totalSize.replace(/^.* /,'')) { - uploadedSize = uploadedSize.replace(/ .*/, ''); - } - statusBar = ( -
-
-
-
- - -
- { uploadedSize } / { totalSize } -
-
Uploading {this.state.upload.fileName}
-
- ); + if (ContentMessages.getCurrentUploads().length > 0) { + var UploadBar = sdk.getComponent('structures.UploadBar'); + statusBar = } else { var typingString = this.getWhoIsTypingString(); //typingString = "Testing typing..."; diff --git a/src/components/structures/UploadBar.js b/src/components/structures/UploadBar.js new file mode 100644 index 0000000000..b494a53bac --- /dev/null +++ b/src/components/structures/UploadBar.js @@ -0,0 +1,93 @@ +/* +Copyright 2015 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 React = require('react'); +var ContentMessages = require('../../ContentMessages'); +var dis = require('../../dispatcher'); +var filesize = require('filesize'); + +module.exports = React.createClass({displayName: 'UploadBar', + propTypes: { + room: React.PropTypes.object + }, + + componentDidMount: function() { + dis.register(this.onAction); + this.mounted = true; + }, + + componentWillUnmount: function() { + this.mounted = false; + }, + + onAction: function(payload) { + switch (payload.action) { + case 'upload_progress': + case 'upload_finished': + case 'upload_failed': + if (this.mounted) this.forceUpdate(); + break; + } + }, + + render: function() { + var uploads = ContentMessages.getCurrentUploads(); + if (uploads.length == 0) { + return
+ } + + var upload; + for (var i = 0; i < uploads.length; ++i) { + if (uploads[i].roomId == this.props.room.roomId) { + upload = uploads[i]; + break; + } + } + if (!upload) { + upload = uploads[0]; + } + + var innerProgressStyle = { + width: ((upload.loaded / (upload.total || 1)) * 100) + '%' + }; + var uploadedSize = filesize(upload.loaded); + var totalSize = filesize(upload.total); + if (uploadedSize.replace(/^.* /,'') === totalSize.replace(/^.* /,'')) { + uploadedSize = uploadedSize.replace(/ .*/, ''); + } + + var others; + if (uploads.length > 1) { + others = 'and '+(uploads.length - 1) + ' other' + (uploads.length > 2 ? 's' : ''); + } + + return ( +
+
+
+
+ + +
+ { uploadedSize } / { totalSize } +
+
Uploading {upload.fileName}{others}
+
+ ); + } +}); From 4c2fe55fdcb601f050dd5b94c2536877e5a5c24b Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 3 Dec 2015 10:52:06 +0000 Subject: [PATCH 005/123] Don't show the error dialog if the request was canceled. Handle 'request entity too large'. --- src/ContentMessages.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ContentMessages.js b/src/ContentMessages.js index 59fea7bff4..094eff18d9 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -108,11 +108,17 @@ class ContentMessages { return matrixClient.sendMessage(roomId, content); }, function(err) { dis.dispatch({action: 'upload_failed', upload: upload}); - var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); - Modal.createDialog(ErrorDialog, { - title: "Upload Failed", - description: "The file '"+upload.fileName+"' failed to upload." - }); + if (!upload.canceled) { + var desc = "The file '"+upload.fileName+"' failed to upload."; + if (err.http_status == 413) { + desc = "The file '"+upload.fileName+"' exceeds this home server's size limit for uploads"; + } + var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); + Modal.createDialog(ErrorDialog, { + title: "Upload Failed", + description: desc + }); + } }).finally(function() { var inprogressKeys = Object.keys(self.inprogress); for (var i = 0; i < self.inprogress.length; ++i) { From 141d757a01c2804c57bdc78ac2e0385ea6db01af Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 4 Dec 2015 12:00:55 +0000 Subject: [PATCH 006/123] Don't recalculate atBottom here - we do it in onScroll and it should only be necessary to do it once. Also remove the magic 150px leeway. --- src/components/structures/RoomView.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 538bf83d03..4320724334 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -169,14 +169,6 @@ module.exports = React.createClass({ if (this.state.joining) return; if (room.roomId != this.props.roomId) return; - var scrollNode = this._getScrollNode(); - if (scrollNode) { - this.atBottom = ( - scrollNode.scrollHeight - scrollNode.scrollTop <= - (scrollNode.clientHeight + 150) // 150? - ); - } - var currentUnread = this.state.numUnreadMessages; if (!toStartOfTimeline && (ev.getSender() !== MatrixClientPeg.get().credentials.userId)) { From 6a67342d02b9afab8dcedb01ef4399d8fe07f6dd Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 4 Dec 2015 12:02:54 +0000 Subject: [PATCH 007/123] Comment + 1 --- src/components/structures/RoomView.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 4320724334..b7ae42ad67 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -362,6 +362,7 @@ module.exports = React.createClass({ if (this.refs.messagePanel) { var messageWrapperScroll = this._getScrollNode(); var wasAtBottom = this.atBottom; + // + 1 here to avoid fractional pixel rounding errors this.atBottom = messageWrapperScroll.scrollHeight - messageWrapperScroll.scrollTop <= messageWrapperScroll.clientHeight + 1; if (this.atBottom && !wasAtBottom) { this.forceUpdate(); // remove unread msg count From 29b3b237d56c009ba5f6dfc0eacd81567a442961 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 4 Dec 2015 14:24:02 +0000 Subject: [PATCH 008/123] Refresh room list on timeline event even if it doesn't change highlight status since we still want to update the ordering. Fixes #413. --- src/components/views/rooms/RoomList.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index db5195d3c0..8610793c40 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -109,17 +109,18 @@ module.exports = React.createClass({ } } + var newState = this.getRoomLists(); if (hl > 0) { - var newState = this.getRoomLists(); - // obviously this won't deep copy but this shouldn't be necessary var amap = this.state.activityMap; amap[room.roomId] = Math.max(amap[room.roomId] || 0, hl); newState.activityMap = amap; - this.setState(newState); } + // still want to update the list even if the highlight status + // hasn't changed because the ordering may have + this.setState(newState); }, onRoomName: function(room) { @@ -300,4 +301,4 @@ module.exports = React.createClass({ ); } -}); \ No newline at end of file +}); From 257dc62a73e2d74a2d42ce700236bccd619a44bd Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 4 Dec 2015 16:15:55 +0000 Subject: [PATCH 009/123] Recalculate levels when the member prop is updated. --- src/components/views/rooms/MemberInfo.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 6b4b441463..81be88fdb8 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -43,11 +43,16 @@ module.exports = React.createClass({ componentDidMount: function() { // work out the current state if (this.props.member) { - var memberState = this._calculateOpsPermissions(); + var memberState = this._calculateOpsPermissions(this.props.member); this.setState(memberState); } }, + componentWillReceiveProps: function(newProps) { + var memberState = this._calculateOpsPermissions(newProps.member); + this.setState(memberState); + }, + onKick: function() { var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); var roomId = this.props.member.roomId; @@ -269,13 +274,13 @@ module.exports = React.createClass({ } }, - _calculateOpsPermissions: function() { + _calculateOpsPermissions: function(member) { var defaultPerms = { can: {}, muted: false, modifyLevel: false }; - var room = MatrixClientPeg.get().getRoom(this.props.member.roomId); + var room = MatrixClientPeg.get().getRoom(member.roomId); if (!room) { return defaultPerms; } @@ -286,7 +291,7 @@ module.exports = React.createClass({ return defaultPerms; } var me = room.getMember(MatrixClientPeg.get().credentials.userId); - var them = this.props.member; + var them = member; return { can: this._calculateCanPermissions( me, them, powerLevels.getContent() From 142ac677994511cb4262a1079e890b3aa0823a45 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 4 Dec 2015 16:20:05 +0000 Subject: [PATCH 010/123] Add a logic class to determine whether an event affects a room's unread count / status & use it --- src/UnreadStatus.js | 30 ++++++++++++++++++++++++++ src/components/views/rooms/RoomList.js | 3 +-- 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 src/UnreadStatus.js diff --git a/src/UnreadStatus.js b/src/UnreadStatus.js new file mode 100644 index 0000000000..c8693c1e50 --- /dev/null +++ b/src/UnreadStatus.js @@ -0,0 +1,30 @@ +/* +Copyright 2015 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. +*/ + +module.exports = { + /** + * Returns true iff this event arriving in a room should affect the room's + * count of unread messages + */ + eventTriggersUnreadCount: function(ev) { + if (ev.getType() == "m.room.member") { + return false; + } else if (ev.getType == 'm.room.message' && ev.getContent().msgtype == 'm.notify') { + return false; + } + return true; + } +}; diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 8610793c40..7c9aa30793 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -98,8 +98,7 @@ module.exports = React.createClass({ room.roomId != this.props.selectedRoom && ev.getSender() != MatrixClientPeg.get().credentials.userId) { - // don't mark rooms as unread for just member changes - if (ev.getType() != "m.room.member") { + if (UnreadStatus.eventTriggersUnreadCount(ev)) { hl = 1; } From 681138678eb8faab71b1f97f3b71b754619be255 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 4 Dec 2015 19:07:28 +0000 Subject: [PATCH 011/123] Fix bolding --- src/components/views/rooms/RoomList.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 7c9aa30793..2ff9541a07 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -20,6 +20,7 @@ var ReactDOM = require("react-dom"); var GeminiScrollbar = require('react-gemini-scrollbar'); var MatrixClientPeg = require("../../../MatrixClientPeg"); var RoomListSorter = require("../../../RoomListSorter"); +var UnreadStatus = require('../../../UnreadStatus'); var dis = require("../../../dispatcher"); var sdk = require('../../../index'); From 08d7c0232a9e21fb998b83d09916b3888321982a Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 6 Dec 2015 22:07:49 +0000 Subject: [PATCH 012/123] svgize images --- src/components/structures/RoomView.js | 6 +++--- src/components/structures/UploadBar.js | 2 +- src/components/views/rooms/MemberInfo.js | 2 +- src/components/views/rooms/MessageComposer.js | 6 +++--- src/components/views/rooms/RoomHeader.js | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index b7ae42ad67..176d61b42e 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -883,7 +883,7 @@ module.exports = React.createClass({ if (this.state.syncState === "ERROR") { statusBar = (
- /!\ + /!\
Connectivity to the server has been lost. @@ -898,7 +898,7 @@ module.exports = React.createClass({ else if (this.state.hasUnsentMessages) { statusBar = (
- /!\ + /!\
Some of your messages have not been sent. @@ -962,7 +962,7 @@ module.exports = React.createClass({ if (this.state.draggingFile) { fileDropTarget =
- Drop File Here
+ Drop File Here
Drop File Here
; diff --git a/src/components/structures/UploadBar.js b/src/components/structures/UploadBar.js index b494a53bac..717c7f57b2 100644 --- a/src/components/structures/UploadBar.js +++ b/src/components/structures/UploadBar.js @@ -80,7 +80,7 @@ module.exports = React.createClass({displayName: 'UploadBar',
-
diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 81be88fdb8..1478ce97a0 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -382,7 +382,7 @@ module.exports = React.createClass({ var MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); return (
- +
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 36be8829ff..045b633403 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -555,14 +555,14 @@ module.exports = React.createClass({