Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/nvl/rich_quoting
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> # Conflicts: # src/components/views/messages/TextualBody.js
This commit is contained in:
commit
1bc9d344ae
187 changed files with 5637 additions and 1498 deletions
|
@ -56,7 +56,7 @@
|
||||||
"browser-encrypt-attachment": "^0.3.0",
|
"browser-encrypt-attachment": "^0.3.0",
|
||||||
"browser-request": "^0.3.3",
|
"browser-request": "^0.3.3",
|
||||||
"classnames": "^2.1.2",
|
"classnames": "^2.1.2",
|
||||||
"commonmark": "^0.27.0",
|
"commonmark": "^0.28.1",
|
||||||
"counterpart": "^0.18.0",
|
"counterpart": "^0.18.0",
|
||||||
"draft-js": "^0.11.0-alpha",
|
"draft-js": "^0.11.0-alpha",
|
||||||
"draft-js-export-html": "^0.6.0",
|
"draft-js-export-html": "^0.6.0",
|
||||||
|
@ -77,6 +77,8 @@
|
||||||
"querystring": "^0.2.0",
|
"querystring": "^0.2.0",
|
||||||
"react": "^15.4.0",
|
"react": "^15.4.0",
|
||||||
"react-addons-css-transition-group": "15.3.2",
|
"react-addons-css-transition-group": "15.3.2",
|
||||||
|
"react-dnd": "^2.1.4",
|
||||||
|
"react-dnd-html5-backend": "^2.1.2",
|
||||||
"react-dom": "^15.4.0",
|
"react-dom": "^15.4.0",
|
||||||
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
||||||
"sanitize-html": "^1.14.1",
|
"sanitize-html": "^1.14.1",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2017 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -58,6 +59,7 @@ import sdk from './index';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import Matrix from 'matrix-js-sdk';
|
import Matrix from 'matrix-js-sdk';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
|
import { showUnknownDeviceDialogForCalls } from './cryptodevices';
|
||||||
|
|
||||||
global.mxCalls = {
|
global.mxCalls = {
|
||||||
//room_id: MatrixCall
|
//room_id: MatrixCall
|
||||||
|
@ -97,19 +99,54 @@ function pause(audioId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _reAttemptCall(call) {
|
||||||
|
if (call.direction === 'outbound') {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'place_call',
|
||||||
|
room_id: call.roomId,
|
||||||
|
type: call.type,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
call.answer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function _setCallListeners(call) {
|
function _setCallListeners(call) {
|
||||||
call.on("error", function(err) {
|
call.on("error", function(err) {
|
||||||
console.error("Call error: %s", err);
|
console.error("Call error: %s", err);
|
||||||
console.error(err.stack);
|
console.error(err.stack);
|
||||||
call.hangup();
|
if (err.code === 'unknown_devices') {
|
||||||
_setCallState(undefined, call.roomId, "ended");
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
|
|
||||||
|
Modal.createTrackedDialog('Call Failed', '', QuestionDialog, {
|
||||||
|
title: _t('Call Failed'),
|
||||||
|
description: _t(
|
||||||
|
"There are unknown devices in this room: "+
|
||||||
|
"if you proceed without verifying them, it will be "+
|
||||||
|
"possible for someone to eavesdrop on your call."
|
||||||
|
),
|
||||||
|
button: _t('Review Devices'),
|
||||||
|
onFinished: function(confirmed) {
|
||||||
|
if (confirmed) {
|
||||||
|
const room = MatrixClientPeg.get().getRoom(call.roomId);
|
||||||
|
showUnknownDeviceDialogForCalls(
|
||||||
|
MatrixClientPeg.get(),
|
||||||
|
room,
|
||||||
|
() => {
|
||||||
|
_reAttemptCall(call);
|
||||||
|
},
|
||||||
|
call.direction === 'outbound' ? _t("Call Anyway") : _t("Answer Anyway"),
|
||||||
|
call.direction === 'outbound' ? _t("Call") : _t("Answer"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
call.on('send_event_error', function(err) {
|
} else {
|
||||||
if (err.name === "UnknownDeviceError") {
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
dis.dispatch({
|
|
||||||
action: 'unknown_device_error',
|
Modal.createTrackedDialog('Call Failed', '', ErrorDialog, {
|
||||||
err: err,
|
title: _t('Call Failed'),
|
||||||
room: MatrixClientPeg.get().getRoom(call.roomId),
|
description: err.message,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -179,7 +216,6 @@ function _setCallState(call, roomId, status) {
|
||||||
function _onAction(payload) {
|
function _onAction(payload) {
|
||||||
function placeCall(newCall) {
|
function placeCall(newCall) {
|
||||||
_setCallListeners(newCall);
|
_setCallListeners(newCall);
|
||||||
_setCallState(newCall, newCall.roomId, "ringback");
|
|
||||||
if (payload.type === 'voice') {
|
if (payload.type === 'voice') {
|
||||||
newCall.placeVoiceCall();
|
newCall.placeVoiceCall();
|
||||||
} else if (payload.type === 'video') {
|
} else if (payload.type === 'video') {
|
||||||
|
|
|
@ -68,3 +68,12 @@ export function isOnlyCtrlOrCmdKeyEvent(ev) {
|
||||||
return ev.ctrlKey && !ev.altKey && !ev.metaKey && !ev.shiftKey;
|
return ev.ctrlKey && !ev.altKey && !ev.metaKey && !ev.shiftKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) {
|
||||||
|
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
||||||
|
if (isMac) {
|
||||||
|
return ev.metaKey && !ev.altKey && !ev.ctrlKey;
|
||||||
|
} else {
|
||||||
|
return ev.ctrlKey && !ev.altKey && !ev.metaKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -389,6 +389,8 @@ function _persistCredentialsToLocalStorage(credentials) {
|
||||||
* Logs the current session out and transitions to the logged-out state
|
* Logs the current session out and transitions to the logged-out state
|
||||||
*/
|
*/
|
||||||
export function logout() {
|
export function logout() {
|
||||||
|
if (!MatrixClientPeg.get()) return;
|
||||||
|
|
||||||
if (MatrixClientPeg.get().isGuest()) {
|
if (MatrixClientPeg.get().isGuest()) {
|
||||||
// logout doesn't work for guest sessions
|
// logout doesn't work for guest sessions
|
||||||
// Also we sometimes want to re-log in a guest session
|
// Also we sometimes want to re-log in a guest session
|
||||||
|
|
|
@ -55,25 +55,6 @@ function is_multi_line(node) {
|
||||||
return par.firstChild != par.lastChild;
|
return par.firstChild != par.lastChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
import linkifyMatrix from './linkify-matrix';
|
|
||||||
import * as linkify from 'linkifyjs';
|
|
||||||
linkifyMatrix(linkify);
|
|
||||||
|
|
||||||
// Thieved from draft-js-export-markdown
|
|
||||||
function escapeMarkdown(s) {
|
|
||||||
return s.replace(/[*_`]/g, '\\$&');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace URLs, room aliases and user IDs with md-escaped URLs
|
|
||||||
function linkifyMarkdown(s) {
|
|
||||||
const links = linkify.find(s);
|
|
||||||
links.forEach((l) => {
|
|
||||||
// This may replace several instances of `l.value` at once, but that's OK
|
|
||||||
s = s.replace(l.value, escapeMarkdown(l.value));
|
|
||||||
});
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that wraps commonmark, adding the ability to see whether
|
* Class that wraps commonmark, adding the ability to see whether
|
||||||
* a given message actually uses any markdown syntax or whether
|
* a given message actually uses any markdown syntax or whether
|
||||||
|
@ -81,7 +62,7 @@ function linkifyMarkdown(s) {
|
||||||
*/
|
*/
|
||||||
export default class Markdown {
|
export default class Markdown {
|
||||||
constructor(input) {
|
constructor(input) {
|
||||||
this.input = linkifyMarkdown(input);
|
this.input = input;
|
||||||
|
|
||||||
const parser = new commonmark.Parser();
|
const parser = new commonmark.Parser();
|
||||||
this.parsed = parser.parse(this.input);
|
this.parsed = parser.parse(this.input);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2017 Vector Creations Ltd.
|
Copyright 2017 Vector Creations Ltd.
|
||||||
|
Copyright 2017 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -22,6 +23,7 @@ import EventTimeline from 'matrix-js-sdk/lib/models/event-timeline';
|
||||||
import EventTimelineSet from 'matrix-js-sdk/lib/models/event-timeline-set';
|
import EventTimelineSet from 'matrix-js-sdk/lib/models/event-timeline-set';
|
||||||
import createMatrixClient from './utils/createMatrixClient';
|
import createMatrixClient from './utils/createMatrixClient';
|
||||||
import SettingsStore from './settings/SettingsStore';
|
import SettingsStore from './settings/SettingsStore';
|
||||||
|
import MatrixActionCreators from './actions/MatrixActionCreators';
|
||||||
|
|
||||||
interface MatrixClientCreds {
|
interface MatrixClientCreds {
|
||||||
homeserverUrl: string,
|
homeserverUrl: string,
|
||||||
|
@ -68,6 +70,8 @@ class MatrixClientPeg {
|
||||||
|
|
||||||
unset() {
|
unset() {
|
||||||
this.matrixClient = null;
|
this.matrixClient = null;
|
||||||
|
|
||||||
|
MatrixActionCreators.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,6 +112,9 @@ class MatrixClientPeg {
|
||||||
// regardless of errors, start the client. If we did error out, we'll
|
// regardless of errors, start the client. If we did error out, we'll
|
||||||
// just end up doing a full initial /sync.
|
// just end up doing a full initial /sync.
|
||||||
|
|
||||||
|
// Connect the matrix client to the dispatcher
|
||||||
|
MatrixActionCreators.start(this.matrixClient);
|
||||||
|
|
||||||
console.log(`MatrixClientPeg: really starting MatrixClient`);
|
console.log(`MatrixClientPeg: really starting MatrixClient`);
|
||||||
this.get().startClient(opts);
|
this.get().startClient(opts);
|
||||||
console.log(`MatrixClientPeg: MatrixClient started`);
|
console.log(`MatrixClientPeg: MatrixClient started`);
|
||||||
|
|
|
@ -19,6 +19,7 @@ limitations under the License.
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require('react-dom');
|
const ReactDOM = require('react-dom');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import Analytics from './Analytics';
|
import Analytics from './Analytics';
|
||||||
import sdk from './index';
|
import sdk from './index';
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ const AsyncWrapper = React.createClass({
|
||||||
/** A function which takes a 'callback' argument which it will call
|
/** A function which takes a 'callback' argument which it will call
|
||||||
* with the real component once it loads.
|
* with the real component once it loads.
|
||||||
*/
|
*/
|
||||||
loader: React.PropTypes.func.isRequired,
|
loader: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -135,6 +135,10 @@ const Notifier = {
|
||||||
const plaf = PlatformPeg.get();
|
const plaf = PlatformPeg.get();
|
||||||
if (!plaf) return;
|
if (!plaf) return;
|
||||||
|
|
||||||
|
// Dev note: We don't set the "notificationsEnabled" setting to true here because it is a
|
||||||
|
// calculated value. It is determined based upon whether or not the master rule is enabled
|
||||||
|
// and other flags. Setting it here would cause a circular reference.
|
||||||
|
|
||||||
Analytics.trackEvent('Notifier', 'Set Enabled', enable);
|
Analytics.trackEvent('Notifier', 'Set Enabled', enable);
|
||||||
|
|
||||||
// make sure that we persist the current setting audio_enabled setting
|
// make sure that we persist the current setting audio_enabled setting
|
||||||
|
@ -168,7 +172,7 @@ const Notifier = {
|
||||||
});
|
});
|
||||||
// clear the notifications_hidden flag, so that if notifications are
|
// clear the notifications_hidden flag, so that if notifications are
|
||||||
// disabled again in the future, we will show the banner again.
|
// disabled again in the future, we will show the banner again.
|
||||||
this.setToolbarHidden(false);
|
this.setToolbarHidden(true);
|
||||||
} else {
|
} else {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "notifier_enabled",
|
action: "notifier_enabled",
|
||||||
|
|
|
@ -44,13 +44,6 @@ module.exports = {
|
||||||
// XXX: temporary logging to try to diagnose
|
// XXX: temporary logging to try to diagnose
|
||||||
// https://github.com/vector-im/riot-web/issues/3148
|
// https://github.com/vector-im/riot-web/issues/3148
|
||||||
console.log('Resend got send failure: ' + err.name + '('+err+')');
|
console.log('Resend got send failure: ' + err.name + '('+err+')');
|
||||||
if (err.name === "UnknownDeviceError") {
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'unknown_device_error',
|
|
||||||
err: err,
|
|
||||||
room: room,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'message_send_failed',
|
action: 'message_send_failed',
|
||||||
|
@ -60,9 +53,5 @@ module.exports = {
|
||||||
},
|
},
|
||||||
removeFromQueue: function(event) {
|
removeFromQueue: function(event) {
|
||||||
MatrixClientPeg.get().cancelPendingEvent(event);
|
MatrixClientPeg.get().cancelPendingEvent(event);
|
||||||
dis.dispatch({
|
|
||||||
action: 'message_send_cancelled',
|
|
||||||
event: event,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
22
src/Rooms.js
22
src/Rooms.js
|
@ -43,7 +43,7 @@ export function getOnlyOtherMember(room, me) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isConfCallRoom(room, me, conferenceHandler) {
|
function _isConfCallRoom(room, me, conferenceHandler) {
|
||||||
if (!conferenceHandler) return false;
|
if (!conferenceHandler) return false;
|
||||||
|
|
||||||
if (me.membership != "join") {
|
if (me.membership != "join") {
|
||||||
|
@ -58,6 +58,26 @@ export function isConfCallRoom(room, me, conferenceHandler) {
|
||||||
if (conferenceHandler.isConferenceUser(otherMember.userId)) {
|
if (conferenceHandler.isConferenceUser(otherMember.userId)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache whether a room is a conference call. Assumes that rooms will always
|
||||||
|
// either will or will not be a conference call room.
|
||||||
|
const isConfCallRoomCache = {
|
||||||
|
// $roomId: bool
|
||||||
|
};
|
||||||
|
|
||||||
|
export function isConfCallRoom(room, me, conferenceHandler) {
|
||||||
|
if (isConfCallRoomCache[room.roomId] !== undefined) {
|
||||||
|
return isConfCallRoomCache[room.roomId];
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = _isConfCallRoom(room, me, conferenceHandler);
|
||||||
|
|
||||||
|
isConfCallRoomCache[room.roomId] = result;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function looksLikeDirectMessageRoom(room, me) {
|
export function looksLikeDirectMessageRoom(room, me) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
const request = require('browser-request');
|
const request = require('browser-request');
|
||||||
|
|
||||||
const SdkConfig = require('./SdkConfig');
|
const SdkConfig = require('./SdkConfig');
|
||||||
|
@ -76,10 +77,40 @@ class ScalarAuthClient {
|
||||||
return defer.promise;
|
return defer.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getScalarPageTitle(url) {
|
||||||
|
const defer = Promise.defer();
|
||||||
|
|
||||||
|
let scalarPageLookupUrl = SdkConfig.get().integrations_rest_url + '/widgets/title_lookup';
|
||||||
|
scalarPageLookupUrl = this.getStarterLink(scalarPageLookupUrl);
|
||||||
|
scalarPageLookupUrl += '&curl=' + encodeURIComponent(url);
|
||||||
|
request({
|
||||||
|
method: 'GET',
|
||||||
|
uri: scalarPageLookupUrl,
|
||||||
|
json: true,
|
||||||
|
}, (err, response, body) => {
|
||||||
|
if (err) {
|
||||||
|
defer.reject(err);
|
||||||
|
} else if (response.statusCode / 100 !== 2) {
|
||||||
|
defer.reject({statusCode: response.statusCode});
|
||||||
|
} else if (!body) {
|
||||||
|
defer.reject(new Error("Missing page title in response"));
|
||||||
|
} else {
|
||||||
|
let title = "";
|
||||||
|
if (body.page_title_cache_item && body.page_title_cache_item.cached_title) {
|
||||||
|
title = body.page_title_cache_item.cached_title;
|
||||||
|
}
|
||||||
|
defer.resolve(title);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return defer.promise;
|
||||||
|
}
|
||||||
|
|
||||||
getScalarInterfaceUrlForRoom(roomId, screen, id) {
|
getScalarInterfaceUrlForRoom(roomId, screen, id) {
|
||||||
let url = SdkConfig.get().integrations_ui_url;
|
let url = SdkConfig.get().integrations_ui_url;
|
||||||
url += "?scalar_token=" + encodeURIComponent(this.scalarToken);
|
url += "?scalar_token=" + encodeURIComponent(this.scalarToken);
|
||||||
url += "&room_id=" + encodeURIComponent(roomId);
|
url += "&room_id=" + encodeURIComponent(roomId);
|
||||||
|
url += "&theme=" + encodeURIComponent(SettingsStore.getValue("theme"));
|
||||||
if (id) {
|
if (id) {
|
||||||
url += '&integ_id=' + encodeURIComponent(id);
|
url += '&integ_id=' + encodeURIComponent(id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -366,6 +366,22 @@ function getWidgets(event, roomId) {
|
||||||
sendResponse(event, widgetStateEvents);
|
sendResponse(event, widgetStateEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRoomEncState(event, roomId) {
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
if (!client) {
|
||||||
|
sendError(event, _t('You need to be logged in.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const room = client.getRoom(roomId);
|
||||||
|
if (!room) {
|
||||||
|
sendError(event, _t('This room is not recognised.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId);
|
||||||
|
|
||||||
|
sendResponse(event, roomIsEncrypted);
|
||||||
|
}
|
||||||
|
|
||||||
function setPlumbingState(event, roomId, status) {
|
function setPlumbingState(event, roomId, status) {
|
||||||
if (typeof status !== 'string') {
|
if (typeof status !== 'string') {
|
||||||
throw new Error('Plumbing state status should be a string');
|
throw new Error('Plumbing state status should be a string');
|
||||||
|
@ -541,8 +557,16 @@ const onMessage = function(event) {
|
||||||
//
|
//
|
||||||
// All strings start with the empty string, so for sanity return if the length
|
// All strings start with the empty string, so for sanity return if the length
|
||||||
// of the event origin is 0.
|
// of the event origin is 0.
|
||||||
|
//
|
||||||
|
// TODO -- Scalar postMessage API should be namespaced with event.data.api field
|
||||||
|
// Fix following "if" statement to respond only to specific API messages.
|
||||||
const url = SdkConfig.get().integrations_ui_url;
|
const url = SdkConfig.get().integrations_ui_url;
|
||||||
if (event.origin.length === 0 || !url.startsWith(event.origin) || !event.data.action) {
|
if (
|
||||||
|
event.origin.length === 0 ||
|
||||||
|
!url.startsWith(event.origin) ||
|
||||||
|
!event.data.action ||
|
||||||
|
event.data.api // Ignore messages with specific API set
|
||||||
|
) {
|
||||||
return; // don't log this - debugging APIs like to spam postMessage which floods the log otherwise
|
return; // don't log this - debugging APIs like to spam postMessage which floods the log otherwise
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,6 +617,9 @@ const onMessage = function(event) {
|
||||||
} else if (event.data.action === "get_widgets") {
|
} else if (event.data.action === "get_widgets") {
|
||||||
getWidgets(event, roomId);
|
getWidgets(event, roomId);
|
||||||
return;
|
return;
|
||||||
|
} else if (event.data.action === "get_room_enc_state") {
|
||||||
|
getRoomEncState(event, roomId);
|
||||||
|
return;
|
||||||
} else if (event.data.action === "can_send_event") {
|
} else if (event.data.action === "can_send_event") {
|
||||||
canSendEvent(event, roomId);
|
canSendEvent(event, roomId);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -96,6 +96,8 @@ const commands = {
|
||||||
colorScheme.primary_color = matches[1];
|
colorScheme.primary_color = matches[1];
|
||||||
if (matches[4]) {
|
if (matches[4]) {
|
||||||
colorScheme.secondary_color = matches[4];
|
colorScheme.secondary_color = matches[4];
|
||||||
|
} else {
|
||||||
|
colorScheme.secondary_color = colorScheme.primary_color;
|
||||||
}
|
}
|
||||||
return success(
|
return success(
|
||||||
SettingsStore.setValue("roomColor", roomId, SettingLevel.ROOM_ACCOUNT, colorScheme),
|
SettingsStore.setValue("roomColor", roomId, SettingLevel.ROOM_ACCOUNT, colorScheme),
|
||||||
|
@ -295,7 +297,7 @@ const commands = {
|
||||||
// Define the power level of a user
|
// Define the power level of a user
|
||||||
op: new Command("op", "<userId> [<power level>]", function(roomId, args) {
|
op: new Command("op", "<userId> [<power level>]", function(roomId, args) {
|
||||||
if (args) {
|
if (args) {
|
||||||
const matches = args.match(/^(\S+?)( +(\d+))?$/);
|
const matches = args.match(/^(\S+?)( +(-?\d+))?$/);
|
||||||
let powerLevel = 50; // default power level for op
|
let powerLevel = 50; // default power level for op
|
||||||
if (matches) {
|
if (matches) {
|
||||||
const userId = matches[1];
|
const userId = matches[1];
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import dis from './dispatcher';
|
|
||||||
import sdk from './index';
|
|
||||||
import Modal from './Modal';
|
|
||||||
|
|
||||||
let isDialogOpen = false;
|
|
||||||
|
|
||||||
const onAction = function(payload) {
|
|
||||||
if (payload.action === 'unknown_device_error' && !isDialogOpen) {
|
|
||||||
const UnknownDeviceDialog = sdk.getComponent('dialogs.UnknownDeviceDialog');
|
|
||||||
isDialogOpen = true;
|
|
||||||
Modal.createTrackedDialog('Unknown Device Error', '', UnknownDeviceDialog, {
|
|
||||||
devices: payload.err.devices,
|
|
||||||
room: payload.room,
|
|
||||||
onFinished: (r) => {
|
|
||||||
isDialogOpen = false;
|
|
||||||
// XXX: temporary logging to try to diagnose
|
|
||||||
// https://github.com/vector-im/riot-web/issues/3148
|
|
||||||
console.log('UnknownDeviceDialog closed with '+r);
|
|
||||||
},
|
|
||||||
}, 'mx_Dialog_unknownDevice');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let ref = null;
|
|
||||||
|
|
||||||
export function startListening() {
|
|
||||||
ref = dis.register(onAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stopListening() {
|
|
||||||
if (ref) {
|
|
||||||
dis.unregister(ref);
|
|
||||||
ref = null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDom = require('react-dom');
|
const ReactDom = require('react-dom');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
const Velocity = require('velocity-vector');
|
const Velocity = require('velocity-vector');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,16 +15,16 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// either a list of child nodes, or a single child.
|
// either a list of child nodes, or a single child.
|
||||||
children: React.PropTypes.any,
|
children: PropTypes.any,
|
||||||
|
|
||||||
// optional transition information for changing existing children
|
// optional transition information for changing existing children
|
||||||
transition: React.PropTypes.object,
|
transition: PropTypes.object,
|
||||||
|
|
||||||
// a list of state objects to apply to each child node in turn
|
// a list of state objects to apply to each child node in turn
|
||||||
startStyles: React.PropTypes.array,
|
startStyles: PropTypes.array,
|
||||||
|
|
||||||
// a list of transition options from the corresponding startStyle
|
// a list of transition options from the corresponding startStyle
|
||||||
enterTransitionOpts: React.PropTypes.array,
|
enterTransitionOpts: PropTypes.array,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
326
src/WidgetMessaging.js
Normal file
326
src/WidgetMessaging.js
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 New Vector 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Listens for incoming postMessage requests from embedded widgets. The following API is exposed:
|
||||||
|
{
|
||||||
|
api: "widget",
|
||||||
|
action: "content_loaded",
|
||||||
|
widgetId: $WIDGET_ID,
|
||||||
|
data: {}
|
||||||
|
// additional request fields
|
||||||
|
}
|
||||||
|
|
||||||
|
The complete request object is returned to the caller with an additional "response" key like so:
|
||||||
|
{
|
||||||
|
api: "widget",
|
||||||
|
action: "content_loaded",
|
||||||
|
widgetId: $WIDGET_ID,
|
||||||
|
data: {},
|
||||||
|
// additional request fields
|
||||||
|
response: { ... }
|
||||||
|
}
|
||||||
|
|
||||||
|
The "api" field is required to use this API, and must be set to "widget" in all requests.
|
||||||
|
|
||||||
|
The "action" determines the format of the request and response. All actions can return an error response.
|
||||||
|
|
||||||
|
Additional data can be sent as additional, abritrary fields. However, typically the data object should be used.
|
||||||
|
|
||||||
|
A success response is an object with zero or more keys.
|
||||||
|
|
||||||
|
An error response is a "response" object which consists of a sole "error" key to indicate an error.
|
||||||
|
They look like:
|
||||||
|
{
|
||||||
|
error: {
|
||||||
|
message: "Unable to invite user into room.",
|
||||||
|
_error: <Original Error Object>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
The "message" key should be a human-friendly string.
|
||||||
|
|
||||||
|
ACTIONS
|
||||||
|
=======
|
||||||
|
** All actions must include an "api" field with valie "widget".**
|
||||||
|
All actions can return an error response instead of the response outlined below.
|
||||||
|
|
||||||
|
content_loaded
|
||||||
|
--------------
|
||||||
|
Indicates that widget contet has fully loaded
|
||||||
|
|
||||||
|
Request:
|
||||||
|
- widgetId is the unique ID of the widget instance in riot / matrix state.
|
||||||
|
- No additional fields.
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
success: true
|
||||||
|
}
|
||||||
|
Example:
|
||||||
|
{
|
||||||
|
api: "widget",
|
||||||
|
action: "content_loaded",
|
||||||
|
widgetId: $WIDGET_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
api_version
|
||||||
|
-----------
|
||||||
|
Get the current version of the widget postMessage API
|
||||||
|
|
||||||
|
Request:
|
||||||
|
- No additional fields.
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
api_version: "0.0.1"
|
||||||
|
}
|
||||||
|
Example:
|
||||||
|
{
|
||||||
|
api: "widget",
|
||||||
|
action: "api_version",
|
||||||
|
}
|
||||||
|
|
||||||
|
supported_api_versions
|
||||||
|
----------------------
|
||||||
|
Get versions of the widget postMessage API that are currently supported
|
||||||
|
|
||||||
|
Request:
|
||||||
|
- No additional fields.
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
api: "widget"
|
||||||
|
supported_versions: ["0.0.1"]
|
||||||
|
}
|
||||||
|
Example:
|
||||||
|
{
|
||||||
|
api: "widget",
|
||||||
|
action: "supported_api_versions",
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import URL from 'url';
|
||||||
|
|
||||||
|
const WIDGET_API_VERSION = '0.0.1'; // Current API version
|
||||||
|
const SUPPORTED_WIDGET_API_VERSIONS = [
|
||||||
|
'0.0.1',
|
||||||
|
];
|
||||||
|
|
||||||
|
import dis from './dispatcher';
|
||||||
|
|
||||||
|
if (!global.mxWidgetMessagingListenerCount) {
|
||||||
|
global.mxWidgetMessagingListenerCount = 0;
|
||||||
|
}
|
||||||
|
if (!global.mxWidgetMessagingMessageEndpoints) {
|
||||||
|
global.mxWidgetMessagingMessageEndpoints = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register widget message event listeners
|
||||||
|
*/
|
||||||
|
function startListening() {
|
||||||
|
if (global.mxWidgetMessagingListenerCount === 0) {
|
||||||
|
window.addEventListener("message", onMessage, false);
|
||||||
|
}
|
||||||
|
global.mxWidgetMessagingListenerCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* De-register widget message event listeners
|
||||||
|
*/
|
||||||
|
function stopListening() {
|
||||||
|
global.mxWidgetMessagingListenerCount -= 1;
|
||||||
|
if (global.mxWidgetMessagingListenerCount === 0) {
|
||||||
|
window.removeEventListener("message", onMessage);
|
||||||
|
}
|
||||||
|
if (global.mxWidgetMessagingListenerCount < 0) {
|
||||||
|
// Make an error so we get a stack trace
|
||||||
|
const e = new Error(
|
||||||
|
"WidgetMessaging: mismatched startListening / stopListening detected." +
|
||||||
|
" Negative count",
|
||||||
|
);
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a widget endpoint for trusted postMessage communication
|
||||||
|
* @param {string} widgetId Unique widget identifier
|
||||||
|
* @param {string} endpointUrl Widget wurl origin (protocol + (optional port) + host)
|
||||||
|
*/
|
||||||
|
function addEndpoint(widgetId, endpointUrl) {
|
||||||
|
const u = URL.parse(endpointUrl);
|
||||||
|
if (!u || !u.protocol || !u.host) {
|
||||||
|
console.warn("Invalid origin:", endpointUrl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const origin = u.protocol + '//' + u.host;
|
||||||
|
const endpoint = new WidgetMessageEndpoint(widgetId, origin);
|
||||||
|
if (global.mxWidgetMessagingMessageEndpoints) {
|
||||||
|
if (global.mxWidgetMessagingMessageEndpoints.some(function(ep) {
|
||||||
|
return (ep.widgetId === widgetId && ep.endpointUrl === endpointUrl);
|
||||||
|
})) {
|
||||||
|
// Message endpoint already registered
|
||||||
|
console.warn("Endpoint already registered");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
global.mxWidgetMessagingMessageEndpoints.push(endpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* De-register a widget endpoint from trusted communication sources
|
||||||
|
* @param {string} widgetId Unique widget identifier
|
||||||
|
* @param {string} endpointUrl Widget wurl origin (protocol + (optional port) + host)
|
||||||
|
* @return {boolean} True if endpoint was successfully removed
|
||||||
|
*/
|
||||||
|
function removeEndpoint(widgetId, endpointUrl) {
|
||||||
|
const u = URL.parse(endpointUrl);
|
||||||
|
if (!u || !u.protocol || !u.host) {
|
||||||
|
console.warn("Invalid origin");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const origin = u.protocol + '//' + u.host;
|
||||||
|
if (global.mxWidgetMessagingMessageEndpoints && global.mxWidgetMessagingMessageEndpoints.length > 0) {
|
||||||
|
const length = global.mxWidgetMessagingMessageEndpoints.length;
|
||||||
|
global.mxWidgetMessagingMessageEndpoints = global.mxWidgetMessagingMessageEndpoints.filter(function(endpoint) {
|
||||||
|
return (endpoint.widgetId != widgetId || endpoint.endpointUrl != origin);
|
||||||
|
});
|
||||||
|
return (length > global.mxWidgetMessagingMessageEndpoints.length);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle widget postMessage events
|
||||||
|
* @param {Event} event Event to handle
|
||||||
|
* @return {undefined}
|
||||||
|
*/
|
||||||
|
function onMessage(event) {
|
||||||
|
if (!event.origin) { // Handle chrome
|
||||||
|
event.origin = event.originalEvent.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event origin is empty string if undefined
|
||||||
|
if (
|
||||||
|
event.origin.length === 0 ||
|
||||||
|
!trustedEndpoint(event.origin) ||
|
||||||
|
event.data.api !== "widget" ||
|
||||||
|
!event.data.widgetId
|
||||||
|
) {
|
||||||
|
return; // don't log this - debugging APIs like to spam postMessage which floods the log otherwise
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = event.data.action;
|
||||||
|
const widgetId = event.data.widgetId;
|
||||||
|
if (action === 'content_loaded') {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'widget_content_loaded',
|
||||||
|
widgetId: widgetId,
|
||||||
|
});
|
||||||
|
sendResponse(event, {success: true});
|
||||||
|
} else if (action === 'supported_api_versions') {
|
||||||
|
sendResponse(event, {
|
||||||
|
api: "widget",
|
||||||
|
supported_versions: SUPPORTED_WIDGET_API_VERSIONS,
|
||||||
|
});
|
||||||
|
} else if (action === 'api_version') {
|
||||||
|
sendResponse(event, {
|
||||||
|
api: "widget",
|
||||||
|
version: WIDGET_API_VERSION,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn("Widget postMessage event unhandled");
|
||||||
|
sendError(event, {message: "The postMessage was unhandled"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if message origin is registered as trusted
|
||||||
|
* @param {string} origin PostMessage origin to check
|
||||||
|
* @return {boolean} True if trusted
|
||||||
|
*/
|
||||||
|
function trustedEndpoint(origin) {
|
||||||
|
if (!origin) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return global.mxWidgetMessagingMessageEndpoints.some((endpoint) => {
|
||||||
|
return endpoint.endpointUrl === origin;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a postmessage response to a postMessage request
|
||||||
|
* @param {Event} event The original postMessage request event
|
||||||
|
* @param {Object} res Response data
|
||||||
|
*/
|
||||||
|
function sendResponse(event, res) {
|
||||||
|
const data = JSON.parse(JSON.stringify(event.data));
|
||||||
|
data.response = res;
|
||||||
|
event.source.postMessage(data, event.origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an error response to a postMessage request
|
||||||
|
* @param {Event} event The original postMessage request event
|
||||||
|
* @param {string} msg Error message
|
||||||
|
* @param {Error} nestedError Nested error event (optional)
|
||||||
|
*/
|
||||||
|
function sendError(event, msg, nestedError) {
|
||||||
|
console.error("Action:" + event.data.action + " failed with message: " + msg);
|
||||||
|
const data = JSON.parse(JSON.stringify(event.data));
|
||||||
|
data.response = {
|
||||||
|
error: {
|
||||||
|
message: msg,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (nestedError) {
|
||||||
|
data.response.error._error = nestedError;
|
||||||
|
}
|
||||||
|
event.source.postMessage(data, event.origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents mapping of widget instance to URLs for trusted postMessage communication.
|
||||||
|
*/
|
||||||
|
class WidgetMessageEndpoint {
|
||||||
|
/**
|
||||||
|
* Mapping of widget instance to URL for trusted postMessage communication.
|
||||||
|
* @param {string} widgetId Unique widget identifier
|
||||||
|
* @param {string} endpointUrl Widget wurl origin.
|
||||||
|
*/
|
||||||
|
constructor(widgetId, endpointUrl) {
|
||||||
|
if (!widgetId) {
|
||||||
|
throw new Error("No widgetId specified in widgetMessageEndpoint constructor");
|
||||||
|
}
|
||||||
|
if (!endpointUrl) {
|
||||||
|
throw new Error("No endpoint specified in widgetMessageEndpoint constructor");
|
||||||
|
}
|
||||||
|
this.widgetId = widgetId;
|
||||||
|
this.endpointUrl = endpointUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
startListening: startListening,
|
||||||
|
stopListening: stopListening,
|
||||||
|
addEndpoint: addEndpoint,
|
||||||
|
removeEndpoint: removeEndpoint,
|
||||||
|
};
|
34
src/actions/GroupActions.js
Normal file
34
src/actions/GroupActions.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 New Vector 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 { asyncAction } from './actionCreators';
|
||||||
|
|
||||||
|
const GroupActions = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an action thunk that will do an asynchronous request to fetch
|
||||||
|
* the groups to which a user is joined.
|
||||||
|
*
|
||||||
|
* @param {MatrixClient} matrixClient the matrix client to query.
|
||||||
|
* @returns {function} an action thunk that will dispatch actions
|
||||||
|
* indicating the status of the request.
|
||||||
|
* @see asyncAction
|
||||||
|
*/
|
||||||
|
GroupActions.fetchJoinedGroups = function(matrixClient) {
|
||||||
|
return asyncAction('GroupActions.fetchJoinedGroups', () => matrixClient.getJoinedGroups());
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GroupActions;
|
108
src/actions/MatrixActionCreators.js
Normal file
108
src/actions/MatrixActionCreators.js
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 New Vector 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 dis from '../dispatcher';
|
||||||
|
|
||||||
|
// TODO: migrate from sync_state to MatrixActions.sync so that more js-sdk events
|
||||||
|
// become dispatches in the same place.
|
||||||
|
/**
|
||||||
|
* Create a MatrixActions.sync action that represents a MatrixClient `sync` event,
|
||||||
|
* each parameter mapping to a key-value in the action.
|
||||||
|
*
|
||||||
|
* @param {MatrixClient} matrixClient the matrix client
|
||||||
|
* @param {string} state the current sync state.
|
||||||
|
* @param {string} prevState the previous sync state.
|
||||||
|
* @returns {Object} an action of type MatrixActions.sync.
|
||||||
|
*/
|
||||||
|
function createSyncAction(matrixClient, state, prevState) {
|
||||||
|
return {
|
||||||
|
action: 'MatrixActions.sync',
|
||||||
|
state,
|
||||||
|
prevState,
|
||||||
|
matrixClient,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef AccountDataAction
|
||||||
|
* @type {Object}
|
||||||
|
* @property {string} action 'MatrixActions.accountData'.
|
||||||
|
* @property {MatrixEvent} event the MatrixEvent that triggered the dispatch.
|
||||||
|
* @property {string} event_type the type of the MatrixEvent, e.g. "m.direct".
|
||||||
|
* @property {Object} event_content the content of the MatrixEvent.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a MatrixActions.accountData action that represents a MatrixClient `accountData`
|
||||||
|
* matrix event.
|
||||||
|
*
|
||||||
|
* @param {MatrixClient} matrixClient the matrix client.
|
||||||
|
* @param {MatrixEvent} accountDataEvent the account data event.
|
||||||
|
* @returns {AccountDataAction} an action of type MatrixActions.accountData.
|
||||||
|
*/
|
||||||
|
function createAccountDataAction(matrixClient, accountDataEvent) {
|
||||||
|
return {
|
||||||
|
action: 'MatrixActions.accountData',
|
||||||
|
event: accountDataEvent,
|
||||||
|
event_type: accountDataEvent.getType(),
|
||||||
|
event_content: accountDataEvent.getContent(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This object is responsible for dispatching actions when certain events are emitted by
|
||||||
|
* the given MatrixClient.
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
// A list of callbacks to call to unregister all listeners added
|
||||||
|
_matrixClientListenersStop: [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start listening to certain events from the MatrixClient and dispatch actions when
|
||||||
|
* they are emitted.
|
||||||
|
* @param {MatrixClient} matrixClient the MatrixClient to listen to events from
|
||||||
|
*/
|
||||||
|
start(matrixClient) {
|
||||||
|
this._addMatrixClientListener(matrixClient, 'sync', createSyncAction);
|
||||||
|
this._addMatrixClientListener(matrixClient, 'accountData', createAccountDataAction);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start listening to events of type eventName on matrixClient and when they are emitted,
|
||||||
|
* dispatch an action created by the actionCreator function.
|
||||||
|
* @param {MatrixClient} matrixClient a MatrixClient to register a listener with.
|
||||||
|
* @param {string} eventName the event to listen to on MatrixClient.
|
||||||
|
* @param {function} actionCreator a function that should return an action to dispatch
|
||||||
|
* when given the MatrixClient as an argument as well as
|
||||||
|
* arguments emitted in the MatrixClient event.
|
||||||
|
*/
|
||||||
|
_addMatrixClientListener(matrixClient, eventName, actionCreator) {
|
||||||
|
const listener = (...args) => {
|
||||||
|
dis.dispatch(actionCreator(matrixClient, ...args));
|
||||||
|
};
|
||||||
|
matrixClient.on(eventName, listener);
|
||||||
|
this._matrixClientListenersStop.push(() => {
|
||||||
|
matrixClient.removeListener(eventName, listener);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop listening to events.
|
||||||
|
*/
|
||||||
|
stop() {
|
||||||
|
this._matrixClientListenersStop.forEach((stopListener) => stopListener());
|
||||||
|
},
|
||||||
|
};
|
47
src/actions/TagOrderActions.js
Normal file
47
src/actions/TagOrderActions.js
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 New Vector 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 Analytics from '../Analytics';
|
||||||
|
import { asyncAction } from './actionCreators';
|
||||||
|
import TagOrderStore from '../stores/TagOrderStore';
|
||||||
|
|
||||||
|
const TagOrderActions = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an action thunk that will do an asynchronous request to
|
||||||
|
* commit TagOrderStore.getOrderedTags() to account data and dispatch
|
||||||
|
* actions to indicate the status of the request.
|
||||||
|
*
|
||||||
|
* @param {MatrixClient} matrixClient the matrix client to set the
|
||||||
|
* account data on.
|
||||||
|
* @returns {function} an action thunk that will dispatch actions
|
||||||
|
* indicating the status of the request.
|
||||||
|
* @see asyncAction
|
||||||
|
*/
|
||||||
|
TagOrderActions.commitTagOrdering = function(matrixClient) {
|
||||||
|
return asyncAction('TagOrderActions.commitTagOrdering', () => {
|
||||||
|
// Only commit tags if the state is ready, i.e. not null
|
||||||
|
const tags = TagOrderStore.getOrderedTags();
|
||||||
|
if (!tags) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Analytics.trackEvent('TagOrderActions', 'commitTagOrdering');
|
||||||
|
return matrixClient.setAccountData('im.vector.web.tag_ordering', {tags});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TagOrderActions;
|
41
src/actions/actionCreators.js
Normal file
41
src/actions/actionCreators.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 New Vector 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an action thunk that will dispatch actions indicating the current
|
||||||
|
* status of the Promise returned by fn.
|
||||||
|
*
|
||||||
|
* @param {string} id the id to give the dispatched actions. This is given a
|
||||||
|
* suffix determining whether it is pending, successful or
|
||||||
|
* a failure.
|
||||||
|
* @param {function} fn a function that returns a Promise.
|
||||||
|
* @returns {function} an action thunk - a function that uses its single
|
||||||
|
* argument as a dispatch function to dispatch the
|
||||||
|
* following actions:
|
||||||
|
* `${id}.pending` and either
|
||||||
|
* `${id}.success` or
|
||||||
|
* `${id}.failure`.
|
||||||
|
*/
|
||||||
|
export function asyncAction(id, fn) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch({action: id + '.pending'});
|
||||||
|
fn().then((result) => {
|
||||||
|
dispatch({action: id + '.success', result});
|
||||||
|
}).catch((err) => {
|
||||||
|
dispatch({action: id + '.failure', err});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const React = require("react");
|
const React = require("react");
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
const sdk = require('../../../index');
|
const sdk = require('../../../index');
|
||||||
const MatrixClientPeg = require("../../../MatrixClientPeg");
|
const MatrixClientPeg = require("../../../MatrixClientPeg");
|
||||||
|
@ -23,8 +24,8 @@ module.exports = React.createClass({
|
||||||
displayName: 'EncryptedEventDialog',
|
displayName: 'EncryptedEventDialog',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
event: React.PropTypes.object.isRequired,
|
event: PropTypes.object.isRequired,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import FileSaver from 'file-saver';
|
import FileSaver from 'file-saver';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
import * as Matrix from 'matrix-js-sdk';
|
import * as Matrix from 'matrix-js-sdk';
|
||||||
|
@ -29,8 +30,8 @@ export default React.createClass({
|
||||||
displayName: 'ExportE2eKeysDialog',
|
displayName: 'ExportE2eKeysDialog',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
|
matrixClient: PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import * as Matrix from 'matrix-js-sdk';
|
import * as Matrix from 'matrix-js-sdk';
|
||||||
import * as MegolmExportEncryption from '../../../utils/MegolmExportEncryption';
|
import * as MegolmExportEncryption from '../../../utils/MegolmExportEncryption';
|
||||||
|
@ -40,8 +41,8 @@ export default React.createClass({
|
||||||
displayName: 'ImportE2eKeysDialog',
|
displayName: 'ImportE2eKeysDialog',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
|
matrixClient: PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
/* These were earlier stateless functional components but had to be converted
|
/* These were earlier stateless functional components but had to be converted
|
||||||
|
@ -42,10 +43,10 @@ export class TextualCompletion extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TextualCompletion.propTypes = {
|
TextualCompletion.propTypes = {
|
||||||
title: React.PropTypes.string,
|
title: PropTypes.string,
|
||||||
subtitle: React.PropTypes.string,
|
subtitle: PropTypes.string,
|
||||||
description: React.PropTypes.string,
|
description: PropTypes.string,
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class PillCompletion extends React.Component {
|
export class PillCompletion extends React.Component {
|
||||||
|
@ -69,9 +70,9 @@ export class PillCompletion extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PillCompletion.propTypes = {
|
PillCompletion.propTypes = {
|
||||||
title: React.PropTypes.string,
|
title: PropTypes.string,
|
||||||
subtitle: React.PropTypes.string,
|
subtitle: PropTypes.string,
|
||||||
description: React.PropTypes.string,
|
description: PropTypes.string,
|
||||||
initialComponent: React.PropTypes.element,
|
initialComponent: PropTypes.element,
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,9 +54,11 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
if (MatrixClientPeg.get()) {
|
||||||
MatrixClientPeg.get().removeListener("Room.timeline", this._onRoomTimelineBound);
|
MatrixClientPeg.get().removeListener("Room.timeline", this._onRoomTimelineBound);
|
||||||
MatrixClientPeg.get().removeListener("RoomState.members", this._onRoomStateMemberBound);
|
MatrixClientPeg.get().removeListener("RoomState.members", this._onRoomStateMemberBound);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_onRoomTimeline(ev, room, toStartOfTimeline, removed, data) {
|
_onRoomTimeline(ev, room, toStartOfTimeline, removed, data) {
|
||||||
if (!room) return;
|
if (!room) return;
|
||||||
|
|
|
@ -20,6 +20,7 @@ limitations under the License.
|
||||||
const classNames = require('classnames');
|
const classNames = require('classnames');
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require('react-dom');
|
const ReactDOM = require('react-dom');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
// Shamelessly ripped off Modal.js. There's probably a better way
|
// Shamelessly ripped off Modal.js. There's probably a better way
|
||||||
// of doing reusable widgets like dialog boxes & menus where we go and
|
// of doing reusable widgets like dialog boxes & menus where we go and
|
||||||
|
@ -29,11 +30,11 @@ module.exports = {
|
||||||
ContextualMenuContainerId: "mx_ContextualMenu_Container",
|
ContextualMenuContainerId: "mx_ContextualMenu_Container",
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
menuWidth: React.PropTypes.number,
|
menuWidth: PropTypes.number,
|
||||||
menuHeight: React.PropTypes.number,
|
menuHeight: PropTypes.number,
|
||||||
chevronOffset: React.PropTypes.number,
|
chevronOffset: PropTypes.number,
|
||||||
menuColour: React.PropTypes.string,
|
menuColour: PropTypes.string,
|
||||||
chevronFace: React.PropTypes.string, // top, bottom, left, right
|
chevronFace: PropTypes.string, // top, bottom, left, right
|
||||||
},
|
},
|
||||||
|
|
||||||
getOrCreateContainer: function() {
|
getOrCreateContainer: function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
import MatrixClientPeg from '../../MatrixClientPeg';
|
||||||
|
@ -30,8 +31,8 @@ module.exports = React.createClass({
|
||||||
displayName: 'CreateRoom',
|
displayName: 'CreateRoom',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onRoomCreated: React.PropTypes.func,
|
onRoomCreated: PropTypes.func,
|
||||||
collapsedRhs: React.PropTypes.bool,
|
collapsedRhs: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
phases: {
|
phases: {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import Matrix from 'matrix-js-sdk';
|
import Matrix from 'matrix-js-sdk';
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
|
@ -28,7 +29,7 @@ const FilePanel = React.createClass({
|
||||||
displayName: 'FilePanel',
|
displayName: 'FilePanel',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
roomId: React.PropTypes.string.isRequired,
|
roomId: PropTypes.string.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -391,7 +391,7 @@ const FeaturedUser = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
const GroupContext = {
|
const GroupContext = {
|
||||||
groupStore: React.PropTypes.instanceOf(GroupStore).isRequired,
|
groupStore: PropTypes.instanceOf(GroupStore).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
CategoryRoomList.contextTypes = GroupContext;
|
CategoryRoomList.contextTypes = GroupContext;
|
||||||
|
@ -409,7 +409,7 @@ export default React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
childContextTypes: {
|
childContextTypes: {
|
||||||
groupStore: React.PropTypes.instanceOf(GroupStore),
|
groupStore: PropTypes.instanceOf(GroupStore),
|
||||||
},
|
},
|
||||||
|
|
||||||
getChildContext: function() {
|
getChildContext: function() {
|
||||||
|
|
|
@ -18,6 +18,7 @@ import Matrix from 'matrix-js-sdk';
|
||||||
const InteractiveAuth = Matrix.InteractiveAuth;
|
const InteractiveAuth = Matrix.InteractiveAuth;
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import {getEntryComponentForLoginType} from '../views/login/InteractiveAuthEntryComponents';
|
import {getEntryComponentForLoginType} from '../views/login/InteractiveAuthEntryComponents';
|
||||||
|
|
||||||
|
@ -26,18 +27,18 @@ export default React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// matrix client to use for UI auth requests
|
// matrix client to use for UI auth requests
|
||||||
matrixClient: React.PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
|
|
||||||
// response from initial request. If not supplied, will do a request on
|
// response from initial request. If not supplied, will do a request on
|
||||||
// mount.
|
// mount.
|
||||||
authData: React.PropTypes.shape({
|
authData: PropTypes.shape({
|
||||||
flows: React.PropTypes.array,
|
flows: PropTypes.array,
|
||||||
params: React.PropTypes.object,
|
params: PropTypes.object,
|
||||||
session: React.PropTypes.string,
|
session: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// callback
|
// callback
|
||||||
makeRequest: React.PropTypes.func.isRequired,
|
makeRequest: PropTypes.func.isRequired,
|
||||||
|
|
||||||
// callback called when the auth process has finished,
|
// callback called when the auth process has finished,
|
||||||
// successfully or unsuccessfully.
|
// successfully or unsuccessfully.
|
||||||
|
@ -51,22 +52,22 @@ export default React.createClass({
|
||||||
// the auth session.
|
// the auth session.
|
||||||
// * clientSecret {string} The client secret used in auth
|
// * clientSecret {string} The client secret used in auth
|
||||||
// sessions with the ID server.
|
// sessions with the ID server.
|
||||||
onAuthFinished: React.PropTypes.func.isRequired,
|
onAuthFinished: PropTypes.func.isRequired,
|
||||||
|
|
||||||
// Inputs provided by the user to the auth process
|
// Inputs provided by the user to the auth process
|
||||||
// and used by various stages. As passed to js-sdk
|
// and used by various stages. As passed to js-sdk
|
||||||
// interactive-auth
|
// interactive-auth
|
||||||
inputs: React.PropTypes.object,
|
inputs: PropTypes.object,
|
||||||
|
|
||||||
// As js-sdk interactive-auth
|
// As js-sdk interactive-auth
|
||||||
makeRegistrationUrl: React.PropTypes.func,
|
makeRegistrationUrl: PropTypes.func,
|
||||||
sessionId: React.PropTypes.string,
|
sessionId: PropTypes.string,
|
||||||
clientSecret: React.PropTypes.string,
|
clientSecret: PropTypes.string,
|
||||||
emailSid: React.PropTypes.string,
|
emailSid: PropTypes.string,
|
||||||
|
|
||||||
// If true, poll to see if the auth flow has been completed
|
// If true, poll to see if the auth flow has been completed
|
||||||
// out-of-band
|
// out-of-band
|
||||||
poll: React.PropTypes.bool,
|
poll: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -18,6 +18,9 @@ limitations under the License.
|
||||||
|
|
||||||
import * as Matrix from 'matrix-js-sdk';
|
import * as Matrix from 'matrix-js-sdk';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { DragDropContext } from 'react-dnd';
|
||||||
|
import HTML5Backend from 'react-dnd-html5-backend';
|
||||||
|
|
||||||
import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
|
import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
|
||||||
import Notifier from '../../Notifier';
|
import Notifier from '../../Notifier';
|
||||||
|
@ -38,27 +41,27 @@ import SettingsStore from "../../settings/SettingsStore";
|
||||||
*
|
*
|
||||||
* Components mounted below us can access the matrix client via the react context.
|
* Components mounted below us can access the matrix client via the react context.
|
||||||
*/
|
*/
|
||||||
export default React.createClass({
|
const LoggedInView = React.createClass({
|
||||||
displayName: 'LoggedInView',
|
displayName: 'LoggedInView',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
|
matrixClient: PropTypes.instanceOf(Matrix.MatrixClient).isRequired,
|
||||||
page_type: React.PropTypes.string.isRequired,
|
page_type: PropTypes.string.isRequired,
|
||||||
onRoomCreated: React.PropTypes.func,
|
onRoomCreated: PropTypes.func,
|
||||||
onUserSettingsClose: React.PropTypes.func,
|
onUserSettingsClose: PropTypes.func,
|
||||||
|
|
||||||
// Called with the credentials of a registered user (if they were a ROU that
|
// Called with the credentials of a registered user (if they were a ROU that
|
||||||
// transitioned to PWLU)
|
// transitioned to PWLU)
|
||||||
onRegistered: React.PropTypes.func,
|
onRegistered: PropTypes.func,
|
||||||
|
|
||||||
teamToken: React.PropTypes.string,
|
teamToken: PropTypes.string,
|
||||||
|
|
||||||
// and lots and lots of other stuff.
|
// and lots and lots of other stuff.
|
||||||
},
|
},
|
||||||
|
|
||||||
childContextTypes: {
|
childContextTypes: {
|
||||||
matrixClient: React.PropTypes.instanceOf(Matrix.MatrixClient),
|
matrixClient: PropTypes.instanceOf(Matrix.MatrixClient),
|
||||||
authCache: React.PropTypes.object,
|
authCache: PropTypes.object,
|
||||||
},
|
},
|
||||||
|
|
||||||
getChildContext: function() {
|
getChildContext: function() {
|
||||||
|
@ -331,7 +334,6 @@ export default React.createClass({
|
||||||
<div className={bodyClasses}>
|
<div className={bodyClasses}>
|
||||||
{ SettingsStore.isFeatureEnabled("feature_tag_panel") ? <TagPanel /> : <div /> }
|
{ SettingsStore.isFeatureEnabled("feature_tag_panel") ? <TagPanel /> : <div /> }
|
||||||
<LeftPanel
|
<LeftPanel
|
||||||
selectedRoom={this.props.currentRoomId}
|
|
||||||
collapsed={this.props.collapseLhs || false}
|
collapsed={this.props.collapseLhs || false}
|
||||||
disabled={this.props.leftDisabled}
|
disabled={this.props.leftDisabled}
|
||||||
/>
|
/>
|
||||||
|
@ -344,3 +346,5 @@ export default React.createClass({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default DragDropContext(HTML5Backend)(LoggedInView);
|
||||||
|
|
|
@ -19,6 +19,7 @@ limitations under the License.
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import Matrix from "matrix-js-sdk";
|
import Matrix from "matrix-js-sdk";
|
||||||
|
|
||||||
import Analytics from "../../Analytics";
|
import Analytics from "../../Analytics";
|
||||||
|
@ -40,7 +41,6 @@ require('../../stores/LifecycleStore');
|
||||||
import PageTypes from '../../PageTypes';
|
import PageTypes from '../../PageTypes';
|
||||||
|
|
||||||
import createRoom from "../../createRoom";
|
import createRoom from "../../createRoom";
|
||||||
import * as UDEHandler from '../../UnknownDeviceErrorHandler';
|
|
||||||
import KeyRequestHandler from '../../KeyRequestHandler';
|
import KeyRequestHandler from '../../KeyRequestHandler';
|
||||||
import { _t, getCurrentLanguage } from '../../languageHandler';
|
import { _t, getCurrentLanguage } from '../../languageHandler';
|
||||||
import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
|
import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
|
||||||
|
@ -84,7 +84,7 @@ const ONBOARDING_FLOW_STARTERS = [
|
||||||
'view_create_group',
|
'view_create_group',
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = React.createClass({
|
export default React.createClass({
|
||||||
// we export this so that the integration tests can use it :-S
|
// we export this so that the integration tests can use it :-S
|
||||||
statics: {
|
statics: {
|
||||||
VIEWS: VIEWS,
|
VIEWS: VIEWS,
|
||||||
|
@ -93,38 +93,38 @@ module.exports = React.createClass({
|
||||||
displayName: 'MatrixChat',
|
displayName: 'MatrixChat',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
config: React.PropTypes.object,
|
config: PropTypes.object,
|
||||||
ConferenceHandler: React.PropTypes.any,
|
ConferenceHandler: PropTypes.any,
|
||||||
onNewScreen: React.PropTypes.func,
|
onNewScreen: PropTypes.func,
|
||||||
registrationUrl: React.PropTypes.string,
|
registrationUrl: PropTypes.string,
|
||||||
enableGuest: React.PropTypes.bool,
|
enableGuest: PropTypes.bool,
|
||||||
|
|
||||||
// the queryParams extracted from the [real] query-string of the URI
|
// the queryParams extracted from the [real] query-string of the URI
|
||||||
realQueryParams: React.PropTypes.object,
|
realQueryParams: PropTypes.object,
|
||||||
|
|
||||||
// the initial queryParams extracted from the hash-fragment of the URI
|
// the initial queryParams extracted from the hash-fragment of the URI
|
||||||
startingFragmentQueryParams: React.PropTypes.object,
|
startingFragmentQueryParams: PropTypes.object,
|
||||||
|
|
||||||
// called when we have completed a token login
|
// called when we have completed a token login
|
||||||
onTokenLoginCompleted: React.PropTypes.func,
|
onTokenLoginCompleted: PropTypes.func,
|
||||||
|
|
||||||
// Represents the screen to display as a result of parsing the initial
|
// Represents the screen to display as a result of parsing the initial
|
||||||
// window.location
|
// window.location
|
||||||
initialScreenAfterLogin: React.PropTypes.shape({
|
initialScreenAfterLogin: PropTypes.shape({
|
||||||
screen: React.PropTypes.string.isRequired,
|
screen: PropTypes.string.isRequired,
|
||||||
params: React.PropTypes.object,
|
params: PropTypes.object,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// displayname, if any, to set on the device when logging
|
// displayname, if any, to set on the device when logging
|
||||||
// in/registering.
|
// in/registering.
|
||||||
defaultDeviceDisplayName: React.PropTypes.string,
|
defaultDeviceDisplayName: PropTypes.string,
|
||||||
|
|
||||||
// A function that makes a registration URL
|
// A function that makes a registration URL
|
||||||
makeRegistrationUrl: React.PropTypes.func.isRequired,
|
makeRegistrationUrl: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
childContextTypes: {
|
childContextTypes: {
|
||||||
appConfig: React.PropTypes.object,
|
appConfig: PropTypes.object,
|
||||||
},
|
},
|
||||||
|
|
||||||
AuxPanel: {
|
AuxPanel: {
|
||||||
|
@ -295,7 +295,6 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
UDEHandler.startListening();
|
|
||||||
|
|
||||||
this.focusComposer = false;
|
this.focusComposer = false;
|
||||||
|
|
||||||
|
@ -361,7 +360,6 @@ module.exports = React.createClass({
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount: function() {
|
||||||
Lifecycle.stopMatrixClient();
|
Lifecycle.stopMatrixClient();
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
UDEHandler.stopListening();
|
|
||||||
window.removeEventListener("focus", this.onFocus);
|
window.removeEventListener("focus", this.onFocus);
|
||||||
window.removeEventListener('resize', this.handleResize);
|
window.removeEventListener('resize', this.handleResize);
|
||||||
},
|
},
|
||||||
|
@ -1068,10 +1066,10 @@ module.exports = React.createClass({
|
||||||
// this if we are not scrolled up in the view. To find out, delegate to
|
// this if we are not scrolled up in the view. To find out, delegate to
|
||||||
// the timeline panel. If the timeline panel doesn't exist, then we assume
|
// the timeline panel. If the timeline panel doesn't exist, then we assume
|
||||||
// it is safe to reset the timeline.
|
// it is safe to reset the timeline.
|
||||||
if (!self.refs.loggedInView) {
|
if (!self._loggedInView) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return self.refs.loggedInView.canResetTimelineInRoom(roomId);
|
return self._loggedInView.getDecoratedComponentInstance().canResetTimelineInRoom(roomId);
|
||||||
});
|
});
|
||||||
|
|
||||||
cli.on('sync', function(state, prevState) {
|
cli.on('sync', function(state, prevState) {
|
||||||
|
@ -1142,6 +1140,37 @@ module.exports = React.createClass({
|
||||||
room.setBlacklistUnverifiedDevices(blacklistEnabled);
|
room.setBlacklistUnverifiedDevices(blacklistEnabled);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
cli.on("crypto.warning", (type) => {
|
||||||
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
switch (type) {
|
||||||
|
case 'CRYPTO_WARNING_ACCOUNT_MIGRATED':
|
||||||
|
Modal.createTrackedDialog('Crypto migrated', '', ErrorDialog, {
|
||||||
|
title: _t('Cryptography data migrated'),
|
||||||
|
description: _t(
|
||||||
|
"A one-off migration of cryptography data has been performed. "+
|
||||||
|
"End-to-end encryption will not work if you go back to an older "+
|
||||||
|
"version of Riot. If you need to use end-to-end cryptography on "+
|
||||||
|
"an older version, log out of Riot first. To retain message history, "+
|
||||||
|
"export and re-import your keys.",
|
||||||
|
),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'CRYPTO_WARNING_OLD_VERSION_DETECTED':
|
||||||
|
Modal.createTrackedDialog('Crypto migrated', '', ErrorDialog, {
|
||||||
|
title: _t('Old cryptography data detected'),
|
||||||
|
description: _t(
|
||||||
|
"Data from an older version of Riot has been detected. "+
|
||||||
|
"This will have caused end-to-end cryptography to malfunction "+
|
||||||
|
"in the older version. End-to-end encrypted messages exchanged "+
|
||||||
|
"recently whilst using the older version may not be decryptable "+
|
||||||
|
"in this version. This may also cause messages exchanged with this "+
|
||||||
|
"version to fail. If you experience problems, log out and back in "+
|
||||||
|
"again. To retain message history, export and re-import your keys.",
|
||||||
|
),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1398,13 +1427,6 @@ module.exports = React.createClass({
|
||||||
cli.sendEvent(roomId, event.getType(), event.getContent()).done(() => {
|
cli.sendEvent(roomId, event.getType(), event.getContent()).done(() => {
|
||||||
dis.dispatch({action: 'message_sent'});
|
dis.dispatch({action: 'message_sent'});
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
if (err.name === 'UnknownDeviceError') {
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'unknown_device_error',
|
|
||||||
err: err,
|
|
||||||
room: cli.getRoom(roomId),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
dis.dispatch({action: 'message_send_failed'});
|
dis.dispatch({action: 'message_send_failed'});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -1466,6 +1488,10 @@ module.exports = React.createClass({
|
||||||
return this.props.makeRegistrationUrl(params);
|
return this.props.makeRegistrationUrl(params);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_collectLoggedInView: function(ref) {
|
||||||
|
this._loggedInView = ref;
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
|
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
|
||||||
|
|
||||||
|
@ -1498,7 +1524,7 @@ module.exports = React.createClass({
|
||||||
*/
|
*/
|
||||||
const LoggedInView = sdk.getComponent('structures.LoggedInView');
|
const LoggedInView = sdk.getComponent('structures.LoggedInView');
|
||||||
return (
|
return (
|
||||||
<LoggedInView ref="loggedInView" matrixClient={MatrixClientPeg.get()}
|
<LoggedInView ref={this._collectLoggedInView} matrixClient={MatrixClientPeg.get()}
|
||||||
onRoomCreated={this.onRoomCreated}
|
onRoomCreated={this.onRoomCreated}
|
||||||
onUserSettingsClose={this.onUserSettingsClose}
|
onUserSettingsClose={this.onUserSettingsClose}
|
||||||
onRegistered={this.onRegistered}
|
onRegistered={this.onRegistered}
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import shouldHideEvent from '../../shouldHideEvent';
|
import shouldHideEvent from '../../shouldHideEvent';
|
||||||
import dis from "../../dispatcher";
|
import dis from "../../dispatcher";
|
||||||
|
@ -32,63 +33,63 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// true to give the component a 'display: none' style.
|
// true to give the component a 'display: none' style.
|
||||||
hidden: React.PropTypes.bool,
|
hidden: PropTypes.bool,
|
||||||
|
|
||||||
// true to show a spinner at the top of the timeline to indicate
|
// true to show a spinner at the top of the timeline to indicate
|
||||||
// back-pagination in progress
|
// back-pagination in progress
|
||||||
backPaginating: React.PropTypes.bool,
|
backPaginating: PropTypes.bool,
|
||||||
|
|
||||||
// true to show a spinner at the end of the timeline to indicate
|
// true to show a spinner at the end of the timeline to indicate
|
||||||
// forward-pagination in progress
|
// forward-pagination in progress
|
||||||
forwardPaginating: React.PropTypes.bool,
|
forwardPaginating: PropTypes.bool,
|
||||||
|
|
||||||
// the list of MatrixEvents to display
|
// the list of MatrixEvents to display
|
||||||
events: React.PropTypes.array.isRequired,
|
events: PropTypes.array.isRequired,
|
||||||
|
|
||||||
// ID of an event to highlight. If undefined, no event will be highlighted.
|
// ID of an event to highlight. If undefined, no event will be highlighted.
|
||||||
highlightedEventId: React.PropTypes.string,
|
highlightedEventId: PropTypes.string,
|
||||||
|
|
||||||
// Should we show URL Previews
|
// Should we show URL Previews
|
||||||
showUrlPreview: React.PropTypes.bool,
|
showUrlPreview: PropTypes.bool,
|
||||||
|
|
||||||
// event after which we should show a read marker
|
// event after which we should show a read marker
|
||||||
readMarkerEventId: React.PropTypes.string,
|
readMarkerEventId: PropTypes.string,
|
||||||
|
|
||||||
// whether the read marker should be visible
|
// whether the read marker should be visible
|
||||||
readMarkerVisible: React.PropTypes.bool,
|
readMarkerVisible: PropTypes.bool,
|
||||||
|
|
||||||
// the userid of our user. This is used to suppress the read marker
|
// the userid of our user. This is used to suppress the read marker
|
||||||
// for pending messages.
|
// for pending messages.
|
||||||
ourUserId: React.PropTypes.string,
|
ourUserId: PropTypes.string,
|
||||||
|
|
||||||
// true to suppress the date at the start of the timeline
|
// true to suppress the date at the start of the timeline
|
||||||
suppressFirstDateSeparator: React.PropTypes.bool,
|
suppressFirstDateSeparator: PropTypes.bool,
|
||||||
|
|
||||||
// whether to show read receipts
|
// whether to show read receipts
|
||||||
showReadReceipts: React.PropTypes.bool,
|
showReadReceipts: PropTypes.bool,
|
||||||
|
|
||||||
// true if updates to the event list should cause the scroll panel to
|
// true if updates to the event list should cause the scroll panel to
|
||||||
// scroll down when we are at the bottom of the window. See ScrollPanel
|
// scroll down when we are at the bottom of the window. See ScrollPanel
|
||||||
// for more details.
|
// for more details.
|
||||||
stickyBottom: React.PropTypes.bool,
|
stickyBottom: PropTypes.bool,
|
||||||
|
|
||||||
// callback which is called when the panel is scrolled.
|
// callback which is called when the panel is scrolled.
|
||||||
onScroll: React.PropTypes.func,
|
onScroll: PropTypes.func,
|
||||||
|
|
||||||
// callback which is called when more content is needed.
|
// callback which is called when more content is needed.
|
||||||
onFillRequest: React.PropTypes.func,
|
onFillRequest: PropTypes.func,
|
||||||
|
|
||||||
// className for the panel
|
// className for the panel
|
||||||
className: React.PropTypes.string.isRequired,
|
className: PropTypes.string.isRequired,
|
||||||
|
|
||||||
// shape parameter to be passed to EventTiles
|
// shape parameter to be passed to EventTiles
|
||||||
tileShape: React.PropTypes.string,
|
tileShape: PropTypes.string,
|
||||||
|
|
||||||
// show twelve hour timestamps
|
// show twelve hour timestamps
|
||||||
isTwelveHour: React.PropTypes.bool,
|
isTwelveHour: PropTypes.bool,
|
||||||
|
|
||||||
// show timestamps always
|
// show timestamps always
|
||||||
alwaysShowTimestamps: React.PropTypes.bool,
|
alwaysShowTimestamps: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import GeminiScrollbar from 'react-gemini-scrollbar';
|
import GeminiScrollbar from 'react-gemini-scrollbar';
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
|
@ -26,7 +27,7 @@ export default withMatrixClient(React.createClass({
|
||||||
displayName: 'MyGroups',
|
displayName: 'MyGroups',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
matrixClient: React.PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
|
Copyright 2017 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,77 +16,85 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Matrix from 'matrix-js-sdk';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
import WhoIsTyping from '../../WhoIsTyping';
|
import WhoIsTyping from '../../WhoIsTyping';
|
||||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
import MatrixClientPeg from '../../MatrixClientPeg';
|
||||||
import MemberAvatar from '../views/avatars/MemberAvatar';
|
import MemberAvatar from '../views/avatars/MemberAvatar';
|
||||||
|
import Resend from '../../Resend';
|
||||||
|
import { showUnknownDeviceDialogForMessages } from '../../cryptodevices';
|
||||||
|
|
||||||
const STATUS_BAR_HIDDEN = 0;
|
const STATUS_BAR_HIDDEN = 0;
|
||||||
const STATUS_BAR_EXPANDED = 1;
|
const STATUS_BAR_EXPANDED = 1;
|
||||||
const STATUS_BAR_EXPANDED_LARGE = 2;
|
const STATUS_BAR_EXPANDED_LARGE = 2;
|
||||||
|
|
||||||
|
function getUnsentMessages(room) {
|
||||||
|
if (!room) { return []; }
|
||||||
|
return room.getPendingEvents().filter(function(ev) {
|
||||||
|
return ev.status === Matrix.EventStatus.NOT_SENT;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'RoomStatusBar',
|
displayName: 'RoomStatusBar',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// the room this statusbar is representing.
|
// the room this statusbar is representing.
|
||||||
room: React.PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
|
|
||||||
// the number of messages which have arrived since we've been scrolled up
|
// the number of messages which have arrived since we've been scrolled up
|
||||||
numUnreadMessages: React.PropTypes.number,
|
numUnreadMessages: PropTypes.number,
|
||||||
|
|
||||||
// string to display when there are messages in the room which had errors on send
|
|
||||||
unsentMessageError: React.PropTypes.string,
|
|
||||||
|
|
||||||
// this is true if we are fully scrolled-down, and are looking at
|
// this is true if we are fully scrolled-down, and are looking at
|
||||||
// the end of the live timeline.
|
// the end of the live timeline.
|
||||||
atEndOfLiveTimeline: React.PropTypes.bool,
|
atEndOfLiveTimeline: PropTypes.bool,
|
||||||
|
|
||||||
// This is true when the user is alone in the room, but has also sent a message.
|
// This is true when the user is alone in the room, but has also sent a message.
|
||||||
// Used to suggest to the user to invite someone
|
// Used to suggest to the user to invite someone
|
||||||
sentMessageAndIsAlone: React.PropTypes.bool,
|
sentMessageAndIsAlone: PropTypes.bool,
|
||||||
|
|
||||||
// true if there is an active call in this room (means we show
|
// true if there is an active call in this room (means we show
|
||||||
// the 'Active Call' text in the status bar if there is nothing
|
// the 'Active Call' text in the status bar if there is nothing
|
||||||
// more interesting)
|
// more interesting)
|
||||||
hasActiveCall: React.PropTypes.bool,
|
hasActiveCall: PropTypes.bool,
|
||||||
|
|
||||||
// Number of names to display in typing indication. E.g. set to 3, will
|
// Number of names to display in typing indication. E.g. set to 3, will
|
||||||
// result in "X, Y, Z and 100 others are typing."
|
// result in "X, Y, Z and 100 others are typing."
|
||||||
whoIsTypingLimit: React.PropTypes.number,
|
whoIsTypingLimit: PropTypes.number,
|
||||||
|
|
||||||
// callback for when the user clicks on the 'resend all' button in the
|
// callback for when the user clicks on the 'resend all' button in the
|
||||||
// 'unsent messages' bar
|
// 'unsent messages' bar
|
||||||
onResendAllClick: React.PropTypes.func,
|
onResendAllClick: PropTypes.func,
|
||||||
|
|
||||||
// callback for when the user clicks on the 'cancel all' button in the
|
// callback for when the user clicks on the 'cancel all' button in the
|
||||||
// 'unsent messages' bar
|
// 'unsent messages' bar
|
||||||
onCancelAllClick: React.PropTypes.func,
|
onCancelAllClick: PropTypes.func,
|
||||||
|
|
||||||
// callback for when the user clicks on the 'invite others' button in the
|
// callback for when the user clicks on the 'invite others' button in the
|
||||||
// 'you are alone' bar
|
// 'you are alone' bar
|
||||||
onInviteClick: React.PropTypes.func,
|
onInviteClick: PropTypes.func,
|
||||||
|
|
||||||
// callback for when the user clicks on the 'stop warning me' button in the
|
// callback for when the user clicks on the 'stop warning me' button in the
|
||||||
// 'you are alone' bar
|
// 'you are alone' bar
|
||||||
onStopWarningClick: React.PropTypes.func,
|
onStopWarningClick: PropTypes.func,
|
||||||
|
|
||||||
// callback for when the user clicks on the 'scroll to bottom' button
|
// callback for when the user clicks on the 'scroll to bottom' button
|
||||||
onScrollToBottomClick: React.PropTypes.func,
|
onScrollToBottomClick: PropTypes.func,
|
||||||
|
|
||||||
// callback for when we do something that changes the size of the
|
// callback for when we do something that changes the size of the
|
||||||
// status bar. This is used to trigger a re-layout in the parent
|
// status bar. This is used to trigger a re-layout in the parent
|
||||||
// component.
|
// component.
|
||||||
onResize: React.PropTypes.func,
|
onResize: PropTypes.func,
|
||||||
|
|
||||||
// callback for when the status bar can be hidden from view, as it is
|
// callback for when the status bar can be hidden from view, as it is
|
||||||
// not displaying anything
|
// not displaying anything
|
||||||
onHidden: React.PropTypes.func,
|
onHidden: PropTypes.func,
|
||||||
|
|
||||||
// callback for when the status bar is displaying something and should
|
// callback for when the status bar is displaying something and should
|
||||||
// be visible
|
// be visible
|
||||||
onVisible: React.PropTypes.func,
|
onVisible: PropTypes.func,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
@ -98,12 +107,14 @@ module.exports = React.createClass({
|
||||||
return {
|
return {
|
||||||
syncState: MatrixClientPeg.get().getSyncState(),
|
syncState: MatrixClientPeg.get().getSyncState(),
|
||||||
usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room),
|
usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room),
|
||||||
|
unsentMessages: getUnsentMessages(this.props.room),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
MatrixClientPeg.get().on("sync", this.onSyncStateChange);
|
MatrixClientPeg.get().on("sync", this.onSyncStateChange);
|
||||||
MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
|
MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
|
||||||
|
MatrixClientPeg.get().on("Room.localEchoUpdated", this._onRoomLocalEchoUpdated);
|
||||||
|
|
||||||
this._checkSize();
|
this._checkSize();
|
||||||
},
|
},
|
||||||
|
@ -118,6 +129,7 @@ module.exports = React.createClass({
|
||||||
if (client) {
|
if (client) {
|
||||||
client.removeListener("sync", this.onSyncStateChange);
|
client.removeListener("sync", this.onSyncStateChange);
|
||||||
client.removeListener("RoomMember.typing", this.onRoomMemberTyping);
|
client.removeListener("RoomMember.typing", this.onRoomMemberTyping);
|
||||||
|
client.removeListener("Room.localEchoUpdated", this._onRoomLocalEchoUpdated);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -136,10 +148,32 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_onResendAllClick: function() {
|
||||||
|
Resend.resendUnsentEvents(this.props.room);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onCancelAllClick: function() {
|
||||||
|
Resend.cancelUnsentEvents(this.props.room);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onShowDevicesClick: function() {
|
||||||
|
showUnknownDeviceDialogForMessages(MatrixClientPeg.get(), this.props.room);
|
||||||
|
},
|
||||||
|
|
||||||
|
_onRoomLocalEchoUpdated: function(event, room, oldEventId, oldStatus) {
|
||||||
|
if (room.roomId !== this.props.room.roomId) return;
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
unsentMessages: getUnsentMessages(this.props.room),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// Check whether current size is greater than 0, if yes call props.onVisible
|
// Check whether current size is greater than 0, if yes call props.onVisible
|
||||||
_checkSize: function() {
|
_checkSize: function() {
|
||||||
if (this.props.onVisible && this._getSize()) {
|
if (this._getSize()) {
|
||||||
this.props.onVisible();
|
if (this.props.onVisible) this.props.onVisible();
|
||||||
|
} else {
|
||||||
|
if (this.props.onHidden) this.props.onHidden();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -155,7 +189,7 @@ module.exports = React.createClass({
|
||||||
this.props.sentMessageAndIsAlone
|
this.props.sentMessageAndIsAlone
|
||||||
) {
|
) {
|
||||||
return STATUS_BAR_EXPANDED;
|
return STATUS_BAR_EXPANDED;
|
||||||
} else if (this.props.unsentMessageError) {
|
} else if (this.state.unsentMessages.length > 0) {
|
||||||
return STATUS_BAR_EXPANDED_LARGE;
|
return STATUS_BAR_EXPANDED_LARGE;
|
||||||
}
|
}
|
||||||
return STATUS_BAR_HIDDEN;
|
return STATUS_BAR_HIDDEN;
|
||||||
|
@ -241,6 +275,61 @@ module.exports = React.createClass({
|
||||||
return avatars;
|
return avatars;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getUnsentMessageContent: function() {
|
||||||
|
const unsentMessages = this.state.unsentMessages;
|
||||||
|
if (!unsentMessages.length) return null;
|
||||||
|
|
||||||
|
let title;
|
||||||
|
let content;
|
||||||
|
|
||||||
|
const hasUDE = unsentMessages.some((m) => {
|
||||||
|
return m.error && m.error.name === "UnknownDeviceError";
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasUDE) {
|
||||||
|
title = _t("Message not sent due to unknown devices being present");
|
||||||
|
content = _t(
|
||||||
|
"<showDevicesText>Show devices</showDevicesText> or <cancelText>cancel all</cancelText>.",
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
'showDevicesText': (sub) => <a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this._onShowDevicesClick}>{ sub }</a>,
|
||||||
|
'cancelText': (sub) => <a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this._onCancelAllClick}>{ sub }</a>,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
unsentMessages.length === 1 &&
|
||||||
|
unsentMessages[0].error &&
|
||||||
|
unsentMessages[0].error.data &&
|
||||||
|
unsentMessages[0].error.data.error
|
||||||
|
) {
|
||||||
|
title = unsentMessages[0].error.data.error;
|
||||||
|
} else {
|
||||||
|
title = _t('%(count)s of your messages have not been sent.', { count: unsentMessages.length });
|
||||||
|
}
|
||||||
|
content = _t("%(count)s <resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. " +
|
||||||
|
"You can also select individual messages to resend or cancel.",
|
||||||
|
{ count: unsentMessages.length },
|
||||||
|
{
|
||||||
|
'resendText': (sub) =>
|
||||||
|
<a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this._onResendAllClick}>{ sub }</a>,
|
||||||
|
'cancelText': (sub) =>
|
||||||
|
<a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this._onCancelAllClick}>{ sub }</a>,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="mx_RoomStatusBar_connectionLostBar">
|
||||||
|
<img src="img/warning.svg" width="24" height="23" title={_t("Warning")} alt={_t("Warning")} />
|
||||||
|
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
||||||
|
{ title }
|
||||||
|
</div>
|
||||||
|
<div className="mx_RoomStatusBar_connectionLostBar_desc">
|
||||||
|
{ content }
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
},
|
||||||
|
|
||||||
// return suitable content for the main (text) part of the status bar.
|
// return suitable content for the main (text) part of the status bar.
|
||||||
_getContent: function() {
|
_getContent: function() {
|
||||||
const EmojiText = sdk.getComponent('elements.EmojiText');
|
const EmojiText = sdk.getComponent('elements.EmojiText');
|
||||||
|
@ -263,28 +352,8 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.unsentMessageError) {
|
if (this.state.unsentMessages.length > 0) {
|
||||||
return (
|
return this._getUnsentMessageContent();
|
||||||
<div className="mx_RoomStatusBar_connectionLostBar">
|
|
||||||
<img src="img/warning.svg" width="24" height="23" title="/!\ " alt="/!\ " />
|
|
||||||
<div className="mx_RoomStatusBar_connectionLostBar_title">
|
|
||||||
{ this.props.unsentMessageError }
|
|
||||||
</div>
|
|
||||||
<div className="mx_RoomStatusBar_connectionLostBar_desc">
|
|
||||||
{
|
|
||||||
_t("<resendText>Resend all</resendText> or <cancelText>cancel all</cancelText> now. " +
|
|
||||||
"You can also select individual messages to resend or cancel.",
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
'resendText': (sub) =>
|
|
||||||
<a className="mx_RoomStatusBar_resend_link" key="resend" onClick={this.props.onResendAllClick}>{ sub }</a>,
|
|
||||||
'cancelText': (sub) =>
|
|
||||||
<a className="mx_RoomStatusBar_resend_link" key="cancel" onClick={this.props.onCancelAllClick}>{ sub }</a>,
|
|
||||||
},
|
|
||||||
) }
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// unread count trumps who is typing since the unread count is only
|
// unread count trumps who is typing since the unread count is only
|
||||||
|
@ -342,7 +411,6 @@ module.exports = React.createClass({
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const content = this._getContent();
|
const content = this._getContent();
|
||||||
const indicator = this._getIndicator(this.state.usersTyping.length > 0);
|
const indicator = this._getIndicator(this.state.usersTyping.length > 0);
|
||||||
|
|
|
@ -24,9 +24,9 @@ import shouldHideEvent from "../../shouldHideEvent";
|
||||||
|
|
||||||
const React = require("react");
|
const React = require("react");
|
||||||
const ReactDOM = require("react-dom");
|
const ReactDOM = require("react-dom");
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
const classNames = require("classnames");
|
const classNames = require("classnames");
|
||||||
const Matrix = require("matrix-js-sdk");
|
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
|
|
||||||
const MatrixClientPeg = require("../../MatrixClientPeg");
|
const MatrixClientPeg = require("../../MatrixClientPeg");
|
||||||
|
@ -34,7 +34,6 @@ const ContentMessages = require("../../ContentMessages");
|
||||||
const Modal = require("../../Modal");
|
const Modal = require("../../Modal");
|
||||||
const sdk = require('../../index');
|
const sdk = require('../../index');
|
||||||
const CallHandler = require('../../CallHandler');
|
const CallHandler = require('../../CallHandler');
|
||||||
const Resend = require("../../Resend");
|
|
||||||
const dis = require("../../dispatcher");
|
const dis = require("../../dispatcher");
|
||||||
const Tinter = require("../../Tinter");
|
const Tinter = require("../../Tinter");
|
||||||
const rate_limited_func = require('../../ratelimitedfunc');
|
const rate_limited_func = require('../../ratelimitedfunc');
|
||||||
|
@ -60,18 +59,18 @@ if (DEBUG) {
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'RoomView',
|
displayName: 'RoomView',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
ConferenceHandler: React.PropTypes.any,
|
ConferenceHandler: PropTypes.any,
|
||||||
|
|
||||||
// Called with the credentials of a registered user (if they were a ROU that
|
// Called with the credentials of a registered user (if they were a ROU that
|
||||||
// transitioned to PWLU)
|
// transitioned to PWLU)
|
||||||
onRegistered: React.PropTypes.func,
|
onRegistered: PropTypes.func,
|
||||||
|
|
||||||
// An object representing a third party invite to join this room
|
// An object representing a third party invite to join this room
|
||||||
// Fields:
|
// Fields:
|
||||||
// * inviteSignUrl (string) The URL used to join this room from an email invite
|
// * inviteSignUrl (string) The URL used to join this room from an email invite
|
||||||
// (given as part of the link in the invite email)
|
// (given as part of the link in the invite email)
|
||||||
// * invitedEmail (string) The email address that was invited to this room
|
// * invitedEmail (string) The email address that was invited to this room
|
||||||
thirdPartyInvite: React.PropTypes.object,
|
thirdPartyInvite: PropTypes.object,
|
||||||
|
|
||||||
// Any data about the room that would normally come from the Home Server
|
// Any data about the room that would normally come from the Home Server
|
||||||
// but has been passed out-of-band, eg. the room name and avatar URL
|
// but has been passed out-of-band, eg. the room name and avatar URL
|
||||||
|
@ -82,10 +81,10 @@ module.exports = React.createClass({
|
||||||
// * avatarUrl (string) The mxc:// avatar URL for the room
|
// * avatarUrl (string) The mxc:// avatar URL for the room
|
||||||
// * inviterName (string) The display name of the person who
|
// * inviterName (string) The display name of the person who
|
||||||
// * invited us tovthe room
|
// * invited us tovthe room
|
||||||
oobData: React.PropTypes.object,
|
oobData: PropTypes.object,
|
||||||
|
|
||||||
// is the RightPanel collapsed?
|
// is the RightPanel collapsed?
|
||||||
collapsedRhs: React.PropTypes.bool,
|
collapsedRhs: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -110,7 +109,6 @@ module.exports = React.createClass({
|
||||||
draggingFile: false,
|
draggingFile: false,
|
||||||
searching: false,
|
searching: false,
|
||||||
searchResults: null,
|
searchResults: null,
|
||||||
unsentMessageError: '',
|
|
||||||
callState: null,
|
callState: null,
|
||||||
guestsCanJoin: false,
|
guestsCanJoin: false,
|
||||||
canPeek: false,
|
canPeek: false,
|
||||||
|
@ -202,7 +200,6 @@ module.exports = React.createClass({
|
||||||
if (initial) {
|
if (initial) {
|
||||||
newState.room = MatrixClientPeg.get().getRoom(newState.roomId);
|
newState.room = MatrixClientPeg.get().getRoom(newState.roomId);
|
||||||
if (newState.room) {
|
if (newState.room) {
|
||||||
newState.unsentMessageError = this._getUnsentMessageError(newState.room);
|
|
||||||
newState.showApps = this._shouldShowApps(newState.room);
|
newState.showApps = this._shouldShowApps(newState.room);
|
||||||
this._onRoomLoaded(newState.room);
|
this._onRoomLoaded(newState.room);
|
||||||
}
|
}
|
||||||
|
@ -462,11 +459,6 @@ module.exports = React.createClass({
|
||||||
case 'message_send_failed':
|
case 'message_send_failed':
|
||||||
case 'message_sent':
|
case 'message_sent':
|
||||||
this._checkIfAlone(this.state.room);
|
this._checkIfAlone(this.state.room);
|
||||||
// no break; to intentionally fall through
|
|
||||||
case 'message_send_cancelled':
|
|
||||||
this.setState({
|
|
||||||
unsentMessageError: this._getUnsentMessageError(this.state.room),
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case 'notifier_enabled':
|
case 'notifier_enabled':
|
||||||
case 'upload_failed':
|
case 'upload_failed':
|
||||||
|
@ -711,35 +703,6 @@ module.exports = React.createClass({
|
||||||
this.setState({isAlone: joinedMembers.length === 1});
|
this.setState({isAlone: joinedMembers.length === 1});
|
||||||
},
|
},
|
||||||
|
|
||||||
_getUnsentMessageError: function(room) {
|
|
||||||
const unsentMessages = this._getUnsentMessages(room);
|
|
||||||
if (!unsentMessages.length) return "";
|
|
||||||
|
|
||||||
if (
|
|
||||||
unsentMessages.length === 1 &&
|
|
||||||
unsentMessages[0].error &&
|
|
||||||
unsentMessages[0].error.data &&
|
|
||||||
unsentMessages[0].error.data.error &&
|
|
||||||
unsentMessages[0].error.name !== "UnknownDeviceError"
|
|
||||||
) {
|
|
||||||
return unsentMessages[0].error.data.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const event of unsentMessages) {
|
|
||||||
if (!event.error || event.error.name !== "UnknownDeviceError") {
|
|
||||||
return _t("Some of your messages have not been sent.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return _t("Message not sent due to unknown devices being present");
|
|
||||||
},
|
|
||||||
|
|
||||||
_getUnsentMessages: function(room) {
|
|
||||||
if (!room) { return []; }
|
|
||||||
return room.getPendingEvents().filter(function(ev) {
|
|
||||||
return ev.status === Matrix.EventStatus.NOT_SENT;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateConfCallNotification: function() {
|
_updateConfCallNotification: function() {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (!room || !this.props.ConferenceHandler) {
|
if (!room || !this.props.ConferenceHandler) {
|
||||||
|
@ -784,14 +747,6 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onResendAllClick: function() {
|
|
||||||
Resend.resendUnsentEvents(this.state.room);
|
|
||||||
},
|
|
||||||
|
|
||||||
onCancelAllClick: function() {
|
|
||||||
Resend.cancelUnsentEvents(this.state.room);
|
|
||||||
},
|
|
||||||
|
|
||||||
onInviteButtonClick: function() {
|
onInviteButtonClick: function() {
|
||||||
// call AddressPickerDialog
|
// call AddressPickerDialog
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
|
@ -900,9 +855,13 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
ev.dataTransfer.dropEffect = 'none';
|
ev.dataTransfer.dropEffect = 'none';
|
||||||
|
|
||||||
const items = ev.dataTransfer.items;
|
const items = [...ev.dataTransfer.items];
|
||||||
if (items.length == 1) {
|
if (items.length >= 1) {
|
||||||
if (items[0].kind == 'file') {
|
const isDraggingFiles = items.every(function(item) {
|
||||||
|
return item.kind == 'file';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isDraggingFiles) {
|
||||||
this.setState({ draggingFile: true });
|
this.setState({ draggingFile: true });
|
||||||
ev.dataTransfer.dropEffect = 'copy';
|
ev.dataTransfer.dropEffect = 'copy';
|
||||||
}
|
}
|
||||||
|
@ -913,10 +872,8 @@ module.exports = React.createClass({
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.setState({ draggingFile: false });
|
this.setState({ draggingFile: false });
|
||||||
const files = ev.dataTransfer.files;
|
const files = [...ev.dataTransfer.files];
|
||||||
if (files.length == 1) {
|
files.forEach(this.uploadFile);
|
||||||
this.uploadFile(files[0]);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onDragLeaveOrEnd: function(ev) {
|
onDragLeaveOrEnd: function(ev) {
|
||||||
|
@ -935,11 +892,7 @@ module.exports = React.createClass({
|
||||||
file, this.state.room.roomId, MatrixClientPeg.get(),
|
file, this.state.room.roomId, MatrixClientPeg.get(),
|
||||||
).done(undefined, (error) => {
|
).done(undefined, (error) => {
|
||||||
if (error.name === "UnknownDeviceError") {
|
if (error.name === "UnknownDeviceError") {
|
||||||
dis.dispatch({
|
// Let the staus bar handle this
|
||||||
action: 'unknown_device_error',
|
|
||||||
err: error,
|
|
||||||
room: this.state.room,
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
|
@ -1395,10 +1348,12 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onStatusBarHidden: function() {
|
onStatusBarHidden: function() {
|
||||||
if (this.unmounted) return;
|
// This is currently not desired as it is annoying if it keeps expanding and collapsing
|
||||||
|
// TODO: Find a less annoying way of hiding the status bar
|
||||||
|
/*if (this.unmounted) return;
|
||||||
this.setState({
|
this.setState({
|
||||||
statusBarVisible: false,
|
statusBarVisible: false,
|
||||||
});
|
});*/
|
||||||
},
|
},
|
||||||
|
|
||||||
showSettings: function(show) {
|
showSettings: function(show) {
|
||||||
|
@ -1571,12 +1526,9 @@ module.exports = React.createClass({
|
||||||
statusBar = <RoomStatusBar
|
statusBar = <RoomStatusBar
|
||||||
room={this.state.room}
|
room={this.state.room}
|
||||||
numUnreadMessages={this.state.numUnreadMessages}
|
numUnreadMessages={this.state.numUnreadMessages}
|
||||||
unsentMessageError={this.state.unsentMessageError}
|
|
||||||
atEndOfLiveTimeline={this.state.atEndOfLiveTimeline}
|
atEndOfLiveTimeline={this.state.atEndOfLiveTimeline}
|
||||||
sentMessageAndIsAlone={this.state.isAlone}
|
sentMessageAndIsAlone={this.state.isAlone}
|
||||||
hasActiveCall={inCall}
|
hasActiveCall={inCall}
|
||||||
onResendAllClick={this.onResendAllClick}
|
|
||||||
onCancelAllClick={this.onCancelAllClick}
|
|
||||||
onInviteClick={this.onInviteButtonClick}
|
onInviteClick={this.onInviteButtonClick}
|
||||||
onStopWarningClick={this.onStopAloneWarningClick}
|
onStopWarningClick={this.onStopAloneWarningClick}
|
||||||
onScrollToBottomClick={this.jumpToLiveTimeline}
|
onScrollToBottomClick={this.jumpToLiveTimeline}
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
const React = require("react");
|
const React = require("react");
|
||||||
const ReactDOM = require("react-dom");
|
const ReactDOM = require("react-dom");
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
const GeminiScrollbar = require('react-gemini-scrollbar');
|
const GeminiScrollbar = require('react-gemini-scrollbar');
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import { KeyCode } from '../../Keyboard';
|
import { KeyCode } from '../../Keyboard';
|
||||||
|
@ -86,7 +87,7 @@ module.exports = React.createClass({
|
||||||
* scroll down to show the new element, rather than preserving the
|
* scroll down to show the new element, rather than preserving the
|
||||||
* existing view.
|
* existing view.
|
||||||
*/
|
*/
|
||||||
stickyBottom: React.PropTypes.bool,
|
stickyBottom: PropTypes.bool,
|
||||||
|
|
||||||
/* startAtBottom: if set to true, the view is assumed to start
|
/* startAtBottom: if set to true, the view is assumed to start
|
||||||
* scrolled to the bottom.
|
* scrolled to the bottom.
|
||||||
|
@ -95,7 +96,7 @@ module.exports = React.createClass({
|
||||||
* behaviour stays the same for other uses of ScrollPanel.
|
* behaviour stays the same for other uses of ScrollPanel.
|
||||||
* If so, let's remove this parameter down the line.
|
* If so, let's remove this parameter down the line.
|
||||||
*/
|
*/
|
||||||
startAtBottom: React.PropTypes.bool,
|
startAtBottom: PropTypes.bool,
|
||||||
|
|
||||||
/* onFillRequest(backwards): a callback which is called on scroll when
|
/* onFillRequest(backwards): a callback which is called on scroll when
|
||||||
* the user nears the start (backwards = true) or end (backwards =
|
* the user nears the start (backwards = true) or end (backwards =
|
||||||
|
@ -110,7 +111,7 @@ module.exports = React.createClass({
|
||||||
* directon (at this time) - which will stop the pagination cycle until
|
* directon (at this time) - which will stop the pagination cycle until
|
||||||
* the user scrolls again.
|
* the user scrolls again.
|
||||||
*/
|
*/
|
||||||
onFillRequest: React.PropTypes.func,
|
onFillRequest: PropTypes.func,
|
||||||
|
|
||||||
/* onUnfillRequest(backwards): a callback which is called on scroll when
|
/* onUnfillRequest(backwards): a callback which is called on scroll when
|
||||||
* there are children elements that are far out of view and could be removed
|
* there are children elements that are far out of view and could be removed
|
||||||
|
@ -121,24 +122,24 @@ module.exports = React.createClass({
|
||||||
* first element to remove if removing from the front/bottom, and last element
|
* first element to remove if removing from the front/bottom, and last element
|
||||||
* to remove if removing from the back/top.
|
* to remove if removing from the back/top.
|
||||||
*/
|
*/
|
||||||
onUnfillRequest: React.PropTypes.func,
|
onUnfillRequest: PropTypes.func,
|
||||||
|
|
||||||
/* onScroll: a callback which is called whenever any scroll happens.
|
/* onScroll: a callback which is called whenever any scroll happens.
|
||||||
*/
|
*/
|
||||||
onScroll: React.PropTypes.func,
|
onScroll: PropTypes.func,
|
||||||
|
|
||||||
/* onResize: a callback which is called whenever the Gemini scroll
|
/* onResize: a callback which is called whenever the Gemini scroll
|
||||||
* panel is resized
|
* panel is resized
|
||||||
*/
|
*/
|
||||||
onResize: React.PropTypes.func,
|
onResize: PropTypes.func,
|
||||||
|
|
||||||
/* className: classnames to add to the top-level div
|
/* className: classnames to add to the top-level div
|
||||||
*/
|
*/
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
|
||||||
/* style: styles to add to the top-level div
|
/* style: styles to add to the top-level div
|
||||||
*/
|
*/
|
||||||
style: React.PropTypes.object,
|
style: PropTypes.object,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -17,79 +17,15 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import classNames from 'classnames';
|
import TagOrderStore from '../../stores/TagOrderStore';
|
||||||
import FilterStore from '../../stores/FilterStore';
|
|
||||||
import FlairStore from '../../stores/FlairStore';
|
import GroupActions from '../../actions/GroupActions';
|
||||||
|
import TagOrderActions from '../../actions/TagOrderActions';
|
||||||
|
|
||||||
import sdk from '../../index';
|
import sdk from '../../index';
|
||||||
import dis from '../../dispatcher';
|
import dis from '../../dispatcher';
|
||||||
import { isOnlyCtrlOrCmdKeyEvent } from '../../Keyboard';
|
|
||||||
|
|
||||||
const TagTile = React.createClass({
|
const TagPanel = React.createClass({
|
||||||
displayName: 'TagTile',
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
groupProfile: PropTypes.object,
|
|
||||||
},
|
|
||||||
|
|
||||||
contextTypes: {
|
|
||||||
matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
hover: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onClick: function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'select_tag',
|
|
||||||
tag: this.props.groupProfile.groupId,
|
|
||||||
ctrlOrCmdKey: isOnlyCtrlOrCmdKeyEvent(e),
|
|
||||||
shiftKey: e.shiftKey,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onMouseOver: function() {
|
|
||||||
this.setState({hover: true});
|
|
||||||
},
|
|
||||||
|
|
||||||
onMouseOut: function() {
|
|
||||||
this.setState({hover: false});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
|
||||||
const RoomTooltip = sdk.getComponent('rooms.RoomTooltip');
|
|
||||||
const profile = this.props.groupProfile || {};
|
|
||||||
const name = profile.name || profile.groupId;
|
|
||||||
const avatarHeight = 35;
|
|
||||||
|
|
||||||
const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp(
|
|
||||||
profile.avatarUrl, avatarHeight, avatarHeight, "crop",
|
|
||||||
) : null;
|
|
||||||
|
|
||||||
const className = classNames({
|
|
||||||
mx_TagTile: true,
|
|
||||||
mx_TagTile_selected: this.props.selected,
|
|
||||||
});
|
|
||||||
|
|
||||||
const tip = this.state.hover ?
|
|
||||||
<RoomTooltip className="mx_TagTile_tooltip" label={name} /> :
|
|
||||||
<div />;
|
|
||||||
return <AccessibleButton className={className} onClick={this.onClick}>
|
|
||||||
<div className="mx_TagTile_avatar" onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
|
|
||||||
<BaseAvatar name={name} url={httpUrl} width={avatarHeight} height={avatarHeight} />
|
|
||||||
{ tip }
|
|
||||||
</div>
|
|
||||||
</AccessibleButton>;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default React.createClass({
|
|
||||||
displayName: 'TagPanel',
|
displayName: 'TagPanel',
|
||||||
|
|
||||||
contextTypes: {
|
contextTypes: {
|
||||||
|
@ -98,7 +34,7 @@ export default React.createClass({
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
joinedGroupProfiles: [],
|
orderedTags: [],
|
||||||
selectedTags: [],
|
selectedTags: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -107,16 +43,17 @@ export default React.createClass({
|
||||||
this.unmounted = false;
|
this.unmounted = false;
|
||||||
this.context.matrixClient.on("Group.myMembership", this._onGroupMyMembership);
|
this.context.matrixClient.on("Group.myMembership", this._onGroupMyMembership);
|
||||||
|
|
||||||
this._filterStoreToken = FilterStore.addListener(() => {
|
this._tagOrderStoreToken = TagOrderStore.addListener(() => {
|
||||||
if (this.unmounted) {
|
if (this.unmounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
selectedTags: FilterStore.getSelectedTags(),
|
orderedTags: TagOrderStore.getOrderedTags() || [],
|
||||||
|
selectedTags: TagOrderStore.getSelectedTags(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// This could be done by anything with a matrix client
|
||||||
this._fetchJoinedRooms();
|
dis.dispatch(GroupActions.fetchJoinedGroups(this.context.matrixClient));
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -129,7 +66,7 @@ export default React.createClass({
|
||||||
|
|
||||||
_onGroupMyMembership() {
|
_onGroupMyMembership() {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
this._fetchJoinedRooms();
|
dis.dispatch(GroupActions.fetchJoinedGroups(this.context.matrixClient));
|
||||||
},
|
},
|
||||||
|
|
||||||
onClick() {
|
onClick() {
|
||||||
|
@ -141,27 +78,21 @@ export default React.createClass({
|
||||||
dis.dispatch({action: 'view_create_group'});
|
dis.dispatch({action: 'view_create_group'});
|
||||||
},
|
},
|
||||||
|
|
||||||
async _fetchJoinedRooms() {
|
onTagTileEndDrag() {
|
||||||
const joinedGroupResponse = await this.context.matrixClient.getJoinedGroups();
|
dis.dispatch(TagOrderActions.commitTagOrdering(this.context.matrixClient));
|
||||||
const joinedGroupIds = joinedGroupResponse.groups;
|
|
||||||
const joinedGroupProfiles = await Promise.all(joinedGroupIds.map(
|
|
||||||
(groupId) => FlairStore.getGroupProfileCached(this.context.matrixClient, groupId),
|
|
||||||
));
|
|
||||||
dis.dispatch({
|
|
||||||
action: 'all_tags',
|
|
||||||
tags: joinedGroupIds,
|
|
||||||
});
|
|
||||||
this.setState({joinedGroupProfiles});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||||
const tags = this.state.joinedGroupProfiles.map((groupProfile, index) => {
|
const DNDTagTile = sdk.getComponent('elements.DNDTagTile');
|
||||||
return <TagTile
|
|
||||||
key={groupProfile.groupId + '_' + index}
|
const tags = this.state.orderedTags.map((tag, index) => {
|
||||||
groupProfile={groupProfile}
|
return <DNDTagTile
|
||||||
selected={this.state.selectedTags.includes(groupProfile.groupId)}
|
key={tag + '_' + index}
|
||||||
|
tag={tag}
|
||||||
|
selected={this.state.selectedTags.includes(tag)}
|
||||||
|
onEndDrag={this.onTagTileEndDrag}
|
||||||
/>;
|
/>;
|
||||||
});
|
});
|
||||||
return <div className="mx_TagPanel" onClick={this.onClick}>
|
return <div className="mx_TagPanel" onClick={this.onClick}>
|
||||||
|
@ -174,3 +105,4 @@ export default React.createClass({
|
||||||
</div>;
|
</div>;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
export default TagPanel;
|
||||||
|
|
|
@ -19,6 +19,7 @@ import SettingsStore from "../../settings/SettingsStore";
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require("react-dom");
|
const ReactDOM = require("react-dom");
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
|
|
||||||
const Matrix = require("matrix-js-sdk");
|
const Matrix = require("matrix-js-sdk");
|
||||||
|
@ -58,49 +59,49 @@ var TimelinePanel = React.createClass({
|
||||||
// representing. This may or may not have a room, depending on what it's
|
// representing. This may or may not have a room, depending on what it's
|
||||||
// a timeline representing. If it has a room, we maintain RRs etc for
|
// a timeline representing. If it has a room, we maintain RRs etc for
|
||||||
// that room.
|
// that room.
|
||||||
timelineSet: React.PropTypes.object.isRequired,
|
timelineSet: PropTypes.object.isRequired,
|
||||||
|
|
||||||
showReadReceipts: React.PropTypes.bool,
|
showReadReceipts: PropTypes.bool,
|
||||||
// Enable managing RRs and RMs. These require the timelineSet to have a room.
|
// Enable managing RRs and RMs. These require the timelineSet to have a room.
|
||||||
manageReadReceipts: React.PropTypes.bool,
|
manageReadReceipts: PropTypes.bool,
|
||||||
manageReadMarkers: React.PropTypes.bool,
|
manageReadMarkers: PropTypes.bool,
|
||||||
|
|
||||||
// true to give the component a 'display: none' style.
|
// true to give the component a 'display: none' style.
|
||||||
hidden: React.PropTypes.bool,
|
hidden: PropTypes.bool,
|
||||||
|
|
||||||
// ID of an event to highlight. If undefined, no event will be highlighted.
|
// ID of an event to highlight. If undefined, no event will be highlighted.
|
||||||
// typically this will be either 'eventId' or undefined.
|
// typically this will be either 'eventId' or undefined.
|
||||||
highlightedEventId: React.PropTypes.string,
|
highlightedEventId: PropTypes.string,
|
||||||
|
|
||||||
// id of an event to jump to. If not given, will go to the end of the
|
// id of an event to jump to. If not given, will go to the end of the
|
||||||
// live timeline.
|
// live timeline.
|
||||||
eventId: React.PropTypes.string,
|
eventId: PropTypes.string,
|
||||||
|
|
||||||
// where to position the event given by eventId, in pixels from the
|
// where to position the event given by eventId, in pixels from the
|
||||||
// bottom of the viewport. If not given, will try to put the event
|
// bottom of the viewport. If not given, will try to put the event
|
||||||
// half way down the viewport.
|
// half way down the viewport.
|
||||||
eventPixelOffset: React.PropTypes.number,
|
eventPixelOffset: PropTypes.number,
|
||||||
|
|
||||||
// Should we show URL Previews
|
// Should we show URL Previews
|
||||||
showUrlPreview: React.PropTypes.bool,
|
showUrlPreview: PropTypes.bool,
|
||||||
|
|
||||||
// callback which is called when the panel is scrolled.
|
// callback which is called when the panel is scrolled.
|
||||||
onScroll: React.PropTypes.func,
|
onScroll: PropTypes.func,
|
||||||
|
|
||||||
// callback which is called when the read-up-to mark is updated.
|
// callback which is called when the read-up-to mark is updated.
|
||||||
onReadMarkerUpdated: React.PropTypes.func,
|
onReadMarkerUpdated: PropTypes.func,
|
||||||
|
|
||||||
// maximum number of events to show in a timeline
|
// maximum number of events to show in a timeline
|
||||||
timelineCap: React.PropTypes.number,
|
timelineCap: PropTypes.number,
|
||||||
|
|
||||||
// classname to use for the messagepanel
|
// classname to use for the messagepanel
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
|
||||||
// shape property to be passed to EventTiles
|
// shape property to be passed to EventTiles
|
||||||
tileShape: React.PropTypes.string,
|
tileShape: PropTypes.string,
|
||||||
|
|
||||||
// placeholder text to use if the timeline is empty
|
// placeholder text to use if the timeline is empty
|
||||||
empty: React.PropTypes.string,
|
empty: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
statics: {
|
statics: {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
const ContentMessages = require('../../ContentMessages');
|
const ContentMessages = require('../../ContentMessages');
|
||||||
const dis = require('../../dispatcher');
|
const dis = require('../../dispatcher');
|
||||||
const filesize = require('filesize');
|
const filesize = require('filesize');
|
||||||
|
@ -22,7 +23,7 @@ import { _t } from '../../languageHandler';
|
||||||
|
|
||||||
module.exports = React.createClass({displayName: 'UploadBar',
|
module.exports = React.createClass({displayName: 'UploadBar',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
room: React.PropTypes.object,
|
room: PropTypes.object,
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import SettingsStore, {SettingLevel} from "../../settings/SettingsStore";
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require('react-dom');
|
const ReactDOM = require('react-dom');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
const sdk = require('../../index');
|
const sdk = require('../../index');
|
||||||
const MatrixClientPeg = require("../../MatrixClientPeg");
|
const MatrixClientPeg = require("../../MatrixClientPeg");
|
||||||
const PlatformPeg = require("../../PlatformPeg");
|
const PlatformPeg = require("../../PlatformPeg");
|
||||||
|
@ -125,8 +126,8 @@ const THEMES = [
|
||||||
|
|
||||||
const IgnoredUser = React.createClass({
|
const IgnoredUser = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
userId: React.PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
onUnignored: React.PropTypes.func.isRequired,
|
onUnignored: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
_onUnignoreClick: function() {
|
_onUnignoreClick: function() {
|
||||||
|
@ -155,16 +156,16 @@ module.exports = React.createClass({
|
||||||
displayName: 'UserSettings',
|
displayName: 'UserSettings',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onClose: React.PropTypes.func,
|
onClose: PropTypes.func,
|
||||||
// The brand string given when creating email pushers
|
// The brand string given when creating email pushers
|
||||||
brand: React.PropTypes.string,
|
brand: PropTypes.string,
|
||||||
|
|
||||||
// The base URL to use in the referral link. Defaults to window.location.origin.
|
// The base URL to use in the referral link. Defaults to window.location.origin.
|
||||||
referralBaseUrl: React.PropTypes.string,
|
referralBaseUrl: PropTypes.string,
|
||||||
|
|
||||||
// Team token for the referral link. If falsy, the referral section will
|
// Team token for the referral link. If falsy, the referral section will
|
||||||
// not appear
|
// not appear
|
||||||
teamToken: React.PropTypes.string,
|
teamToken: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
@ -375,7 +376,7 @@ module.exports = React.createClass({
|
||||||
{ _t("For security, logging out will delete any end-to-end " +
|
{ _t("For security, logging out will delete any end-to-end " +
|
||||||
"encryption keys from this browser. If you want to be able " +
|
"encryption keys from this browser. If you want to be able " +
|
||||||
"to decrypt your conversation history from future Riot sessions, " +
|
"to decrypt your conversation history from future Riot sessions, " +
|
||||||
"please export your room keys for safe-keeping.") }.
|
"please export your room keys for safe-keeping.") }
|
||||||
</div>,
|
</div>,
|
||||||
button: _t("Sign out"),
|
button: _t("Sign out"),
|
||||||
extraButtons: [
|
extraButtons: [
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
|
@ -29,13 +30,13 @@ module.exports = React.createClass({
|
||||||
displayName: 'ForgotPassword',
|
displayName: 'ForgotPassword',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
defaultHsUrl: React.PropTypes.string,
|
defaultHsUrl: PropTypes.string,
|
||||||
defaultIsUrl: React.PropTypes.string,
|
defaultIsUrl: PropTypes.string,
|
||||||
customHsUrl: React.PropTypes.string,
|
customHsUrl: PropTypes.string,
|
||||||
customIsUrl: React.PropTypes.string,
|
customIsUrl: PropTypes.string,
|
||||||
onLoginClick: React.PropTypes.func,
|
onLoginClick: PropTypes.func,
|
||||||
onRegisterClick: React.PropTypes.func,
|
onRegisterClick: PropTypes.func,
|
||||||
onComplete: React.PropTypes.func.isRequired,
|
onComplete: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import * as languageHandler from '../../../languageHandler';
|
import * as languageHandler from '../../../languageHandler';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
|
@ -36,27 +37,27 @@ module.exports = React.createClass({
|
||||||
displayName: 'Login',
|
displayName: 'Login',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onLoggedIn: React.PropTypes.func.isRequired,
|
onLoggedIn: PropTypes.func.isRequired,
|
||||||
|
|
||||||
enableGuest: React.PropTypes.bool,
|
enableGuest: PropTypes.bool,
|
||||||
|
|
||||||
customHsUrl: React.PropTypes.string,
|
customHsUrl: PropTypes.string,
|
||||||
customIsUrl: React.PropTypes.string,
|
customIsUrl: PropTypes.string,
|
||||||
defaultHsUrl: React.PropTypes.string,
|
defaultHsUrl: PropTypes.string,
|
||||||
defaultIsUrl: React.PropTypes.string,
|
defaultIsUrl: PropTypes.string,
|
||||||
// Secondary HS which we try to log into if the user is using
|
// Secondary HS which we try to log into if the user is using
|
||||||
// the default HS but login fails. Useful for migrating to a
|
// the default HS but login fails. Useful for migrating to a
|
||||||
// different home server without confusing users.
|
// different home server without confusing users.
|
||||||
fallbackHsUrl: React.PropTypes.string,
|
fallbackHsUrl: PropTypes.string,
|
||||||
|
|
||||||
defaultDeviceDisplayName: React.PropTypes.string,
|
defaultDeviceDisplayName: PropTypes.string,
|
||||||
|
|
||||||
// login shouldn't know or care how registration is done.
|
// login shouldn't know or care how registration is done.
|
||||||
onRegisterClick: React.PropTypes.func.isRequired,
|
onRegisterClick: PropTypes.func.isRequired,
|
||||||
|
|
||||||
// login shouldn't care how password recovery is done.
|
// login shouldn't care how password recovery is done.
|
||||||
onForgotPasswordClick: React.PropTypes.func,
|
onForgotPasswordClick: PropTypes.func,
|
||||||
onCancelClick: React.PropTypes.func,
|
onCancelClick: PropTypes.func,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -25,7 +26,7 @@ module.exports = React.createClass({
|
||||||
displayName: 'PostRegistration',
|
displayName: 'PostRegistration',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onComplete: React.PropTypes.func.isRequired,
|
onComplete: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import Matrix from 'matrix-js-sdk';
|
||||||
|
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import ServerConfig from '../../views/login/ServerConfig';
|
import ServerConfig from '../../views/login/ServerConfig';
|
||||||
|
@ -35,31 +36,31 @@ module.exports = React.createClass({
|
||||||
displayName: 'Registration',
|
displayName: 'Registration',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onLoggedIn: React.PropTypes.func.isRequired,
|
onLoggedIn: PropTypes.func.isRequired,
|
||||||
clientSecret: React.PropTypes.string,
|
clientSecret: PropTypes.string,
|
||||||
sessionId: React.PropTypes.string,
|
sessionId: PropTypes.string,
|
||||||
makeRegistrationUrl: React.PropTypes.func.isRequired,
|
makeRegistrationUrl: PropTypes.func.isRequired,
|
||||||
idSid: React.PropTypes.string,
|
idSid: PropTypes.string,
|
||||||
customHsUrl: React.PropTypes.string,
|
customHsUrl: PropTypes.string,
|
||||||
customIsUrl: React.PropTypes.string,
|
customIsUrl: PropTypes.string,
|
||||||
defaultHsUrl: React.PropTypes.string,
|
defaultHsUrl: PropTypes.string,
|
||||||
defaultIsUrl: React.PropTypes.string,
|
defaultIsUrl: PropTypes.string,
|
||||||
brand: React.PropTypes.string,
|
brand: PropTypes.string,
|
||||||
email: React.PropTypes.string,
|
email: PropTypes.string,
|
||||||
referrer: React.PropTypes.string,
|
referrer: PropTypes.string,
|
||||||
teamServerConfig: React.PropTypes.shape({
|
teamServerConfig: PropTypes.shape({
|
||||||
// Email address to request new teams
|
// Email address to request new teams
|
||||||
supportEmail: React.PropTypes.string.isRequired,
|
supportEmail: PropTypes.string.isRequired,
|
||||||
// URL of the riot-team-server to get team configurations and track referrals
|
// URL of the riot-team-server to get team configurations and track referrals
|
||||||
teamServerURL: React.PropTypes.string.isRequired,
|
teamServerURL: PropTypes.string.isRequired,
|
||||||
}),
|
}),
|
||||||
teamSelected: React.PropTypes.object,
|
teamSelected: PropTypes.object,
|
||||||
|
|
||||||
defaultDeviceDisplayName: React.PropTypes.string,
|
defaultDeviceDisplayName: PropTypes.string,
|
||||||
|
|
||||||
// registration shouldn't know or care how login is done.
|
// registration shouldn't know or care how login is done.
|
||||||
onLoginClick: React.PropTypes.func.isRequired,
|
onLoginClick: PropTypes.func.isRequired,
|
||||||
onCancelClick: React.PropTypes.func,
|
onCancelClick: PropTypes.func,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import AvatarLogic from '../../../Avatar';
|
import AvatarLogic from '../../../Avatar';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
@ -23,16 +24,16 @@ module.exports = React.createClass({
|
||||||
displayName: 'BaseAvatar',
|
displayName: 'BaseAvatar',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
name: React.PropTypes.string.isRequired, // The name (first initial used as default)
|
name: PropTypes.string.isRequired, // The name (first initial used as default)
|
||||||
idName: React.PropTypes.string, // ID for generating hash colours
|
idName: PropTypes.string, // ID for generating hash colours
|
||||||
title: React.PropTypes.string, // onHover title text
|
title: PropTypes.string, // onHover title text
|
||||||
url: React.PropTypes.string, // highest priority of them all, shortcut to set in urls[0]
|
url: PropTypes.string, // highest priority of them all, shortcut to set in urls[0]
|
||||||
urls: React.PropTypes.array, // [highest_priority, ... , lowest_priority]
|
urls: PropTypes.array, // [highest_priority, ... , lowest_priority]
|
||||||
width: React.PropTypes.number,
|
width: PropTypes.number,
|
||||||
height: React.PropTypes.number,
|
height: PropTypes.number,
|
||||||
// XXX resizeMethod not actually used.
|
// XXX resizeMethod not actually used.
|
||||||
resizeMethod: React.PropTypes.string,
|
resizeMethod: PropTypes.string,
|
||||||
defaultToInitialLetter: React.PropTypes.bool, // true to add default url
|
defaultToInitialLetter: PropTypes.bool, // true to add default url
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
const Avatar = require('../../../Avatar');
|
const Avatar = require('../../../Avatar');
|
||||||
const sdk = require("../../../index");
|
const sdk = require("../../../index");
|
||||||
const dispatcher = require("../../../dispatcher");
|
const dispatcher = require("../../../dispatcher");
|
||||||
|
@ -25,15 +26,15 @@ module.exports = React.createClass({
|
||||||
displayName: 'MemberAvatar',
|
displayName: 'MemberAvatar',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
member: React.PropTypes.object.isRequired,
|
member: PropTypes.object.isRequired,
|
||||||
width: React.PropTypes.number,
|
width: PropTypes.number,
|
||||||
height: React.PropTypes.number,
|
height: PropTypes.number,
|
||||||
resizeMethod: React.PropTypes.string,
|
resizeMethod: PropTypes.string,
|
||||||
// The onClick to give the avatar
|
// The onClick to give the avatar
|
||||||
onClick: React.PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
// Whether the onClick of the avatar should be overriden to dispatch 'view_user'
|
// Whether the onClick of the avatar should be overriden to dispatch 'view_user'
|
||||||
viewUserOnClick: React.PropTypes.bool,
|
viewUserOnClick: PropTypes.bool,
|
||||||
title: React.PropTypes.string,
|
title: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
@ -30,10 +31,10 @@ module.exports = React.createClass({
|
||||||
displayName: 'MemberPresenceAvatar',
|
displayName: 'MemberPresenceAvatar',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
member: React.PropTypes.object.isRequired,
|
member: PropTypes.object.isRequired,
|
||||||
width: React.PropTypes.number,
|
width: PropTypes.number,
|
||||||
height: React.PropTypes.number,
|
height: PropTypes.number,
|
||||||
resizeMethod: React.PropTypes.string,
|
resizeMethod: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import {ContentRepo} from "matrix-js-sdk";
|
import {ContentRepo} from "matrix-js-sdk";
|
||||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
import sdk from "../../../index";
|
import sdk from "../../../index";
|
||||||
|
@ -25,11 +26,11 @@ module.exports = React.createClass({
|
||||||
// oobData.avatarUrl should be set (else there
|
// oobData.avatarUrl should be set (else there
|
||||||
// would be nowhere to get the avatar from)
|
// would be nowhere to get the avatar from)
|
||||||
propTypes: {
|
propTypes: {
|
||||||
room: React.PropTypes.object,
|
room: PropTypes.object,
|
||||||
oobData: React.PropTypes.object,
|
oobData: PropTypes.object,
|
||||||
width: React.PropTypes.number,
|
width: PropTypes.number,
|
||||||
height: React.PropTypes.number,
|
height: PropTypes.number,
|
||||||
resizeMethod: React.PropTypes.string,
|
resizeMethod: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -17,11 +17,12 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'CreateRoomButton',
|
displayName: 'CreateRoomButton',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onCreateRoom: React.PropTypes.func,
|
onCreateRoom: PropTypes.func,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
const Presets = {
|
const Presets = {
|
||||||
|
@ -28,8 +29,8 @@ const Presets = {
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'CreateRoomPresets',
|
displayName: 'CreateRoomPresets',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onChange: React.PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
preset: React.PropTypes.string,
|
preset: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
Presets: Presets,
|
Presets: Presets,
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
|
@ -22,9 +23,9 @@ module.exports = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// Specifying a homeserver will make magical things happen when you,
|
// Specifying a homeserver will make magical things happen when you,
|
||||||
// e.g. start typing in the room alias box.
|
// e.g. start typing in the room alias box.
|
||||||
homeserver: React.PropTypes.string,
|
homeserver: PropTypes.string,
|
||||||
alias: React.PropTypes.string,
|
alias: PropTypes.string,
|
||||||
onChange: React.PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { KeyCode } from '../../../Keyboard';
|
import { KeyCode } from '../../../Keyboard';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
@ -31,20 +32,20 @@ export default React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// onFinished callback to call when Escape is pressed
|
// onFinished callback to call when Escape is pressed
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
|
||||||
// callback to call when Enter is pressed
|
// callback to call when Enter is pressed
|
||||||
onEnterPressed: React.PropTypes.func,
|
onEnterPressed: PropTypes.func,
|
||||||
|
|
||||||
// CSS class to apply to dialog div
|
// CSS class to apply to dialog div
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
|
||||||
// Title for the dialog.
|
// Title for the dialog.
|
||||||
// (could probably actually be something more complicated than a string if desired)
|
// (could probably actually be something more complicated than a string if desired)
|
||||||
title: React.PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
|
|
||||||
// children should be the content of the dialog
|
// children should be the content of the dialog
|
||||||
children: React.PropTypes.node,
|
children: PropTypes.node,
|
||||||
},
|
},
|
||||||
|
|
||||||
_onKeyDown: function(e) {
|
_onKeyDown: function(e) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
|
@ -187,9 +188,9 @@ export default class ChatCreateOrReuseDialog extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
ChatCreateOrReuseDialog.propTyps = {
|
ChatCreateOrReuseDialog.propTyps = {
|
||||||
userId: React.PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
// Called when clicking outside of the dialog
|
// Called when clicking outside of the dialog
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
onNewDMClick: React.PropTypes.func.isRequired,
|
onNewDMClick: PropTypes.func.isRequired,
|
||||||
onExistingRoomSelected: React.PropTypes.func.isRequired,
|
onExistingRoomSelected: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -33,20 +34,20 @@ export default React.createClass({
|
||||||
displayName: 'ConfirmUserActionDialog',
|
displayName: 'ConfirmUserActionDialog',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// matrix-js-sdk (room) member object. Supply either this or 'groupMember'
|
// matrix-js-sdk (room) member object. Supply either this or 'groupMember'
|
||||||
member: React.PropTypes.object,
|
member: PropTypes.object,
|
||||||
// group member object. Supply either this or 'member'
|
// group member object. Supply either this or 'member'
|
||||||
groupMember: GroupMemberType,
|
groupMember: GroupMemberType,
|
||||||
// needed if a group member is specified
|
// needed if a group member is specified
|
||||||
matrixClient: React.PropTypes.instanceOf(MatrixClient),
|
matrixClient: PropTypes.instanceOf(MatrixClient),
|
||||||
action: React.PropTypes.string.isRequired, // eg. 'Ban'
|
action: PropTypes.string.isRequired, // eg. 'Ban'
|
||||||
title: React.PropTypes.string.isRequired, // eg. 'Ban this user?'
|
title: PropTypes.string.isRequired, // eg. 'Ban this user?'
|
||||||
|
|
||||||
// Whether to display a text field for a reason
|
// Whether to display a text field for a reason
|
||||||
// If true, the second argument to onFinished will
|
// If true, the second argument to onFinished will
|
||||||
// be the string entered.
|
// be the string entered.
|
||||||
askReason: React.PropTypes.bool,
|
askReason: PropTypes.bool,
|
||||||
danger: React.PropTypes.bool,
|
danger: PropTypes.bool,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -22,7 +23,7 @@ import { _t } from '../../../languageHandler';
|
||||||
export default React.createClass({
|
export default React.createClass({
|
||||||
displayName: 'CreateRoomDialog',
|
displayName: 'CreateRoomDialog',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import Analytics from '../../../Analytics';
|
import Analytics from '../../../Analytics';
|
||||||
|
@ -136,5 +137,5 @@ export default class DeactivateAccountDialog extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
DeactivateAccountDialog.propTypes = {
|
DeactivateAccountDialog.propTypes = {
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import * as FormattingUtils from '../../../utils/FormattingUtils';
|
import * as FormattingUtils from '../../../utils/FormattingUtils';
|
||||||
|
@ -71,7 +72,7 @@ export default function DeviceVerifyDialog(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceVerifyDialog.propTypes = {
|
DeviceVerifyDialog.propTypes = {
|
||||||
userId: React.PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
device: React.PropTypes.object.isRequired,
|
device: PropTypes.object.isRequired,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,20 +26,21 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
export default React.createClass({
|
export default React.createClass({
|
||||||
displayName: 'ErrorDialog',
|
displayName: 'ErrorDialog',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
title: React.PropTypes.string,
|
title: PropTypes.string,
|
||||||
description: React.PropTypes.oneOfType([
|
description: PropTypes.oneOfType([
|
||||||
React.PropTypes.element,
|
PropTypes.element,
|
||||||
React.PropTypes.string,
|
PropTypes.string,
|
||||||
]),
|
]),
|
||||||
button: React.PropTypes.string,
|
button: PropTypes.string,
|
||||||
focus: React.PropTypes.bool,
|
focus: PropTypes.bool,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -27,22 +28,22 @@ export default React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// matrix client to use for UI auth requests
|
// matrix client to use for UI auth requests
|
||||||
matrixClient: React.PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
|
|
||||||
// response from initial request. If not supplied, will do a request on
|
// response from initial request. If not supplied, will do a request on
|
||||||
// mount.
|
// mount.
|
||||||
authData: React.PropTypes.shape({
|
authData: PropTypes.shape({
|
||||||
flows: React.PropTypes.array,
|
flows: PropTypes.array,
|
||||||
params: React.PropTypes.object,
|
params: PropTypes.object,
|
||||||
session: React.PropTypes.string,
|
session: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// callback
|
// callback
|
||||||
makeRequest: React.PropTypes.func.isRequired,
|
makeRequest: PropTypes.func.isRequired,
|
||||||
|
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
|
||||||
title: React.PropTypes.string,
|
title: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
|
|
||||||
import { _t, _td } from '../../../languageHandler';
|
import { _t, _td } from '../../../languageHandler';
|
||||||
|
@ -30,10 +31,10 @@ import { _t, _td } from '../../../languageHandler';
|
||||||
*/
|
*/
|
||||||
export default React.createClass({
|
export default React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
matrixClient: React.PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
userId: React.PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
deviceId: React.PropTypes.string.isRequired,
|
deviceId: PropTypes.string.isRequired,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
@ -23,13 +24,13 @@ import classnames from 'classnames';
|
||||||
export default React.createClass({
|
export default React.createClass({
|
||||||
displayName: 'QuestionDialog',
|
displayName: 'QuestionDialog',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
title: React.PropTypes.string,
|
title: PropTypes.string,
|
||||||
description: React.PropTypes.node,
|
description: PropTypes.node,
|
||||||
extraButtons: React.PropTypes.node,
|
extraButtons: PropTypes.node,
|
||||||
button: React.PropTypes.string,
|
button: PropTypes.string,
|
||||||
danger: React.PropTypes.bool,
|
danger: PropTypes.bool,
|
||||||
focus: React.PropTypes.bool,
|
focus: PropTypes.bool,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
@ -25,8 +26,8 @@ export default React.createClass({
|
||||||
displayName: 'SessionRestoreErrorDialog',
|
displayName: 'SessionRestoreErrorDialog',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
error: React.PropTypes.string.isRequired,
|
error: PropTypes.string.isRequired,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
_sendBugReport: function() {
|
_sendBugReport: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import Email from '../../../email';
|
import Email from '../../../email';
|
||||||
import AddThreepid from '../../../AddThreepid';
|
import AddThreepid from '../../../AddThreepid';
|
||||||
|
@ -30,7 +31,7 @@ import Modal from '../../../Modal';
|
||||||
export default React.createClass({
|
export default React.createClass({
|
||||||
displayName: 'SetEmailDialog',
|
displayName: 'SetEmailDialog',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
|
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
@ -35,11 +36,11 @@ const USERNAME_CHECK_DEBOUNCE_MS = 250;
|
||||||
export default React.createClass({
|
export default React.createClass({
|
||||||
displayName: 'SetMxIdDialog',
|
displayName: 'SetMxIdDialog',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
// Called when the user requests to register with a different homeserver
|
// Called when the user requests to register with a different homeserver
|
||||||
onDifferentServerClicked: React.PropTypes.func.isRequired,
|
onDifferentServerClicked: PropTypes.func.isRequired,
|
||||||
// Called if the user wants to switch to login instead
|
// Called if the user wants to switch to login instead
|
||||||
onLoginClick: React.PropTypes.func.isRequired,
|
onLoginClick: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -15,21 +15,22 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
export default React.createClass({
|
export default React.createClass({
|
||||||
displayName: 'TextInputDialog',
|
displayName: 'TextInputDialog',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
title: React.PropTypes.string,
|
title: PropTypes.string,
|
||||||
description: React.PropTypes.oneOfType([
|
description: PropTypes.oneOfType([
|
||||||
React.PropTypes.element,
|
PropTypes.element,
|
||||||
React.PropTypes.string,
|
PropTypes.string,
|
||||||
]),
|
]),
|
||||||
value: React.PropTypes.string,
|
value: PropTypes.string,
|
||||||
button: React.PropTypes.string,
|
button: PropTypes.string,
|
||||||
focus: React.PropTypes.bool,
|
focus: PropTypes.bool,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Vector Creations Ltd
|
Copyright 2017 Vector Creations Ltd
|
||||||
|
Copyright 2017 New Vector Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -15,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import GeminiScrollbar from 'react-gemini-scrollbar';
|
import GeminiScrollbar from 'react-gemini-scrollbar';
|
||||||
|
@ -22,6 +24,14 @@ import Resend from '../../../Resend';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
|
||||||
|
function markAllDevicesKnown(devices) {
|
||||||
|
Object.keys(devices).forEach((userId) => {
|
||||||
|
Object.keys(devices[userId]).map((deviceId) => {
|
||||||
|
MatrixClientPeg.get().setDeviceKnown(userId, deviceId, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function DeviceListEntry(props) {
|
function DeviceListEntry(props) {
|
||||||
const {userId, device} = props;
|
const {userId, device} = props;
|
||||||
|
|
||||||
|
@ -38,10 +48,10 @@ function DeviceListEntry(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceListEntry.propTypes = {
|
DeviceListEntry.propTypes = {
|
||||||
userId: React.PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
|
|
||||||
// deviceinfo
|
// deviceinfo
|
||||||
device: React.PropTypes.object.isRequired,
|
device: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,10 +71,10 @@ function UserUnknownDeviceList(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
UserUnknownDeviceList.propTypes = {
|
UserUnknownDeviceList.propTypes = {
|
||||||
userId: React.PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
|
|
||||||
// map from deviceid -> deviceinfo
|
// map from deviceid -> deviceinfo
|
||||||
userDevices: React.PropTypes.object.isRequired,
|
userDevices: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,7 +93,7 @@ function UnknownDeviceList(props) {
|
||||||
|
|
||||||
UnknownDeviceList.propTypes = {
|
UnknownDeviceList.propTypes = {
|
||||||
// map from userid -> deviceid -> deviceinfo
|
// map from userid -> deviceid -> deviceinfo
|
||||||
devices: React.PropTypes.object.isRequired,
|
devices: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,28 +101,63 @@ export default React.createClass({
|
||||||
displayName: 'UnknownDeviceDialog',
|
displayName: 'UnknownDeviceDialog',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
room: React.PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
|
|
||||||
// map from userid -> deviceid -> deviceinfo
|
// map from userid -> deviceid -> deviceinfo or null if devices are not yet loaded
|
||||||
devices: React.PropTypes.object.isRequired,
|
devices: PropTypes.object,
|
||||||
onFinished: React.PropTypes.func.isRequired,
|
|
||||||
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
|
||||||
|
// Label for the button that marks all devices known and tries the send again
|
||||||
|
sendAnywayLabel: PropTypes.string.isRequired,
|
||||||
|
|
||||||
|
// Label for the button that to send the event if you've verified all devices
|
||||||
|
sendLabel: PropTypes.string.isRequired,
|
||||||
|
|
||||||
|
// function to retry the request once all devices are verified / known
|
||||||
|
onSend: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentWillMount: function() {
|
||||||
// Given we've now shown the user the unknown device, it is no longer
|
MatrixClientPeg.get().on("deviceVerificationChanged", this._onDeviceVerificationChanged);
|
||||||
// unknown to them. Therefore mark it as 'known'.
|
},
|
||||||
Object.keys(this.props.devices).forEach((userId) => {
|
|
||||||
Object.keys(this.props.devices[userId]).map((deviceId) => {
|
|
||||||
MatrixClientPeg.get().setDeviceKnown(userId, deviceId, true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// XXX: temporary logging to try to diagnose
|
componentWillUnmount: function() {
|
||||||
// https://github.com/vector-im/riot-web/issues/3148
|
if (MatrixClientPeg.get()) {
|
||||||
console.log('Opening UnknownDeviceDialog');
|
MatrixClientPeg.get().removeListener("deviceVerificationChanged", this._onDeviceVerificationChanged);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDeviceVerificationChanged: function(userId, deviceId, deviceInfo) {
|
||||||
|
if (this.props.devices[userId] && this.props.devices[userId][deviceId]) {
|
||||||
|
// XXX: Mutating props :/
|
||||||
|
this.props.devices[userId][deviceId] = deviceInfo;
|
||||||
|
this.forceUpdate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDismissClicked: function() {
|
||||||
|
this.props.onFinished();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSendAnywayClicked: function() {
|
||||||
|
markAllDevicesKnown(this.props.devices);
|
||||||
|
|
||||||
|
this.props.onFinished();
|
||||||
|
this.props.onSend();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onSendClicked: function() {
|
||||||
|
this.props.onFinished();
|
||||||
|
this.props.onSend();
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
if (this.props.devices === null) {
|
||||||
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
|
return <Spinner />;
|
||||||
|
}
|
||||||
|
|
||||||
let warning;
|
let warning;
|
||||||
if (SettingsStore.getValue("blacklistUnverifiedDevices", this.props.room.roomId)) {
|
if (SettingsStore.getValue("blacklistUnverifiedDevices", this.props.room.roomId)) {
|
||||||
warning = (
|
warning = (
|
||||||
|
@ -133,15 +178,30 @@ export default React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let haveUnknownDevices = false;
|
||||||
|
Object.keys(this.props.devices).forEach((userId) => {
|
||||||
|
Object.keys(this.props.devices[userId]).map((deviceId) => {
|
||||||
|
const device = this.props.devices[userId][deviceId];
|
||||||
|
if (device.isUnverified() && !device.isKnown()) {
|
||||||
|
haveUnknownDevices = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
let sendButton;
|
||||||
|
if (haveUnknownDevices) {
|
||||||
|
sendButton = <button onClick={this._onSendAnywayClicked}>
|
||||||
|
{ this.props.sendAnywayLabel }
|
||||||
|
</button>;
|
||||||
|
} else {
|
||||||
|
sendButton = <button onClick={this._onSendClicked}>
|
||||||
|
{ this.props.sendLabel }
|
||||||
|
</button>;
|
||||||
|
}
|
||||||
|
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
return (
|
return (
|
||||||
<BaseDialog className='mx_UnknownDeviceDialog'
|
<BaseDialog className='mx_UnknownDeviceDialog'
|
||||||
onFinished={() => {
|
onFinished={this.props.onFinished}
|
||||||
// XXX: temporary logging to try to diagnose
|
|
||||||
// https://github.com/vector-im/riot-web/issues/3148
|
|
||||||
console.log("UnknownDeviceDialog closed by escape");
|
|
||||||
this.props.onFinished();
|
|
||||||
}}
|
|
||||||
title={_t('Room contains unknown devices')}
|
title={_t('Room contains unknown devices')}
|
||||||
>
|
>
|
||||||
<GeminiScrollbar autoshow={false} className="mx_Dialog_content">
|
<GeminiScrollbar autoshow={false} className="mx_Dialog_content">
|
||||||
|
@ -154,21 +214,11 @@ export default React.createClass({
|
||||||
<UnknownDeviceList devices={this.props.devices} />
|
<UnknownDeviceList devices={this.props.devices} />
|
||||||
</GeminiScrollbar>
|
</GeminiScrollbar>
|
||||||
<div className="mx_Dialog_buttons">
|
<div className="mx_Dialog_buttons">
|
||||||
|
{sendButton}
|
||||||
<button className="mx_Dialog_primary" autoFocus={true}
|
<button className="mx_Dialog_primary" autoFocus={true}
|
||||||
onClick={() => {
|
onClick={this._onDismissClicked}
|
||||||
this.props.onFinished();
|
>
|
||||||
Resend.resendUnsentEvents(this.props.room);
|
{_t("Dismiss")}
|
||||||
}}>
|
|
||||||
{ _t("Send anyway") }
|
|
||||||
</button>
|
|
||||||
<button className="mx_Dialog_primary" autoFocus={true}
|
|
||||||
onClick={() => {
|
|
||||||
// XXX: temporary logging to try to diagnose
|
|
||||||
// https://github.com/vector-im/riot-web/issues/3148
|
|
||||||
console.log("UnknownDeviceDialog closed by OK");
|
|
||||||
this.props.onFinished();
|
|
||||||
}}>
|
|
||||||
OK
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AccessibleButton is a generic wrapper for any element that should be treated
|
* AccessibleButton is a generic wrapper for any element that should be treated
|
||||||
|
@ -44,9 +45,9 @@ export default function AccessibleButton(props) {
|
||||||
* implemented exactly like a normal onClick handler.
|
* implemented exactly like a normal onClick handler.
|
||||||
*/
|
*/
|
||||||
AccessibleButton.propTypes = {
|
AccessibleButton.propTypes = {
|
||||||
children: React.PropTypes.node,
|
children: PropTypes.node,
|
||||||
element: React.PropTypes.string,
|
element: PropTypes.string,
|
||||||
onClick: React.PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
AccessibleButton.defaultProps = {
|
AccessibleButton.defaultProps = {
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { UserAddressType } from '../../../UserAddress';
|
import { UserAddressType } from '../../../UserAddress';
|
||||||
|
@ -26,17 +27,17 @@ export default React.createClass({
|
||||||
displayName: 'AddressSelector',
|
displayName: 'AddressSelector',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onSelected: React.PropTypes.func.isRequired,
|
onSelected: PropTypes.func.isRequired,
|
||||||
|
|
||||||
// List of the addresses to display
|
// List of the addresses to display
|
||||||
addressList: React.PropTypes.arrayOf(UserAddressType).isRequired,
|
addressList: PropTypes.arrayOf(UserAddressType).isRequired,
|
||||||
// Whether to show the address on the address tiles
|
// Whether to show the address on the address tiles
|
||||||
showAddress: React.PropTypes.bool,
|
showAddress: PropTypes.bool,
|
||||||
truncateAt: React.PropTypes.number.isRequired,
|
truncateAt: PropTypes.number.isRequired,
|
||||||
selected: React.PropTypes.number,
|
selected: PropTypes.number,
|
||||||
|
|
||||||
// Element to put as a header on top of the list
|
// Element to put as a header on top of the list
|
||||||
header: React.PropTypes.node,
|
header: PropTypes.node,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import sdk from "../../../index";
|
import sdk from "../../../index";
|
||||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
|
@ -28,9 +29,9 @@ export default React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
address: UserAddressType.isRequired,
|
address: UserAddressType.isRequired,
|
||||||
canDismiss: React.PropTypes.bool,
|
canDismiss: PropTypes.bool,
|
||||||
onDismissed: React.PropTypes.func,
|
onDismissed: PropTypes.func,
|
||||||
justified: React.PropTypes.bool,
|
justified: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -19,9 +19,11 @@ limitations under the License.
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import qs from 'querystring';
|
import qs from 'querystring';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import PlatformPeg from '../../../PlatformPeg';
|
import PlatformPeg from '../../../PlatformPeg';
|
||||||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
import ScalarAuthClient from '../../../ScalarAuthClient';
|
||||||
|
import WidgetMessaging from '../../../WidgetMessaging';
|
||||||
import TintableSvgButton from './TintableSvgButton';
|
import TintableSvgButton from './TintableSvgButton';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
@ -39,23 +41,25 @@ export default React.createClass({
|
||||||
displayName: 'AppTile',
|
displayName: 'AppTile',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
id: React.PropTypes.string.isRequired,
|
id: PropTypes.string.isRequired,
|
||||||
url: React.PropTypes.string.isRequired,
|
url: PropTypes.string.isRequired,
|
||||||
name: React.PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
room: React.PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
type: React.PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
// Specifying 'fullWidth' as true will render the app tile to fill the width of the app drawer continer.
|
// Specifying 'fullWidth' as true will render the app tile to fill the width of the app drawer continer.
|
||||||
// This should be set to true when there is only one widget in the app drawer, otherwise it should be false.
|
// This should be set to true when there is only one widget in the app drawer, otherwise it should be false.
|
||||||
fullWidth: React.PropTypes.bool,
|
fullWidth: PropTypes.bool,
|
||||||
// UserId of the current user
|
// UserId of the current user
|
||||||
userId: React.PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
// UserId of the entity that added / modified the widget
|
// UserId of the entity that added / modified the widget
|
||||||
creatorUserId: React.PropTypes.string,
|
creatorUserId: PropTypes.string,
|
||||||
|
waitForIframeLoad: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps() {
|
getDefaultProps() {
|
||||||
return {
|
return {
|
||||||
url: "",
|
url: "",
|
||||||
|
waitForIframeLoad: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -70,17 +74,46 @@ export default React.createClass({
|
||||||
const hasPermissionToLoad = localStorage.getItem(widgetPermissionId);
|
const hasPermissionToLoad = localStorage.getItem(widgetPermissionId);
|
||||||
return {
|
return {
|
||||||
initialising: true, // True while we are mangling the widget URL
|
initialising: true, // True while we are mangling the widget URL
|
||||||
loading: true, // True while the iframe content is loading
|
loading: this.props.waitForIframeLoad, // True while the iframe content is loading
|
||||||
widgetUrl: newProps.url,
|
widgetUrl: this._addWurlParams(newProps.url),
|
||||||
widgetPermissionId: widgetPermissionId,
|
widgetPermissionId: widgetPermissionId,
|
||||||
// Assume that widget has permission to load if we are the user who
|
// Assume that widget has permission to load if we are the user who
|
||||||
// added it to the room, or if explicitly granted by the user
|
// added it to the room, or if explicitly granted by the user
|
||||||
hasPermissionToLoad: hasPermissionToLoad === 'true' || newProps.userId === newProps.creatorUserId,
|
hasPermissionToLoad: hasPermissionToLoad === 'true' || newProps.userId === newProps.creatorUserId,
|
||||||
error: null,
|
error: null,
|
||||||
deleting: false,
|
deleting: false,
|
||||||
|
widgetPageTitle: newProps.widgetPageTitle,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add widget instance specific parameters to pass in wUrl
|
||||||
|
* Properties passed to widget instance:
|
||||||
|
* - widgetId
|
||||||
|
* - origin / parent URL
|
||||||
|
* @param {string} urlString Url string to modify
|
||||||
|
* @return {string}
|
||||||
|
* Url string with parameters appended.
|
||||||
|
* If url can not be parsed, it is returned unmodified.
|
||||||
|
*/
|
||||||
|
_addWurlParams(urlString) {
|
||||||
|
const u = url.parse(urlString);
|
||||||
|
if (!u) {
|
||||||
|
console.error("_addWurlParams", "Invalid URL", urlString);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = qs.parse(u.query);
|
||||||
|
// Append widget ID to query parameters
|
||||||
|
params.widgetId = this.props.id;
|
||||||
|
// Append current / parent URL
|
||||||
|
params.parentUrl = window.location.href;
|
||||||
|
u.search = undefined;
|
||||||
|
u.query = params;
|
||||||
|
|
||||||
|
return u.format();
|
||||||
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return this._getNewState(this.props);
|
return this._getNewState(this.props);
|
||||||
},
|
},
|
||||||
|
@ -122,6 +155,8 @@ export default React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
|
WidgetMessaging.startListening();
|
||||||
|
WidgetMessaging.addEndpoint(this.props.id, this.props.url);
|
||||||
window.addEventListener('message', this._onMessage, false);
|
window.addEventListener('message', this._onMessage, false);
|
||||||
this.setScalarToken();
|
this.setScalarToken();
|
||||||
},
|
},
|
||||||
|
@ -137,7 +172,7 @@ export default React.createClass({
|
||||||
console.warn('Non-scalar widget, not setting scalar token!', url);
|
console.warn('Non-scalar widget, not setting scalar token!', url);
|
||||||
this.setState({
|
this.setState({
|
||||||
error: null,
|
error: null,
|
||||||
widgetUrl: this.props.url,
|
widgetUrl: this._addWurlParams(this.props.url),
|
||||||
initialising: false,
|
initialising: false,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -150,7 +185,7 @@ export default React.createClass({
|
||||||
this._scalarClient.getScalarToken().done((token) => {
|
this._scalarClient.getScalarToken().done((token) => {
|
||||||
// Append scalar_token as a query param if not already present
|
// Append scalar_token as a query param if not already present
|
||||||
this._scalarClient.scalarToken = token;
|
this._scalarClient.scalarToken = token;
|
||||||
const u = url.parse(this.props.url);
|
const u = url.parse(this._addWurlParams(this.props.url));
|
||||||
const params = qs.parse(u.query);
|
const params = qs.parse(u.query);
|
||||||
if (!params.scalar_token) {
|
if (!params.scalar_token) {
|
||||||
params.scalar_token = encodeURIComponent(token);
|
params.scalar_token = encodeURIComponent(token);
|
||||||
|
@ -164,6 +199,11 @@ export default React.createClass({
|
||||||
widgetUrl: u.format(),
|
widgetUrl: u.format(),
|
||||||
initialising: false,
|
initialising: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fetch page title from remote content if not already set
|
||||||
|
if (!this.state.widgetPageTitle && params.url) {
|
||||||
|
this._fetchWidgetTitle(params.url);
|
||||||
|
}
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
console.error("Failed to get scalar_token", err);
|
console.error("Failed to get scalar_token", err);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -174,6 +214,8 @@ export default React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
WidgetMessaging.stopListening();
|
||||||
|
WidgetMessaging.removeEndpoint(this.props.id, this.props.url);
|
||||||
window.removeEventListener('message', this._onMessage);
|
window.removeEventListener('message', this._onMessage);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -181,10 +223,14 @@ export default React.createClass({
|
||||||
if (nextProps.url !== this.props.url) {
|
if (nextProps.url !== this.props.url) {
|
||||||
this._getNewState(nextProps);
|
this._getNewState(nextProps);
|
||||||
this.setScalarToken();
|
this.setScalarToken();
|
||||||
} else if (nextProps.show && !this.props.show) {
|
} else if (nextProps.show && !this.props.show && this.props.waitForIframeLoad) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: true,
|
loading: true,
|
||||||
});
|
});
|
||||||
|
} else if (nextProps.widgetPageTitle !== this.props.widgetPageTitle) {
|
||||||
|
this.setState({
|
||||||
|
widgetPageTitle: nextProps.widgetPageTitle,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -256,10 +302,27 @@ export default React.createClass({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when widget iframe has finished loading
|
||||||
|
*/
|
||||||
_onLoaded() {
|
_onLoaded() {
|
||||||
this.setState({loading: false});
|
this.setState({loading: false});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set remote content title on AppTile
|
||||||
|
* @param {string} url Url to check for title
|
||||||
|
*/
|
||||||
|
_fetchWidgetTitle(url) {
|
||||||
|
this._scalarClient.getScalarPageTitle(url).then((widgetPageTitle) => {
|
||||||
|
if (widgetPageTitle) {
|
||||||
|
this.setState({widgetPageTitle: widgetPageTitle});
|
||||||
|
}
|
||||||
|
}, (err) =>{
|
||||||
|
console.error("Failed to get page title", err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// Widget labels to render, depending upon user permissions
|
// Widget labels to render, depending upon user permissions
|
||||||
// These strings are translated at the point that they are inserted in to the DOM, in the render method
|
// These strings are translated at the point that they are inserted in to the DOM, in the render method
|
||||||
_deleteWidgetLabel() {
|
_deleteWidgetLabel() {
|
||||||
|
@ -305,6 +368,15 @@ export default React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getSafeUrl() {
|
||||||
|
const parsedWidgetUrl = url.parse(this.state.widgetUrl);
|
||||||
|
let safeWidgetUrl = '';
|
||||||
|
if (ALLOWED_APP_URL_SCHEMES.indexOf(parsedWidgetUrl.protocol) !== -1) {
|
||||||
|
safeWidgetUrl = url.format(parsedWidgetUrl);
|
||||||
|
}
|
||||||
|
return safeWidgetUrl;
|
||||||
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let appTileBody;
|
let appTileBody;
|
||||||
|
|
||||||
|
@ -320,11 +392,6 @@ export default React.createClass({
|
||||||
// a link to it.
|
// a link to it.
|
||||||
const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox "+
|
const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox "+
|
||||||
"allow-same-origin allow-scripts allow-presentation";
|
"allow-same-origin allow-scripts allow-presentation";
|
||||||
const parsedWidgetUrl = url.parse(this.state.widgetUrl);
|
|
||||||
let safeWidgetUrl = '';
|
|
||||||
if (ALLOWED_APP_URL_SCHEMES.indexOf(parsedWidgetUrl.protocol) !== -1) {
|
|
||||||
safeWidgetUrl = url.format(parsedWidgetUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.props.show) {
|
if (this.props.show) {
|
||||||
const loadingElement = (
|
const loadingElement = (
|
||||||
|
@ -347,7 +414,7 @@ export default React.createClass({
|
||||||
{ this.state.loading && loadingElement }
|
{ this.state.loading && loadingElement }
|
||||||
<iframe
|
<iframe
|
||||||
ref="appFrame"
|
ref="appFrame"
|
||||||
src={safeWidgetUrl}
|
src={this._getSafeUrl()}
|
||||||
allowFullScreen="true"
|
allowFullScreen="true"
|
||||||
sandbox={sandboxFlags}
|
sandbox={sandboxFlags}
|
||||||
onLoad={this._onLoaded}
|
onLoad={this._onLoaded}
|
||||||
|
@ -379,10 +446,24 @@ export default React.createClass({
|
||||||
deleteClasses += ' mx_AppTileMenuBarWidgetDelete';
|
deleteClasses += ' mx_AppTileMenuBarWidgetDelete';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const windowStateIcon = (this.props.show ? 'img/minimize.svg' : 'img/maximize.svg');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={this.props.fullWidth ? "mx_AppTileFullWidth" : "mx_AppTile"} id={this.props.id}>
|
<div className={this.props.fullWidth ? "mx_AppTileFullWidth" : "mx_AppTile"} id={this.props.id}>
|
||||||
<div ref="menu_bar" className="mx_AppTileMenuBar" onClick={this.onClickMenuBar}>
|
<div ref="menu_bar" className="mx_AppTileMenuBar" onClick={this.onClickMenuBar}>
|
||||||
|
<span className="mx_AppTileMenuBarTitle">
|
||||||
|
<TintableSvgButton
|
||||||
|
src={windowStateIcon}
|
||||||
|
className="mx_AppTileMenuBarWidget mx_AppTileMenuBarWidgetPadding"
|
||||||
|
title={_t('Minimize apps')}
|
||||||
|
width="10"
|
||||||
|
height="10"
|
||||||
|
/>
|
||||||
<b>{ this.formatAppTileName() }</b>
|
<b>{ this.formatAppTileName() }</b>
|
||||||
|
{ this.state.widgetPageTitle && this.state.widgetPageTitle != this.formatAppTileName() && (
|
||||||
|
<span> - { this.state.widgetPageTitle }</span>
|
||||||
|
) }
|
||||||
|
</span>
|
||||||
<span className="mx_AppTileMenuBarWidgets">
|
<span className="mx_AppTileMenuBarWidgets">
|
||||||
{ /* Edit widget */ }
|
{ /* Edit widget */ }
|
||||||
{ showEditButton && <TintableSvgButton
|
{ showEditButton && <TintableSvgButton
|
||||||
|
|
85
src/components/views/elements/DNDTagTile.js
Normal file
85
src/components/views/elements/DNDTagTile.js
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/* eslint new-cap: "off" */
|
||||||
|
/*
|
||||||
|
Copyright 2017 New Vector 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 { DragSource, DropTarget } from 'react-dnd';
|
||||||
|
|
||||||
|
import TagTile from './TagTile';
|
||||||
|
import dis from '../../../dispatcher';
|
||||||
|
import { findDOMNode } from 'react-dom';
|
||||||
|
|
||||||
|
const tagTileSource = {
|
||||||
|
canDrag: function(props, monitor) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
beginDrag: function(props) {
|
||||||
|
// Return the data describing the dragged item
|
||||||
|
return {
|
||||||
|
tag: props.tag,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
endDrag: function(props, monitor, component) {
|
||||||
|
const dropResult = monitor.getDropResult();
|
||||||
|
if (!monitor.didDrop() || !dropResult) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
props.onEndDrag();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagTileTarget = {
|
||||||
|
canDrop(props, monitor) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
hover(props, monitor, component) {
|
||||||
|
if (!monitor.canDrop()) return;
|
||||||
|
const draggedY = monitor.getClientOffset().y;
|
||||||
|
const {top, bottom} = findDOMNode(component).getBoundingClientRect();
|
||||||
|
const targetY = (top + bottom) / 2;
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'order_tag',
|
||||||
|
tag: monitor.getItem().tag,
|
||||||
|
targetTag: props.tag,
|
||||||
|
// Note: we indicate that the tag should be after the target when
|
||||||
|
// it's being dragged over the top half of the target.
|
||||||
|
after: draggedY < targetY,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
drop(props) {
|
||||||
|
// Return the data to be returned by getDropResult
|
||||||
|
return {
|
||||||
|
tag: props.tag,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default
|
||||||
|
DropTarget('TagTile', tagTileTarget, (connect, monitor) => ({
|
||||||
|
connectDropTarget: connect.dropTarget(),
|
||||||
|
}))(DragSource('TagTile', tagTileSource, (connect, monitor) => ({
|
||||||
|
connectDragSource: connect.dragSource(),
|
||||||
|
}))((props) => {
|
||||||
|
const { connectDropTarget, connectDragSource, ...otherProps } = props;
|
||||||
|
return connectDropTarget(connectDragSource(
|
||||||
|
<div>
|
||||||
|
<TagTile {...otherProps} />
|
||||||
|
</div>,
|
||||||
|
));
|
||||||
|
}));
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
@ -24,8 +25,8 @@ export default React.createClass({
|
||||||
displayName: 'DeviceVerifyButtons',
|
displayName: 'DeviceVerifyButtons',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
userId: React.PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
device: React.PropTypes.object.isRequired,
|
device: PropTypes.object.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
export default class DirectorySearchBox extends React.Component {
|
export default class DirectorySearchBox extends React.Component {
|
||||||
|
@ -105,10 +106,10 @@ export default class DirectorySearchBox extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
DirectorySearchBox.propTypes = {
|
DirectorySearchBox.propTypes = {
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
onChange: React.PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
onClear: React.PropTypes.func,
|
onClear: PropTypes.func,
|
||||||
onJoinClick: React.PropTypes.func,
|
onJoinClick: PropTypes.func,
|
||||||
placeholder: React.PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
showJoinButton: React.PropTypes.bool,
|
showJoinButton: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import AccessibleButton from './AccessibleButton';
|
import AccessibleButton from './AccessibleButton';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -56,14 +57,14 @@ class MenuOption extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuOption.propTypes = {
|
MenuOption.propTypes = {
|
||||||
children: React.PropTypes.oneOfType([
|
children: PropTypes.oneOfType([
|
||||||
React.PropTypes.arrayOf(React.PropTypes.node),
|
PropTypes.arrayOf(React.PropTypes.node),
|
||||||
React.PropTypes.node,
|
PropTypes.node,
|
||||||
]),
|
]),
|
||||||
highlighted: React.PropTypes.bool,
|
highlighted: PropTypes.bool,
|
||||||
dropdownKey: React.PropTypes.string,
|
dropdownKey: PropTypes.string,
|
||||||
onClick: React.PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
onMouseEnter: React.PropTypes.func.isRequired,
|
onMouseEnter: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -322,20 +323,20 @@ Dropdown.propTypes = {
|
||||||
// The width that the dropdown should be. If specified,
|
// The width that the dropdown should be. If specified,
|
||||||
// the dropped-down part of the menu will be set to this
|
// the dropped-down part of the menu will be set to this
|
||||||
// width.
|
// width.
|
||||||
menuWidth: React.PropTypes.number,
|
menuWidth: PropTypes.number,
|
||||||
// Called when the selected option changes
|
// Called when the selected option changes
|
||||||
onOptionChange: React.PropTypes.func.isRequired,
|
onOptionChange: PropTypes.func.isRequired,
|
||||||
// Called when the value of the search field changes
|
// Called when the value of the search field changes
|
||||||
onSearchChange: React.PropTypes.func,
|
onSearchChange: PropTypes.func,
|
||||||
searchEnabled: React.PropTypes.bool,
|
searchEnabled: PropTypes.bool,
|
||||||
// Function that, given the key of an option, returns
|
// Function that, given the key of an option, returns
|
||||||
// a node representing that option to be displayed in the
|
// a node representing that option to be displayed in the
|
||||||
// box itself as the currently-selected option (ie. as
|
// box itself as the currently-selected option (ie. as
|
||||||
// opposed to in the actual dropped-down part). If
|
// opposed to in the actual dropped-down part). If
|
||||||
// unspecified, the appropriate child element is used as
|
// unspecified, the appropriate child element is used as
|
||||||
// in the dropped-down menu.
|
// in the dropped-down menu.
|
||||||
getShortOption: React.PropTypes.func,
|
getShortOption: PropTypes.func,
|
||||||
value: React.PropTypes.string,
|
value: PropTypes.string,
|
||||||
// negative for consistency with HTML
|
// negative for consistency with HTML
|
||||||
disabled: React.PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
const KEY_TAB = 9;
|
const KEY_TAB = 9;
|
||||||
const KEY_SHIFT = 16;
|
const KEY_SHIFT = 16;
|
||||||
|
@ -26,18 +27,18 @@ module.exports = React.createClass({
|
||||||
displayName: 'EditableText',
|
displayName: 'EditableText',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onValueChanged: React.PropTypes.func,
|
onValueChanged: PropTypes.func,
|
||||||
initialValue: React.PropTypes.string,
|
initialValue: PropTypes.string,
|
||||||
label: React.PropTypes.string,
|
label: PropTypes.string,
|
||||||
placeholder: React.PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
labelClassName: React.PropTypes.string,
|
labelClassName: PropTypes.string,
|
||||||
placeholderClassName: React.PropTypes.string,
|
placeholderClassName: PropTypes.string,
|
||||||
// Overrides blurToSubmit if true
|
// Overrides blurToSubmit if true
|
||||||
blurToCancel: React.PropTypes.bool,
|
blurToCancel: PropTypes.bool,
|
||||||
// Will cause onValueChanged(value, true) to fire on blur
|
// Will cause onValueChanged(value, true) to fire on blur
|
||||||
blurToSubmit: React.PropTypes.bool,
|
blurToSubmit: PropTypes.bool,
|
||||||
editable: React.PropTypes.bool,
|
editable: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
Phases: {
|
Phases: {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import Promise from 'bluebird';
|
import Promise from 'bluebird';
|
||||||
|
|
||||||
|
@ -126,21 +127,21 @@ export default class EditableTextContainer extends React.Component {
|
||||||
|
|
||||||
EditableTextContainer.propTypes = {
|
EditableTextContainer.propTypes = {
|
||||||
/* callback to retrieve the initial value. */
|
/* callback to retrieve the initial value. */
|
||||||
getInitialValue: React.PropTypes.func,
|
getInitialValue: PropTypes.func,
|
||||||
|
|
||||||
/* initial value; used if getInitialValue is not given */
|
/* initial value; used if getInitialValue is not given */
|
||||||
initialValue: React.PropTypes.string,
|
initialValue: PropTypes.string,
|
||||||
|
|
||||||
/* placeholder text to use when the value is empty (and not being
|
/* placeholder text to use when the value is empty (and not being
|
||||||
* edited) */
|
* edited) */
|
||||||
placeholder: React.PropTypes.string,
|
placeholder: PropTypes.string,
|
||||||
|
|
||||||
/* callback to update the value. Called with a single argument: the new
|
/* callback to update the value. Called with a single argument: the new
|
||||||
* value. */
|
* value. */
|
||||||
onSubmit: React.PropTypes.func,
|
onSubmit: PropTypes.func,
|
||||||
|
|
||||||
/* should the input submit when focus is lost? */
|
/* should the input submit when focus is lost? */
|
||||||
blurToSubmit: React.PropTypes.bool,
|
blurToSubmit: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import {emojifyText, containsEmoji} from '../../../HtmlUtils';
|
import {emojifyText, containsEmoji} from '../../../HtmlUtils';
|
||||||
|
|
||||||
export default function EmojiText(props) {
|
export default function EmojiText(props) {
|
||||||
|
@ -32,8 +33,8 @@ export default function EmojiText(props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
EmojiText.propTypes = {
|
EmojiText.propTypes = {
|
||||||
element: React.PropTypes.string,
|
element: PropTypes.string,
|
||||||
children: React.PropTypes.string.isRequired,
|
children: PropTypes.string.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
EmojiText.defaultProps = {
|
EmojiText.defaultProps = {
|
||||||
|
|
|
@ -63,7 +63,7 @@ FlairAvatar.propTypes = {
|
||||||
};
|
};
|
||||||
|
|
||||||
FlairAvatar.contextTypes = {
|
FlairAvatar.contextTypes = {
|
||||||
matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
|
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class Flair extends React.Component {
|
export default class Flair extends React.Component {
|
||||||
|
@ -134,5 +134,5 @@ Flair.propTypes = {
|
||||||
// this.context.matrixClient everywhere instead of this.props.matrixClient.
|
// this.context.matrixClient everywhere instead of this.props.matrixClient.
|
||||||
// See https://github.com/vector-im/riot-web/issues/4951.
|
// See https://github.com/vector-im/riot-web/issues/4951.
|
||||||
Flair.contextTypes = {
|
Flair.contextTypes = {
|
||||||
matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
|
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import * as languageHandler from '../../../languageHandler';
|
import * as languageHandler from '../../../languageHandler';
|
||||||
|
@ -114,7 +115,7 @@ export default class LanguageDropdown extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
LanguageDropdown.propTypes = {
|
LanguageDropdown.propTypes = {
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
onOptionChange: React.PropTypes.func.isRequired,
|
onOptionChange: PropTypes.func.isRequired,
|
||||||
value: React.PropTypes.string,
|
value: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
const MemberAvatar = require('../avatars/MemberAvatar.js');
|
const MemberAvatar = require('../avatars/MemberAvatar.js');
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -23,19 +24,19 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// An array of member events to summarise
|
// An array of member events to summarise
|
||||||
events: React.PropTypes.array.isRequired,
|
events: PropTypes.array.isRequired,
|
||||||
// An array of EventTiles to render when expanded
|
// An array of EventTiles to render when expanded
|
||||||
children: React.PropTypes.array.isRequired,
|
children: PropTypes.array.isRequired,
|
||||||
// The maximum number of names to show in either each summary e.g. 2 would result "A, B and 234 others left"
|
// The maximum number of names to show in either each summary e.g. 2 would result "A, B and 234 others left"
|
||||||
summaryLength: React.PropTypes.number,
|
summaryLength: PropTypes.number,
|
||||||
// The maximum number of avatars to display in the summary
|
// The maximum number of avatars to display in the summary
|
||||||
avatarsMaxLength: React.PropTypes.number,
|
avatarsMaxLength: PropTypes.number,
|
||||||
// The minimum number of events needed to trigger summarisation
|
// The minimum number of events needed to trigger summarisation
|
||||||
threshold: React.PropTypes.number,
|
threshold: PropTypes.number,
|
||||||
// Called when the MELS expansion is toggled
|
// Called when the MELS expansion is toggled
|
||||||
onToggle: React.PropTypes.func,
|
onToggle: PropTypes.func,
|
||||||
// Whether or not to begin with state.expanded=true
|
// Whether or not to begin with state.expanded=true
|
||||||
startExpanded: React.PropTypes.bool,
|
startExpanded: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -478,7 +479,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
const toggleButton = (
|
const toggleButton = (
|
||||||
<div className={"mx_MemberEventListSummary_toggle"} onClick={this._toggleSummary}>
|
<div className={"mx_MemberEventListSummary_toggle"} onClick={this._toggleSummary}>
|
||||||
{ expanded ? 'collapse' : 'expand' }
|
{ expanded ? _t('collapse') : _t('expand') }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import * as Roles from '../../../Roles';
|
import * as Roles from '../../../Roles';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
|
@ -24,23 +25,23 @@ module.exports = React.createClass({
|
||||||
displayName: 'PowerSelector',
|
displayName: 'PowerSelector',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
value: React.PropTypes.number.isRequired,
|
value: PropTypes.number.isRequired,
|
||||||
// The maximum value that can be set with the power selector
|
// The maximum value that can be set with the power selector
|
||||||
maxValue: React.PropTypes.number.isRequired,
|
maxValue: PropTypes.number.isRequired,
|
||||||
|
|
||||||
// Default user power level for the room
|
// Default user power level for the room
|
||||||
usersDefault: React.PropTypes.number.isRequired,
|
usersDefault: PropTypes.number.isRequired,
|
||||||
|
|
||||||
// if true, the <select/> should be a 'controlled' form element and updated by React
|
// if true, the <select/> should be a 'controlled' form element and updated by React
|
||||||
// to reflect the current value, rather than left freeform.
|
// to reflect the current value, rather than left freeform.
|
||||||
// MemberInfo uses controlled; RoomSettings uses non-controlled.
|
// MemberInfo uses controlled; RoomSettings uses non-controlled.
|
||||||
//
|
//
|
||||||
// ignored if disabled is truthy. false by default.
|
// ignored if disabled is truthy. false by default.
|
||||||
controlled: React.PropTypes.bool,
|
controlled: PropTypes.bool,
|
||||||
|
|
||||||
// should the user be able to change the value? false by default.
|
// should the user be able to change the value? false by default.
|
||||||
disabled: React.PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
onChange: React.PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -17,12 +17,13 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'ProgressBar',
|
displayName: 'ProgressBar',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
value: React.PropTypes.number,
|
value: PropTypes.number,
|
||||||
max: React.PropTypes.number,
|
max: PropTypes.number,
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
|
|
@ -15,23 +15,24 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'SettingsFlag',
|
displayName: 'SettingsFlag',
|
||||||
propTypes: {
|
propTypes: {
|
||||||
name: React.PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
level: React.PropTypes.string.isRequired,
|
level: PropTypes.string.isRequired,
|
||||||
roomId: React.PropTypes.string, // for per-room settings
|
roomId: PropTypes.string, // for per-room settings
|
||||||
label: React.PropTypes.string, // untranslated
|
label: PropTypes.string, // untranslated
|
||||||
onChange: React.PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
isExplicit: React.PropTypes.bool,
|
isExplicit: PropTypes.bool,
|
||||||
manualSave: React.PropTypes.bool,
|
manualSave: PropTypes.bool,
|
||||||
|
|
||||||
// If group is supplied, then this will create a radio button instead.
|
// If group is supplied, then this will create a radio button instead.
|
||||||
group: React.PropTypes.string,
|
group: PropTypes.string,
|
||||||
value: React.PropTypes.any, // the value for the radio button
|
value: PropTypes.any, // the value for the radio button
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
119
src/components/views/elements/TagTile.js
Normal file
119
src/components/views/elements/TagTile.js
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 New Vector 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 React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
|
import sdk from '../../../index';
|
||||||
|
import dis from '../../../dispatcher';
|
||||||
|
import { isOnlyCtrlOrCmdIgnoreShiftKeyEvent } from '../../../Keyboard';
|
||||||
|
|
||||||
|
import FlairStore from '../../../stores/FlairStore';
|
||||||
|
|
||||||
|
// A class for a child of TagPanel (possibly wrapped in a DNDTagTile) that represents
|
||||||
|
// a thing to click on for the user to filter the visible rooms in the RoomList to:
|
||||||
|
// - Rooms that are part of the group
|
||||||
|
// - Direct messages with members of the group
|
||||||
|
// with the intention that this could be expanded to arbitrary tags in future.
|
||||||
|
export default React.createClass({
|
||||||
|
displayName: 'TagTile',
|
||||||
|
|
||||||
|
propTypes: {
|
||||||
|
// A string tag such as "m.favourite" or a group ID such as "+groupid:domain.bla"
|
||||||
|
// For now, only group IDs are handled.
|
||||||
|
tag: PropTypes.string,
|
||||||
|
},
|
||||||
|
|
||||||
|
contextTypes: {
|
||||||
|
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
// Whether the mouse is over the tile
|
||||||
|
hover: false,
|
||||||
|
// The profile data of the group if this.props.tag is a group ID
|
||||||
|
profile: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillMount() {
|
||||||
|
this.unmounted = false;
|
||||||
|
if (this.props.tag[0] === '+') {
|
||||||
|
FlairStore.getGroupProfileCached(
|
||||||
|
this.context.matrixClient,
|
||||||
|
this.props.tag,
|
||||||
|
).then((profile) => {
|
||||||
|
if (this.unmounted) return;
|
||||||
|
this.setState({profile});
|
||||||
|
}).catch((err) => {
|
||||||
|
console.warn('Could not fetch group profile for ' + this.props.tag, err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.unmounted = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
onClick: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'select_tag',
|
||||||
|
tag: this.props.tag,
|
||||||
|
ctrlOrCmdKey: isOnlyCtrlOrCmdIgnoreShiftKeyEvent(e),
|
||||||
|
shiftKey: e.shiftKey,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseOver: function() {
|
||||||
|
this.setState({hover: true});
|
||||||
|
},
|
||||||
|
|
||||||
|
onMouseOut: function() {
|
||||||
|
this.setState({hover: false});
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
const RoomTooltip = sdk.getComponent('rooms.RoomTooltip');
|
||||||
|
const profile = this.state.profile || {};
|
||||||
|
const name = profile.name || this.props.tag;
|
||||||
|
const avatarHeight = 35;
|
||||||
|
|
||||||
|
const httpUrl = profile.avatarUrl ? this.context.matrixClient.mxcUrlToHttp(
|
||||||
|
profile.avatarUrl, avatarHeight, avatarHeight, "crop",
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const className = classNames({
|
||||||
|
mx_TagTile: true,
|
||||||
|
mx_TagTile_selected: this.props.selected,
|
||||||
|
});
|
||||||
|
|
||||||
|
const tip = this.state.hover ?
|
||||||
|
<RoomTooltip className="mx_TagTile_tooltip" label={name} /> :
|
||||||
|
<div />;
|
||||||
|
return <AccessibleButton className={className} onClick={this.onClick}>
|
||||||
|
<div className="mx_TagTile_avatar" onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
|
||||||
|
<BaseAvatar name={name} url={httpUrl} width={avatarHeight} height={avatarHeight} />
|
||||||
|
{ tip }
|
||||||
|
</div>
|
||||||
|
</AccessibleButton>;
|
||||||
|
},
|
||||||
|
});
|
|
@ -18,16 +18,17 @@ limitations under the License.
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
const ReactDOM = require("react-dom");
|
const ReactDOM = require("react-dom");
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
const Tinter = require("../../../Tinter");
|
const Tinter = require("../../../Tinter");
|
||||||
|
|
||||||
var TintableSvg = React.createClass({
|
var TintableSvg = React.createClass({
|
||||||
displayName: 'TintableSvg',
|
displayName: 'TintableSvg',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
src: React.PropTypes.string.isRequired,
|
src: PropTypes.string.isRequired,
|
||||||
width: React.PropTypes.string.isRequired,
|
width: PropTypes.string.isRequired,
|
||||||
height: React.PropTypes.string.isRequired,
|
height: PropTypes.string.isRequired,
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
statics: {
|
statics: {
|
||||||
|
|
|
@ -17,14 +17,15 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'UserSelector',
|
displayName: 'UserSelector',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onChange: React.PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
selected_users: React.PropTypes.arrayOf(React.PropTypes.string),
|
selected_users: PropTypes.arrayOf(React.PropTypes.string),
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -66,7 +66,7 @@ const GroupRoomTile = React.createClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
GroupRoomTile.contextTypes = {
|
GroupRoomTile.contextTypes = {
|
||||||
matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
|
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ const GroupTile = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
contextTypes: {
|
contextTypes: {
|
||||||
matrixClient: React.PropTypes.instanceOf(MatrixClient).isRequired,
|
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
|
|
|
@ -18,6 +18,7 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
const DIV_ID = 'mx_recaptcha';
|
const DIV_ID = 'mx_recaptcha';
|
||||||
|
@ -29,10 +30,10 @@ module.exports = React.createClass({
|
||||||
displayName: 'CaptchaForm',
|
displayName: 'CaptchaForm',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
sitePublicKey: React.PropTypes.string,
|
sitePublicKey: PropTypes.string,
|
||||||
|
|
||||||
// called with the captcha response
|
// called with the captcha response
|
||||||
onCaptchaResponse: React.PropTypes.func,
|
onCaptchaResponse: PropTypes.func,
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -17,13 +17,14 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'CasLogin',
|
displayName: 'CasLogin',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onSubmit: React.PropTypes.func, // fn()
|
onSubmit: PropTypes.func, // fn()
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
|
|
||||||
|
@ -131,11 +132,11 @@ export default class CountryDropdown extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
CountryDropdown.propTypes = {
|
CountryDropdown.propTypes = {
|
||||||
className: React.PropTypes.string,
|
className: PropTypes.string,
|
||||||
isSmall: React.PropTypes.bool,
|
isSmall: PropTypes.bool,
|
||||||
// if isSmall, show +44 in the selected value
|
// if isSmall, show +44 in the selected value
|
||||||
showPrefix: React.PropTypes.bool,
|
showPrefix: PropTypes.bool,
|
||||||
onOptionChange: React.PropTypes.func.isRequired,
|
onOptionChange: PropTypes.func.isRequired,
|
||||||
value: React.PropTypes.string,
|
value: PropTypes.string,
|
||||||
disabled: React.PropTypes.bool,
|
disabled: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
@ -69,12 +70,12 @@ export const PasswordAuthEntry = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
matrixClient: React.PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
errorText: React.PropTypes.string,
|
errorText: PropTypes.string,
|
||||||
// is the auth logic currently waiting for something to
|
// is the auth logic currently waiting for something to
|
||||||
// happen?
|
// happen?
|
||||||
busy: React.PropTypes.bool,
|
busy: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -158,10 +159,10 @@ export const RecaptchaAuthEntry = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
stageParams: React.PropTypes.object.isRequired,
|
stageParams: PropTypes.object.isRequired,
|
||||||
errorText: React.PropTypes.string,
|
errorText: PropTypes.string,
|
||||||
busy: React.PropTypes.bool,
|
busy: PropTypes.bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
_onCaptchaResponse: function(response) {
|
_onCaptchaResponse: function(response) {
|
||||||
|
@ -200,15 +201,15 @@ export const EmailIdentityAuthEntry = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
matrixClient: React.PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
authSessionId: React.PropTypes.string.isRequired,
|
authSessionId: PropTypes.string.isRequired,
|
||||||
clientSecret: React.PropTypes.string.isRequired,
|
clientSecret: PropTypes.string.isRequired,
|
||||||
inputs: React.PropTypes.object.isRequired,
|
inputs: PropTypes.object.isRequired,
|
||||||
stageState: React.PropTypes.object.isRequired,
|
stageState: PropTypes.object.isRequired,
|
||||||
fail: React.PropTypes.func.isRequired,
|
fail: PropTypes.func.isRequired,
|
||||||
setEmailSid: React.PropTypes.func.isRequired,
|
setEmailSid: PropTypes.func.isRequired,
|
||||||
makeRegistrationUrl: React.PropTypes.func.isRequired,
|
makeRegistrationUrl: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -275,15 +276,15 @@ export const MsisdnAuthEntry = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
inputs: React.PropTypes.shape({
|
inputs: PropTypes.shape({
|
||||||
phoneCountry: React.PropTypes.string,
|
phoneCountry: PropTypes.string,
|
||||||
phoneNumber: React.PropTypes.string,
|
phoneNumber: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
fail: React.PropTypes.func,
|
fail: PropTypes.func,
|
||||||
clientSecret: React.PropTypes.func,
|
clientSecret: PropTypes.func,
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
matrixClient: React.PropTypes.object,
|
matrixClient: PropTypes.object,
|
||||||
submitAuthDict: React.PropTypes.func,
|
submitAuthDict: PropTypes.func,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
@ -374,7 +375,7 @@ export const MsisdnAuthEntry = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>{ _t("A text message has been sent to %(msisdn)s",
|
<p>{ _t("A text message has been sent to %(msisdn)s",
|
||||||
{ msisdn: <i>this._msisdn</i> },
|
{ msisdn: <i>{ this._msisdn }</i> },
|
||||||
) }
|
) }
|
||||||
</p>
|
</p>
|
||||||
<p>{ _t("Please enter the code it contains:") }</p>
|
<p>{ _t("Please enter the code it contains:") }</p>
|
||||||
|
@ -405,11 +406,11 @@ export const FallbackAuthEntry = React.createClass({
|
||||||
displayName: 'FallbackAuthEntry',
|
displayName: 'FallbackAuthEntry',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
matrixClient: React.PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
authSessionId: React.PropTypes.string.isRequired,
|
authSessionId: PropTypes.string.isRequired,
|
||||||
loginType: React.PropTypes.string.isRequired,
|
loginType: PropTypes.string.isRequired,
|
||||||
submitAuthDict: React.PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
errorText: React.PropTypes.string,
|
errorText: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
componentWillMount: function() {
|
componentWillMount: function() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -256,17 +257,17 @@ PasswordLogin.LOGIN_FIELD_MXID = "login_field_mxid";
|
||||||
PasswordLogin.LOGIN_FIELD_PHONE = "login_field_phone";
|
PasswordLogin.LOGIN_FIELD_PHONE = "login_field_phone";
|
||||||
|
|
||||||
PasswordLogin.propTypes = {
|
PasswordLogin.propTypes = {
|
||||||
onSubmit: React.PropTypes.func.isRequired, // fn(username, password)
|
onSubmit: PropTypes.func.isRequired, // fn(username, password)
|
||||||
onForgotPasswordClick: React.PropTypes.func, // fn()
|
onForgotPasswordClick: PropTypes.func, // fn()
|
||||||
initialUsername: React.PropTypes.string,
|
initialUsername: PropTypes.string,
|
||||||
initialPhoneCountry: React.PropTypes.string,
|
initialPhoneCountry: PropTypes.string,
|
||||||
initialPhoneNumber: React.PropTypes.string,
|
initialPhoneNumber: PropTypes.string,
|
||||||
initialPassword: React.PropTypes.string,
|
initialPassword: PropTypes.string,
|
||||||
onUsernameChanged: React.PropTypes.func,
|
onUsernameChanged: PropTypes.func,
|
||||||
onPhoneCountryChanged: React.PropTypes.func,
|
onPhoneCountryChanged: PropTypes.func,
|
||||||
onPhoneNumberChanged: React.PropTypes.func,
|
onPhoneNumberChanged: PropTypes.func,
|
||||||
onPasswordChanged: React.PropTypes.func,
|
onPasswordChanged: PropTypes.func,
|
||||||
loginIncorrect: React.PropTypes.bool,
|
loginIncorrect: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = PasswordLogin;
|
module.exports = PasswordLogin;
|
||||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { field_input_incorrect } from '../../../UiEffects';
|
import { field_input_incorrect } from '../../../UiEffects';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
import Email from '../../../email';
|
import Email from '../../../email';
|
||||||
|
@ -40,25 +41,25 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
// Values pre-filled in the input boxes when the component loads
|
// Values pre-filled in the input boxes when the component loads
|
||||||
defaultEmail: React.PropTypes.string,
|
defaultEmail: PropTypes.string,
|
||||||
defaultPhoneCountry: React.PropTypes.string,
|
defaultPhoneCountry: PropTypes.string,
|
||||||
defaultPhoneNumber: React.PropTypes.string,
|
defaultPhoneNumber: PropTypes.string,
|
||||||
defaultUsername: React.PropTypes.string,
|
defaultUsername: PropTypes.string,
|
||||||
defaultPassword: React.PropTypes.string,
|
defaultPassword: PropTypes.string,
|
||||||
teamsConfig: React.PropTypes.shape({
|
teamsConfig: PropTypes.shape({
|
||||||
// Email address to request new teams
|
// Email address to request new teams
|
||||||
supportEmail: React.PropTypes.string,
|
supportEmail: PropTypes.string,
|
||||||
teams: React.PropTypes.arrayOf(React.PropTypes.shape({
|
teams: PropTypes.arrayOf(React.PropTypes.shape({
|
||||||
// The displayed name of the team
|
// The displayed name of the team
|
||||||
"name": React.PropTypes.string,
|
"name": PropTypes.string,
|
||||||
// The domain of team email addresses
|
// The domain of team email addresses
|
||||||
"domain": React.PropTypes.string,
|
"domain": PropTypes.string,
|
||||||
})).required,
|
})).required,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
minPasswordLength: React.PropTypes.number,
|
minPasswordLength: PropTypes.number,
|
||||||
onError: React.PropTypes.func,
|
onError: PropTypes.func,
|
||||||
onRegisterClick: React.PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise
|
onRegisterClick: PropTypes.func.isRequired, // onRegisterClick(Object) => ?Promise
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
const Modal = require('../../../Modal');
|
const Modal = require('../../../Modal');
|
||||||
const sdk = require('../../../index');
|
const sdk = require('../../../index');
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -28,24 +29,24 @@ module.exports = React.createClass({
|
||||||
displayName: 'ServerConfig',
|
displayName: 'ServerConfig',
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
onServerConfigChange: React.PropTypes.func,
|
onServerConfigChange: PropTypes.func,
|
||||||
|
|
||||||
// default URLs are defined in config.json (or the hardcoded defaults)
|
// default URLs are defined in config.json (or the hardcoded defaults)
|
||||||
// they are used if the user has not overridden them with a custom URL.
|
// they are used if the user has not overridden them with a custom URL.
|
||||||
// In other words, if the custom URL is blank, the default is used.
|
// In other words, if the custom URL is blank, the default is used.
|
||||||
defaultHsUrl: React.PropTypes.string, // e.g. https://matrix.org
|
defaultHsUrl: PropTypes.string, // e.g. https://matrix.org
|
||||||
defaultIsUrl: React.PropTypes.string, // e.g. https://vector.im
|
defaultIsUrl: PropTypes.string, // e.g. https://vector.im
|
||||||
|
|
||||||
// custom URLs are explicitly provided by the user and override the
|
// custom URLs are explicitly provided by the user and override the
|
||||||
// default URLs. The user enters them via the component's input fields,
|
// default URLs. The user enters them via the component's input fields,
|
||||||
// which is reflected on these properties whenever on..UrlChanged fires.
|
// which is reflected on these properties whenever on..UrlChanged fires.
|
||||||
// They are persisted in localStorage by MatrixClientPeg, and so can
|
// They are persisted in localStorage by MatrixClientPeg, and so can
|
||||||
// override the default URLs when the component initially loads.
|
// override the default URLs when the component initially loads.
|
||||||
customHsUrl: React.PropTypes.string,
|
customHsUrl: PropTypes.string,
|
||||||
customIsUrl: React.PropTypes.string,
|
customIsUrl: PropTypes.string,
|
||||||
|
|
||||||
withToggleButton: React.PropTypes.bool,
|
withToggleButton: PropTypes.bool,
|
||||||
delayTimeMs: React.PropTypes.number, // time to wait before invoking onChanged
|
delayTimeMs: PropTypes.number, // time to wait before invoking onChanged
|
||||||
},
|
},
|
||||||
|
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import filesize from 'filesize';
|
import filesize from 'filesize';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import sdk from '../../../index';
|
import sdk from '../../../index';
|
||||||
|
@ -191,7 +192,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
contextTypes: {
|
contextTypes: {
|
||||||
appConfig: React.PropTypes.object,
|
appConfig: PropTypes.object,
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import MFileBody from './MFileBody';
|
import MFileBody from './MFileBody';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import ImageUtils from '../../../ImageUtils';
|
import ImageUtils from '../../../ImageUtils';
|
||||||
|
@ -33,10 +34,10 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: React.PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
|
|
||||||
/* called when the image has loaded */
|
/* called when the image has loaded */
|
||||||
onWidgetLoad: React.PropTypes.func.isRequired,
|
onWidgetLoad: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import MFileBody from './MFileBody';
|
import MFileBody from './MFileBody';
|
||||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||||
import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile';
|
import { decryptFile, readBlobAsDataUri } from '../../../utils/DecryptFile';
|
||||||
|
@ -29,10 +30,10 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: React.PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
|
|
||||||
/* called when the video has loaded */
|
/* called when the video has loaded */
|
||||||
onWidgetLoad: React.PropTypes.func.isRequired,
|
onWidgetLoad: PropTypes.func.isRequired,
|
||||||
},
|
},
|
||||||
|
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
const sdk = require('../../../index');
|
const sdk = require('../../../index');
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
|
@ -24,22 +25,22 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: React.PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
|
|
||||||
/* a list of words to highlight */
|
/* a list of words to highlight */
|
||||||
highlights: React.PropTypes.array,
|
highlights: PropTypes.array,
|
||||||
|
|
||||||
/* link URL for the highlights */
|
/* link URL for the highlights */
|
||||||
highlightLink: React.PropTypes.string,
|
highlightLink: PropTypes.string,
|
||||||
|
|
||||||
/* should show URL previews for this event */
|
/* should show URL previews for this event */
|
||||||
showUrlPreview: React.PropTypes.bool,
|
showUrlPreview: PropTypes.bool,
|
||||||
|
|
||||||
/* callback called when dynamic content in events are loaded */
|
/* callback called when dynamic content in events are loaded */
|
||||||
onWidgetLoad: React.PropTypes.func,
|
onWidgetLoad: PropTypes.func,
|
||||||
|
|
||||||
/* the shsape of the tile, used */
|
/* the shsape of the tile, used */
|
||||||
tileShape: React.PropTypes.string,
|
tileShape: PropTypes.string,
|
||||||
},
|
},
|
||||||
|
|
||||||
getEventTileOps: function() {
|
getEventTileOps: function() {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue