Merge remote-tracking branch 'origin/develop' into erikj/group_server

pull/4442/head
David Baker 2017-07-05 10:13:53 +01:00
commit 2ccd36d09e
14 changed files with 243 additions and 348 deletions

View File

@ -19,4 +19,4 @@ node_js:
install:
# clone the deps with depth 1: we know we will only ever need that one
# commit.
- scripts/fetch-develop.deps.sh --depth 1 && npm install
- scripts/fetch-develop.deps.sh --depth 1 && npm i phantomjs-prebuilt && npm install

View File

@ -121,6 +121,7 @@
"karma-mocha": "^0.2.2",
"karma-phantomjs-launcher": "^1.0.0",
"karma-webpack": "^1.7.0",
"matrix-mock-request": "^1.0.0",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"mocha": "^2.4.5",

View File

@ -67,7 +67,7 @@ module.exports = React.createClass({
onResendClick: function() {
Resend.resend(this.props.mxEvent);
if (this.props.onFinished) this.props.onFinished();
this.closeMenu();
},
onViewSourceClick: function() {
@ -75,7 +75,7 @@ module.exports = React.createClass({
Modal.createDialog(ViewSource, {
content: this.props.mxEvent.event,
}, 'mx_Dialog_viewsource');
if (this.props.onFinished) this.props.onFinished();
this.closeMenu();
},
onViewClearSourceClick: function() {
@ -84,7 +84,7 @@ module.exports = React.createClass({
// FIXME: _clearEvent is private
content: this.props.mxEvent._clearEvent,
}, 'mx_Dialog_viewsource');
if (this.props.onFinished) this.props.onFinished();
this.closeMenu();
},
onRedactClick: function() {
@ -106,12 +106,12 @@ module.exports = React.createClass({
}).done();
},
}, 'mx_Dialog_confirmredact');
if (this.props.onFinished) this.props.onFinished();
this.closeMenu();
},
onCancelSendClick: function() {
Resend.removeFromQueue(this.props.mxEvent);
if (this.props.onFinished) this.props.onFinished();
this.closeMenu();
},
onForwardClick: function() {
@ -130,7 +130,7 @@ module.exports = React.createClass({
if (this.props.eventTileOps) {
this.props.eventTileOps.unhideWidget();
}
if (this.props.onFinished) this.props.onFinished();
this.closeMenu();
},
onQuoteClick: function() {
@ -139,6 +139,7 @@ module.exports = React.createClass({
action: 'quote',
event: this.props.mxEvent,
});
this.closeMenu();
},
render: function() {

View File

@ -56,6 +56,7 @@
@import "./matrix-react-sdk/views/rooms/_SearchableEntityList.scss";
@import "./matrix-react-sdk/views/rooms/_TabCompleteBar.scss";
@import "./matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss";
@import "./matrix-react-sdk/views/rooms/_AppsDrawer.scss";
@import "./matrix-react-sdk/views/settings/_DevicesPanel.scss";
@import "./matrix-react-sdk/views/settings/_IntegrationsManager.scss";
@import "./matrix-react-sdk/views/voip/_CallView.scss";

View File

@ -14,6 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_MEmoteBody {
white-space: pre-wrap;
}
.mx_MEmoteBody_sender {
cursor: pointer;
}

View File

@ -0,0 +1,159 @@
/*
Copyright 2015, 2016 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.
*/
.mx_AppsDrawer {
}
.mx_AppsContainer {
display: flex;
flex-direction: row;
align-items: center;
}
.mx_AddWidget_button {
order: 2;
cursor: pointer;
padding-right: 12px;
padding: 0;
margin: 0 0 5px 0;
color: $accent-color;
font-size: 12px;
}
.mx_SetAppURLDialog_input {
border-radius: 3px;
border: 1px solid $input-border-color;
padding: 9px;
color: $primary-hairline-color;
background-color: $primary-bg-color;
font-size: 15px;
}
.mx_AppTile {
width: 50%;
margin: 0 5px 2px 0;
border: 1px solid $primary-hairline-color;
border-radius: 2px;
// height: 350px;
// display: inline-block;
}
.mx_AppTileFullWidth {
width: 100%;
margin: 0;
padding: 0;
border: 1px solid $primary-hairline-color;
border-radius: 2px;
// height: 350px;
// display: inline-block;
}
.mx_AppTileMenuBar {
// height: 15px;
margin: 0;
padding: 2px 10px;
// background-color: $e2e-verified-color;
border-bottom: 1px solid $primary-hairline-color;
font-size: 10px;
}
.mx_AppTileMenuBarWidgets {
float: right;
}
.mx_AppTileMenuBarWidget {
// pointer-events: none;
cursor: pointer;
}
.mx_AppTileBody iframe {
width: 100%;
height: 350px;
overflow: hidden;
border: none;
padding: 0;
margin: 0;
display: block;
}
.mx_CloseAppWidget {
}
.mx_AppTileMenuBarWidgetPadding {
margin-right: 5px;
}
.mx_AppIconTile {
background-color: $lightbox-bg-color;
border: 1px solid rgba(0, 0, 0, 0);
width: 200px;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
border-radius: 3px;
margin: 5px;
display: inline-block;
}
.mx_AppIconTile.mx_AppIconTile_active {
color: $accent-color;
border-color: $accent-color;
}
.mx_AppIconTile:hover {
border: 1px solid $accent-color;
box-shadow: 0 0 10px 5px rgba(200,200,200,0.5);
}
.mx_AppIconTile_content {
padding: 2px 16px;
height: 60px;
overflow: hidden;
}
.mx_AppIconTile_content h4 {
margin-top: 5px;
margin-bottom: 2px;
}
.mx_AppIconTile_content p {
margin-top: 0;
margin-bottom: 5px;
font-size: smaller;
}
.mx_AppIconTile_image {
padding: 10px;
width: 75%;
max-width:100px;
max-height:100px;
width: auto;
height: auto;
}
.mx_AppIconTile_imageContainer {
text-align: center;
width: 100%;
background-color: white;
border-radius: 3px 3px 0 0;
height: 155px;
display: flex;
justify-content: center;
align-items: center;
}
form.mx_Custom_Widget_Form div {
margin-top: 10px;
margin-bottom: 10px;
}

View File

@ -38,6 +38,7 @@
.mx_Autocomplete_Completion_pill {
border-radius: 17px;
height: 34px;
padding: 0px 5px;
display: flex;
user-select: none;
cursor: pointer;
@ -45,10 +46,21 @@
color: $primary-fg-color;
}
.mx_Autocomplete_Completion_pill * {
.mx_Autocomplete_Completion_pill > * {
margin: 0 3px;
}
.mx_Autocomplete_provider_name,
.mx_Autocomplete_Completion_title,
.mx_Autocomplete_Completion_subtitle,
.mx_Autocomplete_Completion_description {
/* Ellipsis for long names/subtitles/descriptions*/
max-width: 150px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* container for pill-style completions */
.mx_Autocomplete_Completion_container_pill {
margin: 12px;

View File

@ -128,7 +128,8 @@ limitations under the License.
.mx_MessageComposer_upload,
.mx_MessageComposer_hangup,
.mx_MessageComposer_voicecall,
.mx_MessageComposer_videocall {
.mx_MessageComposer_videocall,
.mx_MessageComposer_apps {
/*display: table-cell;*/
/*vertical-align: middle;*/
/*padding-left: 10px;*/
@ -140,7 +141,8 @@ limitations under the License.
.mx_MessageComposer_upload object,
.mx_MessageComposer_hangup object,
.mx_MessageComposer_voicecall object,
.mx_MessageComposer_videocall object {
.mx_MessageComposer_videocall object,
.mx_MessageComposer_apps object {
pointer-events: none;
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<g>
<rect x="178.846" y="92.087" transform="matrix(-0.7071 -0.7071 0.7071 -0.7071 224.3476 631.1498)" width="128.085" height="354.049"/>
<path d="M471.723,88.393l-48.115-48.114c-11.723-11.724-31.558-10.896-44.304,1.85l-45.202,45.203l90.569,90.568l45.202-45.202
C482.616,119.952,483.445,100.116,471.723,88.393z"/>
<polygon points="64.021,363.252 32,480 148.737,447.979 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 876 B

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="35px"
height="35px" viewBox="0 0 35 35" enable-background="new 0 0 35 35" xml:space="preserve">
<g id="Layer_1">
<path id="Oval-109-Copy" fill="#76CFA6" enable-background="new " d="M17.5,35C27.165,35,35,27.165,35,17.5S27.165,0,17.5,0
S0,7.835,0,17.5S7.835,35,17.5,35z"/>
<g id="Icon">
<g>
<path fill="none" stroke="#FFFFFF" d="M7.5,12.5h5v-5h-5V12.5z M15,27.5h5v-5h-5V27.5z M7.5,27.5h5v-5h-5V27.5z M7.5,20h5v-5h-5
V20z M15,20h5v-5h-5V20z M22.5,7.5v5h5v-5H22.5z M15,12.5h5v-5h-5V12.5z M22.5,20h5v-5h-5V20z M22.5,27.5h5v-5h-5V27.5z"/>
</g>
</g>
</g>
<g id="Layer_2">
<g id="Icon_1_" opacity="0.15">
<g>
<path fill="none" stroke="#76CFA6" d="M7.5,12.5h5v-5h-5V12.5z M15,27.5h5v-5h-5V27.5z M7.5,27.5h5v-5h-5V27.5z M7.5,20h5v-5h-5
V20z M15,20h5v-5h-5V20z M22.5,7.5v5h5v-5H22.5z M15,12.5h5v-5h-5V12.5z M22.5,20h5v-5h-5V20z M22.5,27.5h5v-5h-5V27.5z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 15.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="35px" height="35px" viewBox="0 0 35 35" enable-background="new 0 0 35 35" xml:space="preserve">
<path id="Oval-109-Copy" opacity="0.15" fill="#76CFA6" enable-background="new " d="M17.5,35C27.165,35,35,27.165,35,17.5
S27.165,0,17.5,0S0,7.835,0,17.5S7.835,35,17.5,35z"/>
<g id="Icon">
<g>
<path fill="none" stroke="#76CFA6" d="M7.5,12.5h5v-5h-5V12.5z M15,27.5h5v-5h-5V27.5z M7.5,27.5h5v-5h-5V27.5z M7.5,20h5v-5h-5
V20z M15,20h5v-5h-5V20z M22.5,7.5v5h5v-5H22.5z M15,12.5h5v-5h-5V12.5z M22.5,20h5v-5h-5V20z M22.5,27.5h5v-5h-5V27.5z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 941 B

View File

@ -36,7 +36,7 @@ var expect = require('expect');
var q = require('q');
var test_utils = require('../test-utils');
var MockHttpBackend = require('../mock-request');
var MockHttpBackend = require('matrix-mock-request');
var HS_URL='http://localhost';
var IS_URL='http://localhost';

View File

@ -33,7 +33,7 @@ import {VIEWS} from 'matrix-react-sdk/lib/components/structures/MatrixChat';
import dis from 'matrix-react-sdk/lib/dispatcher';
import * as test_utils from '../test-utils';
import MockHttpBackend from '../mock-request';
import MockHttpBackend from 'matrix-mock-request';
import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils';
var DEFAULT_HS_URL='http://my_server';

View File

@ -1,336 +0,0 @@
"use strict";
const q = require("q");
import expect from 'expect';
/**
* Construct a mock HTTP backend, heavily inspired by Angular.js.
* @constructor
*/
function HttpBackend() {
this.requests = [];
this.expectedRequests = [];
const self = this;
// the request function dependency that the SDK needs.
this.requestFn = function(opts, callback) {
const req = new Request(opts, callback);
console.log(`${Date.now()} HTTP backend received request: ${req}`);
self.requests.push(req);
const abort = function() {
const idx = self.requests.indexOf(req);
if (idx >= 0) {
console.log("Aborting HTTP request: %s %s", opts.method,
opts.uri);
self.requests.splice(idx, 1);
req.callback("aborted");
}
};
return {
abort: abort,
};
};
// very simplistic mapping from the whatwg fetch interface onto the request
// interface, so we can use the same mock backend for both.
this.fetchFn = function(input, init) {
init = init || {};
const requestOpts = {
uri: input,
method: init.method || 'GET',
body: init.body,
};
return new Promise((resolve, reject) => {
function callback(err, response, body) {
if (err) {
reject(err);
}
resolve({
ok: response.statusCode >= 200 && response.statusCode < 300,
json: () => body,
});
};
const req = new Request(requestOpts, callback);
console.log(`HTTP backend received request: ${req}`);
self.requests.push(req);
});
};
}
HttpBackend.prototype = {
/**
* Respond to all of the requests (flush the queue).
* @param {string} path The path to flush (optional) default: all.
* @param {integer} numToFlush The number of things to flush (optional), default: all.
* @param {integer=} waitTime The time (in ms) to wait for a request to happen.
* default: 100
*
* @return {Promise} resolves when there is nothing left to flush, with the
* number of requests flushed
*/
flush: function(path, numToFlush, waitTime) {
const defer = q.defer();
const self = this;
let flushed = 0;
if (waitTime === undefined) {
waitTime = 100;
}
function log(msg) {
console.log(`${Date.now()} flush[${path || ''}]: ${msg}`);
}
log("HTTP backend flushing... (path=" + path
+ " numToFlush=" + numToFlush
+ " waitTime=" + waitTime
+ ")",
);
const endTime = waitTime + Date.now();
const tryFlush = function() {
// if there's more real requests and more expected requests, flush 'em.
log(` trying to flush => reqs=[${self.requests}] ` +
`expected=[${self.expectedRequests}]`,
);
if (self._takeFromQueue(path)) {
// try again on the next tick.
flushed += 1;
if (numToFlush && flushed === numToFlush) {
log(`Flushed assigned amount: ${numToFlush}`);
defer.resolve(flushed);
} else {
log(` flushed. Trying for more.`);
setTimeout(tryFlush, 0);
}
} else if (flushed === 0 && Date.now() < endTime) {
// we may not have made the request yet, wait a generous amount of
// time before giving up.
log(` nothing to flush yet; waiting for requests.`);
setTimeout(tryFlush, 5);
} else {
if (flushed === 0) {
log("nothing to flush; giving up");
} else {
log(`no more flushes after flushing ${flushed} requests`);
}
defer.resolve(flushed);
}
};
setTimeout(tryFlush, 0);
return defer.promise;
},
/**
* Attempts to resolve requests/expected requests.
* @param {string} path The path to flush (optional) default: all.
* @return {boolean} true if something was resolved.
*/
_takeFromQueue: function(path) {
let req = null;
let i;
let j;
let matchingReq = null;
let expectedReq = null;
let testResponse = null;
for (i = 0; i < this.requests.length; i++) {
req = this.requests[i];
for (j = 0; j < this.expectedRequests.length; j++) {
expectedReq = this.expectedRequests[j];
if (path && path !== expectedReq.path) {
continue;
}
if (expectedReq.method === req.method &&
req.path.indexOf(expectedReq.path) !== -1) {
if (!expectedReq.data || (JSON.stringify(expectedReq.data) ===
JSON.stringify(req.data))) {
matchingReq = expectedReq;
this.expectedRequests.splice(j, 1);
break;
}
}
}
if (matchingReq) {
// remove from request queue
this.requests.splice(i, 1);
i--;
for (j = 0; j < matchingReq.checks.length; j++) {
matchingReq.checks[j](req);
}
testResponse = matchingReq.response;
console.log(`${Date.now()} responding to ${matchingReq.path}`);
let body = testResponse.body;
if (Object.prototype.toString.call(body) == "[object Function]") {
body = body(req.path, req.data);
}
req.callback(
testResponse.err, testResponse.response, body,
);
matchingReq = null;
}
}
if (testResponse) { // flushed something
return true;
}
return false;
},
/**
* Makes sure that the SDK hasn't sent any more requests to the backend.
*/
verifyNoOutstandingRequests: function() {
const firstOutstandingReq = this.requests[0] || {};
expect(this.requests.length).toEqual(0,
"Expected no more HTTP requests but received request to " +
firstOutstandingReq.path,
);
},
/**
* Makes sure that the test doesn't have any unresolved requests.
*/
verifyNoOutstandingExpectation: function() {
const firstOutstandingExpectation = this.expectedRequests[0] || {};
expect(this.expectedRequests.length).toEqual(0,
"Expected to see HTTP request for " + firstOutstandingExpectation.path,
);
},
/**
* Create an expected request.
* @param {string} method The HTTP method
* @param {string} path The path (which can be partial)
* @param {Object} data The expected data.
* @return {Request} An expected request.
*/
when: function(method, path, data) {
const pendingReq = new ExpectedRequest(method, path, data);
this.expectedRequests.push(pendingReq);
return pendingReq;
},
};
/**
* Represents the expectation of a request.
*
* <p>Includes the conditions to be matched against, the checks to be made,
* and the response to be returned.
*
* @constructor
* @param {string} method
* @param {string} path
* @param {object?} data
*/
function ExpectedRequest(method, path, data) {
this.method = method;
this.path = path;
this.data = data;
this.response = null;
this.checks = [];
}
ExpectedRequest.prototype = {
toString: function() {
return this.method + " " + this.path
},
/**
* Execute a check when this request has been satisfied.
* @param {Function} fn The function to execute.
* @return {Request} for chaining calls.
*/
check: function(fn) {
this.checks.push(fn);
return this;
},
/**
* Respond with the given data when this request is satisfied.
* @param {Number} code The HTTP status code.
* @param {Object|Function} data The HTTP JSON body. If this is a function,
* it will be invoked when the JSON body is required (which should be returned).
*/
respond: function(code, data) {
this.response = {
response: {
statusCode: code,
headers: {},
},
body: data,
err: null,
};
},
/**
* Fail with an Error when this request is satisfied.
* @param {Number} code The HTTP status code.
* @param {Error} err The error to throw (e.g. Network Error)
*/
fail: function(code, err) {
this.response = {
response: {
statusCode: code,
headers: {},
},
body: null,
err: err,
};
},
};
/**
* Represents a request made by the app.
*
* @constructor
* @param {object} opts opts passed to request()
* @param {function} callback
*/
function Request(opts, callback) {
this.opts = opts;
this.callback = callback;
Object.defineProperty(this, 'method', {
get: function() {
return opts.method;
},
});
Object.defineProperty(this, 'path', {
get: function() {
return opts.uri;
},
});
Object.defineProperty(this, 'data', {
get: function() {
return opts.body;
},
});
Object.defineProperty(this, 'queryParams', {
get: function() {
return opts.qs;
},
});
Object.defineProperty(this, 'headers', {
get: function() {
return opts.headers || {};
},
});
}
Request.prototype = {
toString: function() {
return this.method + " " + this.path;
},
};
/**
* The HttpBackend class.
*/
module.exports = HttpBackend;