Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 67bd2cf

Browse files
committed
Merge branch 'matthew/warn-unknown-devices' into matthew/blacklist-unverified
2 parents 7bc3fc8 + 60d7575 commit 67bd2cf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2231
-662
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
src/component-index.js

.eslintrc.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1+
const path = require('path');
2+
3+
// get the path of the js-sdk so we can extend the config
4+
// eslint supports loading extended configs by module,
5+
// but only if they come from a module that starts with eslint-config-
6+
// So we load the filename directly (and it could be in node_modules/
7+
// or or ../node_modules/ etc)
8+
const matrixJsSdkPath = path.dirname(require.resolve('matrix-js-sdk'));
9+
110
module.exports = {
211
parser: "babel-eslint",
3-
extends: ["./node_modules/matrix-js-sdk/.eslintrc.js"],
12+
extends: [matrixJsSdkPath + "/.eslintrc.js"],
413
plugins: [
514
"react",
615
"flowtype",

jenkins.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ npm install
1919
npm run test
2020

2121
# run eslint
22-
npm run lint -- -f checkstyle -o eslint.xml || true
22+
npm run lintall -- -f checkstyle -o eslint.xml || true
2323

2424
# delete the old tarball, if it exists
2525
rm -f matrix-react-sdk-*.tgz

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"license": "Apache-2.0",
1111
"main": "lib/index.js",
1212
"files": [
13+
".eslintrc.js",
1314
"CHANGELOG.md",
1415
"CONTRIBUTING.rst",
1516
"LICENSE",
@@ -46,10 +47,12 @@
4647
"browser-encrypt-attachment": "^0.3.0",
4748
"browser-request": "^0.3.3",
4849
"classnames": "^2.1.2",
50+
"commonmark": "^0.27.0",
4951
"draft-js": "^0.8.1",
5052
"draft-js-export-html": "^0.5.0",
5153
"draft-js-export-markdown": "^0.2.0",
5254
"emojione": "2.2.3",
55+
"file-saver": "^1.3.3",
5356
"filesize": "^3.1.2",
5457
"flux": "^2.0.3",
5558
"fuse.js": "^2.2.0",
@@ -58,7 +61,6 @@
5861
"isomorphic-fetch": "^2.2.1",
5962
"linkifyjs": "^2.1.3",
6063
"lodash": "^4.13.1",
61-
"commonmark": "^0.27.0",
6264
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
6365
"optimist": "^0.6.1",
6466
"q": "^1.4.1",

src/Invite.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ import MultiInviter from './utils/MultiInviter';
1919

2020
const emailRegex = /^\S+@\S+\.\S+$/;
2121

22+
// We allow localhost for mxids to avoid confusion
23+
const mxidRegex = /^@\S+:(?:\S+\.\S+|localhost)$/
24+
2225
export function getAddressType(inputText) {
23-
const isEmailAddress = /^\S+@\S+\.\S+$/.test(inputText);
24-
const isMatrixId = inputText[0] === '@' && inputText.indexOf(":") > 0;
26+
const isEmailAddress = emailRegex.test(inputText);
27+
const isMatrixId = mxidRegex.test(inputText);
2528

2629
// sanity check the input for user IDs
2730
if (isEmailAddress) {

src/KeyCode.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module.exports = {
2020
TAB: 9,
2121
ENTER: 13,
2222
SHIFT: 16,
23+
ESCAPE: 27,
2324
PAGE_UP: 33,
2425
PAGE_DOWN: 34,
2526
END: 35,

src/Modal.js

Lines changed: 78 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ var React = require('react');
2121
var ReactDOM = require('react-dom');
2222
import sdk from './index';
2323

24+
const DIALOG_CONTAINER_ID = "mx_Dialog_Container";
25+
2426
/**
2527
* Wrap an asynchronous loader function with a react component which shows a
2628
* spinner until the real component loads.
@@ -67,24 +69,37 @@ const AsyncWrapper = React.createClass({
6769
},
6870
});
6971

70-
module.exports = {
71-
DialogContainerId: "mx_Dialog_Container",
72+
class ModalManager {
73+
constructor() {
74+
this._counter = 0;
75+
76+
/** list of the modals we have stacked up, with the most recent at [0] */
77+
this._modals = [
78+
/* {
79+
elem: React component for this dialog
80+
onFinished: caller-supplied onFinished callback
81+
className: CSS class for the dialog wrapper div
82+
} */
83+
];
84+
85+
this.closeAll = this.closeAll.bind(this);
86+
}
7287

73-
getOrCreateContainer: function() {
74-
var container = document.getElementById(this.DialogContainerId);
88+
getOrCreateContainer() {
89+
let container = document.getElementById(DIALOG_CONTAINER_ID);
7590

7691
if (!container) {
7792
container = document.createElement("div");
78-
container.id = this.DialogContainerId;
93+
container.id = DIALOG_CONTAINER_ID;
7994
document.body.appendChild(container);
8095
}
8196

8297
return container;
83-
},
98+
}
8499

85-
createDialog: function(Element, props, className) {
100+
createDialog(Element, props, className) {
86101
return this.createDialogAsync((cb) => {cb(Element);}, props, className);
87-
},
102+
}
88103

89104
/**
90105
* Open a modal view.
@@ -105,27 +120,73 @@ module.exports = {
105120
*
106121
* @param {String} className CSS class to apply to the modal wrapper
107122
*/
108-
createDialogAsync: function(loader, props, className) {
123+
createDialogAsync(loader, props, className) {
109124
var self = this;
110-
// never call this via modal.close() from onFinished() otherwise it will loop
125+
const modal = {};
126+
127+
// never call this from onFinished() otherwise it will loop
128+
//
129+
// nb explicit function() rather than arrow function, to get `arguments`
111130
var closeDialog = function() {
112131
if (props && props.onFinished) props.onFinished.apply(null, arguments);
113-
ReactDOM.unmountComponentAtNode(self.getOrCreateContainer());
132+
var i = self._modals.indexOf(modal);
133+
if (i >= 0) {
134+
self._modals.splice(i, 1);
135+
}
136+
self._reRender();
114137
};
115138

139+
// don't attempt to reuse the same AsyncWrapper for different dialogs,
140+
// otherwise we'll get confused.
141+
const modalCount = this._counter++;
142+
116143
// FIXME: If a dialog uses getDefaultProps it clobbers the onFinished
117144
// property set here so you can't close the dialog from a button click!
145+
modal.elem = (
146+
<AsyncWrapper key={modalCount} loader={loader} {...props}
147+
onFinished={closeDialog}/>
148+
);
149+
modal.onFinished = props ? props.onFinished : null;
150+
modal.className = className;
151+
152+
this._modals.unshift(modal);
153+
154+
this._reRender();
155+
return {close: closeDialog};
156+
}
157+
158+
closeAll() {
159+
const modals = this._modals;
160+
this._modals = [];
161+
162+
for (let i = 0; i < modals.length; i++) {
163+
const m = modals[i];
164+
if (m.onFinished) {
165+
m.onFinished(false);
166+
}
167+
}
168+
169+
this._reRender();
170+
}
171+
172+
_reRender() {
173+
if (this._modals.length == 0) {
174+
ReactDOM.unmountComponentAtNode(this.getOrCreateContainer());
175+
return;
176+
}
177+
178+
var modal = this._modals[0];
118179
var dialog = (
119-
<div className={"mx_Dialog_wrapper " + className}>
180+
<div className={"mx_Dialog_wrapper " + modal.className}>
120181
<div className="mx_Dialog">
121-
<AsyncWrapper loader={loader} {...props} onFinished={closeDialog}/>
182+
{modal.elem}
122183
</div>
123-
<div className="mx_Dialog_background" onClick={ closeDialog.bind(this, false) }></div>
184+
<div className="mx_Dialog_background" onClick={ this.closeAll }></div>
124185
</div>
125186
);
126187

127188
ReactDOM.render(dialog, this.getOrCreateContainer());
189+
}
190+
}
128191

129-
return {close: closeDialog};
130-
},
131-
};
192+
export default new ModalManager();

src/SdkConfig.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ var DEFAULTS = {
1919
integrations_ui_url: "https://scalar.vector.im/",
2020
// Base URL to the REST interface of the integrations server
2121
integrations_rest_url: "https://scalar.vector.im/api",
22+
// Where to send bug reports. If not specified, bugs cannot be sent.
23+
bug_report_endpoint_url: null,
2224
};
2325

2426
class SdkConfig {

src/WhoIsTyping.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,24 @@ module.exports = {
3232
return whoIsTyping;
3333
},
3434

35-
whoIsTypingString: function(room) {
36-
var whoIsTyping = this.usersTypingApartFromMe(room);
35+
whoIsTypingString: function(room, limit) {
36+
const whoIsTyping = this.usersTypingApartFromMe(room);
37+
const othersCount = limit === undefined ?
38+
0 : Math.max(whoIsTyping.length - limit, 0);
3739
if (whoIsTyping.length == 0) {
38-
return null;
40+
return '';
3941
} else if (whoIsTyping.length == 1) {
4042
return whoIsTyping[0].name + ' is typing';
43+
}
44+
const names = whoIsTyping.map(function(m) {
45+
return m.name;
46+
});
47+
if (othersCount) {
48+
const other = ' other' + (othersCount > 1 ? 's' : '');
49+
return names.slice(0, limit).join(', ') + ' and ' +
50+
othersCount + other + ' are typing';
4151
} else {
42-
var names = whoIsTyping.map(function(m) {
43-
return m.name;
44-
});
45-
var lastPerson = names.shift();
52+
const lastPerson = names.pop();
4653
return names.join(', ') + ' and ' + lastPerson + ' are typing';
4754
}
4855
}

0 commit comments

Comments
 (0)