You've already forked matrix-react-sdk
mirror of
https://github.com/matrix-org/matrix-react-sdk.git
synced 2025-11-17 17:42:41 +03:00
Merge branches 'develop' and 't3chguy/report_event' of https://github.com/matrix-org/matrix-react-sdk into t3chguy/report_event
Conflicts: src/i18n/strings/en_EN.json
This commit is contained in:
@@ -16,10 +16,11 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'CompatibilityPage',
|
||||
propTypes: {
|
||||
onAccept: PropTypes.func,
|
||||
|
||||
@@ -15,6 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Matrix from 'matrix-js-sdk';
|
||||
@@ -25,7 +26,7 @@ import { _t } from '../../languageHandler';
|
||||
/*
|
||||
* Component which shows the filtered file using a TimelinePanel
|
||||
*/
|
||||
const FilePanel = React.createClass({
|
||||
const FilePanel = createReactClass({
|
||||
displayName: 'FilePanel',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import Promise from 'bluebird';
|
||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
||||
@@ -67,7 +68,7 @@ const UserSummaryType = PropTypes.shape({
|
||||
}).isRequired,
|
||||
});
|
||||
|
||||
const CategoryRoomList = React.createClass({
|
||||
const CategoryRoomList = createReactClass({
|
||||
displayName: 'CategoryRoomList',
|
||||
|
||||
props: {
|
||||
@@ -119,7 +120,7 @@ const CategoryRoomList = React.createClass({
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@@ -156,7 +157,7 @@ const CategoryRoomList = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
const FeaturedRoom = React.createClass({
|
||||
const FeaturedRoom = createReactClass({
|
||||
displayName: 'FeaturedRoom',
|
||||
|
||||
props: {
|
||||
@@ -244,7 +245,7 @@ const FeaturedRoom = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
const RoleUserList = React.createClass({
|
||||
const RoleUserList = createReactClass({
|
||||
displayName: 'RoleUserList',
|
||||
|
||||
props: {
|
||||
@@ -296,7 +297,7 @@ const RoleUserList = React.createClass({
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
@@ -327,7 +328,7 @@ const RoleUserList = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
const FeaturedUser = React.createClass({
|
||||
const FeaturedUser = createReactClass({
|
||||
displayName: 'FeaturedUser',
|
||||
|
||||
props: {
|
||||
@@ -399,7 +400,7 @@ const FeaturedUser = React.createClass({
|
||||
const GROUP_JOINPOLICY_OPEN = "open";
|
||||
const GROUP_JOINPOLICY_INVITE = "invite";
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'GroupView',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -19,7 +19,7 @@ import PropTypes from "prop-types";
|
||||
import AutoHideScrollbar from "./AutoHideScrollbar";
|
||||
|
||||
export default class IndicatorScrollbar extends React.Component {
|
||||
static PropTypes = {
|
||||
static propTypes = {
|
||||
// If true, the scrollbar will append mx_IndicatorScrollbar_leftOverflowIndicator
|
||||
// and mx_IndicatorScrollbar_rightOverflowIndicator elements to the list for positioning
|
||||
// by the parent element.
|
||||
|
||||
@@ -19,13 +19,14 @@ import Matrix from 'matrix-js-sdk';
|
||||
const InteractiveAuth = Matrix.InteractiveAuth;
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {getEntryComponentForLoginType} from '../views/auth/InteractiveAuthEntryComponents';
|
||||
|
||||
import sdk from '../../index';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'InteractiveAuth',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -14,9 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
@@ -30,7 +29,7 @@ import {_t} from "../../languageHandler";
|
||||
import Analytics from "../../Analytics";
|
||||
|
||||
|
||||
const LeftPanel = React.createClass({
|
||||
const LeftPanel = createReactClass({
|
||||
displayName: 'LeftPanel',
|
||||
|
||||
// NB. If you add props, don't forget to update
|
||||
@@ -82,6 +81,9 @@ const LeftPanel = React.createClass({
|
||||
if (this.state.searchFilter !== nextState.searchFilter) {
|
||||
return true;
|
||||
}
|
||||
if (this.state.searchExpanded !== nextState.searchExpanded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
@@ -204,12 +206,23 @@ const LeftPanel = React.createClass({
|
||||
if (source === "keyboard") {
|
||||
dis.dispatch({action: 'focus_composer'});
|
||||
}
|
||||
this.setState({searchExpanded: false});
|
||||
},
|
||||
|
||||
collectRoomList: function(ref) {
|
||||
this._roomList = ref;
|
||||
},
|
||||
|
||||
_onSearchFocus: function() {
|
||||
this.setState({searchExpanded: true});
|
||||
},
|
||||
|
||||
_onSearchBlur: function(event) {
|
||||
if (event.target.value.length === 0) {
|
||||
this.setState({searchExpanded: false});
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const RoomList = sdk.getComponent('rooms.RoomList');
|
||||
const RoomBreadcrumbs = sdk.getComponent('rooms.RoomBreadcrumbs');
|
||||
@@ -218,6 +231,7 @@ const LeftPanel = React.createClass({
|
||||
const TopLeftMenuButton = sdk.getComponent('structures.TopLeftMenuButton');
|
||||
const SearchBox = sdk.getComponent('structures.SearchBox');
|
||||
const CallPreview = sdk.getComponent('voip.CallPreview');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
||||
const tagPanelEnabled = SettingsStore.getValue("TagPanel.enableTagPanel");
|
||||
let tagPanelContainer;
|
||||
@@ -241,11 +255,23 @@ const LeftPanel = React.createClass({
|
||||
},
|
||||
);
|
||||
|
||||
let exploreButton;
|
||||
if (!this.props.collapsed) {
|
||||
exploreButton = (
|
||||
<div className={classNames("mx_LeftPanel_explore", {"mx_LeftPanel_explore_hidden": this.state.searchExpanded})}>
|
||||
<AccessibleButton onClick={() => dis.dispatch({action: 'view_room_directory'})}>{_t("Explore")}</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const searchBox = (<SearchBox
|
||||
enableRoomSearchFocus={true}
|
||||
placeholder={ _t('Filter room names') }
|
||||
blurredPlaceholder={ _t('Filter') }
|
||||
placeholder={ _t('Filter rooms…') }
|
||||
onSearch={ this.onSearch }
|
||||
onCleared={ this.onSearchCleared }
|
||||
onFocus={this._onSearchFocus}
|
||||
onBlur={this._onSearchBlur}
|
||||
collapsed={this.props.collapsed} />);
|
||||
|
||||
let breadcrumbs;
|
||||
@@ -259,7 +285,10 @@ const LeftPanel = React.createClass({
|
||||
<aside className={"mx_LeftPanel dark-panel"} onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
|
||||
<TopLeftMenuButton collapsed={ this.props.collapsed } />
|
||||
{ breadcrumbs }
|
||||
{ searchBox }
|
||||
<div className="mx_LeftPanel_exploreAndFilterRow">
|
||||
{ exploreButton }
|
||||
{ searchBox }
|
||||
</div>
|
||||
<CallPreview ConferenceHandler={VectorConferenceHandler} />
|
||||
<RoomList
|
||||
ref={this.collectRoomList}
|
||||
|
||||
@@ -18,6 +18,7 @@ limitations under the License.
|
||||
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { DragDropContext } from 'react-beautiful-dnd';
|
||||
|
||||
@@ -58,7 +59,7 @@ function canElementReceiveInput(el) {
|
||||
*
|
||||
* Components mounted below us can access the matrix client via the react context.
|
||||
*/
|
||||
const LoggedInView = React.createClass({
|
||||
const LoggedInView = createReactClass({
|
||||
displayName: 'LoggedInView',
|
||||
|
||||
propTypes: {
|
||||
@@ -349,7 +350,8 @@ const LoggedInView = React.createClass({
|
||||
|
||||
let handled = false;
|
||||
const ctrlCmdOnly = isOnlyCtrlOrCmdKeyEvent(ev);
|
||||
const hasModifier = ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey;
|
||||
const hasModifier = ev.altKey || ev.ctrlKey || ev.metaKey || ev.shiftKey ||
|
||||
ev.key === "Alt" || ev.key === "Control" || ev.key === "Meta" || ev.key === "Shift";
|
||||
|
||||
switch (ev.keyCode) {
|
||||
case KeyCode.PAGE_UP:
|
||||
|
||||
@@ -20,6 +20,7 @@ limitations under the License.
|
||||
import Promise from 'bluebird';
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import Matrix from "matrix-js-sdk";
|
||||
|
||||
@@ -106,7 +107,7 @@ const ONBOARDING_FLOW_STARTERS = [
|
||||
'view_create_group',
|
||||
];
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
// we export this so that the integration tests can use it :-S
|
||||
statics: {
|
||||
VIEWS: VIEWS,
|
||||
@@ -446,6 +447,29 @@ export default React.createClass({
|
||||
}
|
||||
|
||||
switch (payload.action) {
|
||||
case 'MatrixActions.accountData':
|
||||
// XXX: This is a collection of several hacks to solve a minor problem. We want to
|
||||
// update our local state when the ID server changes, but don't want to put that in
|
||||
// the js-sdk as we'd be then dictating how all consumers need to behave. However,
|
||||
// this component is already bloated and we probably don't want this tiny logic in
|
||||
// here, but there's no better place in the react-sdk for it. Additionally, we're
|
||||
// abusing the MatrixActionCreator stuff to avoid errors on dispatches.
|
||||
if (payload.event_type === 'm.identity_server') {
|
||||
const fullUrl = payload.event_content ? payload.event_content['base_url'] : null;
|
||||
if (!fullUrl) {
|
||||
MatrixClientPeg.get().setIdentityServerUrl(null);
|
||||
localStorage.removeItem("mx_is_access_token");
|
||||
localStorage.removeItem("mx_is_url");
|
||||
} else {
|
||||
MatrixClientPeg.get().setIdentityServerUrl(fullUrl);
|
||||
localStorage.removeItem("mx_is_access_token"); // clear token
|
||||
localStorage.setItem("mx_is_url", fullUrl); // XXX: Do we still need this?
|
||||
}
|
||||
|
||||
// redispatch the change with a more specific action
|
||||
dis.dispatch({action: 'id_server_changed'});
|
||||
}
|
||||
break;
|
||||
case 'logout':
|
||||
Lifecycle.logout();
|
||||
break;
|
||||
@@ -931,18 +955,17 @@ export default React.createClass({
|
||||
}).close;
|
||||
},
|
||||
|
||||
_createRoom: function() {
|
||||
_createRoom: async function() {
|
||||
const CreateRoomDialog = sdk.getComponent('dialogs.CreateRoomDialog');
|
||||
Modal.createTrackedDialog('Create Room', '', CreateRoomDialog, {
|
||||
onFinished: (shouldCreate, name, noFederate) => {
|
||||
if (shouldCreate) {
|
||||
const createOpts = {};
|
||||
if (name) createOpts.name = name;
|
||||
if (noFederate) createOpts.creation_content = {'m.federate': false};
|
||||
createRoom({createOpts}).done();
|
||||
}
|
||||
},
|
||||
});
|
||||
const modal = Modal.createTrackedDialog('Create Room', '', CreateRoomDialog);
|
||||
|
||||
const [shouldCreate, name, noFederate] = await modal.finished;
|
||||
if (shouldCreate) {
|
||||
const createOpts = {};
|
||||
if (name) createOpts.name = name;
|
||||
if (noFederate) createOpts.creation_content = {'m.federate': false};
|
||||
createRoom({createOpts}).done();
|
||||
}
|
||||
},
|
||||
|
||||
_chatCreateOrReuse: function(userId) {
|
||||
|
||||
@@ -18,6 +18,7 @@ limitations under the License.
|
||||
/* global Velocity */
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
@@ -35,7 +36,7 @@ const isMembershipChange = (e) => e.getType() === 'm.room.member' || e.getType()
|
||||
|
||||
/* (almost) stateless UI component which builds the event tiles in the room timeline.
|
||||
*/
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'MessagePanel',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -16,6 +16,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import sdk from '../../index';
|
||||
@@ -23,7 +24,7 @@ import { _t } from '../../languageHandler';
|
||||
import dis from '../../dispatcher';
|
||||
import AccessibleButton from '../views/elements/AccessibleButton';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'MyGroups',
|
||||
|
||||
getInitialState: function() {
|
||||
|
||||
@@ -15,7 +15,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { _t } from '../../languageHandler';
|
||||
const sdk = require('../../index');
|
||||
const MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
@@ -23,7 +24,7 @@ const MatrixClientPeg = require("../../MatrixClientPeg");
|
||||
/*
|
||||
* Component which shows the global notification list using a TimelinePanel
|
||||
*/
|
||||
const NotificationPanel = React.createClass({
|
||||
const NotificationPanel = createReactClass({
|
||||
displayName: 'NotificationPanel',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -15,9 +16,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
|
||||
const MatrixClientPeg = require('../../MatrixClientPeg');
|
||||
const ContentRepo = require("matrix-js-sdk").ContentRepo;
|
||||
@@ -39,7 +39,7 @@ function track(action) {
|
||||
Analytics.trackEvent('RoomDirectory', action);
|
||||
}
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'RoomDirectory',
|
||||
|
||||
propTypes: {
|
||||
@@ -141,6 +141,10 @@ module.exports = React.createClass({
|
||||
getMoreRooms: function() {
|
||||
if (!MatrixClientPeg.get()) return Promise.resolve();
|
||||
|
||||
this.setState({
|
||||
loading: true,
|
||||
});
|
||||
|
||||
const my_filter_string = this.state.filterString;
|
||||
const my_server = this.state.roomServer;
|
||||
// remember the next batch token when we sent the request
|
||||
@@ -322,12 +326,7 @@ module.exports = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
onCreateRoomClicked: function() {
|
||||
this.props.onFinished();
|
||||
dis.dispatch({action: 'view_create_room'});
|
||||
},
|
||||
|
||||
onJoinClick: function(alias) {
|
||||
onJoinFromSearchClick: function(alias) {
|
||||
// If we don't have a particular instance id selected, just show that rooms alias
|
||||
if (!this.state.instanceId) {
|
||||
// If the user specified an alias without a domain, add on whichever server is selected
|
||||
@@ -369,6 +368,39 @@ module.exports = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
onPreviewClick: function(room) {
|
||||
this.props.onFinished();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: room.room_id,
|
||||
should_peek: true,
|
||||
});
|
||||
},
|
||||
|
||||
onViewClick: function(room) {
|
||||
this.props.onFinished();
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: room.room_id,
|
||||
should_peek: false,
|
||||
});
|
||||
},
|
||||
|
||||
onJoinClick: function(room) {
|
||||
this.props.onFinished();
|
||||
MatrixClientPeg.get().joinRoom(room.room_id);
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: room.room_id,
|
||||
joining: true,
|
||||
});
|
||||
},
|
||||
|
||||
onCreateRoomClick: function(room) {
|
||||
this.props.onFinished();
|
||||
dis.dispatch({action: 'view_create_room'});
|
||||
},
|
||||
|
||||
showRoomAlias: function(alias, autoJoin=false) {
|
||||
this.showRoom(null, alias, autoJoin);
|
||||
},
|
||||
@@ -413,74 +445,70 @@ module.exports = React.createClass({
|
||||
dis.dispatch(payload);
|
||||
},
|
||||
|
||||
getRows: function() {
|
||||
getRow(room) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const clientRoom = client.getRoom(room.room_id);
|
||||
const hasJoinedRoom = clientRoom && clientRoom.getMyMembership() === "join";
|
||||
const isGuest = client.isGuest();
|
||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
let previewButton;
|
||||
let joinOrViewButton;
|
||||
|
||||
if (!this.state.publicRooms) return [];
|
||||
|
||||
const rooms = this.state.publicRooms;
|
||||
const rows = [];
|
||||
const self = this;
|
||||
let guestRead; let guestJoin; let perms;
|
||||
for (let i = 0; i < rooms.length; i++) {
|
||||
guestRead = null;
|
||||
guestJoin = null;
|
||||
|
||||
if (rooms[i].world_readable) {
|
||||
guestRead = (
|
||||
<div className="mx_RoomDirectory_perm">{ _t('World readable') }</div>
|
||||
);
|
||||
}
|
||||
if (rooms[i].guest_can_join) {
|
||||
guestJoin = (
|
||||
<div className="mx_RoomDirectory_perm">{ _t('Guests can join') }</div>
|
||||
);
|
||||
}
|
||||
|
||||
perms = null;
|
||||
if (guestRead || guestJoin) {
|
||||
perms = <div className="mx_RoomDirectory_perms">{guestRead}{guestJoin}</div>;
|
||||
}
|
||||
|
||||
let name = rooms[i].name || get_display_alias_for_room(rooms[i]) || _t('Unnamed room');
|
||||
if (name.length > MAX_NAME_LENGTH) {
|
||||
name = `${name.substring(0, MAX_NAME_LENGTH)}...`;
|
||||
}
|
||||
|
||||
let topic = rooms[i].topic || '';
|
||||
if (topic.length > MAX_TOPIC_LENGTH) {
|
||||
topic = `${topic.substring(0, MAX_TOPIC_LENGTH)}...`;
|
||||
}
|
||||
topic = linkifyAndSanitizeHtml(topic);
|
||||
|
||||
rows.push(
|
||||
<tr key={ rooms[i].room_id }
|
||||
onClick={self.onRoomClicked.bind(self, rooms[i])}
|
||||
// cancel onMouseDown otherwise shift-clicking highlights text
|
||||
onMouseDown={(ev) => {ev.preventDefault();}}
|
||||
>
|
||||
<td className="mx_RoomDirectory_roomAvatar">
|
||||
<BaseAvatar width={24} height={24} resizeMethod='crop'
|
||||
name={ name } idName={ name }
|
||||
url={ ContentRepo.getHttpUriForMxc(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
rooms[i].avatar_url, 24, 24, "crop") } />
|
||||
</td>
|
||||
<td className="mx_RoomDirectory_roomDescription">
|
||||
<div className="mx_RoomDirectory_name">{ name }</div>
|
||||
{ perms }
|
||||
<div className="mx_RoomDirectory_topic"
|
||||
onClick={ function(e) { e.stopPropagation(); } }
|
||||
dangerouslySetInnerHTML={{ __html: topic }} />
|
||||
<div className="mx_RoomDirectory_alias">{ get_display_alias_for_room(rooms[i]) }</div>
|
||||
</td>
|
||||
<td className="mx_RoomDirectory_roomMemberCount">
|
||||
{ rooms[i].num_joined_members }
|
||||
</td>
|
||||
</tr>,
|
||||
if (room.world_readable && !hasJoinedRoom) {
|
||||
previewButton = (
|
||||
<AccessibleButton kind="secondary" onClick={() => this.onPreviewClick(room)}>{_t("Preview")}</AccessibleButton>
|
||||
);
|
||||
}
|
||||
return rows;
|
||||
if (hasJoinedRoom) {
|
||||
joinOrViewButton = (
|
||||
<AccessibleButton kind="secondary" onClick={() => this.onViewClick(room)}>{_t("View")}</AccessibleButton>
|
||||
);
|
||||
} else if (!isGuest || room.guest_can_join) {
|
||||
joinOrViewButton = (
|
||||
<AccessibleButton kind="primary" onClick={() => this.onJoinClick(room)}>{_t("Join")}</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
let name = room.name || get_display_alias_for_room(room) || _t('Unnamed room');
|
||||
if (name.length > MAX_NAME_LENGTH) {
|
||||
name = `${name.substring(0, MAX_NAME_LENGTH)}...`;
|
||||
}
|
||||
|
||||
let topic = room.topic || '';
|
||||
if (topic.length > MAX_TOPIC_LENGTH) {
|
||||
topic = `${topic.substring(0, MAX_TOPIC_LENGTH)}...`;
|
||||
}
|
||||
topic = linkifyAndSanitizeHtml(topic);
|
||||
const avatarUrl = ContentRepo.getHttpUriForMxc(
|
||||
MatrixClientPeg.get().getHomeserverUrl(),
|
||||
room.avatar_url, 32, 32, "crop",
|
||||
);
|
||||
return (
|
||||
<tr key={ room.room_id }
|
||||
onClick={() => this.onRoomClicked(room)}
|
||||
// cancel onMouseDown otherwise shift-clicking highlights text
|
||||
onMouseDown={(ev) => {ev.preventDefault();}}
|
||||
>
|
||||
<td className="mx_RoomDirectory_roomAvatar">
|
||||
<BaseAvatar width={32} height={32} resizeMethod='crop'
|
||||
name={ name } idName={ name }
|
||||
url={ avatarUrl } />
|
||||
</td>
|
||||
<td className="mx_RoomDirectory_roomDescription">
|
||||
<div className="mx_RoomDirectory_name">{ name }</div>
|
||||
<div className="mx_RoomDirectory_topic"
|
||||
onClick={ (ev) => { ev.stopPropagation(); } }
|
||||
dangerouslySetInnerHTML={{ __html: topic }} />
|
||||
<div className="mx_RoomDirectory_alias">{ get_display_alias_for_room(room) }</div>
|
||||
</td>
|
||||
<td className="mx_RoomDirectory_roomMemberCount">
|
||||
{ room.num_joined_members }
|
||||
</td>
|
||||
<td className="mx_RoomDirectory_preview">{previewButton}</td>
|
||||
<td className="mx_RoomDirectory_join">{joinOrViewButton}</td>
|
||||
</tr>
|
||||
);
|
||||
},
|
||||
|
||||
collectScrollPanel: function(element) {
|
||||
@@ -531,20 +559,26 @@ module.exports = React.createClass({
|
||||
let content;
|
||||
if (this.state.error) {
|
||||
content = this.state.error;
|
||||
} else if (this.state.protocolsLoading || this.state.loading) {
|
||||
} else if (this.state.protocolsLoading) {
|
||||
content = <Loader />;
|
||||
} else {
|
||||
const rows = this.getRows();
|
||||
const rows = (this.state.publicRooms || []).map(room => this.getRow(room));
|
||||
// we still show the scrollpanel, at least for now, because
|
||||
// otherwise we don't fetch more because we don't get a fill
|
||||
// request from the scrollpanel because there isn't one
|
||||
|
||||
let spinner;
|
||||
if (this.state.loading) {
|
||||
spinner = <Loader />;
|
||||
}
|
||||
|
||||
let scrollpanel_content;
|
||||
if (rows.length == 0) {
|
||||
if (rows.length === 0 && !this.state.loading) {
|
||||
scrollpanel_content = <i>{ _t('No rooms to show') }</i>;
|
||||
} else {
|
||||
scrollpanel_content = <table ref="directory_table" className="mx_RoomDirectory_table">
|
||||
<tbody>
|
||||
{ this.getRows() }
|
||||
{ rows }
|
||||
</tbody>
|
||||
</table>;
|
||||
}
|
||||
@@ -556,6 +590,7 @@ module.exports = React.createClass({
|
||||
startAtBottom={false}
|
||||
>
|
||||
{ scrollpanel_content }
|
||||
{ spinner }
|
||||
</ScrollPanel>;
|
||||
}
|
||||
|
||||
@@ -577,10 +612,9 @@ module.exports = React.createClass({
|
||||
instance_expected_field_type = this.protocols[protocolName].field_types[last_field];
|
||||
}
|
||||
|
||||
|
||||
let placeholder = _t('Search for a room');
|
||||
let placeholder = _t('Find a room…');
|
||||
if (!this.state.instanceId) {
|
||||
placeholder = _t('Search for a room like #example') + ':' + this.state.roomServer;
|
||||
placeholder = _t("Find a room… (e.g. %(exampleRoom)s)", {exampleRoom: "#example:" + this.state.roomServer});
|
||||
} else if (instance_expected_field_type) {
|
||||
placeholder = instance_expected_field_type.placeholder;
|
||||
}
|
||||
@@ -596,27 +630,31 @@ module.exports = React.createClass({
|
||||
listHeader = <div className="mx_RoomDirectory_listheader">
|
||||
<DirectorySearchBox
|
||||
className="mx_RoomDirectory_searchbox"
|
||||
onChange={this.onFilterChange} onClear={this.onFilterClear} onJoinClick={this.onJoinClick}
|
||||
onChange={this.onFilterChange} onClear={this.onFilterClear} onJoinClick={this.onJoinFromSearchClick}
|
||||
placeholder={placeholder} showJoinButton={showJoinButton}
|
||||
/>
|
||||
<NetworkDropdown config={this.props.config} protocols={this.protocols} onOptionChange={this.onOptionChange} />
|
||||
</div>;
|
||||
}
|
||||
|
||||
const createRoomButton = (<AccessibleButton
|
||||
onClick={this.onCreateRoomClicked}
|
||||
className="mx_RoomDirectory_createRoom"
|
||||
>{_t("Create new room")}</AccessibleButton>);
|
||||
const explanation =
|
||||
_t("If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.", null,
|
||||
{a: sub => {
|
||||
return (<AccessibleButton
|
||||
kind="secondary"
|
||||
onClick={this.onCreateRoomClick}
|
||||
>{sub}</AccessibleButton>);
|
||||
}},
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
className={'mx_RoomDirectory_dialog'}
|
||||
hasCancel={true}
|
||||
onFinished={this.props.onFinished}
|
||||
headerButton={createRoomButton}
|
||||
title={_t("Room directory")}
|
||||
title={_t("Explore rooms")}
|
||||
>
|
||||
<div className="mx_RoomDirectory">
|
||||
<p>{explanation}</p>
|
||||
<div className="mx_RoomDirectory_list">
|
||||
{listHeader}
|
||||
{content}
|
||||
|
||||
@@ -16,13 +16,12 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import Matrix from 'matrix-js-sdk';
|
||||
import { _t, _td } from '../../languageHandler';
|
||||
import sdk from '../../index';
|
||||
import WhoIsTyping from '../../WhoIsTyping';
|
||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
||||
import MemberAvatar from '../views/avatars/MemberAvatar';
|
||||
import Resend from '../../Resend';
|
||||
import * as cryptodevices from '../../cryptodevices';
|
||||
import dis from '../../dispatcher';
|
||||
@@ -39,7 +38,7 @@ function getUnsentMessages(room) {
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'RoomStatusBar',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import classNames from 'classnames';
|
||||
import sdk from '../../index';
|
||||
import dis from '../../dispatcher';
|
||||
@@ -34,7 +35,7 @@ import {_t} from "../../languageHandler";
|
||||
// turn this on for drop & drag console debugging galore
|
||||
const debug = false;
|
||||
|
||||
const RoomSubList = React.createClass({
|
||||
const RoomSubList = createReactClass({
|
||||
displayName: 'RoomSubList',
|
||||
|
||||
debug: debug,
|
||||
|
||||
@@ -24,6 +24,7 @@ limitations under the License.
|
||||
import shouldHideEvent from '../../shouldHideEvent';
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import Promise from 'bluebird';
|
||||
@@ -70,7 +71,7 @@ const RoomContext = PropTypes.shape({
|
||||
room: PropTypes.instanceOf(Room),
|
||||
});
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'RoomView',
|
||||
propTypes: {
|
||||
ConferenceHandler: PropTypes.any,
|
||||
@@ -582,7 +583,7 @@ module.exports = React.createClass({
|
||||
payload.data.description || payload.data.name);
|
||||
break;
|
||||
case 'picture_snapshot':
|
||||
return ContentMessages.sharedInstance().sendContentListToRoom(
|
||||
ContentMessages.sharedInstance().sendContentListToRoom(
|
||||
[payload.file], this.state.room.roomId, MatrixClientPeg.get(),
|
||||
);
|
||||
break;
|
||||
@@ -623,6 +624,11 @@ module.exports = React.createClass({
|
||||
showApps: payload.show,
|
||||
});
|
||||
break;
|
||||
case 'reply_to_event':
|
||||
if (this.state.searchResults && payload.event.getRoomId() === this.state.roomId && !this.unmounted) {
|
||||
this.onCancelSearchClick();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1550,7 +1556,6 @@ module.exports = React.createClass({
|
||||
|
||||
render: function() {
|
||||
const RoomHeader = sdk.getComponent('rooms.RoomHeader');
|
||||
const MessageComposer = sdk.getComponent('rooms.MessageComposer');
|
||||
const ForwardMessage = sdk.getComponent("rooms.ForwardMessage");
|
||||
const AuxPanel = sdk.getComponent("rooms.AuxPanel");
|
||||
const SearchBar = sdk.getComponent("rooms.SearchBar");
|
||||
@@ -1778,15 +1783,29 @@ module.exports = React.createClass({
|
||||
myMembership === 'join' && !this.state.searchResults
|
||||
);
|
||||
if (canSpeak) {
|
||||
messageComposer =
|
||||
<MessageComposer
|
||||
room={this.state.room}
|
||||
callState={this.state.callState}
|
||||
disabled={this.props.disabled}
|
||||
showApps={this.state.showApps}
|
||||
e2eStatus={this.state.e2eStatus}
|
||||
permalinkCreator={this._getPermalinkCreatorForRoom(this.state.room)}
|
||||
/>;
|
||||
if (SettingsStore.isFeatureEnabled("feature_cider_composer")) {
|
||||
const MessageComposer = sdk.getComponent('rooms.MessageComposer');
|
||||
messageComposer =
|
||||
<MessageComposer
|
||||
room={this.state.room}
|
||||
callState={this.state.callState}
|
||||
disabled={this.props.disabled}
|
||||
showApps={this.state.showApps}
|
||||
e2eStatus={this.state.e2eStatus}
|
||||
permalinkCreator={this._getPermalinkCreatorForRoom(this.state.room)}
|
||||
/>;
|
||||
} else {
|
||||
const SlateMessageComposer = sdk.getComponent('rooms.SlateMessageComposer');
|
||||
messageComposer =
|
||||
<SlateMessageComposer
|
||||
room={this.state.room}
|
||||
callState={this.state.callState}
|
||||
disabled={this.props.disabled}
|
||||
showApps={this.state.showApps}
|
||||
e2eStatus={this.state.e2eStatus}
|
||||
permalinkCreator={this._getPermalinkCreatorForRoom(this.state.room)}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Why aren't we storing the term/scope/count in this format
|
||||
|
||||
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
const React = require("react");
|
||||
import React from "react";
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import Promise from 'bluebird';
|
||||
import { KeyCode } from '../../Keyboard';
|
||||
@@ -84,7 +85,7 @@ if (DEBUG_SCROLL) {
|
||||
* offset as normal.
|
||||
*/
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'ScrollPanel',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -16,13 +16,15 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { KeyCode } from '../../Keyboard';
|
||||
import dis from '../../dispatcher';
|
||||
import { throttle } from 'lodash';
|
||||
import AccessibleButton from '../../components/views/elements/AccessibleButton';
|
||||
import classNames from 'classnames';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'SearchBox',
|
||||
|
||||
propTypes: {
|
||||
@@ -46,6 +48,7 @@ module.exports = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
searchTerm: "",
|
||||
blurred: true,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -93,7 +96,18 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
_onFocus: function(ev) {
|
||||
this.setState({blurred: false});
|
||||
ev.target.select();
|
||||
if (this.props.onFocus) {
|
||||
this.props.onFocus(ev);
|
||||
}
|
||||
},
|
||||
|
||||
_onBlur: function(ev) {
|
||||
this.setState({blurred: true});
|
||||
if (this.props.onBlur) {
|
||||
this.props.onBlur(ev);
|
||||
}
|
||||
},
|
||||
|
||||
_clearSearch: function(source) {
|
||||
@@ -112,15 +126,21 @@ module.exports = React.createClass({
|
||||
if (this.props.collapsed) {
|
||||
return null;
|
||||
}
|
||||
const clearButton = this.state.searchTerm.length > 0 ?
|
||||
const clearButton = !this.state.blurred ?
|
||||
(<AccessibleButton key="button"
|
||||
className="mx_SearchBox_closeButton"
|
||||
onClick={ () => {this._clearSearch("button"); } }>
|
||||
</AccessibleButton>) : undefined;
|
||||
|
||||
// show a shorter placeholder when blurred, if requested
|
||||
// this is used for the room filter field that has
|
||||
// the explore button next to it when blurred
|
||||
const placeholder = this.state.blurred ?
|
||||
(this.props.blurredPlaceholder || this.props.placeholder) :
|
||||
this.props.placeholder;
|
||||
const className = this.props.className || "";
|
||||
return (
|
||||
<div className="mx_SearchBox mx_textinput">
|
||||
<div className={classNames("mx_SearchBox", "mx_textinput", {"mx_SearchBox_blurred": this.state.blurred})}>
|
||||
<input
|
||||
key="searchfield"
|
||||
type="text"
|
||||
@@ -130,7 +150,8 @@ module.exports = React.createClass({
|
||||
onFocus={ this._onFocus }
|
||||
onChange={ this.onChange }
|
||||
onKeyDown={ this._onKeyDown }
|
||||
placeholder={ this.props.placeholder }
|
||||
onBlur={this._onBlur}
|
||||
placeholder={ placeholder }
|
||||
/>
|
||||
{ clearButton }
|
||||
</div>
|
||||
|
||||
@@ -15,6 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import TagOrderStore from '../../stores/TagOrderStore';
|
||||
@@ -28,7 +29,7 @@ import { _t } from '../../languageHandler';
|
||||
import { Droppable } from 'react-beautiful-dnd';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const TagPanel = React.createClass({
|
||||
const TagPanel = createReactClass({
|
||||
displayName: 'TagPanel',
|
||||
|
||||
contextTypes: {
|
||||
|
||||
@@ -15,12 +15,13 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../index';
|
||||
import dis from '../../dispatcher';
|
||||
import Modal from '../../Modal';
|
||||
import { _t } from '../../languageHandler';
|
||||
|
||||
const TagPanelButtons = React.createClass({
|
||||
const TagPanelButtons = createReactClass({
|
||||
displayName: 'TagPanelButtons',
|
||||
|
||||
|
||||
|
||||
@@ -19,8 +19,9 @@ limitations under the License.
|
||||
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
|
||||
const React = require('react');
|
||||
const ReactDOM = require("react-dom");
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import ReactDOM from "react-dom";
|
||||
import PropTypes from 'prop-types';
|
||||
import Promise from 'bluebird';
|
||||
|
||||
@@ -58,7 +59,7 @@ if (DEBUG) {
|
||||
*
|
||||
* Also responsible for handling and sending read receipts.
|
||||
*/
|
||||
const TimelinePanel = React.createClass({
|
||||
const TimelinePanel = createReactClass({
|
||||
displayName: 'TimelinePanel',
|
||||
|
||||
propTypes: {
|
||||
@@ -684,20 +685,26 @@ const TimelinePanel = React.createClass({
|
||||
}
|
||||
this.lastRMSentEventId = this.state.readMarkerEventId;
|
||||
|
||||
const roomId = this.props.timelineSet.room.roomId;
|
||||
const hiddenRR = !SettingsStore.getValue("sendReadReceipts", roomId);
|
||||
|
||||
debuglog('TimelinePanel: Sending Read Markers for ',
|
||||
this.props.timelineSet.room.roomId,
|
||||
'rm', this.state.readMarkerEventId,
|
||||
lastReadEvent ? 'rr ' + lastReadEvent.getId() : '',
|
||||
' hidden:' + hiddenRR,
|
||||
);
|
||||
MatrixClientPeg.get().setRoomReadMarkers(
|
||||
this.props.timelineSet.room.roomId,
|
||||
this.state.readMarkerEventId,
|
||||
lastReadEvent, // Could be null, in which case no RR is sent
|
||||
{hidden: hiddenRR},
|
||||
).catch((e) => {
|
||||
// /read_markers API is not implemented on this HS, fallback to just RR
|
||||
if (e.errcode === 'M_UNRECOGNIZED' && lastReadEvent) {
|
||||
return MatrixClientPeg.get().sendReadReceipt(
|
||||
lastReadEvent,
|
||||
{hidden: hiddenRR},
|
||||
).catch((e) => {
|
||||
console.error(e);
|
||||
this.lastRRSentEventId = undefined;
|
||||
|
||||
@@ -14,14 +14,16 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import ContentMessages from '../../ContentMessages';
|
||||
const dis = require('../../dispatcher');
|
||||
const filesize = require('filesize');
|
||||
import { _t } from '../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({displayName: 'UploadBar',
|
||||
module.exports = createReactClass({
|
||||
displayName: 'UploadBar',
|
||||
propTypes: {
|
||||
room: PropTypes.object,
|
||||
},
|
||||
|
||||
@@ -16,13 +16,14 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import SyntaxHighlight from '../views/elements/SyntaxHighlight';
|
||||
import {_t} from "../../languageHandler";
|
||||
import sdk from "../../index";
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'ViewSource',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017, 2018, 2019 New Vector Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +17,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import sdk from '../../../index';
|
||||
@@ -37,7 +39,7 @@ const PHASE_EMAIL_SENT = 3;
|
||||
// User has clicked the link in email and completed reset
|
||||
const PHASE_DONE = 4;
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'ForgotPassword',
|
||||
|
||||
propTypes: {
|
||||
@@ -62,10 +64,12 @@ module.exports = React.createClass({
|
||||
serverIsAlive: true,
|
||||
serverErrorIsFatal: false,
|
||||
serverDeadError: "",
|
||||
serverRequiresIdServer: null,
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.reset = null;
|
||||
this._checkServerLiveliness(this.props.serverConfig);
|
||||
},
|
||||
|
||||
@@ -83,7 +87,14 @@ module.exports = React.createClass({
|
||||
serverConfig.hsUrl,
|
||||
serverConfig.isUrl,
|
||||
);
|
||||
this.setState({serverIsAlive: true});
|
||||
|
||||
const pwReset = new PasswordReset(serverConfig.hsUrl, serverConfig.isUrl);
|
||||
const serverRequiresIdServer = await pwReset.doesServerRequireIdServerParam();
|
||||
|
||||
this.setState({
|
||||
serverIsAlive: true,
|
||||
serverRequiresIdServer,
|
||||
});
|
||||
} catch (e) {
|
||||
this.setState(AutoDiscoveryUtils.authComponentStateForError(e, "forgot_password"));
|
||||
}
|
||||
@@ -199,6 +210,7 @@ module.exports = React.createClass({
|
||||
serverConfig={this.props.serverConfig}
|
||||
onServerConfigChange={this.props.onServerConfigChange}
|
||||
delayTimeMs={0}
|
||||
showIdentityServerIfRequiredByHomeserver={true}
|
||||
onAfterSubmit={this.onServerDetailsNextPhaseClick}
|
||||
submitText={_t("Next")}
|
||||
submitClass="mx_Login_submit"
|
||||
@@ -256,7 +268,7 @@ module.exports = React.createClass({
|
||||
</a>;
|
||||
}
|
||||
|
||||
if (!this.props.serverConfig.isUrl) {
|
||||
if (!this.props.serverConfig.isUrl && this.state.serverRequiresIdServer) {
|
||||
return <div>
|
||||
<h3>
|
||||
{yourMatrixAccountText}
|
||||
|
||||
@@ -16,9 +16,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import {_t, _td} from '../../../languageHandler';
|
||||
import sdk from '../../../index';
|
||||
@@ -54,7 +53,7 @@ _td("General failure");
|
||||
/**
|
||||
* A wire component which glues together login UI components and Login logic
|
||||
*/
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'Login',
|
||||
|
||||
propTypes: {
|
||||
@@ -94,7 +93,7 @@ module.exports = React.createClass({
|
||||
// Phase of the overall login dialog.
|
||||
phase: PHASE_LOGIN,
|
||||
// The current login flow, such as password, SSO, etc.
|
||||
currentFlow: "m.login.password",
|
||||
currentFlow: null, // we need to load the flows from the server
|
||||
|
||||
// We perform liveliness checks later, but for now suppress the errors.
|
||||
// We also track the server dead errors independently of the regular errors so
|
||||
@@ -373,6 +372,7 @@ module.exports = React.createClass({
|
||||
|
||||
this.setState({
|
||||
busy: true,
|
||||
currentFlow: null, // reset flow
|
||||
loginIncorrect: false,
|
||||
});
|
||||
|
||||
@@ -566,6 +566,13 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
_renderSsoStep: function(url) {
|
||||
const SignInToText = sdk.getComponent('views.auth.SignInToText');
|
||||
|
||||
let onEditServerDetailsClick = null;
|
||||
// If custom URLs are allowed, wire up the server details edit link.
|
||||
if (PHASES_ENABLED && !SdkConfig.get()['disable_custom_urls']) {
|
||||
onEditServerDetailsClick = this.onEditServerDetailsClick;
|
||||
}
|
||||
// XXX: This link does *not* have a target="_blank" because single sign-on relies on
|
||||
// redirecting the user back to a URI once they're logged in. On the web, this means
|
||||
// we use the same window and redirect back to riot. On electron, this actually
|
||||
@@ -575,7 +582,12 @@ module.exports = React.createClass({
|
||||
// user's browser, let them log into their SSO provider, then redirect their browser
|
||||
// to vector://vector which, of course, will not work.
|
||||
return (
|
||||
<a href={url} className="mx_Login_sso_link mx_Login_submit">{ _t('Sign in with single sign-on') }</a>
|
||||
<div>
|
||||
<SignInToText serverConfig={this.props.serverConfig}
|
||||
onEditServerDetailsClick={onEditServerDetailsClick} />
|
||||
|
||||
<a href={url} className="mx_Login_sso_link mx_Login_submit">{ _t('Sign in with single sign-on') }</a>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
@@ -14,15 +14,14 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'PostRegistration',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -20,6 +20,7 @@ limitations under the License.
|
||||
import Matrix from 'matrix-js-sdk';
|
||||
import Promise from 'bluebird';
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
@@ -40,7 +41,7 @@ const PHASE_REGISTRATION = 1;
|
||||
// Enable phases for registration
|
||||
const PHASES_ENABLED = true;
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'Registration',
|
||||
|
||||
propTypes: {
|
||||
@@ -98,6 +99,9 @@ module.exports = React.createClass({
|
||||
// component without it.
|
||||
matrixClient: null,
|
||||
|
||||
// whether the HS requires an ID server to register with a threepid
|
||||
serverRequiresIdServer: null,
|
||||
|
||||
// The user ID we've just registered
|
||||
registeredUsername: null,
|
||||
|
||||
@@ -204,13 +208,23 @@ module.exports = React.createClass({
|
||||
}
|
||||
|
||||
const {hsUrl, isUrl} = serverConfig;
|
||||
this.setState({
|
||||
matrixClient: Matrix.createClient({
|
||||
baseUrl: hsUrl,
|
||||
idBaseUrl: isUrl,
|
||||
}),
|
||||
const cli = Matrix.createClient({
|
||||
baseUrl: hsUrl,
|
||||
idBaseUrl: isUrl,
|
||||
});
|
||||
|
||||
let serverRequiresIdServer = true;
|
||||
try {
|
||||
serverRequiresIdServer = await cli.doesServerRequireIdServerParam();
|
||||
} catch (e) {
|
||||
console.log("Unable to determine is server needs id_server param", e);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
matrixClient: cli,
|
||||
serverRequiresIdServer,
|
||||
busy: false,
|
||||
});
|
||||
this.setState({busy: false});
|
||||
try {
|
||||
await this._makeRegisterRequest({});
|
||||
// This should never succeed since we specified an empty
|
||||
@@ -403,14 +417,9 @@ module.exports = React.createClass({
|
||||
// clicking the email link.
|
||||
let inhibitLogin = Boolean(this.state.formVals.email);
|
||||
|
||||
// Only send the bind params if we're sending username / pw params
|
||||
// Only send inhibitLogin if we're sending username / pw params
|
||||
// (Since we need to send no params at all to use the ones saved in the
|
||||
// session).
|
||||
const bindThreepids = this.state.formVals.password ? {
|
||||
email: true,
|
||||
msisdn: true,
|
||||
} : {};
|
||||
// Likewise inhibitLogin
|
||||
if (!this.state.formVals.password) inhibitLogin = null;
|
||||
|
||||
return this.state.matrixClient.register(
|
||||
@@ -418,7 +427,7 @@ module.exports = React.createClass({
|
||||
this.state.formVals.password,
|
||||
undefined, // session id: included in the auth dict already
|
||||
auth,
|
||||
bindThreepids,
|
||||
null,
|
||||
null,
|
||||
inhibitLogin,
|
||||
);
|
||||
@@ -491,6 +500,7 @@ module.exports = React.createClass({
|
||||
serverConfig={this.props.serverConfig}
|
||||
onServerConfigChange={this.props.onServerConfigChange}
|
||||
delayTimeMs={250}
|
||||
showIdentityServerIfRequiredByHomeserver={true}
|
||||
{...serverDetailsProps}
|
||||
/>;
|
||||
break;
|
||||
@@ -555,6 +565,7 @@ module.exports = React.createClass({
|
||||
flows={this.state.flows}
|
||||
serverConfig={this.props.serverConfig}
|
||||
canSubmit={!this.state.serverErrorIsFatal}
|
||||
serverRequiresIdServer={this.state.serverRequiresIdServer}
|
||||
/>;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -15,12 +15,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { _t } from '../../../languageHandler';
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'AuthFooter',
|
||||
|
||||
render: function() {
|
||||
|
||||
@@ -15,12 +15,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../index';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'AuthHeader',
|
||||
|
||||
render: function() {
|
||||
|
||||
@@ -15,12 +15,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../index';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'AuthPage',
|
||||
|
||||
render: function() {
|
||||
|
||||
@@ -14,9 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
@@ -25,7 +24,7 @@ const DIV_ID = 'mx_recaptcha';
|
||||
/**
|
||||
* A pure UI component which displays a captcha form.
|
||||
*/
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'CaptchaForm',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -15,9 +15,10 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'CustomServerDialog',
|
||||
|
||||
render: function() {
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import url from 'url';
|
||||
import classnames from 'classnames';
|
||||
@@ -63,7 +64,7 @@ import SettingsStore from "../../../settings/SettingsStore";
|
||||
* focus: set the input focus appropriately in the form.
|
||||
*/
|
||||
|
||||
export const PasswordAuthEntry = React.createClass({
|
||||
export const PasswordAuthEntry = createReactClass({
|
||||
displayName: 'PasswordAuthEntry',
|
||||
|
||||
statics: {
|
||||
@@ -162,7 +163,7 @@ export const PasswordAuthEntry = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
export const RecaptchaAuthEntry = React.createClass({
|
||||
export const RecaptchaAuthEntry = createReactClass({
|
||||
displayName: 'RecaptchaAuthEntry',
|
||||
|
||||
statics: {
|
||||
@@ -212,7 +213,7 @@ export const RecaptchaAuthEntry = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
export const TermsAuthEntry = React.createClass({
|
||||
export const TermsAuthEntry = createReactClass({
|
||||
displayName: 'TermsAuthEntry',
|
||||
|
||||
statics: {
|
||||
@@ -351,7 +352,7 @@ export const TermsAuthEntry = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
export const EmailIdentityAuthEntry = React.createClass({
|
||||
export const EmailIdentityAuthEntry = createReactClass({
|
||||
displayName: 'EmailIdentityAuthEntry',
|
||||
|
||||
statics: {
|
||||
@@ -393,7 +394,7 @@ export const EmailIdentityAuthEntry = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
export const MsisdnAuthEntry = React.createClass({
|
||||
export const MsisdnAuthEntry = createReactClass({
|
||||
displayName: 'MsisdnAuthEntry',
|
||||
|
||||
statics: {
|
||||
@@ -540,7 +541,7 @@ export const MsisdnAuthEntry = React.createClass({
|
||||
},
|
||||
});
|
||||
|
||||
export const FallbackAuthEntry = React.createClass({
|
||||
export const FallbackAuthEntry = createReactClass({
|
||||
displayName: 'FallbackAuthEntry',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -15,13 +15,13 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils";
|
||||
import * as ServerType from '../../views/auth/ServerTypeSelector';
|
||||
import ServerConfig from "./ServerConfig";
|
||||
|
||||
const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_campaign=riot-web-authentication';
|
||||
|
||||
@@ -33,49 +33,8 @@ const MODULAR_URL = 'https://modular.im/?utm_source=riot-web&utm_medium=web&utm_
|
||||
* This is a variant of ServerConfig with only the HS field and different body
|
||||
* text that is specific to the Modular case.
|
||||
*/
|
||||
export default class ModularServerConfig extends React.PureComponent {
|
||||
static propTypes = {
|
||||
onServerConfigChange: PropTypes.func,
|
||||
|
||||
// The current configuration that the user is expecting to change.
|
||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||
|
||||
delayTimeMs: PropTypes.number, // time to wait before invoking onChanged
|
||||
|
||||
// Called after the component calls onServerConfigChange
|
||||
onAfterSubmit: PropTypes.func,
|
||||
|
||||
// Optional text for the submit button. If falsey, no button will be shown.
|
||||
submitText: PropTypes.string,
|
||||
|
||||
// Optional class for the submit button. Only applies if the submit button
|
||||
// is to be rendered.
|
||||
submitClass: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
onServerConfigChange: function() {},
|
||||
customHsUrl: "",
|
||||
delayTimeMs: 0,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
busy: false,
|
||||
errorText: "",
|
||||
hsUrl: props.serverConfig.hsUrl,
|
||||
isUrl: props.serverConfig.isUrl,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
if (newProps.serverConfig.hsUrl === this.state.hsUrl &&
|
||||
newProps.serverConfig.isUrl === this.state.isUrl) return;
|
||||
|
||||
this.validateAndApplyServer(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl);
|
||||
}
|
||||
export default class ModularServerConfig extends ServerConfig {
|
||||
static propTypes = ServerConfig.propTypes;
|
||||
|
||||
async validateAndApplyServer(hsUrl, isUrl) {
|
||||
// Always try and use the defaults first
|
||||
@@ -120,35 +79,6 @@ export default class ModularServerConfig extends React.PureComponent {
|
||||
return this.validateAndApplyServer(this.state.hsUrl, ServerType.TYPES.PREMIUM.identityServerUrl);
|
||||
}
|
||||
|
||||
onHomeserverBlur = (ev) => {
|
||||
this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => {
|
||||
this.validateServer();
|
||||
});
|
||||
};
|
||||
|
||||
onHomeserverChange = (ev) => {
|
||||
const hsUrl = ev.target.value;
|
||||
this.setState({ hsUrl });
|
||||
};
|
||||
|
||||
onSubmit = async (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const result = await this.validateServer();
|
||||
if (!result) return; // Do not continue.
|
||||
|
||||
if (this.props.onAfterSubmit) {
|
||||
this.props.onAfterSubmit();
|
||||
}
|
||||
};
|
||||
|
||||
_waitThenInvoke(existingTimeoutId, fn) {
|
||||
if (existingTimeoutId) {
|
||||
clearTimeout(existingTimeoutId);
|
||||
}
|
||||
return setTimeout(fn.bind(this), this.props.delayTimeMs);
|
||||
}
|
||||
|
||||
render() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
||||
@@ -31,6 +31,7 @@ export default class PasswordLogin extends React.Component {
|
||||
static propTypes = {
|
||||
onSubmit: PropTypes.func.isRequired, // fn(username, password)
|
||||
onError: PropTypes.func,
|
||||
onEditServerDetailsClick: PropTypes.func,
|
||||
onForgotPasswordClick: PropTypes.func, // fn()
|
||||
initialUsername: PropTypes.string,
|
||||
initialPhoneCountry: PropTypes.string,
|
||||
@@ -257,6 +258,7 @@ export default class PasswordLogin extends React.Component {
|
||||
|
||||
render() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
const SignInToText = sdk.getComponent('views.auth.SignInToText');
|
||||
|
||||
let forgotPasswordJsx;
|
||||
|
||||
@@ -273,33 +275,6 @@ export default class PasswordLogin extends React.Component {
|
||||
</span>;
|
||||
}
|
||||
|
||||
let signInToText = _t('Sign in to your Matrix account on %(serverName)s', {
|
||||
serverName: this.props.serverConfig.hsName,
|
||||
});
|
||||
if (this.props.serverConfig.hsNameIsDifferent) {
|
||||
const TextWithTooltip = sdk.getComponent("elements.TextWithTooltip");
|
||||
|
||||
signInToText = _t('Sign in to your Matrix account on <underlinedServerName />', {}, {
|
||||
'underlinedServerName': () => {
|
||||
return <TextWithTooltip
|
||||
class="mx_Login_underlinedServerName"
|
||||
tooltip={this.props.serverConfig.hsUrl}
|
||||
>
|
||||
{this.props.serverConfig.hsName}
|
||||
</TextWithTooltip>;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let editLink = null;
|
||||
if (this.props.onEditServerDetailsClick) {
|
||||
editLink = <a className="mx_AuthBody_editServerDetails"
|
||||
href="#" onClick={this.props.onEditServerDetailsClick}
|
||||
>
|
||||
{_t('Change')}
|
||||
</a>;
|
||||
}
|
||||
|
||||
const pwFieldClass = classNames({
|
||||
error: this.props.loginIncorrect && !this.isLoginEmpty(), // only error password if error isn't top field
|
||||
});
|
||||
@@ -342,10 +317,8 @@ export default class PasswordLogin extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>
|
||||
{signInToText}
|
||||
{editLink}
|
||||
</h3>
|
||||
<SignInToText serverConfig={this.props.serverConfig}
|
||||
onEditServerDetailsClick={this.props.onEditServerDetailsClick} />
|
||||
<form onSubmit={this.onSubmitForm}>
|
||||
{loginType}
|
||||
{loginField}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018, 2019 New Vector Ltd
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,6 +18,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import Email from '../../../email';
|
||||
@@ -39,7 +41,7 @@ const PASSWORD_MIN_SCORE = 3; // safely unguessable: moderate protection from of
|
||||
/**
|
||||
* A pure UI component which displays a registration form.
|
||||
*/
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'RegistrationForm',
|
||||
|
||||
propTypes: {
|
||||
@@ -54,6 +56,7 @@ module.exports = React.createClass({
|
||||
flows: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||
canSubmit: PropTypes.bool,
|
||||
serverRequiresIdServer: PropTypes.bool,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
@@ -69,10 +72,10 @@ module.exports = React.createClass({
|
||||
fieldValid: {},
|
||||
// The ISO2 country code selected in the phone number entry
|
||||
phoneCountry: this.props.defaultPhoneCountry,
|
||||
username: "",
|
||||
email: "",
|
||||
phoneNumber: "",
|
||||
password: "",
|
||||
username: this.props.defaultUsername || "",
|
||||
email: this.props.defaultEmail || "",
|
||||
phoneNumber: this.props.defaultPhoneNumber || "",
|
||||
password: this.props.defaultPassword || "",
|
||||
passwordConfirm: "",
|
||||
passwordComplexity: null,
|
||||
passwordSafe: false,
|
||||
@@ -90,7 +93,7 @@ module.exports = React.createClass({
|
||||
}
|
||||
|
||||
const self = this;
|
||||
if (this.state.email == '') {
|
||||
if (this.state.email === '') {
|
||||
const haveIs = Boolean(this.props.serverConfig.isUrl);
|
||||
|
||||
let desc;
|
||||
@@ -436,7 +439,17 @@ module.exports = React.createClass({
|
||||
|
||||
_showEmail() {
|
||||
const haveIs = Boolean(this.props.serverConfig.isUrl);
|
||||
if (!haveIs || !this._authStepIsUsed('m.login.email.identity')) {
|
||||
if ((this.props.serverRequiresIdServer && !haveIs) || !this._authStepIsUsed('m.login.email.identity')) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
_showPhoneNumber() {
|
||||
const threePidLogin = !SdkConfig.get().disable_3pid_login;
|
||||
const haveIs = Boolean(this.props.serverConfig.isUrl);
|
||||
const haveRequiredIs = this.props.serverRequiresIdServer && !haveIs;
|
||||
if (!threePidLogin || haveRequiredIs || !this._authStepIsUsed('m.login.msisdn')) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -455,7 +468,6 @@ module.exports = React.createClass({
|
||||
ref={field => this[FIELD_EMAIL] = field}
|
||||
type="text"
|
||||
label={emailPlaceholder}
|
||||
defaultValue={this.props.defaultEmail}
|
||||
value={this.state.email}
|
||||
onChange={this.onEmailChange}
|
||||
onValidate={this.onEmailValidate}
|
||||
@@ -469,7 +481,6 @@ module.exports = React.createClass({
|
||||
ref={field => this[FIELD_PASSWORD] = field}
|
||||
type="password"
|
||||
label={_t("Password")}
|
||||
defaultValue={this.props.defaultPassword}
|
||||
value={this.state.password}
|
||||
onChange={this.onPasswordChange}
|
||||
onValidate={this.onPasswordValidate}
|
||||
@@ -483,7 +494,6 @@ module.exports = React.createClass({
|
||||
ref={field => this[FIELD_PASSWORD_CONFIRM] = field}
|
||||
type="password"
|
||||
label={_t("Confirm")}
|
||||
defaultValue={this.props.defaultPassword}
|
||||
value={this.state.passwordConfirm}
|
||||
onChange={this.onPasswordConfirmChange}
|
||||
onValidate={this.onPasswordConfirmValidate}
|
||||
@@ -491,9 +501,7 @@ module.exports = React.createClass({
|
||||
},
|
||||
|
||||
renderPhoneNumber() {
|
||||
const threePidLogin = !SdkConfig.get().disable_3pid_login;
|
||||
const haveIs = Boolean(this.props.serverConfig.isUrl);
|
||||
if (!threePidLogin || !haveIs || !this._authStepIsUsed('m.login.msisdn')) {
|
||||
if (!this._showPhoneNumber()) {
|
||||
return null;
|
||||
}
|
||||
const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown');
|
||||
@@ -512,7 +520,6 @@ module.exports = React.createClass({
|
||||
ref={field => this[FIELD_PHONE_NUMBER] = field}
|
||||
type="text"
|
||||
label={phoneLabel}
|
||||
defaultValue={this.props.defaultPhoneNumber}
|
||||
value={this.state.phoneNumber}
|
||||
prefix={phoneCountry}
|
||||
onChange={this.onPhoneNumberChange}
|
||||
@@ -528,7 +535,6 @@ module.exports = React.createClass({
|
||||
type="text"
|
||||
autoFocus={true}
|
||||
label={_t("Username")}
|
||||
defaultValue={this.props.defaultUsername}
|
||||
value={this.state.username}
|
||||
onChange={this.onUsernameChange}
|
||||
onValidate={this.onUsernameValidate}
|
||||
@@ -567,11 +573,24 @@ module.exports = React.createClass({
|
||||
<input className="mx_Login_submit" type="submit" value={_t("Register")} disabled={!this.props.canSubmit} />
|
||||
);
|
||||
|
||||
const emailHelperText = this._showEmail() ? <div>
|
||||
{_t("Use an email address to recover your account.") + " "}
|
||||
{_t("Other users can invite you to rooms using your contact details.")}
|
||||
</div> : null;
|
||||
|
||||
let emailHelperText = null;
|
||||
if (this._showEmail()) {
|
||||
if (this._showPhoneNumber()) {
|
||||
emailHelperText = <div>
|
||||
{_t(
|
||||
"Set an email for account recovery. " +
|
||||
"Use email or phone to optionally be discoverable by existing contacts.",
|
||||
)}
|
||||
</div>;
|
||||
} else {
|
||||
emailHelperText = <div>
|
||||
{_t(
|
||||
"Set an email for account recovery. " +
|
||||
"Use email to optionally be discoverable by existing contacts.",
|
||||
)}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
const haveIs = Boolean(this.props.serverConfig.isUrl);
|
||||
const noIsText = haveIs ? null : <div>
|
||||
{_t(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2019 New Vector Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +24,8 @@ import { _t } from '../../../languageHandler';
|
||||
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
||||
import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import { createClient } from 'matrix-js-sdk/lib/matrix';
|
||||
import classNames from 'classnames';
|
||||
|
||||
/*
|
||||
* A pure UI component which displays the HS and IS to use.
|
||||
@@ -46,6 +49,10 @@ export default class ServerConfig extends React.PureComponent {
|
||||
// Optional class for the submit button. Only applies if the submit button
|
||||
// is to be rendered.
|
||||
submitClass: PropTypes.string,
|
||||
|
||||
// Whether the flow this component is embedded in requires an identity
|
||||
// server when the homeserver says it will need one. Default false.
|
||||
showIdentityServerIfRequiredByHomeserver: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
@@ -61,6 +68,7 @@ export default class ServerConfig extends React.PureComponent {
|
||||
errorText: "",
|
||||
hsUrl: props.serverConfig.hsUrl,
|
||||
isUrl: props.serverConfig.isUrl,
|
||||
showIdentityServer: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -75,14 +83,41 @@ export default class ServerConfig extends React.PureComponent {
|
||||
// TODO: Do we want to support .well-known lookups here?
|
||||
// If for some reason someone enters "matrix.org" for a URL, we could do a lookup to
|
||||
// find their homeserver without demanding they use "https://matrix.org"
|
||||
return this.validateAndApplyServer(this.state.hsUrl, this.state.isUrl);
|
||||
const result = this.validateAndApplyServer(this.state.hsUrl, this.state.isUrl);
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// If the UI flow this component is embedded in requires an identity
|
||||
// server when the homeserver says it will need one, check first and
|
||||
// reveal this field if not already shown.
|
||||
// XXX: This a backward compatibility path for homeservers that require
|
||||
// an identity server to be passed during certain flows.
|
||||
// See also https://github.com/matrix-org/synapse/pull/5868.
|
||||
if (
|
||||
this.props.showIdentityServerIfRequiredByHomeserver &&
|
||||
!this.state.showIdentityServer &&
|
||||
await this.isIdentityServerRequiredByHomeserver()
|
||||
) {
|
||||
this.setState({
|
||||
showIdentityServer: true,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async validateAndApplyServer(hsUrl, isUrl) {
|
||||
// Always try and use the defaults first
|
||||
const defaultConfig: ValidatedServerConfig = SdkConfig.get()["validated_server_config"];
|
||||
if (defaultConfig.hsUrl === hsUrl && defaultConfig.isUrl === isUrl) {
|
||||
this.setState({busy: false, errorText: ""});
|
||||
this.setState({
|
||||
hsUrl: defaultConfig.hsUrl,
|
||||
isUrl: defaultConfig.isUrl,
|
||||
busy: false,
|
||||
errorText: "",
|
||||
});
|
||||
this.props.onServerConfigChange(defaultConfig);
|
||||
return defaultConfig;
|
||||
}
|
||||
@@ -126,6 +161,15 @@ export default class ServerConfig extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
async isIdentityServerRequiredByHomeserver() {
|
||||
// XXX: We shouldn't have to create a whole new MatrixClient just to
|
||||
// check if the homeserver requires an identity server... Should it be
|
||||
// extracted to a static utils function...?
|
||||
return createClient({
|
||||
baseUrl: this.state.hsUrl,
|
||||
}).doesServerRequireIdServerParam();
|
||||
}
|
||||
|
||||
onHomeserverBlur = (ev) => {
|
||||
this._hsTimeoutId = this._waitThenInvoke(this._hsTimeoutId, () => {
|
||||
this.validateServer();
|
||||
@@ -171,8 +215,49 @@ export default class ServerConfig extends React.PureComponent {
|
||||
Modal.createTrackedDialog('Custom Server Dialog', '', CustomServerDialog);
|
||||
};
|
||||
|
||||
render() {
|
||||
_renderHomeserverSection() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
return <div>
|
||||
{_t("Enter your custom homeserver URL <a>What does this mean?</a>", {}, {
|
||||
a: sub => <a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>
|
||||
{sub}
|
||||
</a>,
|
||||
})}
|
||||
<Field id="mx_ServerConfig_hsUrl"
|
||||
label={_t("Homeserver URL")}
|
||||
placeholder={this.props.serverConfig.hsUrl}
|
||||
value={this.state.hsUrl}
|
||||
onBlur={this.onHomeserverBlur}
|
||||
onChange={this.onHomeserverChange}
|
||||
disabled={this.state.busy}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
_renderIdentityServerSection() {
|
||||
const Field = sdk.getComponent('elements.Field');
|
||||
const classes = classNames({
|
||||
"mx_ServerConfig_identityServer": true,
|
||||
"mx_ServerConfig_identityServer_shown": this.state.showIdentityServer,
|
||||
});
|
||||
return <div className={classes}>
|
||||
{_t("Enter your custom identity server URL <a>What does this mean?</a>", {}, {
|
||||
a: sub => <a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>
|
||||
{sub}
|
||||
</a>,
|
||||
})}
|
||||
<Field id="mx_ServerConfig_isUrl"
|
||||
label={_t("Identity Server URL")}
|
||||
placeholder={this.props.serverConfig.isUrl}
|
||||
value={this.state.isUrl || ''}
|
||||
onBlur={this.onIdentityServerBlur}
|
||||
onChange={this.onIdentityServerChange}
|
||||
disabled={this.state.busy}
|
||||
/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
render() {
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
|
||||
const errorText = this.state.errorText
|
||||
@@ -191,31 +276,10 @@ export default class ServerConfig extends React.PureComponent {
|
||||
return (
|
||||
<div className="mx_ServerConfig">
|
||||
<h3>{_t("Other servers")}</h3>
|
||||
{_t("Enter custom server URLs <a>What does this mean?</a>", {}, {
|
||||
a: sub => <a className="mx_ServerConfig_help" href="#" onClick={this.showHelpPopup}>
|
||||
{ sub }
|
||||
</a>,
|
||||
})}
|
||||
{errorText}
|
||||
{this._renderHomeserverSection()}
|
||||
{this._renderIdentityServerSection()}
|
||||
<form onSubmit={this.onSubmit} autoComplete={false} action={null}>
|
||||
<div className="mx_ServerConfig_fields">
|
||||
<Field id="mx_ServerConfig_hsUrl"
|
||||
label={_t("Homeserver URL")}
|
||||
placeholder={this.props.serverConfig.hsUrl}
|
||||
value={this.state.hsUrl}
|
||||
onBlur={this.onHomeserverBlur}
|
||||
onChange={this.onHomeserverChange}
|
||||
disabled={this.state.busy}
|
||||
/>
|
||||
<Field id="mx_ServerConfig_isUrl"
|
||||
label={_t("Identity Server URL")}
|
||||
placeholder={this.props.serverConfig.isUrl}
|
||||
value={this.state.isUrl || ''}
|
||||
onBlur={this.onIdentityServerBlur}
|
||||
onChange={this.onIdentityServerChange}
|
||||
disabled={this.state.busy}
|
||||
/>
|
||||
</div>
|
||||
{submitButton}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
62
src/components/views/auth/SignInToText.js
Normal file
62
src/components/views/auth/SignInToText.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 {_t} from "../../../languageHandler";
|
||||
import sdk from "../../../index";
|
||||
import PropTypes from "prop-types";
|
||||
import {ValidatedServerConfig} from "../../../utils/AutoDiscoveryUtils";
|
||||
|
||||
export default class SignInToText extends React.PureComponent {
|
||||
static propTypes = {
|
||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||
onEditServerDetailsClick: PropTypes.func,
|
||||
};
|
||||
|
||||
render() {
|
||||
let signInToText = _t('Sign in to your Matrix account on %(serverName)s', {
|
||||
serverName: this.props.serverConfig.hsName,
|
||||
});
|
||||
if (this.props.serverConfig.hsNameIsDifferent) {
|
||||
const TextWithTooltip = sdk.getComponent("elements.TextWithTooltip");
|
||||
|
||||
signInToText = _t('Sign in to your Matrix account on <underlinedServerName />', {}, {
|
||||
'underlinedServerName': () => {
|
||||
return <TextWithTooltip
|
||||
class="mx_Login_underlinedServerName"
|
||||
tooltip={this.props.serverConfig.hsUrl}
|
||||
>
|
||||
{this.props.serverConfig.hsName}
|
||||
</TextWithTooltip>;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let editLink = null;
|
||||
if (this.props.onEditServerDetailsClick) {
|
||||
editLink = <a className="mx_AuthBody_editServerDetails"
|
||||
href="#" onClick={this.props.onEditServerDetailsClick}
|
||||
>
|
||||
{_t('Change')}
|
||||
</a>;
|
||||
}
|
||||
|
||||
return <h3>
|
||||
{signInToText}
|
||||
{editLink}
|
||||
</h3>;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -17,13 +18,13 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import AvatarLogic from '../../../Avatar';
|
||||
import sdk from '../../../index';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'BaseAvatar',
|
||||
|
||||
propTypes: {
|
||||
@@ -121,6 +122,10 @@ module.exports = React.createClass({
|
||||
);
|
||||
urls.push(defaultImageUrl); // lowest priority
|
||||
}
|
||||
|
||||
// deduplicate URLs
|
||||
urls = Array.from(new Set(urls));
|
||||
|
||||
return {
|
||||
imageUrls: urls,
|
||||
defaultImageUrl: defaultImageUrl,
|
||||
|
||||
@@ -16,10 +16,11 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'GroupAvatar',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -14,15 +14,14 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
const Avatar = require('../../../Avatar');
|
||||
const sdk = require("../../../index");
|
||||
const dispatcher = require("../../../dispatcher");
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'MemberAvatar',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -15,13 +15,14 @@ limitations under the License.
|
||||
*/
|
||||
import React from "react";
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import {ContentRepo} from "matrix-js-sdk";
|
||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||
import Modal from '../../../Modal';
|
||||
import sdk from "../../../index";
|
||||
import Avatar from '../../../Avatar';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'RoomAvatar',
|
||||
|
||||
// Room may be left unset here, but if it is,
|
||||
|
||||
@@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -26,7 +24,7 @@ import PropTypes from 'prop-types';
|
||||
|
||||
|
||||
export default class GenericElementContextMenu extends React.Component {
|
||||
static PropTypes = {
|
||||
static propTypes = {
|
||||
element: PropTypes.element.isRequired,
|
||||
// Function to be called when the parent window is resized
|
||||
// This can be used to reposition or close the menu on resize and
|
||||
|
||||
@@ -14,13 +14,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export default class GenericTextContextMenu extends React.Component {
|
||||
static PropTypes = {
|
||||
static propTypes = {
|
||||
message: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import {EventStatus} from 'matrix-js-sdk';
|
||||
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
@@ -34,7 +35,7 @@ function canCancel(eventStatus) {
|
||||
return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
|
||||
}
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'MessageContextMenu',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -18,8 +18,9 @@ limitations under the License.
|
||||
|
||||
import Promise from 'bluebird';
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import classNames from 'classnames';
|
||||
import sdk from '../../../index';
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
@@ -31,7 +32,7 @@ import Modal from '../../../Modal';
|
||||
import RoomListActions from '../../../actions/RoomListActions';
|
||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'RoomTileContextMenu',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { _t } from '../../../languageHandler';
|
||||
module.exports = React.createClass({
|
||||
|
||||
module.exports = createReactClass({
|
||||
displayName: 'CreateRoomButton',
|
||||
propTypes: {
|
||||
onCreateRoom: PropTypes.func,
|
||||
|
||||
@@ -14,10 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
import React from "react";
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
const Presets = {
|
||||
@@ -26,7 +25,7 @@ const Presets = {
|
||||
Custom: "custom",
|
||||
};
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'CreateRoomPresets',
|
||||
propTypes: {
|
||||
onChange: PropTypes.func,
|
||||
|
||||
@@ -14,11 +14,12 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'RoomAlias',
|
||||
propTypes: {
|
||||
// Specifying a homeserver will make magical things happen when you,
|
||||
|
||||
@@ -19,15 +19,19 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
|
||||
import { _t, _td } from '../../../languageHandler';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import dis from '../../../dispatcher';
|
||||
import Promise from 'bluebird';
|
||||
import { addressTypes, getAddressType } from '../../../UserAddress.js';
|
||||
import GroupStore from '../../../stores/GroupStore';
|
||||
import * as Email from '../../../email';
|
||||
import IdentityAuthClient from '../../../IdentityAuthClient';
|
||||
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from '../../../utils/IdentityServerUtils';
|
||||
import { abbreviateUrl } from '../../../utils/UrlUtils';
|
||||
|
||||
const TRUNCATE_QUERY_LIST = 40;
|
||||
const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200;
|
||||
@@ -39,7 +43,7 @@ const addressTypeName = {
|
||||
};
|
||||
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: "AddressPickerDialog",
|
||||
|
||||
propTypes: {
|
||||
@@ -48,7 +52,7 @@ module.exports = React.createClass({
|
||||
// Extra node inserted after picker input, dropdown and errors
|
||||
extraNode: PropTypes.node,
|
||||
value: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
placeholder: PropTypes.oneOfType(PropTypes.string, PropTypes.func),
|
||||
roomId: PropTypes.string,
|
||||
button: PropTypes.string,
|
||||
focus: PropTypes.bool,
|
||||
@@ -90,6 +94,9 @@ module.exports = React.createClass({
|
||||
// List of UserAddressType objects representing the set of
|
||||
// auto-completion results for the current search query.
|
||||
suggestedList: [],
|
||||
// List of address types initialised from props, but may change while the
|
||||
// dialog is open.
|
||||
validAddressTypes: this.props.validAddressTypes,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -100,6 +107,15 @@ module.exports = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
getPlaceholder() {
|
||||
const { placeholder } = this.props;
|
||||
if (typeof placeholder === "string") {
|
||||
return placeholder;
|
||||
}
|
||||
// Otherwise it's a function, as checked by prop types.
|
||||
return placeholder(this.state.validAddressTypes);
|
||||
},
|
||||
|
||||
onButtonClick: function() {
|
||||
let selectedList = this.state.selectedList.slice();
|
||||
// Check the text input field to see if user has an unconverted address
|
||||
@@ -433,7 +449,7 @@ module.exports = React.createClass({
|
||||
// This is important, otherwise there's no way to invite
|
||||
// a perfectly valid address if there are close matches.
|
||||
const addrType = getAddressType(query);
|
||||
if (this.props.validAddressTypes.includes(addrType)) {
|
||||
if (this.state.validAddressTypes.includes(addrType)) {
|
||||
if (addrType === 'email' && !Email.looksValid(query)) {
|
||||
this.setState({searchError: _t("That doesn't look like a valid email address")});
|
||||
return;
|
||||
@@ -469,7 +485,7 @@ module.exports = React.createClass({
|
||||
isKnown: false,
|
||||
};
|
||||
|
||||
if (!this.props.validAddressTypes.includes(addrType)) {
|
||||
if (!this.state.validAddressTypes.includes(addrType)) {
|
||||
hasError = true;
|
||||
} else if (addrType === 'mx-user-id') {
|
||||
const user = MatrixClientPeg.get().getUser(addrObj.address);
|
||||
@@ -570,12 +586,37 @@ module.exports = React.createClass({
|
||||
this._addAddressesToList(text.split(/[\s,]+/));
|
||||
},
|
||||
|
||||
onUseDefaultIdentityServerClick(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Update the IS in account data. Actually using it may trigger terms.
|
||||
useDefaultIdentityServer();
|
||||
|
||||
// Add email as a valid address type.
|
||||
const { validAddressTypes } = this.state;
|
||||
validAddressTypes.push('email');
|
||||
this.setState({ validAddressTypes });
|
||||
},
|
||||
|
||||
onManageSettingsClick(e) {
|
||||
e.preventDefault();
|
||||
dis.dispatch({ action: 'view_user_settings' });
|
||||
this.onCancel();
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
const AddressSelector = sdk.getComponent("elements.AddressSelector");
|
||||
this.scrollElement = null;
|
||||
|
||||
let inputLabel;
|
||||
if (this.props.description) {
|
||||
inputLabel = <div className="mx_AddressPickerDialog_label">
|
||||
<label htmlFor="textinput">{this.props.description}</label>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const query = [];
|
||||
// create the invite list
|
||||
if (this.state.selectedList.length > 0) {
|
||||
@@ -602,7 +643,7 @@ module.exports = React.createClass({
|
||||
ref="textinput"
|
||||
className="mx_AddressPickerDialog_input"
|
||||
onChange={this.onQueryChanged}
|
||||
placeholder={this.props.placeholder}
|
||||
placeholder={this.getPlaceholder()}
|
||||
defaultValue={this.props.value}
|
||||
autoFocus={this.props.focus}>
|
||||
</textarea>,
|
||||
@@ -613,7 +654,7 @@ module.exports = React.createClass({
|
||||
let error;
|
||||
let addressSelector;
|
||||
if (this.state.invalidAddressError) {
|
||||
const validTypeDescriptions = this.props.validAddressTypes.map((t) => _t(addressTypeName[t]));
|
||||
const validTypeDescriptions = this.state.validAddressTypes.map((t) => _t(addressTypeName[t]));
|
||||
error = <div className="mx_AddressPickerDialog_error">
|
||||
{ _t("You have entered an invalid address.") }
|
||||
<br />
|
||||
@@ -636,17 +677,43 @@ module.exports = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
let identityServer;
|
||||
if (this.props.pickerType === 'user' && !this.state.validAddressTypes.includes('email')) {
|
||||
const defaultIdentityServerUrl = getDefaultIdentityServerUrl();
|
||||
if (defaultIdentityServerUrl) {
|
||||
identityServer = <div className="mx_AddressPickerDialog_identityServer">{_t(
|
||||
"Use an identity server to invite by email. " +
|
||||
"<default>Use the default (%(defaultIdentityServerName)s)</default> " +
|
||||
"or manage in <settings>Settings</settings>.",
|
||||
{
|
||||
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
|
||||
},
|
||||
{
|
||||
default: sub => <a href="#" onClick={this.onUseDefaultIdentityServerClick}>{sub}</a>,
|
||||
settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{sub}</a>,
|
||||
},
|
||||
)}</div>;
|
||||
} else {
|
||||
identityServer = <div className="mx_AddressPickerDialog_identityServer">{_t(
|
||||
"Use an identity server to invite by email. " +
|
||||
"Manage in <settings>Settings</settings>.",
|
||||
{}, {
|
||||
settings: sub => <a href="#" onClick={this.onManageSettingsClick}>{sub}</a>,
|
||||
},
|
||||
)}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseDialog className="mx_AddressPickerDialog" onKeyDown={this.onKeyDown}
|
||||
onFinished={this.props.onFinished} title={this.props.title}>
|
||||
<div className="mx_AddressPickerDialog_label">
|
||||
<label htmlFor="textinput">{ this.props.description }</label>
|
||||
</div>
|
||||
{inputLabel}
|
||||
<div className="mx_Dialog_content">
|
||||
<div className="mx_AddressPickerDialog_inputContainer">{ query }</div>
|
||||
{ error }
|
||||
{ addressSelector }
|
||||
{ this.props.extraNode }
|
||||
{ identityServer }
|
||||
</div>
|
||||
<DialogButtons primaryButton={this.props.button}
|
||||
onPrimaryButtonClick={this.onButtonClick}
|
||||
|
||||
@@ -16,12 +16,13 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {SettingLevel} from "../../../settings/SettingsStore";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
propTypes: {
|
||||
unknownProfileUsers: PropTypes.array.isRequired, // [ {userId, errorText}... ]
|
||||
onInviteAnyways: PropTypes.func.isRequired,
|
||||
|
||||
@@ -16,6 +16,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
@@ -32,7 +33,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
* Includes a div for the title, and a keypress handler which cancels the
|
||||
* dialog on escape.
|
||||
*/
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'BaseDialog',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -15,13 +15,14 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
/*
|
||||
* A dialog for confirming a redaction.
|
||||
*/
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'ConfirmRedactDialog',
|
||||
|
||||
render: function() {
|
||||
|
||||
@@ -15,6 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import sdk from '../../../index';
|
||||
@@ -29,7 +30,7 @@ import { GroupMemberType } from '../../../groups';
|
||||
* to make it obvious what is going to happen.
|
||||
* Also tweaks the style for 'dangerous' actions (albeit only with colour)
|
||||
*/
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'ConfirmUserActionDialog',
|
||||
propTypes: {
|
||||
// matrix-js-sdk (room) member object. Supply either this or 'groupMember'
|
||||
|
||||
@@ -15,13 +15,14 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import dis from '../../../dispatcher';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'CreateGroupDialog',
|
||||
propTypes: {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
@@ -15,12 +15,13 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'CreateRoomDialog',
|
||||
propTypes: {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2019 New Vector Ltd
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -241,6 +242,16 @@ export default class DeviceVerifyDialog extends React.Component {
|
||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||
const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton');
|
||||
|
||||
let text;
|
||||
if (MatrixClientPeg.get().getUserId() === this.props.userId) {
|
||||
text = _t("To verify that this device can be trusted, please check that the key you see " +
|
||||
"in User Settings on that device matches the key below:");
|
||||
} else {
|
||||
text = _t("To verify that this device can be trusted, please contact its owner using some other " +
|
||||
"means (e.g. in person or a phone call) and ask them whether the key they see in their User Settings " +
|
||||
"for this device matches the key below:");
|
||||
}
|
||||
|
||||
const key = FormattingUtils.formatCryptoKey(this.props.device.getFingerprint());
|
||||
const body = (
|
||||
<div>
|
||||
@@ -250,10 +261,7 @@ export default class DeviceVerifyDialog extends React.Component {
|
||||
{_t("Use two-way text verification")}
|
||||
</AccessibleButton>
|
||||
<p>
|
||||
{ _t("To verify that this device can be trusted, please contact its " +
|
||||
"owner using some other means (e.g. in person or a phone call) " +
|
||||
"and ask them whether the key they see in their User Settings " +
|
||||
"for this device matches the key below:") }
|
||||
{ text }
|
||||
</p>
|
||||
<div className="mx_DeviceVerifyDialog_cryptoSection">
|
||||
<ul>
|
||||
|
||||
@@ -26,11 +26,12 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'ErrorDialog',
|
||||
propTypes: {
|
||||
title: PropTypes.string,
|
||||
|
||||
@@ -17,12 +17,13 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import classNames from "classnames";
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'InfoDialog',
|
||||
propTypes: {
|
||||
className: PropTypes.string,
|
||||
|
||||
@@ -16,6 +16,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import sdk from '../../../index';
|
||||
@@ -23,7 +24,7 @@ import { _t } from '../../../languageHandler';
|
||||
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'InteractiveAuthDialog',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -16,6 +16,7 @@ limitations under the License.
|
||||
|
||||
import Modal from '../../../Modal';
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
|
||||
@@ -29,7 +30,7 @@ import { _t, _td } from '../../../languageHandler';
|
||||
* should not, and `undefined` if the dialog is cancelled. (In other words:
|
||||
* truthy: do the key share. falsy: don't share the keys).
|
||||
*/
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
propTypes: {
|
||||
matrixClient: PropTypes.object.isRequired,
|
||||
userId: PropTypes.string.isRequired,
|
||||
|
||||
@@ -16,11 +16,12 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'QuestionDialog',
|
||||
propTypes: {
|
||||
title: PropTypes.string,
|
||||
|
||||
@@ -15,13 +15,14 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import Modal from '../../../Modal';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'RoomUpgradeDialog',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -16,6 +16,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
@@ -23,7 +24,7 @@ import Modal from '../../../Modal';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'SessionRestoreErrorDialog',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -16,6 +16,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import Email from '../../../email';
|
||||
@@ -29,7 +30,7 @@ import Modal from '../../../Modal';
|
||||
*
|
||||
* On success, `onFinished(true)` is called.
|
||||
*/
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'SetEmailDialog',
|
||||
propTypes: {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
|
||||
import Promise from 'bluebird';
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
@@ -34,7 +35,7 @@ const USERNAME_CHECK_DEBOUNCE_MS = 250;
|
||||
*
|
||||
* On success, `onFinished(true, newDisplayName)` is called.
|
||||
*/
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'SetMxIdDialog',
|
||||
propTypes: {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
@@ -62,7 +63,7 @@ const WarmFuzzy = function(props) {
|
||||
*
|
||||
* On success, `onFinished()` when finished
|
||||
*/
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'SetPasswordDialog',
|
||||
propTypes: {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
172
src/components/views/dialogs/TabbedIntegrationManagerDialog.js
Normal file
172
src/components/views/dialogs/TabbedIntegrationManagerDialog.js
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||
import {Room} from "matrix-js-sdk";
|
||||
import sdk from '../../../index';
|
||||
import {dialogTermsInteractionCallback, TermsNotSignedError} from "../../../Terms";
|
||||
import classNames from 'classnames';
|
||||
import ScalarMessaging from "../../../ScalarMessaging";
|
||||
|
||||
export default class TabbedIntegrationManagerDialog extends React.Component {
|
||||
static propTypes = {
|
||||
/**
|
||||
* Called with:
|
||||
* * success {bool} True if the user accepted any douments, false if cancelled
|
||||
* * agreedUrls {string[]} List of agreed URLs
|
||||
*/
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
|
||||
/**
|
||||
* Optional room where the integration manager should be open to
|
||||
*/
|
||||
room: PropTypes.instanceOf(Room),
|
||||
|
||||
/**
|
||||
* Optional screen to open on the integration manager
|
||||
*/
|
||||
screen: PropTypes.string,
|
||||
|
||||
/**
|
||||
* Optional integration ID to open in the integration manager
|
||||
*/
|
||||
integrationId: PropTypes.string,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
managers: IntegrationManagers.sharedInstance().getOrderedManagers(),
|
||||
busy: true,
|
||||
currentIndex: 0,
|
||||
currentConnected: false,
|
||||
currentLoading: true,
|
||||
currentScalarClient: null,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.openManager(0, true);
|
||||
}
|
||||
|
||||
openManager = async (i: number, force = false) => {
|
||||
if (i === this.state.currentIndex && !force) return;
|
||||
|
||||
const manager = this.state.managers[i];
|
||||
const client = manager.getScalarClient();
|
||||
this.setState({
|
||||
busy: true,
|
||||
currentIndex: i,
|
||||
currentLoading: true,
|
||||
currentConnected: false,
|
||||
currentScalarClient: client,
|
||||
});
|
||||
|
||||
ScalarMessaging.setOpenManagerUrl(manager.uiUrl);
|
||||
|
||||
client.setTermsInteractionCallback((policyInfo, agreedUrls) => {
|
||||
// To avoid visual glitching of two modals stacking briefly, we customise the
|
||||
// terms dialog sizing when it will appear for the integrations manager so that
|
||||
// it gets the same basic size as the IM's own modal.
|
||||
return dialogTermsInteractionCallback(
|
||||
policyInfo, agreedUrls, 'mx_TermsDialog_forIntegrationsManager',
|
||||
);
|
||||
});
|
||||
|
||||
try {
|
||||
await client.connect();
|
||||
if (!client.hasCredentials()) {
|
||||
this.setState({
|
||||
busy: false,
|
||||
currentLoading: false,
|
||||
currentConnected: false,
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
busy: false,
|
||||
currentLoading: false,
|
||||
currentConnected: true,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof TermsNotSignedError) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(e);
|
||||
this.setState({
|
||||
busy: false,
|
||||
currentLoading: false,
|
||||
currentConnected: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_renderTabs() {
|
||||
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
|
||||
return this.state.managers.map((m, i) => {
|
||||
const classes = classNames({
|
||||
'mx_TabbedIntegrationManagerDialog_tab': true,
|
||||
'mx_TabbedIntegrationManagerDialog_currentTab': this.state.currentIndex === i,
|
||||
});
|
||||
return (
|
||||
<AccessibleButton
|
||||
className={classes}
|
||||
onClick={() => this.openManager(i)}
|
||||
key={`tab_${i}`}
|
||||
disabled={this.state.busy}
|
||||
>
|
||||
{m.name}
|
||||
</AccessibleButton>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_renderTab() {
|
||||
const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager");
|
||||
let uiUrl = null;
|
||||
if (this.state.currentScalarClient) {
|
||||
uiUrl = this.state.currentScalarClient.getScalarInterfaceUrlForRoom(
|
||||
this.props.room,
|
||||
this.props.screen,
|
||||
this.props.integrationId,
|
||||
);
|
||||
}
|
||||
return <IntegrationsManager
|
||||
configured={true}
|
||||
loading={this.state.currentLoading}
|
||||
connected={this.state.currentConnected}
|
||||
url={uiUrl}
|
||||
onFinished={() => {/* no-op */}}
|
||||
/>;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className='mx_TabbedIntegrationManagerDialog_container'>
|
||||
<div className='mx_TabbedIntegrationManagerDialog_tabs'>
|
||||
{this._renderTabs()}
|
||||
</div>
|
||||
<div className='mx_TabbedIntegrationManagerDialog_currentManager'>
|
||||
{this._renderTab()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,11 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'TextInputDialog',
|
||||
propTypes: {
|
||||
title: PropTypes.string,
|
||||
|
||||
@@ -16,11 +16,10 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import GeminiScrollbar from 'react-gemini-scrollbar';
|
||||
import Resend from '../../../Resend';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { markAllDevicesKnown } from '../../../cryptodevices';
|
||||
@@ -67,7 +66,7 @@ UnknownDeviceList.propTypes = {
|
||||
};
|
||||
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'UnknownDeviceDialog',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -15,6 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../../index';
|
||||
import MatrixClientPeg from '../../../../MatrixClientPeg';
|
||||
import Modal from '../../../../Modal';
|
||||
@@ -29,7 +30,7 @@ const RESTORE_TYPE_RECOVERYKEY = 1;
|
||||
/**
|
||||
* Dialog for restoring e2e keys from a backup and the user's recovery key
|
||||
*/
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
backupInfo: null,
|
||||
|
||||
@@ -16,12 +16,13 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import AccessibleButton from './AccessibleButton';
|
||||
import dis from '../../../dispatcher';
|
||||
import sdk from '../../../index';
|
||||
import Analytics from '../../../Analytics';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'RoleButton',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -15,15 +15,14 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../index';
|
||||
import classNames from 'classnames';
|
||||
import { UserAddressType } from '../../../UserAddress';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'AddressSelector',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import classNames from 'classnames';
|
||||
import sdk from "../../../index";
|
||||
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||
@@ -24,7 +25,7 @@ import { _t } from '../../../languageHandler';
|
||||
import { UserAddressType } from '../../../UserAddress.js';
|
||||
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'AddressTile',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -15,14 +16,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import url from 'url';
|
||||
import qs from 'querystring';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
||||
import WidgetMessaging from '../../../WidgetMessaging';
|
||||
import AccessibleButton from './AccessibleButton';
|
||||
import Modal from '../../../Modal';
|
||||
@@ -35,7 +33,8 @@ import WidgetUtils from '../../../utils/WidgetUtils';
|
||||
import dis from '../../../dispatcher';
|
||||
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
||||
import classNames from 'classnames';
|
||||
import { showIntegrationsManager } from '../../../integrations/integrations';
|
||||
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
const ALLOWED_APP_URL_SCHEMES = ['https:', 'http:'];
|
||||
const ENABLE_REACT_PERF = false;
|
||||
@@ -157,7 +156,7 @@ export default class AppTile extends React.Component {
|
||||
|
||||
// if it's not remaining on screen, get rid of the PersistedElement container
|
||||
if (!ActiveWidgetStore.getWidgetPersistence(this.props.id)) {
|
||||
ActiveWidgetStore.destroyPersistentWidget();
|
||||
ActiveWidgetStore.destroyPersistentWidget(this.props.id);
|
||||
const PersistedElement = sdk.getComponent("elements.PersistedElement");
|
||||
PersistedElement.destroyElement(this._persistKey);
|
||||
}
|
||||
@@ -178,9 +177,22 @@ export default class AppTile extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const managers = IntegrationManagers.sharedInstance();
|
||||
if (!managers.hasManager()) {
|
||||
console.warn("No integration manager - not setting scalar token", url);
|
||||
this.setState({
|
||||
error: null,
|
||||
widgetUrl: this._addWurlParams(this.props.url),
|
||||
initialising: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Pick the right manager for the widget
|
||||
|
||||
// Fetch the token before loading the iframe as we need it to mangle the URL
|
||||
if (!this._scalarClient) {
|
||||
this._scalarClient = new ScalarAuthClient();
|
||||
this._scalarClient = managers.getPrimaryManager().getScalarClient();
|
||||
}
|
||||
this._scalarClient.getScalarToken().done((token) => {
|
||||
// Append scalar_token as a query param if not already present
|
||||
@@ -189,7 +201,7 @@ export default class AppTile extends React.Component {
|
||||
const params = qs.parse(u.query);
|
||||
if (!params.scalar_token) {
|
||||
params.scalar_token = encodeURIComponent(token);
|
||||
// u.search must be set to undefined, so that u.format() uses query paramerters - https://nodejs.org/docs/latest/api/url.html#url_url_format_url_options
|
||||
// u.search must be set to undefined, so that u.format() uses query parameters - https://nodejs.org/docs/latest/api/url.html#url_url_format_url_options
|
||||
u.search = undefined;
|
||||
u.query = params;
|
||||
}
|
||||
@@ -251,11 +263,20 @@ export default class AppTile extends React.Component {
|
||||
if (this.props.onEditClick) {
|
||||
this.props.onEditClick();
|
||||
} else {
|
||||
showIntegrationsManager({
|
||||
room: this.props.room,
|
||||
screen: 'type_' + this.props.type,
|
||||
integrationId: this.props.id,
|
||||
});
|
||||
// TODO: Open the right manager for the widget
|
||||
if (SettingsStore.isFeatureEnabled("feature_many_integration_managers")) {
|
||||
IntegrationManagers.sharedInstance().openAll(
|
||||
this.props.room,
|
||||
'type_' + this.props.type,
|
||||
this.props.id,
|
||||
);
|
||||
} else {
|
||||
IntegrationManagers.sharedInstance().getPrimaryManager().open(
|
||||
this.props.room,
|
||||
'type_' + this.props.type,
|
||||
this.props.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,7 +450,7 @@ export default class AppTile extends React.Component {
|
||||
this.setState({hasPermissionToLoad: false});
|
||||
|
||||
// Force the widget to be non-persistent
|
||||
ActiveWidgetStore.destroyPersistentWidget();
|
||||
ActiveWidgetStore.destroyPersistentWidget(this.props.id);
|
||||
const PersistedElement = sdk.getComponent("elements.PersistedElement");
|
||||
PersistedElement.destroyElement(this._persistKey);
|
||||
}
|
||||
@@ -575,11 +596,10 @@ export default class AppTile extends React.Component {
|
||||
src={this._getSafeUrl()}
|
||||
allowFullScreen="true"
|
||||
sandbox={sandboxFlags}
|
||||
onLoad={this._onLoaded}
|
||||
></iframe>
|
||||
onLoad={this._onLoaded} />
|
||||
</div>
|
||||
);
|
||||
// if the widget would be allowed to remian on screen, we must put it in
|
||||
// if the widget would be allowed to remain on screen, we must put it in
|
||||
// a PersistedElement from the get-go, otherwise the iframe will be
|
||||
// re-mounted later when we do.
|
||||
if (this.props.whitelistCapabilities.includes('m.always_on_screen')) {
|
||||
|
||||
@@ -16,12 +16,13 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import sdk from '../../../index';
|
||||
import Modal from '../../../Modal';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
displayName: 'DeviceVerifyButtons',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -17,12 +17,13 @@ limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import createReactClass from 'create-react-class';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
/**
|
||||
* Basic container for buttons in modal dialogs.
|
||||
*/
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: "DialogButtons",
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -17,8 +17,9 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'EditableText',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -18,7 +18,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import sdk from '../../../index';
|
||||
import { throttle } from 'lodash';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
// Invoke validation from user input (when typing, etc.) at most once every N ms.
|
||||
const VALIDATION_THROTTLE_MS = 200;
|
||||
@@ -46,6 +46,14 @@ export default class Field extends React.PureComponent {
|
||||
// and a `feedback` react component field to provide feedback
|
||||
// to the user.
|
||||
onValidate: PropTypes.func,
|
||||
// If specified, overrides the value returned by onValidate.
|
||||
flagInvalid: PropTypes.bool,
|
||||
// If specified, contents will appear as a tooltip on the element and
|
||||
// validation feedback tooltips will be suppressed.
|
||||
tooltipContent: PropTypes.node,
|
||||
// If specified alongside tooltipContent, the class name to apply to the
|
||||
// tooltip itself.
|
||||
tooltipClassName: PropTypes.string,
|
||||
// All other props pass through to the <input>.
|
||||
};
|
||||
|
||||
@@ -118,14 +126,25 @@ export default class Field extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
validateOnChange = throttle(() => {
|
||||
/*
|
||||
* This was changed from throttle to debounce: this is more traditional for
|
||||
* form validation since it means that the validation doesn't happen at all
|
||||
* until the user stops typing for a bit (debounce defaults to not running on
|
||||
* the leading edge). If we're doing an HTTP hit on each validation, we have more
|
||||
* incentive to prevent validating input that's very unlikely to be valid.
|
||||
* We may find that we actually want different behaviour for registration
|
||||
* fields, in which case we can add some options to control it.
|
||||
*/
|
||||
validateOnChange = debounce(() => {
|
||||
this.validate({
|
||||
focused: true,
|
||||
});
|
||||
}, VALIDATION_THROTTLE_MS);
|
||||
|
||||
render() {
|
||||
const { element, prefix, onValidate, children, ...inputProps } = this.props;
|
||||
const {
|
||||
element, prefix, onValidate, children, tooltipContent, flagInvalid,
|
||||
tooltipClassName, ...inputProps} = this.props;
|
||||
|
||||
const inputElement = element || "input";
|
||||
|
||||
@@ -145,23 +164,27 @@ export default class Field extends React.PureComponent {
|
||||
prefixContainer = <span className="mx_Field_prefix">{prefix}</span>;
|
||||
}
|
||||
|
||||
const hasValidationFlag = flagInvalid !== null && flagInvalid !== undefined;
|
||||
const fieldClasses = classNames("mx_Field", `mx_Field_${inputElement}`, {
|
||||
// If we have a prefix element, leave the label always at the top left and
|
||||
// don't animate it, as it looks a bit clunky and would add complexity to do
|
||||
// properly.
|
||||
mx_Field_labelAlwaysTopLeft: prefix,
|
||||
mx_Field_valid: onValidate && this.state.valid === true,
|
||||
mx_Field_invalid: onValidate && this.state.valid === false,
|
||||
mx_Field_invalid: hasValidationFlag
|
||||
? flagInvalid
|
||||
: onValidate && this.state.valid === false,
|
||||
});
|
||||
|
||||
// Handle displaying feedback on validity
|
||||
const Tooltip = sdk.getComponent("elements.Tooltip");
|
||||
let tooltip;
|
||||
if (this.state.feedback) {
|
||||
tooltip = <Tooltip
|
||||
tooltipClassName="mx_Field_tooltip"
|
||||
let fieldTooltip;
|
||||
if (tooltipContent || this.state.feedback) {
|
||||
const addlClassName = tooltipClassName ? tooltipClassName : '';
|
||||
fieldTooltip = <Tooltip
|
||||
tooltipClassName={`mx_Field_tooltip ${addlClassName}`}
|
||||
visible={this.state.feedbackVisible}
|
||||
label={this.state.feedback}
|
||||
label={tooltipContent || this.state.feedback}
|
||||
/>;
|
||||
}
|
||||
|
||||
@@ -169,7 +192,7 @@ export default class Field extends React.PureComponent {
|
||||
{prefixContainer}
|
||||
{fieldInput}
|
||||
<label htmlFor={this.props.id}>{this.props.label}</label>
|
||||
{tooltip}
|
||||
{fieldTooltip}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
const React = require('react');
|
||||
import React from "react";
|
||||
import createReactClass from 'create-react-class';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'InlineSpinner',
|
||||
|
||||
render: function() {
|
||||
|
||||
@@ -18,9 +18,9 @@ limitations under the License.
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import sdk from '../../../index';
|
||||
import ScalarAuthClient from '../../../ScalarAuthClient';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { showIntegrationsManager } from '../../../integrations/integrations';
|
||||
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
export default class ManageIntegsButton extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -30,12 +30,21 @@ export default class ManageIntegsButton extends React.Component {
|
||||
onManageIntegrations = (ev) => {
|
||||
ev.preventDefault();
|
||||
|
||||
showIntegrationsManager({ room: this.props.room });
|
||||
const managers = IntegrationManagers.sharedInstance();
|
||||
if (!managers.hasManager()) {
|
||||
managers.openNoManagerDialog();
|
||||
} else {
|
||||
if (SettingsStore.isFeatureEnabled("feature_many_integration_managers")) {
|
||||
managers.openAll(this.props.room);
|
||||
} else {
|
||||
managers.getPrimaryManager().open(this.props.room);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let integrationsButton = <div />;
|
||||
if (ScalarAuthClient.isPossible()) {
|
||||
if (IntegrationManagers.sharedInstance().hasManager()) {
|
||||
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||
integrationsButton = (
|
||||
<AccessibleButton
|
||||
|
||||
@@ -18,11 +18,12 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import MemberAvatar from '../avatars/MemberAvatar';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import { formatCommaSeparatedList } from '../../../utils/FormattingUtils';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'MemberEventListSummary',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -15,8 +15,9 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'MessageSpinner',
|
||||
|
||||
render: function() {
|
||||
|
||||
@@ -15,13 +15,14 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||
import sdk from '../../../index';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'PersistentApp',
|
||||
|
||||
getInitialState: function() {
|
||||
|
||||
@@ -15,6 +15,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../index';
|
||||
import dis from '../../../dispatcher';
|
||||
import classNames from 'classnames';
|
||||
@@ -31,7 +32,7 @@ const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
|
||||
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
|
||||
const REGEX_LOCAL_MATRIXTO = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/;
|
||||
|
||||
const Pill = React.createClass({
|
||||
const Pill = createReactClass({
|
||||
statics: {
|
||||
isPillUrl: (url) => {
|
||||
return !!REGEX_MATRIXTO.exec(url);
|
||||
|
||||
@@ -14,15 +14,14 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import * as Roles from '../../../Roles';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Field from "./Field";
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'PowerSelector',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -14,12 +14,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
import React from "react";
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'ProgressBar',
|
||||
propTypes: {
|
||||
value: PropTypes.number,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright 2017 New Vector Ltd
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -176,6 +177,9 @@ export default class ReplyThread extends React.Component {
|
||||
componentWillMount() {
|
||||
this.unmounted = false;
|
||||
this.room = this.context.matrixClient.getRoom(this.props.parentEv.getRoomId());
|
||||
this.room.on("Room.redaction", this.onRoomRedaction);
|
||||
// same event handler as Room.redaction as for both we just do forceUpdate
|
||||
this.room.on("Room.redactionCancelled", this.onRoomRedaction);
|
||||
this.initialize();
|
||||
}
|
||||
|
||||
@@ -185,8 +189,21 @@ export default class ReplyThread extends React.Component {
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unmounted = true;
|
||||
if (this.room) {
|
||||
this.room.removeListener("Room.redaction", this.onRoomRedaction);
|
||||
this.room.removeListener("Room.redactionCancelled", this.onRoomRedaction);
|
||||
}
|
||||
}
|
||||
|
||||
onRoomRedaction = (ev, room) => {
|
||||
if (this.unmounted) return;
|
||||
|
||||
// If one of the events we are rendering gets redacted, force a re-render
|
||||
if (this.state.events.some(event => event.getId() === ev.getId())) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
async initialize() {
|
||||
const {parentEv} = this.props;
|
||||
// at time of making this component we checked that props.parentEv has a parentEventId
|
||||
@@ -298,11 +315,13 @@ export default class ReplyThread extends React.Component {
|
||||
|
||||
return <blockquote className="mx_ReplyThread" key={ev.getId()}>
|
||||
{ dateSep }
|
||||
<EventTile mxEvent={ev}
|
||||
tileShape="reply"
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} />
|
||||
<EventTile
|
||||
mxEvent={ev}
|
||||
tileShape="reply"
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
isRedacted={ev.isRedacted()}
|
||||
isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} />
|
||||
</blockquote>;
|
||||
});
|
||||
|
||||
|
||||
@@ -16,11 +16,12 @@ limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { _t } from '../../../languageHandler';
|
||||
import ToggleSwitch from "./ToggleSwitch";
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'SettingsFlag',
|
||||
propTypes: {
|
||||
name: PropTypes.string.isRequired,
|
||||
|
||||
@@ -14,11 +14,10 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
import React from "react";
|
||||
import createReactClass from 'create-react-class';
|
||||
|
||||
const React = require('react');
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'Spinner',
|
||||
|
||||
render: function() {
|
||||
|
||||
51
src/components/views/elements/Spoiler.js
Normal file
51
src/components/views/elements/Spoiler.js
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2019 Sorunome
|
||||
|
||||
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';
|
||||
|
||||
export default class Spoiler extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visible: false,
|
||||
};
|
||||
}
|
||||
|
||||
toggleVisible(e) {
|
||||
if (!this.state.visible) {
|
||||
// we are un-blurring, we don't want this click to propagate to potential child pills
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
this.setState({ visible: !this.state.visible });
|
||||
}
|
||||
|
||||
render() {
|
||||
const reason = this.props.reason ? (
|
||||
<span className="mx_EventTile_spoiler_reason">{"(" + this.props.reason + ")"}</span>
|
||||
) : null;
|
||||
// react doesn't allow appending a DOM node as child.
|
||||
// as such, we pass the this.props.contentHtml instead and then set the raw
|
||||
// HTML content. This is secure as the contents have already been parsed previously
|
||||
return (
|
||||
<span className={"mx_EventTile_spoiler" + (this.state.visible ? " visible" : "")} onClick={this.toggleVisible.bind(this)}>
|
||||
{ reason }
|
||||
|
||||
<span className="mx_EventTile_spoiler_content" dangerouslySetInnerHTML={{ __html: this.props.contentHtml }} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import classNames from 'classnames';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import sdk from '../../../index';
|
||||
@@ -34,7 +35,7 @@ import TagOrderStore from '../../../stores/TagOrderStore';
|
||||
// - 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({
|
||||
export default createReactClass({
|
||||
displayName: 'TagTile',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -14,14 +14,12 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const ReactDOM = require("react-dom");
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
const Tinter = require("../../../Tinter");
|
||||
import createReactClass from 'create-react-class';
|
||||
import Tinter from "../../../Tinter";
|
||||
|
||||
var TintableSvg = React.createClass({
|
||||
const TintableSvg = createReactClass({
|
||||
displayName: 'TintableSvg',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -20,12 +20,13 @@ limitations under the License.
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import dis from '../../../dispatcher';
|
||||
import classNames from 'classnames';
|
||||
|
||||
const MIN_TOOLTIP_HEIGHT = 25;
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'Tooltip',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -16,9 +16,10 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../index';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'TooltipButton',
|
||||
|
||||
getInitialState: function() {
|
||||
|
||||
@@ -17,9 +17,10 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'TruncatedList',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -14,13 +14,12 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'UserSelector',
|
||||
|
||||
propTypes: {
|
||||
|
||||
@@ -14,14 +14,13 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import Notifier from '../../../Notifier';
|
||||
import AccessibleButton from '../../../components/views/elements/AccessibleButton';
|
||||
|
||||
module.exports = React.createClass({
|
||||
module.exports = createReactClass({
|
||||
displayName: 'MatrixToolbar',
|
||||
|
||||
hideToolbar: function() {
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../index';
|
||||
import Modal from '../../../Modal';
|
||||
import PlatformPeg from '../../../PlatformPeg';
|
||||
@@ -31,7 +32,7 @@ function checkVersion(ver) {
|
||||
return parts.length == 5 && parts[1] == 'react' && parts[3] == 'js';
|
||||
}
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
propTypes: {
|
||||
version: PropTypes.string.isRequired,
|
||||
newVersion: PropTypes.string.isRequired,
|
||||
|
||||
@@ -16,11 +16,12 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import createReactClass from 'create-react-class';
|
||||
import sdk from '../../../index';
|
||||
import Modal from '../../../Modal';
|
||||
import { _t } from '../../../languageHandler';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
onUpdateClicked: function() {
|
||||
const SetPasswordDialog = sdk.getComponent('dialogs.SetPasswordDialog');
|
||||
Modal.createTrackedDialog('Set Password Dialog', 'Password Nag Bar', SetPasswordDialog);
|
||||
|
||||
@@ -16,11 +16,12 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import classNames from 'classnames';
|
||||
import { _td } from '../../../languageHandler';
|
||||
import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
|
||||
|
||||
export default React.createClass({
|
||||
export default createReactClass({
|
||||
propTypes: {
|
||||
// 'hard' if the logged in user has been locked out, 'soft' if they haven't
|
||||
kind: PropTypes.string,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user