Remove create-react-class
This commit is contained in:
parent
672d0fe97b
commit
72498df28f
108 changed files with 3059 additions and 3545 deletions
|
@ -61,7 +61,6 @@
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"commonmark": "^0.29.1",
|
"commonmark": "^0.29.1",
|
||||||
"counterpart": "^0.18.6",
|
"counterpart": "^0.18.6",
|
||||||
"create-react-class": "^15.6.3",
|
|
||||||
"diff-dom": "^4.1.6",
|
"diff-dom": "^4.1.6",
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
"emojibase-data": "^5.0.1",
|
"emojibase-data": "^5.0.1",
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import createReactClass from 'create-react-class';
|
import React from "react";
|
||||||
import * as sdk from './index';
|
import * as sdk from './index';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
|
@ -24,21 +24,19 @@ import { _t } from './languageHandler';
|
||||||
* Wrap an asynchronous loader function with a react component which shows a
|
* Wrap an asynchronous loader function with a react component which shows a
|
||||||
* spinner until the real component loads.
|
* spinner until the real component loads.
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class AsyncWrapper extends React.Component {
|
||||||
propTypes: {
|
static propTypes = {
|
||||||
/** A promise which resolves with the real component
|
/** A promise which resolves with the real component
|
||||||
*/
|
*/
|
||||||
prom: PropTypes.object.isRequired,
|
prom: PropTypes.object.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
component: null,
|
||||||
component: null,
|
error: null,
|
||||||
error: null,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
// XXX: temporary logging to try to diagnose
|
// XXX: temporary logging to try to diagnose
|
||||||
// https://github.com/vector-im/element-web/issues/3148
|
// https://github.com/vector-im/element-web/issues/3148
|
||||||
|
@ -56,17 +54,17 @@ export default createReactClass({
|
||||||
console.warn('AsyncWrapper promise failed', e);
|
console.warn('AsyncWrapper promise failed', e);
|
||||||
this.setState({error: e});
|
this.setState({error: e});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
_onWrapperCancelClick: function() {
|
_onWrapperCancelClick = () => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
if (this.state.component) {
|
if (this.state.component) {
|
||||||
const Component = this.state.component;
|
const Component = this.state.component;
|
||||||
return <Component {...this.props} />;
|
return <Component {...this.props} />;
|
||||||
|
@ -87,6 +85,6 @@ export default createReactClass({
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
return <Spinner />;
|
return <Spinner />;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
import FileSaver from 'file-saver';
|
import FileSaver from 'file-saver';
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
|
@ -27,34 +26,31 @@ import * as sdk from '../../../index';
|
||||||
const PHASE_EDIT = 1;
|
const PHASE_EDIT = 1;
|
||||||
const PHASE_EXPORTING = 2;
|
const PHASE_EXPORTING = 2;
|
||||||
|
|
||||||
export default createReactClass({
|
export default class ExportE2eKeysDialog extends React.Component {
|
||||||
displayName: 'ExportE2eKeysDialog',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
phase: PHASE_EDIT,
|
|
||||||
errStr: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
|
|
||||||
this._passphrase1 = createRef();
|
this._passphrase1 = createRef();
|
||||||
this._passphrase2 = createRef();
|
this._passphrase2 = createRef();
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
this.state = {
|
||||||
|
phase: PHASE_EDIT,
|
||||||
|
errStr: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
_onPassphraseFormSubmit: function(ev) {
|
_onPassphraseFormSubmit = (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
const passphrase = this._passphrase1.current.value;
|
const passphrase = this._passphrase1.current.value;
|
||||||
|
@ -69,9 +65,9 @@ export default createReactClass({
|
||||||
|
|
||||||
this._startExport(passphrase);
|
this._startExport(passphrase);
|
||||||
return false;
|
return false;
|
||||||
},
|
};
|
||||||
|
|
||||||
_startExport: function(passphrase) {
|
_startExport(passphrase) {
|
||||||
// extra Promise.resolve() to turn synchronous exceptions into
|
// extra Promise.resolve() to turn synchronous exceptions into
|
||||||
// asynchronous ones.
|
// asynchronous ones.
|
||||||
Promise.resolve().then(() => {
|
Promise.resolve().then(() => {
|
||||||
|
@ -102,15 +98,15 @@ export default createReactClass({
|
||||||
errStr: null,
|
errStr: null,
|
||||||
phase: PHASE_EXPORTING,
|
phase: PHASE_EXPORTING,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_onCancelClick: function(ev) {
|
_onCancelClick = (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
return false;
|
return false;
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
|
|
||||||
const disableForm = (this.state.phase === PHASE_EXPORTING);
|
const disableForm = (this.state.phase === PHASE_EXPORTING);
|
||||||
|
@ -184,5 +180,5 @@ export default createReactClass({
|
||||||
</form>
|
</form>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
|
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import * as MegolmExportEncryption from '../../../utils/MegolmExportEncryption';
|
import * as MegolmExportEncryption from '../../../utils/MegolmExportEncryption';
|
||||||
|
@ -38,48 +37,45 @@ function readFileAsArrayBuffer(file) {
|
||||||
const PHASE_EDIT = 1;
|
const PHASE_EDIT = 1;
|
||||||
const PHASE_IMPORTING = 2;
|
const PHASE_IMPORTING = 2;
|
||||||
|
|
||||||
export default createReactClass({
|
export default class ImportE2eKeysDialog extends React.Component {
|
||||||
displayName: 'ImportE2eKeysDialog',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
matrixClient: PropTypes.instanceOf(MatrixClient).isRequired,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
enableSubmit: false,
|
|
||||||
phase: PHASE_EDIT,
|
|
||||||
errStr: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
|
|
||||||
this._file = createRef();
|
this._file = createRef();
|
||||||
this._passphrase = createRef();
|
this._passphrase = createRef();
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
this.state = {
|
||||||
|
enableSubmit: false,
|
||||||
|
phase: PHASE_EDIT,
|
||||||
|
errStr: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
_onFormChange: function(ev) {
|
_onFormChange = (ev) => {
|
||||||
const files = this._file.current.files || [];
|
const files = this._file.current.files || [];
|
||||||
this.setState({
|
this.setState({
|
||||||
enableSubmit: (this._passphrase.current.value !== "" && files.length > 0),
|
enableSubmit: (this._passphrase.current.value !== "" && files.length > 0),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onFormSubmit: function(ev) {
|
_onFormSubmit = (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this._startImport(this._file.current.files[0], this._passphrase.current.value);
|
this._startImport(this._file.current.files[0], this._passphrase.current.value);
|
||||||
return false;
|
return false;
|
||||||
},
|
};
|
||||||
|
|
||||||
_startImport: function(file, passphrase) {
|
_startImport(file, passphrase) {
|
||||||
this.setState({
|
this.setState({
|
||||||
errStr: null,
|
errStr: null,
|
||||||
phase: PHASE_IMPORTING,
|
phase: PHASE_IMPORTING,
|
||||||
|
@ -105,15 +101,15 @@ export default createReactClass({
|
||||||
phase: PHASE_EDIT,
|
phase: PHASE_EDIT,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_onCancelClick: function(ev) {
|
_onCancelClick = (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
return false;
|
return false;
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
|
|
||||||
const disableForm = (this.state.phase !== PHASE_EDIT);
|
const disableForm = (this.state.phase !== PHASE_EDIT);
|
||||||
|
@ -188,5 +184,5 @@ export default createReactClass({
|
||||||
</form>
|
</form>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import {Filter} from 'matrix-js-sdk';
|
import {Filter} from 'matrix-js-sdk';
|
||||||
|
@ -28,23 +27,20 @@ import { _t } from '../../languageHandler';
|
||||||
/*
|
/*
|
||||||
* Component which shows the filtered file using a TimelinePanel
|
* Component which shows the filtered file using a TimelinePanel
|
||||||
*/
|
*/
|
||||||
const FilePanel = createReactClass({
|
class FilePanel extends React.Component {
|
||||||
displayName: 'FilePanel',
|
static propTypes = {
|
||||||
|
roomId: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
// This is used to track if a decrypted event was a live event and should be
|
// This is used to track if a decrypted event was a live event and should be
|
||||||
// added to the timeline.
|
// added to the timeline.
|
||||||
decryptingEvents: new Set(),
|
decryptingEvents = new Set();
|
||||||
|
|
||||||
propTypes: {
|
state = {
|
||||||
roomId: PropTypes.string.isRequired,
|
timelineSet: null,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
onRoomTimeline = (ev, room, toStartOfTimeline, removed, data) => {
|
||||||
return {
|
|
||||||
timelineSet: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onRoomTimeline(ev, room, toStartOfTimeline, removed, data) {
|
|
||||||
if (room.roomId !== this.props.roomId) return;
|
if (room.roomId !== this.props.roomId) return;
|
||||||
if (toStartOfTimeline || !data || !data.liveEvent || ev.isRedacted()) return;
|
if (toStartOfTimeline || !data || !data.liveEvent || ev.isRedacted()) return;
|
||||||
|
|
||||||
|
@ -53,9 +49,9 @@ const FilePanel = createReactClass({
|
||||||
} else {
|
} else {
|
||||||
this.addEncryptedLiveEvent(ev);
|
this.addEncryptedLiveEvent(ev);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onEventDecrypted(ev, err) {
|
onEventDecrypted = (ev, err) => {
|
||||||
if (ev.getRoomId() !== this.props.roomId) return;
|
if (ev.getRoomId() !== this.props.roomId) return;
|
||||||
const eventId = ev.getId();
|
const eventId = ev.getId();
|
||||||
|
|
||||||
|
@ -63,7 +59,7 @@ const FilePanel = createReactClass({
|
||||||
if (err) return;
|
if (err) return;
|
||||||
|
|
||||||
this.addEncryptedLiveEvent(ev);
|
this.addEncryptedLiveEvent(ev);
|
||||||
},
|
};
|
||||||
|
|
||||||
addEncryptedLiveEvent(ev, toStartOfTimeline) {
|
addEncryptedLiveEvent(ev, toStartOfTimeline) {
|
||||||
if (!this.state.timelineSet) return;
|
if (!this.state.timelineSet) return;
|
||||||
|
@ -77,7 +73,7 @@ const FilePanel = createReactClass({
|
||||||
if (!this.state.timelineSet.eventIdToTimeline(ev.getId())) {
|
if (!this.state.timelineSet.eventIdToTimeline(ev.getId())) {
|
||||||
this.state.timelineSet.addEventToTimeline(ev, timeline, false);
|
this.state.timelineSet.addEventToTimeline(ev, timeline, false);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
|
@ -98,7 +94,7 @@ const FilePanel = createReactClass({
|
||||||
client.on('Room.timeline', this.onRoomTimeline);
|
client.on('Room.timeline', this.onRoomTimeline);
|
||||||
client.on('Event.decrypted', this.onEventDecrypted);
|
client.on('Event.decrypted', this.onEventDecrypted);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
|
@ -110,7 +106,7 @@ const FilePanel = createReactClass({
|
||||||
client.removeListener('Room.timeline', this.onRoomTimeline);
|
client.removeListener('Room.timeline', this.onRoomTimeline);
|
||||||
client.removeListener('Event.decrypted', this.onEventDecrypted);
|
client.removeListener('Event.decrypted', this.onEventDecrypted);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async fetchFileEventsServer(room) {
|
async fetchFileEventsServer(room) {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
|
@ -134,9 +130,9 @@ const FilePanel = createReactClass({
|
||||||
const timelineSet = room.getOrCreateFilteredTimelineSet(filter);
|
const timelineSet = room.getOrCreateFilteredTimelineSet(filter);
|
||||||
|
|
||||||
return timelineSet;
|
return timelineSet;
|
||||||
},
|
}
|
||||||
|
|
||||||
onPaginationRequest(timelineWindow, direction, limit) {
|
onPaginationRequest = (timelineWindow, direction, limit) => {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const eventIndex = EventIndexPeg.get();
|
const eventIndex = EventIndexPeg.get();
|
||||||
const roomId = this.props.roomId;
|
const roomId = this.props.roomId;
|
||||||
|
@ -152,7 +148,7 @@ const FilePanel = createReactClass({
|
||||||
} else {
|
} else {
|
||||||
return timelineWindow.paginate(direction, limit);
|
return timelineWindow.paginate(direction, limit);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
async updateTimelineSet(roomId: string) {
|
async updateTimelineSet(roomId: string) {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
|
@ -188,9 +184,9 @@ const FilePanel = createReactClass({
|
||||||
} else {
|
} else {
|
||||||
console.error("Failed to add filtered timelineSet for FilePanel as no room!");
|
console.error("Failed to add filtered timelineSet for FilePanel as no room!");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
if (MatrixClientPeg.get().isGuest()) {
|
if (MatrixClientPeg.get().isGuest()) {
|
||||||
return <div className="mx_FilePanel mx_RoomView_messageListWrapper">
|
return <div className="mx_FilePanel mx_RoomView_messageListWrapper">
|
||||||
<div className="mx_RoomView_empty">
|
<div className="mx_RoomView_empty">
|
||||||
|
@ -239,7 +235,7 @@ const FilePanel = createReactClass({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export default FilePanel;
|
export default FilePanel;
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../MatrixClientPeg';
|
||||||
import * as sdk from '../../index';
|
import * as sdk from '../../index';
|
||||||
|
@ -70,10 +69,8 @@ const UserSummaryType = PropTypes.shape({
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
});
|
});
|
||||||
|
|
||||||
const CategoryRoomList = createReactClass({
|
class CategoryRoomList extends React.Component {
|
||||||
displayName: 'CategoryRoomList',
|
static propTypes = {
|
||||||
|
|
||||||
props: {
|
|
||||||
rooms: PropTypes.arrayOf(RoomSummaryType).isRequired,
|
rooms: PropTypes.arrayOf(RoomSummaryType).isRequired,
|
||||||
category: PropTypes.shape({
|
category: PropTypes.shape({
|
||||||
profile: PropTypes.shape({
|
profile: PropTypes.shape({
|
||||||
|
@ -84,9 +81,9 @@ const CategoryRoomList = createReactClass({
|
||||||
|
|
||||||
// Whether the list should be editable
|
// Whether the list should be editable
|
||||||
editing: PropTypes.bool.isRequired,
|
editing: PropTypes.bool.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
onAddRoomsToSummaryClicked: function(ev) {
|
onAddRoomsToSummaryClicked = (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
||||||
Modal.createTrackedDialog('Add Rooms to Group Summary', '', AddressPickerDialog, {
|
Modal.createTrackedDialog('Add Rooms to Group Summary', '', AddressPickerDialog, {
|
||||||
|
@ -122,9 +119,9 @@ const CategoryRoomList = createReactClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
const addButton = this.props.editing ?
|
const addButton = this.props.editing ?
|
||||||
(<AccessibleButton className="mx_GroupView_featuredThings_addButton"
|
(<AccessibleButton className="mx_GroupView_featuredThings_addButton"
|
||||||
|
@ -155,19 +152,17 @@ const CategoryRoomList = createReactClass({
|
||||||
{ roomNodes }
|
{ roomNodes }
|
||||||
{ addButton }
|
{ addButton }
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
const FeaturedRoom = createReactClass({
|
class FeaturedRoom extends React.Component {
|
||||||
displayName: 'FeaturedRoom',
|
static propTypes = {
|
||||||
|
|
||||||
props: {
|
|
||||||
summaryInfo: RoomSummaryType.isRequired,
|
summaryInfo: RoomSummaryType.isRequired,
|
||||||
editing: PropTypes.bool.isRequired,
|
editing: PropTypes.bool.isRequired,
|
||||||
groupId: PropTypes.string.isRequired,
|
groupId: PropTypes.string.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
onClick: function(e) {
|
onClick = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
@ -176,9 +171,9 @@ const FeaturedRoom = createReactClass({
|
||||||
room_alias: this.props.summaryInfo.profile.canonical_alias,
|
room_alias: this.props.summaryInfo.profile.canonical_alias,
|
||||||
room_id: this.props.summaryInfo.room_id,
|
room_id: this.props.summaryInfo.room_id,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onDeleteClicked: function(e) {
|
onDeleteClicked = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
GroupStore.removeRoomFromGroupSummary(
|
GroupStore.removeRoomFromGroupSummary(
|
||||||
|
@ -201,9 +196,9 @@ const FeaturedRoom = createReactClass({
|
||||||
description: _t("The room '%(roomName)s' could not be removed from the summary.", {roomName}),
|
description: _t("The room '%(roomName)s' could not be removed from the summary.", {roomName}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
|
const RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
|
||||||
|
|
||||||
const roomName = this.props.summaryInfo.profile.name ||
|
const roomName = this.props.summaryInfo.profile.name ||
|
||||||
|
@ -243,13 +238,11 @@ const FeaturedRoom = createReactClass({
|
||||||
<div className="mx_GroupView_featuredThing_name">{ roomNameNode }</div>
|
<div className="mx_GroupView_featuredThing_name">{ roomNameNode }</div>
|
||||||
{ deleteButton }
|
{ deleteButton }
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
const RoleUserList = createReactClass({
|
class RoleUserList extends React.Component {
|
||||||
displayName: 'RoleUserList',
|
static propTypes = {
|
||||||
|
|
||||||
props: {
|
|
||||||
users: PropTypes.arrayOf(UserSummaryType).isRequired,
|
users: PropTypes.arrayOf(UserSummaryType).isRequired,
|
||||||
role: PropTypes.shape({
|
role: PropTypes.shape({
|
||||||
profile: PropTypes.shape({
|
profile: PropTypes.shape({
|
||||||
|
@ -260,9 +253,9 @@ const RoleUserList = createReactClass({
|
||||||
|
|
||||||
// Whether the list should be editable
|
// Whether the list should be editable
|
||||||
editing: PropTypes.bool.isRequired,
|
editing: PropTypes.bool.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
onAddUsersClicked: function(ev) {
|
onAddUsersClicked = (ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
||||||
Modal.createTrackedDialog('Add Users to Group Summary', '', AddressPickerDialog, {
|
Modal.createTrackedDialog('Add Users to Group Summary', '', AddressPickerDialog, {
|
||||||
|
@ -298,9 +291,9 @@ const RoleUserList = createReactClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
const addButton = this.props.editing ?
|
const addButton = this.props.editing ?
|
||||||
(<AccessibleButton className="mx_GroupView_featuredThings_addButton" onClick={this.onAddUsersClicked}>
|
(<AccessibleButton className="mx_GroupView_featuredThings_addButton" onClick={this.onAddUsersClicked}>
|
||||||
|
@ -325,19 +318,17 @@ const RoleUserList = createReactClass({
|
||||||
{ userNodes }
|
{ userNodes }
|
||||||
{ addButton }
|
{ addButton }
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
const FeaturedUser = createReactClass({
|
class FeaturedUser extends React.Component {
|
||||||
displayName: 'FeaturedUser',
|
static propTypes = {
|
||||||
|
|
||||||
props: {
|
|
||||||
summaryInfo: UserSummaryType.isRequired,
|
summaryInfo: UserSummaryType.isRequired,
|
||||||
editing: PropTypes.bool.isRequired,
|
editing: PropTypes.bool.isRequired,
|
||||||
groupId: PropTypes.string.isRequired,
|
groupId: PropTypes.string.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
onClick: function(e) {
|
onClick = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
@ -345,9 +336,9 @@ const FeaturedUser = createReactClass({
|
||||||
action: 'view_start_chat_or_reuse',
|
action: 'view_start_chat_or_reuse',
|
||||||
user_id: this.props.summaryInfo.user_id,
|
user_id: this.props.summaryInfo.user_id,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onDeleteClicked: function(e) {
|
onDeleteClicked = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
GroupStore.removeUserFromGroupSummary(
|
GroupStore.removeUserFromGroupSummary(
|
||||||
|
@ -368,9 +359,9 @@ const FeaturedUser = createReactClass({
|
||||||
description: _t("The user '%(displayName)s' could not be removed from the summary.", {displayName}),
|
description: _t("The user '%(displayName)s' could not be removed from the summary.", {displayName}),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||||
const name = this.props.summaryInfo.displayname || this.props.summaryInfo.user_id;
|
const name = this.props.summaryInfo.displayname || this.props.summaryInfo.user_id;
|
||||||
|
|
||||||
|
@ -394,41 +385,37 @@ const FeaturedUser = createReactClass({
|
||||||
<div className="mx_GroupView_featuredThing_name">{ userNameNode }</div>
|
<div className="mx_GroupView_featuredThing_name">{ userNameNode }</div>
|
||||||
{ deleteButton }
|
{ deleteButton }
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
const GROUP_JOINPOLICY_OPEN = "open";
|
const GROUP_JOINPOLICY_OPEN = "open";
|
||||||
const GROUP_JOINPOLICY_INVITE = "invite";
|
const GROUP_JOINPOLICY_INVITE = "invite";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class GroupView extends React.Component {
|
||||||
displayName: 'GroupView',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
groupId: PropTypes.string.isRequired,
|
groupId: PropTypes.string.isRequired,
|
||||||
// Whether this is the first time the group admin is viewing the group
|
// Whether this is the first time the group admin is viewing the group
|
||||||
groupIsNew: PropTypes.bool,
|
groupIsNew: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
summary: null,
|
||||||
summary: null,
|
isGroupPublicised: null,
|
||||||
isGroupPublicised: null,
|
isUserPrivileged: null,
|
||||||
isUserPrivileged: null,
|
groupRooms: null,
|
||||||
groupRooms: null,
|
groupRoomsLoading: null,
|
||||||
groupRoomsLoading: null,
|
error: null,
|
||||||
error: null,
|
editing: false,
|
||||||
editing: false,
|
saving: false,
|
||||||
saving: false,
|
uploadingAvatar: false,
|
||||||
uploadingAvatar: false,
|
avatarChanged: false,
|
||||||
avatarChanged: false,
|
membershipBusy: false,
|
||||||
membershipBusy: false,
|
publicityBusy: false,
|
||||||
publicityBusy: false,
|
inviterProfile: null,
|
||||||
inviterProfile: null,
|
showRightPanel: RightPanelStore.getSharedInstance().isOpenForGroup,
|
||||||
showRightPanel: RightPanelStore.getSharedInstance().isOpenForGroup,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
this._matrixClient = MatrixClientPeg.get();
|
this._matrixClient = MatrixClientPeg.get();
|
||||||
this._matrixClient.on("Group.myMembership", this._onGroupMyMembership);
|
this._matrixClient.on("Group.myMembership", this._onGroupMyMembership);
|
||||||
|
@ -437,9 +424,9 @@ export default createReactClass({
|
||||||
|
|
||||||
this._dispatcherRef = dis.register(this._onAction);
|
this._dispatcherRef = dis.register(this._onAction);
|
||||||
this._rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this._onRightPanelStoreUpdate);
|
this._rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this._onRightPanelStoreUpdate);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
this._matrixClient.removeListener("Group.myMembership", this._onGroupMyMembership);
|
this._matrixClient.removeListener("Group.myMembership", this._onGroupMyMembership);
|
||||||
dis.unregister(this._dispatcherRef);
|
dis.unregister(this._dispatcherRef);
|
||||||
|
@ -448,10 +435,11 @@ export default createReactClass({
|
||||||
if (this._rightPanelStoreToken) {
|
if (this._rightPanelStoreToken) {
|
||||||
this._rightPanelStoreToken.remove();
|
this._rightPanelStoreToken.remove();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
UNSAFE_componentWillReceiveProps: function(newProps) {
|
// eslint-disable-next-line camelcase
|
||||||
|
UNSAFE_componentWillReceiveProps(newProps) {
|
||||||
if (this.props.groupId !== newProps.groupId) {
|
if (this.props.groupId !== newProps.groupId) {
|
||||||
this.setState({
|
this.setState({
|
||||||
summary: null,
|
summary: null,
|
||||||
|
@ -460,24 +448,24 @@ export default createReactClass({
|
||||||
this._initGroupStore(newProps.groupId);
|
this._initGroupStore(newProps.groupId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_onRightPanelStoreUpdate: function() {
|
_onRightPanelStoreUpdate = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showRightPanel: RightPanelStore.getSharedInstance().isOpenForGroup,
|
showRightPanel: RightPanelStore.getSharedInstance().isOpenForGroup,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onGroupMyMembership: function(group) {
|
_onGroupMyMembership = (group) => {
|
||||||
if (this._unmounted || group.groupId !== this.props.groupId) return;
|
if (this._unmounted || group.groupId !== this.props.groupId) return;
|
||||||
if (group.myMembership === 'leave') {
|
if (group.myMembership === 'leave') {
|
||||||
// Leave settings - the user might have clicked the "Leave" button
|
// Leave settings - the user might have clicked the "Leave" button
|
||||||
this._closeSettings();
|
this._closeSettings();
|
||||||
}
|
}
|
||||||
this.setState({membershipBusy: false});
|
this.setState({membershipBusy: false});
|
||||||
},
|
};
|
||||||
|
|
||||||
_initGroupStore: function(groupId, firstInit) {
|
_initGroupStore(groupId, firstInit) {
|
||||||
const group = this._matrixClient.getGroup(groupId);
|
const group = this._matrixClient.getGroup(groupId);
|
||||||
if (group && group.inviter && group.inviter.userId) {
|
if (group && group.inviter && group.inviter.userId) {
|
||||||
this._fetchInviterProfile(group.inviter.userId);
|
this._fetchInviterProfile(group.inviter.userId);
|
||||||
|
@ -506,9 +494,9 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onGroupStoreUpdated(firstInit) {
|
onGroupStoreUpdated = (firstInit) => {
|
||||||
if (this._unmounted) return;
|
if (this._unmounted) return;
|
||||||
const summary = GroupStore.getSummary(this.props.groupId);
|
const summary = GroupStore.getSummary(this.props.groupId);
|
||||||
if (summary.profile) {
|
if (summary.profile) {
|
||||||
|
@ -533,7 +521,7 @@ export default createReactClass({
|
||||||
if (this.props.groupIsNew && firstInit) {
|
if (this.props.groupIsNew && firstInit) {
|
||||||
this._onEditClick();
|
this._onEditClick();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_fetchInviterProfile(userId) {
|
_fetchInviterProfile(userId) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -555,9 +543,9 @@ export default createReactClass({
|
||||||
inviterProfileBusy: false,
|
inviterProfileBusy: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_onEditClick: function() {
|
_onEditClick = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
editing: true,
|
editing: true,
|
||||||
profileForm: Object.assign({}, this.state.summary.profile),
|
profileForm: Object.assign({}, this.state.summary.profile),
|
||||||
|
@ -568,20 +556,20 @@ export default createReactClass({
|
||||||
GROUP_JOINPOLICY_INVITE,
|
GROUP_JOINPOLICY_INVITE,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onShareClick: function() {
|
_onShareClick = () => {
|
||||||
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
||||||
Modal.createTrackedDialog('share community dialog', '', ShareDialog, {
|
Modal.createTrackedDialog('share community dialog', '', ShareDialog, {
|
||||||
target: this._matrixClient.getGroup(this.props.groupId) || new Group(this.props.groupId),
|
target: this._matrixClient.getGroup(this.props.groupId) || new Group(this.props.groupId),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onCancelClick: function() {
|
_onCancelClick = () => {
|
||||||
this._closeSettings();
|
this._closeSettings();
|
||||||
},
|
};
|
||||||
|
|
||||||
_onAction(payload) {
|
_onAction = (payload) => {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
// NOTE: close_settings is an app-wide dispatch; as it is dispatched from MatrixChat
|
// NOTE: close_settings is an app-wide dispatch; as it is dispatched from MatrixChat
|
||||||
case 'close_settings':
|
case 'close_settings':
|
||||||
|
@ -593,34 +581,34 @@ export default createReactClass({
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_closeSettings() {
|
_closeSettings = () => {
|
||||||
dis.dispatch({action: 'close_settings'});
|
dis.dispatch({action: 'close_settings'});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onNameChange: function(value) {
|
_onNameChange = (value) => {
|
||||||
const newProfileForm = Object.assign(this.state.profileForm, { name: value });
|
const newProfileForm = Object.assign(this.state.profileForm, { name: value });
|
||||||
this.setState({
|
this.setState({
|
||||||
profileForm: newProfileForm,
|
profileForm: newProfileForm,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onShortDescChange: function(value) {
|
_onShortDescChange = (value) => {
|
||||||
const newProfileForm = Object.assign(this.state.profileForm, { short_description: value });
|
const newProfileForm = Object.assign(this.state.profileForm, { short_description: value });
|
||||||
this.setState({
|
this.setState({
|
||||||
profileForm: newProfileForm,
|
profileForm: newProfileForm,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onLongDescChange: function(e) {
|
_onLongDescChange = (e) => {
|
||||||
const newProfileForm = Object.assign(this.state.profileForm, { long_description: e.target.value });
|
const newProfileForm = Object.assign(this.state.profileForm, { long_description: e.target.value });
|
||||||
this.setState({
|
this.setState({
|
||||||
profileForm: newProfileForm,
|
profileForm: newProfileForm,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onAvatarSelected: function(ev) {
|
_onAvatarSelected = ev => {
|
||||||
const file = ev.target.files[0];
|
const file = ev.target.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
|
||||||
|
@ -644,15 +632,15 @@ export default createReactClass({
|
||||||
description: _t('Failed to upload image'),
|
description: _t('Failed to upload image'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onJoinableChange: function(ev) {
|
_onJoinableChange = ev => {
|
||||||
this.setState({
|
this.setState({
|
||||||
joinableForm: { policyType: ev.target.value },
|
joinableForm: { policyType: ev.target.value },
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onSaveClick: function() {
|
_onSaveClick = () => {
|
||||||
this.setState({saving: true});
|
this.setState({saving: true});
|
||||||
const savePromise = this.state.isUserPrivileged ? this._saveGroup() : Promise.resolve();
|
const savePromise = this.state.isUserPrivileged ? this._saveGroup() : Promise.resolve();
|
||||||
savePromise.then((result) => {
|
savePromise.then((result) => {
|
||||||
|
@ -683,16 +671,16 @@ export default createReactClass({
|
||||||
avatarChanged: false,
|
avatarChanged: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_saveGroup: async function() {
|
async _saveGroup() {
|
||||||
await this._matrixClient.setGroupProfile(this.props.groupId, this.state.profileForm);
|
await this._matrixClient.setGroupProfile(this.props.groupId, this.state.profileForm);
|
||||||
await this._matrixClient.setGroupJoinPolicy(this.props.groupId, {
|
await this._matrixClient.setGroupJoinPolicy(this.props.groupId, {
|
||||||
type: this.state.joinableForm.policyType,
|
type: this.state.joinableForm.policyType,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_onAcceptInviteClick: async function() {
|
_onAcceptInviteClick = async () => {
|
||||||
this.setState({membershipBusy: true});
|
this.setState({membershipBusy: true});
|
||||||
|
|
||||||
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
|
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
|
||||||
|
@ -709,9 +697,9 @@ export default createReactClass({
|
||||||
description: _t("Unable to accept invite"),
|
description: _t("Unable to accept invite"),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onRejectInviteClick: async function() {
|
_onRejectInviteClick = async () => {
|
||||||
this.setState({membershipBusy: true});
|
this.setState({membershipBusy: true});
|
||||||
|
|
||||||
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
|
// Wait 500ms to prevent flashing. Do this before sending a request otherwise we risk the
|
||||||
|
@ -728,9 +716,9 @@ export default createReactClass({
|
||||||
description: _t("Unable to reject invite"),
|
description: _t("Unable to reject invite"),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onJoinClick: async function() {
|
_onJoinClick = async () => {
|
||||||
if (this._matrixClient.isGuest()) {
|
if (this._matrixClient.isGuest()) {
|
||||||
dis.dispatch({action: 'require_registration', screen_after: {screen: `group/${this.props.groupId}`}});
|
dis.dispatch({action: 'require_registration', screen_after: {screen: `group/${this.props.groupId}`}});
|
||||||
return;
|
return;
|
||||||
|
@ -752,9 +740,9 @@ export default createReactClass({
|
||||||
description: _t("Unable to join community"),
|
description: _t("Unable to join community"),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_leaveGroupWarnings: function() {
|
_leaveGroupWarnings() {
|
||||||
const warnings = [];
|
const warnings = [];
|
||||||
|
|
||||||
if (this.state.isUserPrivileged) {
|
if (this.state.isUserPrivileged) {
|
||||||
|
@ -768,10 +756,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return warnings;
|
return warnings;
|
||||||
},
|
}
|
||||||
|
|
||||||
|
_onLeaveClick = () => {
|
||||||
_onLeaveClick: function() {
|
|
||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
const warnings = this._leaveGroupWarnings();
|
const warnings = this._leaveGroupWarnings();
|
||||||
|
|
||||||
|
@ -806,13 +793,13 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onAddRoomsClick: function() {
|
_onAddRoomsClick = () => {
|
||||||
showGroupAddRoomDialog(this.props.groupId);
|
showGroupAddRoomDialog(this.props.groupId);
|
||||||
},
|
};
|
||||||
|
|
||||||
_getGroupSection: function() {
|
_getGroupSection() {
|
||||||
const groupSettingsSectionClasses = classnames({
|
const groupSettingsSectionClasses = classnames({
|
||||||
"mx_GroupView_group": this.state.editing,
|
"mx_GroupView_group": this.state.editing,
|
||||||
"mx_GroupView_group_disabled": this.state.editing && !this.state.isUserPrivileged,
|
"mx_GroupView_group_disabled": this.state.editing && !this.state.isUserPrivileged,
|
||||||
|
@ -856,9 +843,9 @@ export default createReactClass({
|
||||||
{ this._getLongDescriptionNode() }
|
{ this._getLongDescriptionNode() }
|
||||||
{ this._getRoomsNode() }
|
{ this._getRoomsNode() }
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getRoomsNode: function() {
|
_getRoomsNode() {
|
||||||
const RoomDetailList = sdk.getComponent('rooms.RoomDetailList');
|
const RoomDetailList = sdk.getComponent('rooms.RoomDetailList');
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||||
|
@ -902,9 +889,9 @@ export default createReactClass({
|
||||||
className={roomDetailListClassName} />
|
className={roomDetailListClassName} />
|
||||||
}
|
}
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getFeaturedRoomsNode: function() {
|
_getFeaturedRoomsNode() {
|
||||||
const summary = this.state.summary;
|
const summary = this.state.summary;
|
||||||
|
|
||||||
const defaultCategoryRooms = [];
|
const defaultCategoryRooms = [];
|
||||||
|
@ -943,9 +930,9 @@ export default createReactClass({
|
||||||
{ defaultCategoryNode }
|
{ defaultCategoryNode }
|
||||||
{ categoryRoomNodes }
|
{ categoryRoomNodes }
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getFeaturedUsersNode: function() {
|
_getFeaturedUsersNode() {
|
||||||
const summary = this.state.summary;
|
const summary = this.state.summary;
|
||||||
|
|
||||||
const noRoleUsers = [];
|
const noRoleUsers = [];
|
||||||
|
@ -984,9 +971,9 @@ export default createReactClass({
|
||||||
{ noRoleNode }
|
{ noRoleNode }
|
||||||
{ roleUserNodes }
|
{ roleUserNodes }
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getMembershipSection: function() {
|
_getMembershipSection() {
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||||
|
|
||||||
|
@ -1100,9 +1087,9 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getJoinableNode: function() {
|
_getJoinableNode() {
|
||||||
const InlineSpinner = sdk.getComponent('elements.InlineSpinner');
|
const InlineSpinner = sdk.getComponent('elements.InlineSpinner');
|
||||||
return this.state.editing ? <div>
|
return this.state.editing ? <div>
|
||||||
<h3>
|
<h3>
|
||||||
|
@ -1136,9 +1123,9 @@ export default createReactClass({
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div> : null;
|
</div> : null;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getLongDescriptionNode: function() {
|
_getLongDescriptionNode() {
|
||||||
const summary = this.state.summary;
|
const summary = this.state.summary;
|
||||||
let description = null;
|
let description = null;
|
||||||
if (summary.profile && summary.profile.long_description) {
|
if (summary.profile && summary.profile.long_description) {
|
||||||
|
@ -1175,9 +1162,9 @@ export default createReactClass({
|
||||||
<div className="mx_GroupView_groupDesc">
|
<div className="mx_GroupView_groupDesc">
|
||||||
{ description }
|
{ description }
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const GroupAvatar = sdk.getComponent("avatars.GroupAvatar");
|
const GroupAvatar = sdk.getComponent("avatars.GroupAvatar");
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
|
|
||||||
|
@ -1366,5 +1353,5 @@ export default createReactClass({
|
||||||
console.error("Invalid state for GroupView");
|
console.error("Invalid state for GroupView");
|
||||||
return <div />;
|
return <div />;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
|
|
||||||
import {InteractiveAuth} from "matrix-js-sdk";
|
import {InteractiveAuth} from "matrix-js-sdk";
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import getEntryComponentForLoginType from '../views/auth/InteractiveAuthEntryComponents';
|
import getEntryComponentForLoginType from '../views/auth/InteractiveAuthEntryComponents';
|
||||||
|
@ -26,10 +25,8 @@ import * as sdk from '../../index';
|
||||||
|
|
||||||
export const ERROR_USER_CANCELLED = new Error("User cancelled auth session");
|
export const ERROR_USER_CANCELLED = new Error("User cancelled auth session");
|
||||||
|
|
||||||
export default createReactClass({
|
export default class InteractiveAuthComponent extends React.Component {
|
||||||
displayName: 'InteractiveAuth',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// matrix client to use for UI auth requests
|
// matrix client to use for UI auth requests
|
||||||
matrixClient: PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
|
|
||||||
|
@ -86,20 +83,19 @@ export default createReactClass({
|
||||||
// continueText and continueKind are passed straight through to the AuthEntryComponent.
|
// continueText and continueKind are passed straight through to the AuthEntryComponent.
|
||||||
continueText: PropTypes.string,
|
continueText: PropTypes.string,
|
||||||
continueKind: PropTypes.string,
|
continueKind: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
authStage: null,
|
authStage: null,
|
||||||
busy: false,
|
busy: false,
|
||||||
errorText: null,
|
errorText: null,
|
||||||
stageErrorText: null,
|
stageErrorText: null,
|
||||||
submitButtonEnabled: false,
|
submitButtonEnabled: false,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
this._authLogic = new InteractiveAuth({
|
this._authLogic = new InteractiveAuth({
|
||||||
authData: this.props.authData,
|
authData: this.props.authData,
|
||||||
|
@ -141,17 +137,17 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this._stageComponent = createRef();
|
this._stageComponent = createRef();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
|
|
||||||
if (this._intervalId !== null) {
|
if (this._intervalId !== null) {
|
||||||
clearInterval(this._intervalId);
|
clearInterval(this._intervalId);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_requestEmailToken: async function(...args) {
|
_requestEmailToken = async (...args) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
});
|
});
|
||||||
|
@ -162,15 +158,15 @@ export default createReactClass({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
tryContinue: function() {
|
tryContinue = () => {
|
||||||
if (this._stageComponent.current && this._stageComponent.current.tryContinue) {
|
if (this._stageComponent.current && this._stageComponent.current.tryContinue) {
|
||||||
this._stageComponent.current.tryContinue();
|
this._stageComponent.current.tryContinue();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_authStateUpdated: function(stageType, stageState) {
|
_authStateUpdated = (stageType, stageState) => {
|
||||||
const oldStage = this.state.authStage;
|
const oldStage = this.state.authStage;
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: false,
|
busy: false,
|
||||||
|
@ -180,16 +176,16 @@ export default createReactClass({
|
||||||
}, () => {
|
}, () => {
|
||||||
if (oldStage != stageType) this._setFocus();
|
if (oldStage != stageType) this._setFocus();
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_requestCallback: function(auth) {
|
_requestCallback = (auth) => {
|
||||||
// This wrapper just exists because the js-sdk passes a second
|
// This wrapper just exists because the js-sdk passes a second
|
||||||
// 'busy' param for backwards compat. This throws the tests off
|
// 'busy' param for backwards compat. This throws the tests off
|
||||||
// so discard it here.
|
// so discard it here.
|
||||||
return this.props.makeRequest(auth);
|
return this.props.makeRequest(auth);
|
||||||
},
|
};
|
||||||
|
|
||||||
_onBusyChanged: function(busy) {
|
_onBusyChanged = (busy) => {
|
||||||
// if we've started doing stuff, reset the error messages
|
// if we've started doing stuff, reset the error messages
|
||||||
if (busy) {
|
if (busy) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -204,29 +200,29 @@ export default createReactClass({
|
||||||
// there's a new screen to show the user. This is implemented by setting
|
// there's a new screen to show the user. This is implemented by setting
|
||||||
// `busy: false` in `_authStateUpdated`.
|
// `busy: false` in `_authStateUpdated`.
|
||||||
// See also https://github.com/vector-im/element-web/issues/12546
|
// See also https://github.com/vector-im/element-web/issues/12546
|
||||||
},
|
};
|
||||||
|
|
||||||
_setFocus: function() {
|
_setFocus() {
|
||||||
if (this._stageComponent.current && this._stageComponent.current.focus) {
|
if (this._stageComponent.current && this._stageComponent.current.focus) {
|
||||||
this._stageComponent.current.focus();
|
this._stageComponent.current.focus();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_submitAuthDict: function(authData) {
|
_submitAuthDict = authData => {
|
||||||
this._authLogic.submitAuthDict(authData);
|
this._authLogic.submitAuthDict(authData);
|
||||||
},
|
};
|
||||||
|
|
||||||
_onPhaseChange: function(newPhase) {
|
_onPhaseChange = newPhase => {
|
||||||
if (this.props.onStagePhaseChange) {
|
if (this.props.onStagePhaseChange) {
|
||||||
this.props.onStagePhaseChange(this.state.authStage, newPhase || 0);
|
this.props.onStagePhaseChange(this.state.authStage, newPhase || 0);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_onStageCancel: function() {
|
_onStageCancel = () => {
|
||||||
this.props.onAuthFinished(false, ERROR_USER_CANCELLED);
|
this.props.onAuthFinished(false, ERROR_USER_CANCELLED);
|
||||||
},
|
};
|
||||||
|
|
||||||
_renderCurrentStage: function() {
|
_renderCurrentStage() {
|
||||||
const stage = this.state.authStage;
|
const stage = this.state.authStage;
|
||||||
if (!stage) {
|
if (!stage) {
|
||||||
if (this.state.busy) {
|
if (this.state.busy) {
|
||||||
|
@ -260,16 +256,17 @@ export default createReactClass({
|
||||||
onCancel={this._onStageCancel}
|
onCancel={this._onStageCancel}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
_onAuthStageFailed: function(e) {
|
_onAuthStageFailed = e => {
|
||||||
this.props.onAuthFinished(false, e);
|
this.props.onAuthFinished(false, e);
|
||||||
},
|
};
|
||||||
_setEmailSid: function(sid) {
|
|
||||||
this._authLogic.setEmailSid(sid);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
_setEmailSid = sid => {
|
||||||
|
this._authLogic.setEmailSid(sid);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
let error = null;
|
let error = null;
|
||||||
if (this.state.errorText) {
|
if (this.state.errorText) {
|
||||||
error = (
|
error = (
|
||||||
|
@ -287,5 +284,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../index';
|
import * as sdk from '../../index';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
import SdkConfig from '../../SdkConfig';
|
import SdkConfig from '../../SdkConfig';
|
||||||
|
@ -26,29 +25,23 @@ import AccessibleButton from '../views/elements/AccessibleButton';
|
||||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||||
import AutoHideScrollbar from "./AutoHideScrollbar";
|
import AutoHideScrollbar from "./AutoHideScrollbar";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class MyGroups extends React.Component {
|
||||||
displayName: 'MyGroups',
|
static contextType = MatrixClientContext;
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
groups: null,
|
||||||
groups: null,
|
error: null,
|
||||||
error: null,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
statics: {
|
componentDidMount() {
|
||||||
contextType: MatrixClientContext,
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this._fetch();
|
this._fetch();
|
||||||
},
|
}
|
||||||
|
|
||||||
_onCreateGroupClick: function() {
|
_onCreateGroupClick = () => {
|
||||||
dis.dispatch({action: 'view_create_group'});
|
dis.dispatch({action: 'view_create_group'});
|
||||||
},
|
};
|
||||||
|
|
||||||
_fetch: function() {
|
_fetch() {
|
||||||
this.context.getJoinedGroups().then((result) => {
|
this.context.getJoinedGroups().then((result) => {
|
||||||
this.setState({groups: result.groups, error: null});
|
this.setState({groups: result.groups, error: null});
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
|
@ -59,9 +52,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
this.setState({groups: null, error: err});
|
this.setState({groups: null, error: err});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
|
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
|
||||||
|
@ -149,5 +142,5 @@ export default createReactClass({
|
||||||
{ content }
|
{ content }
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
import {MatrixClientPeg} from "../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../MatrixClientPeg";
|
||||||
import * as sdk from "../../index";
|
import * as sdk from "../../index";
|
||||||
|
@ -25,13 +24,8 @@ import * as sdk from "../../index";
|
||||||
/*
|
/*
|
||||||
* Component which shows the global notification list using a TimelinePanel
|
* Component which shows the global notification list using a TimelinePanel
|
||||||
*/
|
*/
|
||||||
const NotificationPanel = createReactClass({
|
class NotificationPanel extends React.Component {
|
||||||
displayName: 'NotificationPanel',
|
render() {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
// wrap a TimelinePanel with the jump-to-event bits turned off.
|
// wrap a TimelinePanel with the jump-to-event bits turned off.
|
||||||
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
|
@ -45,7 +39,7 @@ const NotificationPanel = createReactClass({
|
||||||
if (timelineSet) {
|
if (timelineSet) {
|
||||||
return (
|
return (
|
||||||
<div className="mx_NotificationPanel" role="tabpanel">
|
<div className="mx_NotificationPanel" role="tabpanel">
|
||||||
<TimelinePanel key={"NotificationPanel_" + this.props.roomId}
|
<TimelinePanel
|
||||||
manageReadReceipts={false}
|
manageReadReceipts={false}
|
||||||
manageReadMarkers={false}
|
manageReadMarkers={false}
|
||||||
timelineSet={timelineSet}
|
timelineSet={timelineSet}
|
||||||
|
@ -63,7 +57,7 @@ const NotificationPanel = createReactClass({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export default NotificationPanel;
|
export default NotificationPanel;
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {MatrixClientPeg} from "../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../MatrixClientPeg";
|
||||||
import * as sdk from "../../index";
|
import * as sdk from "../../index";
|
||||||
import dis from "../../dispatcher/dispatcher";
|
import dis from "../../dispatcher/dispatcher";
|
||||||
|
@ -42,16 +41,16 @@ function track(action) {
|
||||||
Analytics.trackEvent('RoomDirectory', action);
|
Analytics.trackEvent('RoomDirectory', action);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomDirectory extends React.Component {
|
||||||
displayName: 'RoomDirectory',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
const selectedCommunityId = TagOrderStore.getSelectedTags()[0];
|
const selectedCommunityId = TagOrderStore.getSelectedTags()[0];
|
||||||
return {
|
this.state = {
|
||||||
publicRooms: [],
|
publicRooms: [],
|
||||||
loading: true,
|
loading: true,
|
||||||
protocolsLoading: true,
|
protocolsLoading: true,
|
||||||
|
@ -64,10 +63,7 @@ export default createReactClass({
|
||||||
: null,
|
: null,
|
||||||
communityName: null,
|
communityName: null,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Move this to constructor
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
this.nextBatch = null;
|
this.nextBatch = null;
|
||||||
this.filterTimeout = null;
|
this.filterTimeout = null;
|
||||||
|
@ -115,16 +111,16 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refreshRoomList();
|
this.refreshRoomList();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
if (this.filterTimeout) {
|
if (this.filterTimeout) {
|
||||||
clearTimeout(this.filterTimeout);
|
clearTimeout(this.filterTimeout);
|
||||||
}
|
}
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
refreshRoomList: function() {
|
refreshRoomList = () => {
|
||||||
if (this.state.selectedCommunityId) {
|
if (this.state.selectedCommunityId) {
|
||||||
this.setState({
|
this.setState({
|
||||||
publicRooms: GroupStore.getGroupRooms(this.state.selectedCommunityId).map(r => {
|
publicRooms: GroupStore.getGroupRooms(this.state.selectedCommunityId).map(r => {
|
||||||
|
@ -158,9 +154,9 @@ export default createReactClass({
|
||||||
loading: true,
|
loading: true,
|
||||||
});
|
});
|
||||||
this.getMoreRooms();
|
this.getMoreRooms();
|
||||||
},
|
};
|
||||||
|
|
||||||
getMoreRooms: function() {
|
getMoreRooms() {
|
||||||
if (this.state.selectedCommunityId) return Promise.resolve(); // no more rooms
|
if (this.state.selectedCommunityId) return Promise.resolve(); // no more rooms
|
||||||
if (!MatrixClientPeg.get()) return Promise.resolve();
|
if (!MatrixClientPeg.get()) return Promise.resolve();
|
||||||
|
|
||||||
|
@ -233,7 +229,7 @@ export default createReactClass({
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A limited interface for removing rooms from the directory.
|
* A limited interface for removing rooms from the directory.
|
||||||
|
@ -242,7 +238,7 @@ export default createReactClass({
|
||||||
* HS admins to do this through the RoomSettings interface, but
|
* HS admins to do this through the RoomSettings interface, but
|
||||||
* this needs SPEC-417.
|
* this needs SPEC-417.
|
||||||
*/
|
*/
|
||||||
removeFromDirectory: function(room) {
|
removeFromDirectory(room) {
|
||||||
const alias = get_display_alias_for_room(room);
|
const alias = get_display_alias_for_room(room);
|
||||||
const name = room.name || alias || _t('Unnamed room');
|
const name = room.name || alias || _t('Unnamed room');
|
||||||
|
|
||||||
|
@ -284,18 +280,18 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onRoomClicked: function(room, ev) {
|
onRoomClicked = (room, ev) => {
|
||||||
if (ev.shiftKey && !this.state.selectedCommunityId) {
|
if (ev.shiftKey && !this.state.selectedCommunityId) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.removeFromDirectory(room);
|
this.removeFromDirectory(room);
|
||||||
} else {
|
} else {
|
||||||
this.showRoom(room);
|
this.showRoom(room);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onOptionChange: function(server, instanceId) {
|
onOptionChange = (server, instanceId) => {
|
||||||
// clear next batch so we don't try to load more rooms
|
// clear next batch so we don't try to load more rooms
|
||||||
this.nextBatch = null;
|
this.nextBatch = null;
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -313,15 +309,15 @@ export default createReactClass({
|
||||||
// find the five gitter ones, at which point we do not want
|
// find the five gitter ones, at which point we do not want
|
||||||
// to render all those rooms when switching back to 'all networks'.
|
// to render all those rooms when switching back to 'all networks'.
|
||||||
// Easiest to just blow away the state & re-fetch.
|
// Easiest to just blow away the state & re-fetch.
|
||||||
},
|
};
|
||||||
|
|
||||||
onFillRequest: function(backwards) {
|
onFillRequest = (backwards) => {
|
||||||
if (backwards || !this.nextBatch) return Promise.resolve(false);
|
if (backwards || !this.nextBatch) return Promise.resolve(false);
|
||||||
|
|
||||||
return this.getMoreRooms();
|
return this.getMoreRooms();
|
||||||
},
|
};
|
||||||
|
|
||||||
onFilterChange: function(alias) {
|
onFilterChange = (alias) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
filterString: alias || null,
|
filterString: alias || null,
|
||||||
});
|
});
|
||||||
|
@ -337,9 +333,9 @@ export default createReactClass({
|
||||||
this.filterTimeout = null;
|
this.filterTimeout = null;
|
||||||
this.refreshRoomList();
|
this.refreshRoomList();
|
||||||
}, 700);
|
}, 700);
|
||||||
},
|
};
|
||||||
|
|
||||||
onFilterClear: function() {
|
onFilterClear = () => {
|
||||||
// update immediately
|
// update immediately
|
||||||
this.setState({
|
this.setState({
|
||||||
filterString: null,
|
filterString: null,
|
||||||
|
@ -348,9 +344,9 @@ export default createReactClass({
|
||||||
if (this.filterTimeout) {
|
if (this.filterTimeout) {
|
||||||
clearTimeout(this.filterTimeout);
|
clearTimeout(this.filterTimeout);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onJoinFromSearchClick: function(alias) {
|
onJoinFromSearchClick = (alias) => {
|
||||||
// If we don't have a particular instance id selected, just show that rooms alias
|
// If we don't have a particular instance id selected, just show that rooms alias
|
||||||
if (!this.state.instanceId || this.state.instanceId === ALL_ROOMS) {
|
if (!this.state.instanceId || this.state.instanceId === ALL_ROOMS) {
|
||||||
// If the user specified an alias without a domain, add on whichever server is selected
|
// If the user specified an alias without a domain, add on whichever server is selected
|
||||||
|
@ -391,9 +387,9 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onPreviewClick: function(ev, room) {
|
onPreviewClick = (ev, room) => {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
|
@ -401,9 +397,9 @@ export default createReactClass({
|
||||||
should_peek: true,
|
should_peek: true,
|
||||||
});
|
});
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
},
|
};
|
||||||
|
|
||||||
onViewClick: function(ev, room) {
|
onViewClick = (ev, room) => {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
|
@ -411,26 +407,26 @@ export default createReactClass({
|
||||||
should_peek: false,
|
should_peek: false,
|
||||||
});
|
});
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
},
|
};
|
||||||
|
|
||||||
onJoinClick: function(ev, room) {
|
onJoinClick = (ev, room) => {
|
||||||
this.showRoom(room, null, true);
|
this.showRoom(room, null, true);
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
},
|
};
|
||||||
|
|
||||||
onCreateRoomClick: function(room) {
|
onCreateRoomClick = room => {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_create_room',
|
action: 'view_create_room',
|
||||||
public: true,
|
public: true,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
showRoomAlias: function(alias, autoJoin=false) {
|
showRoomAlias(alias, autoJoin=false) {
|
||||||
this.showRoom(null, alias, autoJoin);
|
this.showRoom(null, alias, autoJoin);
|
||||||
},
|
}
|
||||||
|
|
||||||
showRoom: function(room, room_alias, autoJoin=false) {
|
showRoom(room, room_alias, autoJoin=false) {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
const payload = {
|
const payload = {
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
|
@ -474,7 +470,7 @@ export default createReactClass({
|
||||||
payload.room_id = room.room_id;
|
payload.room_id = room.room_id;
|
||||||
}
|
}
|
||||||
dis.dispatch(payload);
|
dis.dispatch(payload);
|
||||||
},
|
}
|
||||||
|
|
||||||
getRow(room) {
|
getRow(room) {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
|
@ -540,22 +536,22 @@ export default createReactClass({
|
||||||
<td className="mx_RoomDirectory_join">{joinOrViewButton}</td>
|
<td className="mx_RoomDirectory_join">{joinOrViewButton}</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
collectScrollPanel: function(element) {
|
collectScrollPanel = (element) => {
|
||||||
this.scrollPanel = element;
|
this.scrollPanel = element;
|
||||||
},
|
};
|
||||||
|
|
||||||
_stringLooksLikeId: function(s, field_type) {
|
_stringLooksLikeId(s, field_type) {
|
||||||
let pat = /^#[^\s]+:[^\s]/;
|
let pat = /^#[^\s]+:[^\s]/;
|
||||||
if (field_type && field_type.regexp) {
|
if (field_type && field_type.regexp) {
|
||||||
pat = new RegExp(field_type.regexp);
|
pat = new RegExp(field_type.regexp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pat.test(s);
|
return pat.test(s);
|
||||||
},
|
}
|
||||||
|
|
||||||
_getFieldsForThirdPartyLocation: function(userInput, protocol, instance) {
|
_getFieldsForThirdPartyLocation(userInput, protocol, instance) {
|
||||||
// make an object with the fields specified by that protocol. We
|
// make an object with the fields specified by that protocol. We
|
||||||
// require that the values of all but the last field come from the
|
// require that the values of all but the last field come from the
|
||||||
// instance. The last is the user input.
|
// instance. The last is the user input.
|
||||||
|
@ -569,20 +565,20 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
fields[requiredFields[requiredFields.length - 1]] = userInput;
|
fields[requiredFields[requiredFields.length - 1]] = userInput;
|
||||||
return fields;
|
return fields;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* called by the parent component when PageUp/Down/etc is pressed.
|
* called by the parent component when PageUp/Down/etc is pressed.
|
||||||
*
|
*
|
||||||
* We pass it down to the scroll panel.
|
* We pass it down to the scroll panel.
|
||||||
*/
|
*/
|
||||||
handleScrollKey: function(ev) {
|
handleScrollKey = ev => {
|
||||||
if (this.scrollPanel) {
|
if (this.scrollPanel) {
|
||||||
this.scrollPanel.handleScrollKey(ev);
|
this.scrollPanel.handleScrollKey(ev);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
@ -712,8 +708,8 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Similar to matrix-react-sdk's MatrixTools.getDisplayAliasForRoom
|
// Similar to matrix-react-sdk's MatrixTools.getDisplayAliasForRoom
|
||||||
// but works with the objects we get from the public room list
|
// but works with the objects we get from the public room list
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Matrix from 'matrix-js-sdk';
|
import Matrix from 'matrix-js-sdk';
|
||||||
import { _t, _td } from '../../languageHandler';
|
import { _t, _td } from '../../languageHandler';
|
||||||
|
@ -39,10 +38,8 @@ function getUnsentMessages(room) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomStatusBar extends React.Component {
|
||||||
displayName: 'RoomStatusBar',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// the room this statusbar is representing.
|
// the room this statusbar is representing.
|
||||||
room: PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
// This is true when the user is alone in the room, but has also sent a message.
|
// This is true when the user is alone in the room, but has also sent a message.
|
||||||
|
@ -86,37 +83,35 @@ export default createReactClass({
|
||||||
// callback for when the status bar is displaying something and should
|
// callback for when the status bar is displaying something and should
|
||||||
// be visible
|
// be visible
|
||||||
onVisible: PropTypes.func,
|
onVisible: PropTypes.func,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
syncState: MatrixClientPeg.get().getSyncState(),
|
||||||
syncState: MatrixClientPeg.get().getSyncState(),
|
syncStateData: MatrixClientPeg.get().getSyncStateData(),
|
||||||
syncStateData: MatrixClientPeg.get().getSyncStateData(),
|
unsentMessages: getUnsentMessages(this.props.room),
|
||||||
unsentMessages: getUnsentMessages(this.props.room),
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
MatrixClientPeg.get().on("sync", this.onSyncStateChange);
|
MatrixClientPeg.get().on("sync", this.onSyncStateChange);
|
||||||
MatrixClientPeg.get().on("Room.localEchoUpdated", this._onRoomLocalEchoUpdated);
|
MatrixClientPeg.get().on("Room.localEchoUpdated", this._onRoomLocalEchoUpdated);
|
||||||
|
|
||||||
this._checkSize();
|
this._checkSize();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate() {
|
||||||
this._checkSize();
|
this._checkSize();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
|
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
if (client) {
|
if (client) {
|
||||||
client.removeListener("sync", this.onSyncStateChange);
|
client.removeListener("sync", this.onSyncStateChange);
|
||||||
client.removeListener("Room.localEchoUpdated", this._onRoomLocalEchoUpdated);
|
client.removeListener("Room.localEchoUpdated", this._onRoomLocalEchoUpdated);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onSyncStateChange: function(state, prevState, data) {
|
onSyncStateChange = (state, prevState, data) => {
|
||||||
if (state === "SYNCING" && prevState === "SYNCING") {
|
if (state === "SYNCING" && prevState === "SYNCING") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -124,39 +119,39 @@ export default createReactClass({
|
||||||
syncState: state,
|
syncState: state,
|
||||||
syncStateData: data,
|
syncStateData: data,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onResendAllClick: function() {
|
_onResendAllClick = () => {
|
||||||
Resend.resendUnsentEvents(this.props.room);
|
Resend.resendUnsentEvents(this.props.room);
|
||||||
dis.fire(Action.FocusComposer);
|
dis.fire(Action.FocusComposer);
|
||||||
},
|
};
|
||||||
|
|
||||||
_onCancelAllClick: function() {
|
_onCancelAllClick = () => {
|
||||||
Resend.cancelUnsentEvents(this.props.room);
|
Resend.cancelUnsentEvents(this.props.room);
|
||||||
dis.fire(Action.FocusComposer);
|
dis.fire(Action.FocusComposer);
|
||||||
},
|
};
|
||||||
|
|
||||||
_onRoomLocalEchoUpdated: function(event, room, oldEventId, oldStatus) {
|
_onRoomLocalEchoUpdated = (event, room, oldEventId, oldStatus) => {
|
||||||
if (room.roomId !== this.props.room.roomId) return;
|
if (room.roomId !== this.props.room.roomId) return;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
unsentMessages: getUnsentMessages(this.props.room),
|
unsentMessages: getUnsentMessages(this.props.room),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
// Check whether current size is greater than 0, if yes call props.onVisible
|
// Check whether current size is greater than 0, if yes call props.onVisible
|
||||||
_checkSize: function() {
|
_checkSize() {
|
||||||
if (this._getSize()) {
|
if (this._getSize()) {
|
||||||
if (this.props.onVisible) this.props.onVisible();
|
if (this.props.onVisible) this.props.onVisible();
|
||||||
} else {
|
} else {
|
||||||
if (this.props.onHidden) this.props.onHidden();
|
if (this.props.onHidden) this.props.onHidden();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// We don't need the actual height - just whether it is likely to have
|
// We don't need the actual height - just whether it is likely to have
|
||||||
// changed - so we use '0' to indicate normal size, and other values to
|
// changed - so we use '0' to indicate normal size, and other values to
|
||||||
// indicate other sizes.
|
// indicate other sizes.
|
||||||
_getSize: function() {
|
_getSize() {
|
||||||
if (this._shouldShowConnectionError() ||
|
if (this._shouldShowConnectionError() ||
|
||||||
this.props.hasActiveCall ||
|
this.props.hasActiveCall ||
|
||||||
this.props.sentMessageAndIsAlone
|
this.props.sentMessageAndIsAlone
|
||||||
|
@ -166,10 +161,10 @@ export default createReactClass({
|
||||||
return STATUS_BAR_EXPANDED_LARGE;
|
return STATUS_BAR_EXPANDED_LARGE;
|
||||||
}
|
}
|
||||||
return STATUS_BAR_HIDDEN;
|
return STATUS_BAR_HIDDEN;
|
||||||
},
|
}
|
||||||
|
|
||||||
// return suitable content for the image on the left of the status bar.
|
// return suitable content for the image on the left of the status bar.
|
||||||
_getIndicator: function() {
|
_getIndicator() {
|
||||||
if (this.props.hasActiveCall) {
|
if (this.props.hasActiveCall) {
|
||||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
return (
|
return (
|
||||||
|
@ -182,9 +177,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
}
|
||||||
|
|
||||||
_shouldShowConnectionError: function() {
|
_shouldShowConnectionError() {
|
||||||
// no conn bar trumps the "some not sent" msg since you can't resend without
|
// no conn bar trumps the "some not sent" msg since you can't resend without
|
||||||
// a connection!
|
// a connection!
|
||||||
// There's one situation in which we don't show this 'no connection' bar, and that's
|
// There's one situation in which we don't show this 'no connection' bar, and that's
|
||||||
|
@ -195,9 +190,9 @@ export default createReactClass({
|
||||||
this.state.syncStateData.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED',
|
this.state.syncStateData.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED',
|
||||||
);
|
);
|
||||||
return this.state.syncState === "ERROR" && !errorIsMauError;
|
return this.state.syncState === "ERROR" && !errorIsMauError;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getUnsentMessageContent: function() {
|
_getUnsentMessageContent() {
|
||||||
const unsentMessages = this.state.unsentMessages;
|
const unsentMessages = this.state.unsentMessages;
|
||||||
if (!unsentMessages.length) return null;
|
if (!unsentMessages.length) return null;
|
||||||
|
|
||||||
|
@ -272,10 +267,10 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
// return suitable content for the main (text) part of the status bar.
|
// return suitable content for the main (text) part of the status bar.
|
||||||
_getContent: function() {
|
_getContent() {
|
||||||
if (this._shouldShowConnectionError()) {
|
if (this._shouldShowConnectionError()) {
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomStatusBar_connectionLostBar">
|
<div className="mx_RoomStatusBar_connectionLostBar">
|
||||||
|
@ -323,9 +318,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const content = this._getContent();
|
const content = this._getContent();
|
||||||
const indicator = this._getIndicator();
|
const indicator = this._getIndicator();
|
||||||
|
|
||||||
|
@ -339,5 +334,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ limitations under the License.
|
||||||
import shouldHideEvent from '../../shouldHideEvent';
|
import shouldHideEvent from '../../shouldHideEvent';
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
|
@ -68,9 +67,8 @@ if (DEBUG) {
|
||||||
debuglog = console.log.bind(console);
|
debuglog = console.log.bind(console);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomView extends React.Component {
|
||||||
displayName: 'RoomView',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
ConferenceHandler: PropTypes.any,
|
ConferenceHandler: PropTypes.any,
|
||||||
|
|
||||||
// Called with the credentials of a registered user (if they were a ROU that
|
// Called with the credentials of a registered user (if they were a ROU that
|
||||||
|
@ -97,15 +95,15 @@ export default createReactClass({
|
||||||
|
|
||||||
// Servers the RoomView can use to try and assist joins
|
// Servers the RoomView can use to try and assist joins
|
||||||
viaServers: PropTypes.arrayOf(PropTypes.string),
|
viaServers: PropTypes.arrayOf(PropTypes.string),
|
||||||
},
|
};
|
||||||
|
|
||||||
statics: {
|
static contextType = MatrixClientContext;
|
||||||
contextType: MatrixClientContext,
|
|
||||||
},
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
const llMembers = this.context.hasLazyLoadMembersEnabled();
|
const llMembers = this.context.hasLazyLoadMembersEnabled();
|
||||||
return {
|
this.state = {
|
||||||
room: null,
|
room: null,
|
||||||
roomId: null,
|
roomId: null,
|
||||||
roomLoading: true,
|
roomLoading: true,
|
||||||
|
@ -171,10 +169,7 @@ export default createReactClass({
|
||||||
|
|
||||||
matrixClientIsReady: this.context && this.context.isInitialSyncComplete(),
|
matrixClientIsReady: this.context && this.context.isInitialSyncComplete(),
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
this.context.on("Room", this.onRoom);
|
this.context.on("Room", this.onRoom);
|
||||||
this.context.on("Room.timeline", this.onRoomTimeline);
|
this.context.on("Room.timeline", this.onRoomTimeline);
|
||||||
|
@ -201,15 +196,15 @@ export default createReactClass({
|
||||||
this._searchResultsPanel = createRef();
|
this._searchResultsPanel = createRef();
|
||||||
|
|
||||||
this._layoutWatcherRef = SettingsStore.watchSetting("useIRCLayout", null, this.onLayoutChange);
|
this._layoutWatcherRef = SettingsStore.watchSetting("useIRCLayout", null, this.onLayoutChange);
|
||||||
},
|
}
|
||||||
|
|
||||||
_onReadReceiptsChange: function() {
|
_onReadReceiptsChange = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showReadReceipts: SettingsStore.getValue("showReadReceipts", this.state.roomId),
|
showReadReceipts: SettingsStore.getValue("showReadReceipts", this.state.roomId),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onRoomViewStoreUpdate: function(initial) {
|
_onRoomViewStoreUpdate = initial => {
|
||||||
if (this.unmounted) {
|
if (this.unmounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -303,7 +298,7 @@ export default createReactClass({
|
||||||
if (initial) {
|
if (initial) {
|
||||||
this._setupRoom(newState.room, newState.roomId, newState.joining, newState.shouldPeek);
|
this._setupRoom(newState.room, newState.roomId, newState.joining, newState.shouldPeek);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_getRoomId() {
|
_getRoomId() {
|
||||||
// According to `_onRoomViewStoreUpdate`, `state.roomId` can be null
|
// According to `_onRoomViewStoreUpdate`, `state.roomId` can be null
|
||||||
|
@ -312,9 +307,9 @@ export default createReactClass({
|
||||||
// the bare room ID. (We may want to update `state.roomId` after
|
// the bare room ID. (We may want to update `state.roomId` after
|
||||||
// resolving aliases, so we could always trust it.)
|
// resolving aliases, so we could always trust it.)
|
||||||
return this.state.room ? this.state.room.roomId : this.state.roomId;
|
return this.state.room ? this.state.room.roomId : this.state.roomId;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getPermalinkCreatorForRoom: function(room) {
|
_getPermalinkCreatorForRoom(room) {
|
||||||
if (!this._permalinkCreators) this._permalinkCreators = {};
|
if (!this._permalinkCreators) this._permalinkCreators = {};
|
||||||
if (this._permalinkCreators[room.roomId]) return this._permalinkCreators[room.roomId];
|
if (this._permalinkCreators[room.roomId]) return this._permalinkCreators[room.roomId];
|
||||||
|
|
||||||
|
@ -327,22 +322,22 @@ export default createReactClass({
|
||||||
this._permalinkCreators[room.roomId].load();
|
this._permalinkCreators[room.roomId].load();
|
||||||
}
|
}
|
||||||
return this._permalinkCreators[room.roomId];
|
return this._permalinkCreators[room.roomId];
|
||||||
},
|
}
|
||||||
|
|
||||||
_stopAllPermalinkCreators: function() {
|
_stopAllPermalinkCreators() {
|
||||||
if (!this._permalinkCreators) return;
|
if (!this._permalinkCreators) return;
|
||||||
for (const roomId of Object.keys(this._permalinkCreators)) {
|
for (const roomId of Object.keys(this._permalinkCreators)) {
|
||||||
this._permalinkCreators[roomId].stop();
|
this._permalinkCreators[roomId].stop();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_onWidgetEchoStoreUpdate: function() {
|
_onWidgetEchoStoreUpdate = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showApps: this._shouldShowApps(this.state.room),
|
showApps: this._shouldShowApps(this.state.room),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_setupRoom: function(room, roomId, joining, shouldPeek) {
|
_setupRoom(room, roomId, joining, shouldPeek) {
|
||||||
// if this is an unknown room then we're in one of three states:
|
// if this is an unknown room then we're in one of three states:
|
||||||
// - This is a room we can peek into (search engine) (we can /peek)
|
// - This is a room we can peek into (search engine) (we can /peek)
|
||||||
// - This is a room we can publicly join or were invited to. (we can /join)
|
// - This is a room we can publicly join or were invited to. (we can /join)
|
||||||
|
@ -404,9 +399,9 @@ export default createReactClass({
|
||||||
this.setState({isPeeking: false});
|
this.setState({isPeeking: false});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_shouldShowApps: function(room) {
|
_shouldShowApps(room) {
|
||||||
if (!BROWSER_SUPPORTS_SANDBOX) return false;
|
if (!BROWSER_SUPPORTS_SANDBOX) return false;
|
||||||
|
|
||||||
// Check if user has previously chosen to hide the app drawer for this
|
// Check if user has previously chosen to hide the app drawer for this
|
||||||
|
@ -417,9 +412,9 @@ export default createReactClass({
|
||||||
// This is confusing, but it means to say that we default to the tray being
|
// This is confusing, but it means to say that we default to the tray being
|
||||||
// hidden unless the user clicked to open it.
|
// hidden unless the user clicked to open it.
|
||||||
return hideWidgetDrawer === "false";
|
return hideWidgetDrawer === "false";
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
const call = this._getCallForRoom();
|
const call = this._getCallForRoom();
|
||||||
const callState = call ? call.call_state : "ended";
|
const callState = call ? call.call_state : "ended";
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -435,14 +430,14 @@ export default createReactClass({
|
||||||
this.onResize();
|
this.onResize();
|
||||||
|
|
||||||
document.addEventListener("keydown", this.onNativeKeyDown);
|
document.addEventListener("keydown", this.onNativeKeyDown);
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldComponentUpdate: function(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
return (!ObjectUtils.shallowEqual(this.props, nextProps) ||
|
return (!ObjectUtils.shallowEqual(this.props, nextProps) ||
|
||||||
!ObjectUtils.shallowEqual(this.state, nextState));
|
!ObjectUtils.shallowEqual(this.state, nextState));
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate() {
|
||||||
if (this._roomView.current) {
|
if (this._roomView.current) {
|
||||||
const roomView = this._roomView.current;
|
const roomView = this._roomView.current;
|
||||||
if (!roomView.ondrop) {
|
if (!roomView.ondrop) {
|
||||||
|
@ -464,9 +459,9 @@ export default createReactClass({
|
||||||
atEndOfLiveTimeline: this._messagePanel.isAtEndOfLiveTimeline(),
|
atEndOfLiveTimeline: this._messagePanel.isAtEndOfLiveTimeline(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
// set a boolean to say we've been unmounted, which any pending
|
// set a boolean to say we've been unmounted, which any pending
|
||||||
// promises can use to throw away their results.
|
// promises can use to throw away their results.
|
||||||
//
|
//
|
||||||
|
@ -543,21 +538,21 @@ export default createReactClass({
|
||||||
// Tinter.tint(); // reset colourscheme
|
// Tinter.tint(); // reset colourscheme
|
||||||
|
|
||||||
SettingsStore.unwatchSetting(this._layoutWatcherRef);
|
SettingsStore.unwatchSetting(this._layoutWatcherRef);
|
||||||
},
|
}
|
||||||
|
|
||||||
onLayoutChange: function() {
|
onLayoutChange = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
useIRCLayout: SettingsStore.getValue("useIRCLayout"),
|
useIRCLayout: SettingsStore.getValue("useIRCLayout"),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onRightPanelStoreUpdate: function() {
|
_onRightPanelStoreUpdate = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
|
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onPageUnload(event) {
|
onPageUnload = event => {
|
||||||
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
|
if (ContentMessages.sharedInstance().getCurrentUploads().length > 0) {
|
||||||
return event.returnValue =
|
return event.returnValue =
|
||||||
_t("You seem to be uploading files, are you sure you want to quit?");
|
_t("You seem to be uploading files, are you sure you want to quit?");
|
||||||
|
@ -565,10 +560,10 @@ export default createReactClass({
|
||||||
return event.returnValue =
|
return event.returnValue =
|
||||||
_t("You seem to be in a call, are you sure you want to quit?");
|
_t("You seem to be in a call, are you sure you want to quit?");
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
// we register global shortcuts here, they *must not conflict* with local shortcuts elsewhere or both will fire
|
// we register global shortcuts here, they *must not conflict* with local shortcuts elsewhere or both will fire
|
||||||
onNativeKeyDown: function(ev) {
|
onNativeKeyDown = ev => {
|
||||||
let handled = false;
|
let handled = false;
|
||||||
const ctrlCmdOnly = isOnlyCtrlOrCmdKeyEvent(ev);
|
const ctrlCmdOnly = isOnlyCtrlOrCmdKeyEvent(ev);
|
||||||
|
|
||||||
|
@ -592,9 +587,9 @@ export default createReactClass({
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onReactKeyDown: function(ev) {
|
onReactKeyDown = ev => {
|
||||||
let handled = false;
|
let handled = false;
|
||||||
|
|
||||||
switch (ev.key) {
|
switch (ev.key) {
|
||||||
|
@ -613,7 +608,7 @@ export default createReactClass({
|
||||||
break;
|
break;
|
||||||
case Key.U.toUpperCase():
|
case Key.U.toUpperCase():
|
||||||
if (isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) && ev.shiftKey) {
|
if (isOnlyCtrlOrCmdIgnoreShiftKeyEvent(ev) && ev.shiftKey) {
|
||||||
dis.dispatch({ action: "upload_file" })
|
dis.dispatch({ action: "upload_file" });
|
||||||
handled = true;
|
handled = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -623,9 +618,9 @@ export default createReactClass({
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onAction: function(payload) {
|
onAction = payload => {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'message_send_failed':
|
case 'message_send_failed':
|
||||||
case 'message_sent':
|
case 'message_sent':
|
||||||
|
@ -709,9 +704,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomTimeline: function(ev, room, toStartOfTimeline, removed, data) {
|
onRoomTimeline = (ev, room, toStartOfTimeline, removed, data) => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
// ignore events for other rooms
|
// ignore events for other rooms
|
||||||
|
@ -747,51 +742,51 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomName: function(room) {
|
onRoomName = room => {
|
||||||
if (this.state.room && room.roomId == this.state.room.roomId) {
|
if (this.state.room && room.roomId == this.state.room.roomId) {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomRecoveryReminderDontAskAgain: function() {
|
onRoomRecoveryReminderDontAskAgain = () => {
|
||||||
// Called when the option to not ask again is set:
|
// Called when the option to not ask again is set:
|
||||||
// force an update to hide the recovery reminder
|
// force an update to hide the recovery reminder
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
onKeyBackupStatus() {
|
onKeyBackupStatus = () => {
|
||||||
// Key backup status changes affect whether the in-room recovery
|
// Key backup status changes affect whether the in-room recovery
|
||||||
// reminder is displayed.
|
// reminder is displayed.
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
canResetTimeline: function() {
|
canResetTimeline = () => {
|
||||||
if (!this._messagePanel) {
|
if (!this._messagePanel) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return this._messagePanel.canResetTimeline();
|
return this._messagePanel.canResetTimeline();
|
||||||
},
|
};
|
||||||
|
|
||||||
// called when state.room is first initialised (either at initial load,
|
// called when state.room is first initialised (either at initial load,
|
||||||
// after a successful peek, or after we join the room).
|
// after a successful peek, or after we join the room).
|
||||||
_onRoomLoaded: function(room) {
|
_onRoomLoaded = room => {
|
||||||
this._calculatePeekRules(room);
|
this._calculatePeekRules(room);
|
||||||
this._updatePreviewUrlVisibility(room);
|
this._updatePreviewUrlVisibility(room);
|
||||||
this._loadMembersIfJoined(room);
|
this._loadMembersIfJoined(room);
|
||||||
this._calculateRecommendedVersion(room);
|
this._calculateRecommendedVersion(room);
|
||||||
this._updateE2EStatus(room);
|
this._updateE2EStatus(room);
|
||||||
this._updatePermissions(room);
|
this._updatePermissions(room);
|
||||||
},
|
};
|
||||||
|
|
||||||
_calculateRecommendedVersion: async function(room) {
|
async _calculateRecommendedVersion(room) {
|
||||||
this.setState({
|
this.setState({
|
||||||
upgradeRecommendation: await room.getRecommendedVersion(),
|
upgradeRecommendation: await room.getRecommendedVersion(),
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_loadMembersIfJoined: async function(room) {
|
async _loadMembersIfJoined(room) {
|
||||||
// lazy load members if enabled
|
// lazy load members if enabled
|
||||||
if (this.context.hasLazyLoadMembersEnabled()) {
|
if (this.context.hasLazyLoadMembersEnabled()) {
|
||||||
if (room && room.getMyMembership() === 'join') {
|
if (room && room.getMyMembership() === 'join') {
|
||||||
|
@ -808,9 +803,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_calculatePeekRules: function(room) {
|
_calculatePeekRules(room) {
|
||||||
const guestAccessEvent = room.currentState.getStateEvents("m.room.guest_access", "");
|
const guestAccessEvent = room.currentState.getStateEvents("m.room.guest_access", "");
|
||||||
if (guestAccessEvent && guestAccessEvent.getContent().guest_access === "can_join") {
|
if (guestAccessEvent && guestAccessEvent.getContent().guest_access === "can_join") {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -824,17 +819,17 @@ export default createReactClass({
|
||||||
canPeek: true,
|
canPeek: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_updatePreviewUrlVisibility: function({roomId}) {
|
_updatePreviewUrlVisibility({roomId}) {
|
||||||
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
|
// URL Previews in E2EE rooms can be a privacy leak so use a different setting which is per-room explicit
|
||||||
const key = this.context.isRoomEncrypted(roomId) ? 'urlPreviewsEnabled_e2ee' : 'urlPreviewsEnabled';
|
const key = this.context.isRoomEncrypted(roomId) ? 'urlPreviewsEnabled_e2ee' : 'urlPreviewsEnabled';
|
||||||
this.setState({
|
this.setState({
|
||||||
showUrlPreview: SettingsStore.getValue(key, roomId),
|
showUrlPreview: SettingsStore.getValue(key, roomId),
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onRoom: function(room) {
|
onRoom = room => {
|
||||||
if (!room || room.roomId !== this.state.roomId) {
|
if (!room || room.roomId !== this.state.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -843,32 +838,32 @@ export default createReactClass({
|
||||||
}, () => {
|
}, () => {
|
||||||
this._onRoomLoaded(room);
|
this._onRoomLoaded(room);
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onDeviceVerificationChanged: function(userId, device) {
|
onDeviceVerificationChanged = (userId, device) => {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (!room.currentState.getMember(userId)) {
|
if (!room.currentState.getMember(userId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._updateE2EStatus(room);
|
this._updateE2EStatus(room);
|
||||||
},
|
};
|
||||||
|
|
||||||
onUserVerificationChanged: function(userId, _trustStatus) {
|
onUserVerificationChanged = (userId, _trustStatus) => {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (!room || !room.currentState.getMember(userId)) {
|
if (!room || !room.currentState.getMember(userId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._updateE2EStatus(room);
|
this._updateE2EStatus(room);
|
||||||
},
|
};
|
||||||
|
|
||||||
onCrossSigningKeysChanged: function() {
|
onCrossSigningKeysChanged = () => {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (room) {
|
if (room) {
|
||||||
this._updateE2EStatus(room);
|
this._updateE2EStatus(room);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_updateE2EStatus: async function(room) {
|
async _updateE2EStatus(room) {
|
||||||
if (!this.context.isRoomEncrypted(room.roomId)) {
|
if (!this.context.isRoomEncrypted(room.roomId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -886,26 +881,26 @@ export default createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
e2eStatus: await shieldStatusForRoom(this.context, room),
|
e2eStatus: await shieldStatusForRoom(this.context, room),
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
updateTint: function() {
|
updateTint() {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (!room) return;
|
if (!room) return;
|
||||||
|
|
||||||
console.log("Tinter.tint from updateTint");
|
console.log("Tinter.tint from updateTint");
|
||||||
const colorScheme = SettingsStore.getValue("roomColor", room.roomId);
|
const colorScheme = SettingsStore.getValue("roomColor", room.roomId);
|
||||||
Tinter.tint(colorScheme.primary_color, colorScheme.secondary_color);
|
Tinter.tint(colorScheme.primary_color, colorScheme.secondary_color);
|
||||||
},
|
}
|
||||||
|
|
||||||
onAccountData: function(event) {
|
onAccountData = event => {
|
||||||
const type = event.getType();
|
const type = event.getType();
|
||||||
if ((type === "org.matrix.preview_urls" || type === "im.vector.web.settings") && this.state.room) {
|
if ((type === "org.matrix.preview_urls" || type === "im.vector.web.settings") && this.state.room) {
|
||||||
// non-e2ee url previews are stored in legacy event type `org.matrix.room.preview_urls`
|
// non-e2ee url previews are stored in legacy event type `org.matrix.room.preview_urls`
|
||||||
this._updatePreviewUrlVisibility(this.state.room);
|
this._updatePreviewUrlVisibility(this.state.room);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomAccountData: function(event, room) {
|
onRoomAccountData = (event, room) => {
|
||||||
if (room.roomId == this.state.roomId) {
|
if (room.roomId == this.state.roomId) {
|
||||||
const type = event.getType();
|
const type = event.getType();
|
||||||
if (type === "org.matrix.room.color_scheme") {
|
if (type === "org.matrix.room.color_scheme") {
|
||||||
|
@ -918,18 +913,18 @@ export default createReactClass({
|
||||||
this._updatePreviewUrlVisibility(room);
|
this._updatePreviewUrlVisibility(room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomStateEvents: function(ev, state) {
|
onRoomStateEvents = (ev, state) => {
|
||||||
// ignore if we don't have a room yet
|
// ignore if we don't have a room yet
|
||||||
if (!this.state.room || this.state.room.roomId !== state.roomId) {
|
if (!this.state.room || this.state.room.roomId !== state.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updatePermissions(this.state.room);
|
this._updatePermissions(this.state.room);
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomStateMember: function(ev, state, member) {
|
onRoomStateMember = (ev, state, member) => {
|
||||||
// ignore if we don't have a room yet
|
// ignore if we don't have a room yet
|
||||||
if (!this.state.room) {
|
if (!this.state.room) {
|
||||||
return;
|
return;
|
||||||
|
@ -941,17 +936,17 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateRoomMembers(member);
|
this._updateRoomMembers(member);
|
||||||
},
|
};
|
||||||
|
|
||||||
onMyMembership: function(room, membership, oldMembership) {
|
onMyMembership = (room, membership, oldMembership) => {
|
||||||
if (room.roomId === this.state.roomId) {
|
if (room.roomId === this.state.roomId) {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
this._loadMembersIfJoined(room);
|
this._loadMembersIfJoined(room);
|
||||||
this._updatePermissions(room);
|
this._updatePermissions(room);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_updatePermissions: function(room) {
|
_updatePermissions(room) {
|
||||||
if (room) {
|
if (room) {
|
||||||
const me = this.context.getUserId();
|
const me = this.context.getUserId();
|
||||||
const canReact = room.getMyMembership() === "join" && room.currentState.maySendEvent("m.reaction", me);
|
const canReact = room.getMyMembership() === "join" && room.currentState.maySendEvent("m.reaction", me);
|
||||||
|
@ -959,11 +954,11 @@ export default createReactClass({
|
||||||
|
|
||||||
this.setState({canReact, canReply});
|
this.setState({canReact, canReply});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// rate limited because a power level change will emit an event for every
|
// rate limited because a power level change will emit an event for every
|
||||||
// member in the room.
|
// member in the room.
|
||||||
_updateRoomMembers: rate_limited_func(function(dueToMember) {
|
_updateRoomMembers = rate_limited_func((dueToMember) => {
|
||||||
// a member state changed in this room
|
// a member state changed in this room
|
||||||
// refresh the conf call notification state
|
// refresh the conf call notification state
|
||||||
this._updateConfCallNotification();
|
this._updateConfCallNotification();
|
||||||
|
@ -978,9 +973,9 @@ export default createReactClass({
|
||||||
this._checkIfAlone(this.state.room, memberCountInfluence);
|
this._checkIfAlone(this.state.room, memberCountInfluence);
|
||||||
|
|
||||||
this._updateE2EStatus(this.state.room);
|
this._updateE2EStatus(this.state.room);
|
||||||
}, 500),
|
}, 500);
|
||||||
|
|
||||||
_checkIfAlone: function(room, countInfluence) {
|
_checkIfAlone(room, countInfluence) {
|
||||||
let warnedAboutLonelyRoom = false;
|
let warnedAboutLonelyRoom = false;
|
||||||
if (localStorage) {
|
if (localStorage) {
|
||||||
warnedAboutLonelyRoom = localStorage.getItem('mx_user_alone_warned_' + this.state.room.roomId);
|
warnedAboutLonelyRoom = localStorage.getItem('mx_user_alone_warned_' + this.state.room.roomId);
|
||||||
|
@ -993,9 +988,9 @@ export default createReactClass({
|
||||||
let joinedOrInvitedMemberCount = room.getJoinedMemberCount() + room.getInvitedMemberCount();
|
let joinedOrInvitedMemberCount = room.getJoinedMemberCount() + room.getInvitedMemberCount();
|
||||||
if (countInfluence) joinedOrInvitedMemberCount += countInfluence;
|
if (countInfluence) joinedOrInvitedMemberCount += countInfluence;
|
||||||
this.setState({isAlone: joinedOrInvitedMemberCount === 1});
|
this.setState({isAlone: joinedOrInvitedMemberCount === 1});
|
||||||
},
|
}
|
||||||
|
|
||||||
_updateConfCallNotification: function() {
|
_updateConfCallNotification() {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
if (!room || !this.props.ConferenceHandler) {
|
if (!room || !this.props.ConferenceHandler) {
|
||||||
return;
|
return;
|
||||||
|
@ -1017,7 +1012,7 @@ export default createReactClass({
|
||||||
confMember.membership === "join"
|
confMember.membership === "join"
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_updateDMState() {
|
_updateDMState() {
|
||||||
const room = this.state.room;
|
const room = this.state.room;
|
||||||
|
@ -1028,9 +1023,9 @@ export default createReactClass({
|
||||||
if (dmInviter) {
|
if (dmInviter) {
|
||||||
Rooms.setDMRoom(room.roomId, dmInviter);
|
Rooms.setDMRoom(room.roomId, dmInviter);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onSearchResultsFillRequest: function(backwards) {
|
onSearchResultsFillRequest = backwards => {
|
||||||
if (!backwards) {
|
if (!backwards) {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
@ -1043,25 +1038,25 @@ export default createReactClass({
|
||||||
debuglog("no more search results");
|
debuglog("no more search results");
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onInviteButtonClick: function() {
|
onInviteButtonClick = () => {
|
||||||
// call AddressPickerDialog
|
// call AddressPickerDialog
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_invite',
|
action: 'view_invite',
|
||||||
roomId: this.state.room.roomId,
|
roomId: this.state.room.roomId,
|
||||||
});
|
});
|
||||||
this.setState({isAlone: false}); // there's a good chance they'll invite someone
|
this.setState({isAlone: false}); // there's a good chance they'll invite someone
|
||||||
},
|
};
|
||||||
|
|
||||||
onStopAloneWarningClick: function() {
|
onStopAloneWarningClick = () => {
|
||||||
if (localStorage) {
|
if (localStorage) {
|
||||||
localStorage.setItem('mx_user_alone_warned_' + this.state.room.roomId, true);
|
localStorage.setItem('mx_user_alone_warned_' + this.state.room.roomId, true);
|
||||||
}
|
}
|
||||||
this.setState({isAlone: false});
|
this.setState({isAlone: false});
|
||||||
},
|
};
|
||||||
|
|
||||||
onJoinButtonClicked: function(ev) {
|
onJoinButtonClicked = ev => {
|
||||||
// If the user is a ROU, allow them to transition to a PWLU
|
// If the user is a ROU, allow them to transition to a PWLU
|
||||||
if (this.context && this.context.isGuest()) {
|
if (this.context && this.context.isGuest()) {
|
||||||
// Join this room once the user has registered and logged in
|
// Join this room once the user has registered and logged in
|
||||||
|
@ -1120,10 +1115,9 @@ export default createReactClass({
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
},
|
onMessageListScroll = ev => {
|
||||||
|
|
||||||
onMessageListScroll: function(ev) {
|
|
||||||
if (this._messagePanel.isAtEndOfLiveTimeline()) {
|
if (this._messagePanel.isAtEndOfLiveTimeline()) {
|
||||||
this.setState({
|
this.setState({
|
||||||
numUnreadMessages: 0,
|
numUnreadMessages: 0,
|
||||||
|
@ -1135,9 +1129,9 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._updateTopUnreadMessagesBar();
|
this._updateTopUnreadMessagesBar();
|
||||||
},
|
};
|
||||||
|
|
||||||
onDragOver: function(ev) {
|
onDragOver = ev => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
|
@ -1154,9 +1148,9 @@ export default createReactClass({
|
||||||
ev.dataTransfer.dropEffect = 'copy';
|
ev.dataTransfer.dropEffect = 'copy';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onDrop: function(ev) {
|
onDrop = ev => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ContentMessages.sharedInstance().sendContentListToRoom(
|
ContentMessages.sharedInstance().sendContentListToRoom(
|
||||||
|
@ -1164,15 +1158,15 @@ export default createReactClass({
|
||||||
);
|
);
|
||||||
this.setState({ draggingFile: false });
|
this.setState({ draggingFile: false });
|
||||||
dis.fire(Action.FocusComposer);
|
dis.fire(Action.FocusComposer);
|
||||||
},
|
};
|
||||||
|
|
||||||
onDragLeaveOrEnd: function(ev) {
|
onDragLeaveOrEnd = ev => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
this.setState({ draggingFile: false });
|
this.setState({ draggingFile: false });
|
||||||
},
|
};
|
||||||
|
|
||||||
injectSticker: function(url, info, text) {
|
injectSticker(url, info, text) {
|
||||||
if (this.context.isGuest()) {
|
if (this.context.isGuest()) {
|
||||||
dis.dispatch({action: 'require_registration'});
|
dis.dispatch({action: 'require_registration'});
|
||||||
return;
|
return;
|
||||||
|
@ -1185,9 +1179,9 @@ export default createReactClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onSearch: function(term, scope) {
|
onSearch = (term, scope) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
searchTerm: term,
|
searchTerm: term,
|
||||||
searchScope: scope,
|
searchScope: scope,
|
||||||
|
@ -1213,9 +1207,9 @@ export default createReactClass({
|
||||||
debuglog("sending search request");
|
debuglog("sending search request");
|
||||||
const searchPromise = eventSearch(term, roomId);
|
const searchPromise = eventSearch(term, roomId);
|
||||||
this._handleSearchResult(searchPromise);
|
this._handleSearchResult(searchPromise);
|
||||||
},
|
};
|
||||||
|
|
||||||
_handleSearchResult: function(searchPromise) {
|
_handleSearchResult(searchPromise) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
// keep a record of the current search id, so that if the search terms
|
// keep a record of the current search id, so that if the search terms
|
||||||
|
@ -1266,9 +1260,9 @@ export default createReactClass({
|
||||||
searchInProgress: false,
|
searchInProgress: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getSearchResultTiles: function() {
|
getSearchResultTiles() {
|
||||||
const SearchResultTile = sdk.getComponent('rooms.SearchResultTile');
|
const SearchResultTile = sdk.getComponent('rooms.SearchResultTile');
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
|
|
||||||
|
@ -1348,20 +1342,20 @@ export default createReactClass({
|
||||||
onHeightChanged={onHeightChanged} />);
|
onHeightChanged={onHeightChanged} />);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
},
|
}
|
||||||
|
|
||||||
onPinnedClick: function() {
|
onPinnedClick = () => {
|
||||||
const nowShowingPinned = !this.state.showingPinned;
|
const nowShowingPinned = !this.state.showingPinned;
|
||||||
const roomId = this.state.room.roomId;
|
const roomId = this.state.room.roomId;
|
||||||
this.setState({showingPinned: nowShowingPinned, searching: false});
|
this.setState({showingPinned: nowShowingPinned, searching: false});
|
||||||
SettingsStore.setValue("PinnedEvents.isOpen", roomId, SettingLevel.ROOM_DEVICE, nowShowingPinned);
|
SettingsStore.setValue("PinnedEvents.isOpen", roomId, SettingLevel.ROOM_DEVICE, nowShowingPinned);
|
||||||
},
|
};
|
||||||
|
|
||||||
onSettingsClick: function() {
|
onSettingsClick = () => {
|
||||||
dis.dispatch({ action: 'open_room_settings' });
|
dis.dispatch({ action: 'open_room_settings' });
|
||||||
},
|
};
|
||||||
|
|
||||||
onCancelClick: function() {
|
onCancelClick = () => {
|
||||||
console.log("updateTint from onCancelClick");
|
console.log("updateTint from onCancelClick");
|
||||||
this.updateTint();
|
this.updateTint();
|
||||||
if (this.state.forwardingEvent) {
|
if (this.state.forwardingEvent) {
|
||||||
|
@ -1371,23 +1365,23 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
dis.fire(Action.FocusComposer);
|
dis.fire(Action.FocusComposer);
|
||||||
},
|
};
|
||||||
|
|
||||||
onLeaveClick: function() {
|
onLeaveClick = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'leave_room',
|
action: 'leave_room',
|
||||||
room_id: this.state.room.roomId,
|
room_id: this.state.room.roomId,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onForgetClick: function() {
|
onForgetClick = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'forget_room',
|
action: 'forget_room',
|
||||||
room_id: this.state.room.roomId,
|
room_id: this.state.room.roomId,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onRejectButtonClicked: function(ev) {
|
onRejectButtonClicked = ev => {
|
||||||
const self = this;
|
const self = this;
|
||||||
this.setState({
|
this.setState({
|
||||||
rejecting: true,
|
rejecting: true,
|
||||||
|
@ -1412,9 +1406,9 @@ export default createReactClass({
|
||||||
rejectError: error,
|
rejectError: error,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onRejectAndIgnoreClick: async function() {
|
onRejectAndIgnoreClick = async () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
rejecting: true,
|
rejecting: true,
|
||||||
});
|
});
|
||||||
|
@ -1446,49 +1440,49 @@ export default createReactClass({
|
||||||
rejectError: error,
|
rejectError: error,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRejectThreepidInviteButtonClicked: function(ev) {
|
onRejectThreepidInviteButtonClicked = ev => {
|
||||||
// We can reject 3pid invites in the same way that we accept them,
|
// We can reject 3pid invites in the same way that we accept them,
|
||||||
// using /leave rather than /join. In the short term though, we
|
// using /leave rather than /join. In the short term though, we
|
||||||
// just ignore them.
|
// just ignore them.
|
||||||
// https://github.com/vector-im/vector-web/issues/1134
|
// https://github.com/vector-im/vector-web/issues/1134
|
||||||
dis.fire(Action.ViewRoomDirectory);
|
dis.fire(Action.ViewRoomDirectory);
|
||||||
},
|
};
|
||||||
|
|
||||||
onSearchClick: function() {
|
onSearchClick = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
searching: !this.state.searching,
|
searching: !this.state.searching,
|
||||||
showingPinned: false,
|
showingPinned: false,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onCancelSearchClick: function() {
|
onCancelSearchClick = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
searching: false,
|
searching: false,
|
||||||
searchResults: null,
|
searchResults: null,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
// jump down to the bottom of this room, where new events are arriving
|
// jump down to the bottom of this room, where new events are arriving
|
||||||
jumpToLiveTimeline: function() {
|
jumpToLiveTimeline = () => {
|
||||||
this._messagePanel.jumpToLiveTimeline();
|
this._messagePanel.jumpToLiveTimeline();
|
||||||
dis.fire(Action.FocusComposer);
|
dis.fire(Action.FocusComposer);
|
||||||
},
|
};
|
||||||
|
|
||||||
// jump up to wherever our read marker is
|
// jump up to wherever our read marker is
|
||||||
jumpToReadMarker: function() {
|
jumpToReadMarker = () => {
|
||||||
this._messagePanel.jumpToReadMarker();
|
this._messagePanel.jumpToReadMarker();
|
||||||
},
|
};
|
||||||
|
|
||||||
// update the read marker to match the read-receipt
|
// update the read marker to match the read-receipt
|
||||||
forgetReadMarker: function(ev) {
|
forgetReadMarker = ev => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this._messagePanel.forgetReadMarker();
|
this._messagePanel.forgetReadMarker();
|
||||||
},
|
};
|
||||||
|
|
||||||
// decide whether or not the top 'unread messages' bar should be shown
|
// decide whether or not the top 'unread messages' bar should be shown
|
||||||
_updateTopUnreadMessagesBar: function() {
|
_updateTopUnreadMessagesBar = () => {
|
||||||
if (!this._messagePanel) {
|
if (!this._messagePanel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1497,12 +1491,12 @@ export default createReactClass({
|
||||||
if (this.state.showTopUnreadMessagesBar != showBar) {
|
if (this.state.showTopUnreadMessagesBar != showBar) {
|
||||||
this.setState({showTopUnreadMessagesBar: showBar});
|
this.setState({showTopUnreadMessagesBar: showBar});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
// get the current scroll position of the room, so that it can be
|
// get the current scroll position of the room, so that it can be
|
||||||
// restored when we switch back to it.
|
// restored when we switch back to it.
|
||||||
//
|
//
|
||||||
_getScrollState: function() {
|
_getScrollState() {
|
||||||
const messagePanel = this._messagePanel;
|
const messagePanel = this._messagePanel;
|
||||||
if (!messagePanel) return null;
|
if (!messagePanel) return null;
|
||||||
|
|
||||||
|
@ -1537,9 +1531,9 @@ export default createReactClass({
|
||||||
focussedEvent: scrollState.trackedScrollToken,
|
focussedEvent: scrollState.trackedScrollToken,
|
||||||
pixelOffset: scrollState.pixelOffset,
|
pixelOffset: scrollState.pixelOffset,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
onResize: function() {
|
onResize = () => {
|
||||||
// It seems flexbox doesn't give us a way to constrain the auxPanel height to have
|
// It seems flexbox doesn't give us a way to constrain the auxPanel height to have
|
||||||
// a minimum of the height of the video element, whilst also capping it from pushing out the page
|
// a minimum of the height of the video element, whilst also capping it from pushing out the page
|
||||||
// so we have to do it via JS instead. In this implementation we cap the height by putting
|
// so we have to do it via JS instead. In this implementation we cap the height by putting
|
||||||
|
@ -1557,16 +1551,16 @@ export default createReactClass({
|
||||||
if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50;
|
if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50;
|
||||||
|
|
||||||
this.setState({auxPanelMaxHeight: auxPanelMaxHeight});
|
this.setState({auxPanelMaxHeight: auxPanelMaxHeight});
|
||||||
},
|
};
|
||||||
|
|
||||||
onFullscreenClick: function() {
|
onFullscreenClick = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'video_fullscreen',
|
action: 'video_fullscreen',
|
||||||
fullscreen: true,
|
fullscreen: true,
|
||||||
}, true);
|
}, true);
|
||||||
},
|
};
|
||||||
|
|
||||||
onMuteAudioClick: function() {
|
onMuteAudioClick = () => {
|
||||||
const call = this._getCallForRoom();
|
const call = this._getCallForRoom();
|
||||||
if (!call) {
|
if (!call) {
|
||||||
return;
|
return;
|
||||||
|
@ -1574,9 +1568,9 @@ export default createReactClass({
|
||||||
const newState = !call.isMicrophoneMuted();
|
const newState = !call.isMicrophoneMuted();
|
||||||
call.setMicrophoneMuted(newState);
|
call.setMicrophoneMuted(newState);
|
||||||
this.forceUpdate(); // TODO: just update the voip buttons
|
this.forceUpdate(); // TODO: just update the voip buttons
|
||||||
},
|
};
|
||||||
|
|
||||||
onMuteVideoClick: function() {
|
onMuteVideoClick = () => {
|
||||||
const call = this._getCallForRoom();
|
const call = this._getCallForRoom();
|
||||||
if (!call) {
|
if (!call) {
|
||||||
return;
|
return;
|
||||||
|
@ -1584,29 +1578,29 @@ export default createReactClass({
|
||||||
const newState = !call.isLocalVideoMuted();
|
const newState = !call.isLocalVideoMuted();
|
||||||
call.setLocalVideoMuted(newState);
|
call.setLocalVideoMuted(newState);
|
||||||
this.forceUpdate(); // TODO: just update the voip buttons
|
this.forceUpdate(); // TODO: just update the voip buttons
|
||||||
},
|
};
|
||||||
|
|
||||||
onStatusBarVisible: function() {
|
onStatusBarVisible = () => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
this.setState({
|
this.setState({
|
||||||
statusBarVisible: true,
|
statusBarVisible: true,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onStatusBarHidden: function() {
|
onStatusBarHidden = () => {
|
||||||
// This is currently not desired as it is annoying if it keeps expanding and collapsing
|
// This is currently not desired as it is annoying if it keeps expanding and collapsing
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
this.setState({
|
this.setState({
|
||||||
statusBarVisible: false,
|
statusBarVisible: false,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* called by the parent component when PageUp/Down/etc is pressed.
|
* called by the parent component when PageUp/Down/etc is pressed.
|
||||||
*
|
*
|
||||||
* We pass it down to the scroll panel.
|
* We pass it down to the scroll panel.
|
||||||
*/
|
*/
|
||||||
handleScrollKey: function(ev) {
|
handleScrollKey = ev => {
|
||||||
let panel;
|
let panel;
|
||||||
if (this._searchResultsPanel.current) {
|
if (this._searchResultsPanel.current) {
|
||||||
panel = this._searchResultsPanel.current;
|
panel = this._searchResultsPanel.current;
|
||||||
|
@ -1617,48 +1611,48 @@ export default createReactClass({
|
||||||
if (panel) {
|
if (panel) {
|
||||||
panel.handleScrollKey(ev);
|
panel.handleScrollKey(ev);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get any current call for this room
|
* get any current call for this room
|
||||||
*/
|
*/
|
||||||
_getCallForRoom: function() {
|
_getCallForRoom() {
|
||||||
if (!this.state.room) {
|
if (!this.state.room) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return CallHandler.getCallForRoom(this.state.room.roomId);
|
return CallHandler.getCallForRoom(this.state.room.roomId);
|
||||||
},
|
}
|
||||||
|
|
||||||
// this has to be a proper method rather than an unnamed function,
|
// this has to be a proper method rather than an unnamed function,
|
||||||
// otherwise react calls it with null on each update.
|
// otherwise react calls it with null on each update.
|
||||||
_gatherTimelinePanelRef: function(r) {
|
_gatherTimelinePanelRef = r => {
|
||||||
this._messagePanel = r;
|
this._messagePanel = r;
|
||||||
if (r) {
|
if (r) {
|
||||||
console.log("updateTint from RoomView._gatherTimelinePanelRef");
|
console.log("updateTint from RoomView._gatherTimelinePanelRef");
|
||||||
this.updateTint();
|
this.updateTint();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_getOldRoom: function() {
|
_getOldRoom() {
|
||||||
const createEvent = this.state.room.currentState.getStateEvents("m.room.create", "");
|
const createEvent = this.state.room.currentState.getStateEvents("m.room.create", "");
|
||||||
if (!createEvent || !createEvent.getContent()['predecessor']) return null;
|
if (!createEvent || !createEvent.getContent()['predecessor']) return null;
|
||||||
|
|
||||||
return this.context.getRoom(createEvent.getContent()['predecessor']['room_id']);
|
return this.context.getRoom(createEvent.getContent()['predecessor']['room_id']);
|
||||||
},
|
}
|
||||||
|
|
||||||
_getHiddenHighlightCount: function() {
|
_getHiddenHighlightCount() {
|
||||||
const oldRoom = this._getOldRoom();
|
const oldRoom = this._getOldRoom();
|
||||||
if (!oldRoom) return 0;
|
if (!oldRoom) return 0;
|
||||||
return oldRoom.getUnreadNotificationCount('highlight');
|
return oldRoom.getUnreadNotificationCount('highlight');
|
||||||
},
|
}
|
||||||
|
|
||||||
_onHiddenHighlightsClick: function() {
|
_onHiddenHighlightsClick = () => {
|
||||||
const oldRoom = this._getOldRoom();
|
const oldRoom = this._getOldRoom();
|
||||||
if (!oldRoom) return;
|
if (!oldRoom) return;
|
||||||
dis.dispatch({action: "view_room", room_id: oldRoom.roomId});
|
dis.dispatch({action: "view_room", room_id: oldRoom.roomId});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const RoomHeader = sdk.getComponent('rooms.RoomHeader');
|
const RoomHeader = sdk.getComponent('rooms.RoomHeader');
|
||||||
const ForwardMessage = sdk.getComponent("rooms.ForwardMessage");
|
const ForwardMessage = sdk.getComponent("rooms.ForwardMessage");
|
||||||
const AuxPanel = sdk.getComponent("rooms.AuxPanel");
|
const AuxPanel = sdk.getComponent("rooms.AuxPanel");
|
||||||
|
@ -2118,5 +2112,5 @@ export default createReactClass({
|
||||||
</main>
|
</main>
|
||||||
</RoomContext.Provider>
|
</RoomContext.Provider>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {createRef} from "react";
|
import React, {createRef} from "react";
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Key } from '../../Keyboard';
|
import { Key } from '../../Keyboard';
|
||||||
import Timer from '../../utils/Timer';
|
import Timer from '../../utils/Timer';
|
||||||
|
@ -84,10 +83,8 @@ if (DEBUG_SCROLL) {
|
||||||
* offset as normal.
|
* offset as normal.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default createReactClass({
|
export default class ScrollPanel extends React.Component {
|
||||||
displayName: 'ScrollPanel',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
/* stickyBottom: if set to true, then once the user hits the bottom of
|
/* stickyBottom: if set to true, then once the user hits the bottom of
|
||||||
* the list, any new children added to the list will cause the list to
|
* the list, any new children added to the list will cause the list to
|
||||||
* scroll down to show the new element, rather than preserving the
|
* scroll down to show the new element, rather than preserving the
|
||||||
|
@ -149,20 +146,19 @@ export default createReactClass({
|
||||||
* of the wrapper
|
* of the wrapper
|
||||||
*/
|
*/
|
||||||
fixedChildren: PropTypes.node,
|
fixedChildren: PropTypes.node,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
stickyBottom: true,
|
||||||
stickyBottom: true,
|
startAtBottom: true,
|
||||||
startAtBottom: true,
|
onFillRequest: function(backwards) { return Promise.resolve(false); },
|
||||||
onFillRequest: function(backwards) { return Promise.resolve(false); },
|
onUnfillRequest: function(backwards, scrollToken) {},
|
||||||
onUnfillRequest: function(backwards, scrollToken) {},
|
onScroll: function() {},
|
||||||
onScroll: function() {},
|
};
|
||||||
};
|
|
||||||
},
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._pendingFillRequests = {b: null, f: null};
|
this._pendingFillRequests = {b: null, f: null};
|
||||||
|
|
||||||
if (this.props.resizeNotifier) {
|
if (this.props.resizeNotifier) {
|
||||||
|
@ -172,13 +168,13 @@ export default createReactClass({
|
||||||
this.resetScrollState();
|
this.resetScrollState();
|
||||||
|
|
||||||
this._itemlist = createRef();
|
this._itemlist = createRef();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this.checkScroll();
|
this.checkScroll();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate() {
|
||||||
// after adding event tiles, we may need to tweak the scroll (either to
|
// after adding event tiles, we may need to tweak the scroll (either to
|
||||||
// keep at the bottom of the timeline, or to maintain the view after
|
// keep at the bottom of the timeline, or to maintain the view after
|
||||||
// adding events to the top).
|
// adding events to the top).
|
||||||
|
@ -186,9 +182,9 @@ export default createReactClass({
|
||||||
// This will also re-check the fill state, in case the paginate was inadequate
|
// This will also re-check the fill state, in case the paginate was inadequate
|
||||||
this.checkScroll();
|
this.checkScroll();
|
||||||
this.updatePreventShrinking();
|
this.updatePreventShrinking();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
// set a boolean to say we've been unmounted, which any pending
|
// set a boolean to say we've been unmounted, which any pending
|
||||||
// promises can use to throw away their results.
|
// promises can use to throw away their results.
|
||||||
//
|
//
|
||||||
|
@ -198,41 +194,41 @@ export default createReactClass({
|
||||||
if (this.props.resizeNotifier) {
|
if (this.props.resizeNotifier) {
|
||||||
this.props.resizeNotifier.removeListener("middlePanelResized", this.onResize);
|
this.props.resizeNotifier.removeListener("middlePanelResized", this.onResize);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onScroll: function(ev) {
|
onScroll = ev => {
|
||||||
debuglog("onScroll", this._getScrollNode().scrollTop);
|
debuglog("onScroll", this._getScrollNode().scrollTop);
|
||||||
this._scrollTimeout.restart();
|
this._scrollTimeout.restart();
|
||||||
this._saveScrollState();
|
this._saveScrollState();
|
||||||
this.updatePreventShrinking();
|
this.updatePreventShrinking();
|
||||||
this.props.onScroll(ev);
|
this.props.onScroll(ev);
|
||||||
this.checkFillState();
|
this.checkFillState();
|
||||||
},
|
};
|
||||||
|
|
||||||
onResize: function() {
|
onResize = () => {
|
||||||
this.checkScroll();
|
this.checkScroll();
|
||||||
// update preventShrinkingState if present
|
// update preventShrinkingState if present
|
||||||
if (this.preventShrinkingState) {
|
if (this.preventShrinkingState) {
|
||||||
this.preventShrinking();
|
this.preventShrinking();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
// after an update to the contents of the panel, check that the scroll is
|
// after an update to the contents of the panel, check that the scroll is
|
||||||
// where it ought to be, and set off pagination requests if necessary.
|
// where it ought to be, and set off pagination requests if necessary.
|
||||||
checkScroll: function() {
|
checkScroll = () => {
|
||||||
if (this.unmounted) {
|
if (this.unmounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._restoreSavedScrollState();
|
this._restoreSavedScrollState();
|
||||||
this.checkFillState();
|
this.checkFillState();
|
||||||
},
|
};
|
||||||
|
|
||||||
// return true if the content is fully scrolled down right now; else false.
|
// return true if the content is fully scrolled down right now; else false.
|
||||||
//
|
//
|
||||||
// note that this is independent of the 'stuckAtBottom' state - it is simply
|
// note that this is independent of the 'stuckAtBottom' state - it is simply
|
||||||
// about whether the content is scrolled down right now, irrespective of
|
// about whether the content is scrolled down right now, irrespective of
|
||||||
// whether it will stay that way when the children update.
|
// whether it will stay that way when the children update.
|
||||||
isAtBottom: function() {
|
isAtBottom = () => {
|
||||||
const sn = this._getScrollNode();
|
const sn = this._getScrollNode();
|
||||||
// fractional values (both too big and too small)
|
// fractional values (both too big and too small)
|
||||||
// for scrollTop happen on certain browsers/platforms
|
// for scrollTop happen on certain browsers/platforms
|
||||||
|
@ -240,7 +236,7 @@ export default createReactClass({
|
||||||
// so check difference <= 1;
|
// so check difference <= 1;
|
||||||
return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) <= 1;
|
return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) <= 1;
|
||||||
|
|
||||||
},
|
};
|
||||||
|
|
||||||
// returns the vertical height in the given direction that can be removed from
|
// returns the vertical height in the given direction that can be removed from
|
||||||
// the content box (which has a height of scrollHeight, see checkFillState) without
|
// the content box (which has a height of scrollHeight, see checkFillState) without
|
||||||
|
@ -273,7 +269,7 @@ export default createReactClass({
|
||||||
// |#########| - |
|
// |#########| - |
|
||||||
// |#########| |
|
// |#########| |
|
||||||
// `---------' -
|
// `---------' -
|
||||||
_getExcessHeight: function(backwards) {
|
_getExcessHeight(backwards) {
|
||||||
const sn = this._getScrollNode();
|
const sn = this._getScrollNode();
|
||||||
const contentHeight = this._getMessagesHeight();
|
const contentHeight = this._getMessagesHeight();
|
||||||
const listHeight = this._getListHeight();
|
const listHeight = this._getListHeight();
|
||||||
|
@ -285,10 +281,10 @@ export default createReactClass({
|
||||||
} else {
|
} else {
|
||||||
return contentHeight - (unclippedScrollTop + 2*sn.clientHeight) - UNPAGINATION_PADDING;
|
return contentHeight - (unclippedScrollTop + 2*sn.clientHeight) - UNPAGINATION_PADDING;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// check the scroll state and send out backfill requests if necessary.
|
// check the scroll state and send out backfill requests if necessary.
|
||||||
checkFillState: async function(depth=0) {
|
checkFillState = async (depth=0) => {
|
||||||
if (this.unmounted) {
|
if (this.unmounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -368,10 +364,10 @@ export default createReactClass({
|
||||||
this._fillRequestWhileRunning = false;
|
this._fillRequestWhileRunning = false;
|
||||||
this.checkFillState();
|
this.checkFillState();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
// check if unfilling is possible and send an unfill request if necessary
|
// check if unfilling is possible and send an unfill request if necessary
|
||||||
_checkUnfillState: function(backwards) {
|
_checkUnfillState(backwards) {
|
||||||
let excessHeight = this._getExcessHeight(backwards);
|
let excessHeight = this._getExcessHeight(backwards);
|
||||||
if (excessHeight <= 0) {
|
if (excessHeight <= 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -417,10 +413,10 @@ export default createReactClass({
|
||||||
this.props.onUnfillRequest(backwards, markerScrollToken);
|
this.props.onUnfillRequest(backwards, markerScrollToken);
|
||||||
}, UNFILL_REQUEST_DEBOUNCE_MS);
|
}, UNFILL_REQUEST_DEBOUNCE_MS);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// check if there is already a pending fill request. If not, set one off.
|
// check if there is already a pending fill request. If not, set one off.
|
||||||
_maybeFill: function(depth, backwards) {
|
_maybeFill(depth, backwards) {
|
||||||
const dir = backwards ? 'b' : 'f';
|
const dir = backwards ? 'b' : 'f';
|
||||||
if (this._pendingFillRequests[dir]) {
|
if (this._pendingFillRequests[dir]) {
|
||||||
debuglog("Already a "+dir+" fill in progress - not starting another");
|
debuglog("Already a "+dir+" fill in progress - not starting another");
|
||||||
|
@ -456,7 +452,7 @@ export default createReactClass({
|
||||||
return this.checkFillState(depth + 1);
|
return this.checkFillState(depth + 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
/* get the current scroll state. This returns an object with the following
|
/* get the current scroll state. This returns an object with the following
|
||||||
* properties:
|
* properties:
|
||||||
|
@ -472,9 +468,7 @@ export default createReactClass({
|
||||||
* the number of pixels the bottom of the tracked child is above the
|
* the number of pixels the bottom of the tracked child is above the
|
||||||
* bottom of the scroll panel.
|
* bottom of the scroll panel.
|
||||||
*/
|
*/
|
||||||
getScrollState: function() {
|
getScrollState = () => this.scrollState;
|
||||||
return this.scrollState;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* reset the saved scroll state.
|
/* reset the saved scroll state.
|
||||||
*
|
*
|
||||||
|
@ -488,7 +482,7 @@ export default createReactClass({
|
||||||
* no use if no children exist yet, or if you are about to replace the
|
* no use if no children exist yet, or if you are about to replace the
|
||||||
* child list.)
|
* child list.)
|
||||||
*/
|
*/
|
||||||
resetScrollState: function() {
|
resetScrollState = () => {
|
||||||
this.scrollState = {
|
this.scrollState = {
|
||||||
stuckAtBottom: this.props.startAtBottom,
|
stuckAtBottom: this.props.startAtBottom,
|
||||||
};
|
};
|
||||||
|
@ -496,20 +490,20 @@ export default createReactClass({
|
||||||
this._pages = 0;
|
this._pages = 0;
|
||||||
this._scrollTimeout = new Timer(100);
|
this._scrollTimeout = new Timer(100);
|
||||||
this._heightUpdateInProgress = false;
|
this._heightUpdateInProgress = false;
|
||||||
},
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* jump to the top of the content.
|
* jump to the top of the content.
|
||||||
*/
|
*/
|
||||||
scrollToTop: function() {
|
scrollToTop = () => {
|
||||||
this._getScrollNode().scrollTop = 0;
|
this._getScrollNode().scrollTop = 0;
|
||||||
this._saveScrollState();
|
this._saveScrollState();
|
||||||
},
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* jump to the bottom of the content.
|
* jump to the bottom of the content.
|
||||||
*/
|
*/
|
||||||
scrollToBottom: function() {
|
scrollToBottom = () => {
|
||||||
// the easiest way to make sure that the scroll state is correctly
|
// the easiest way to make sure that the scroll state is correctly
|
||||||
// saved is to do the scroll, then save the updated state. (Calculating
|
// saved is to do the scroll, then save the updated state. (Calculating
|
||||||
// it ourselves is hard, and we can't rely on an onScroll callback
|
// it ourselves is hard, and we can't rely on an onScroll callback
|
||||||
|
@ -517,25 +511,25 @@ export default createReactClass({
|
||||||
const sn = this._getScrollNode();
|
const sn = this._getScrollNode();
|
||||||
sn.scrollTop = sn.scrollHeight;
|
sn.scrollTop = sn.scrollHeight;
|
||||||
this._saveScrollState();
|
this._saveScrollState();
|
||||||
},
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page up/down.
|
* Page up/down.
|
||||||
*
|
*
|
||||||
* @param {number} mult: -1 to page up, +1 to page down
|
* @param {number} mult: -1 to page up, +1 to page down
|
||||||
*/
|
*/
|
||||||
scrollRelative: function(mult) {
|
scrollRelative = mult => {
|
||||||
const scrollNode = this._getScrollNode();
|
const scrollNode = this._getScrollNode();
|
||||||
const delta = mult * scrollNode.clientHeight * 0.5;
|
const delta = mult * scrollNode.clientHeight * 0.5;
|
||||||
scrollNode.scrollBy(0, delta);
|
scrollNode.scrollBy(0, delta);
|
||||||
this._saveScrollState();
|
this._saveScrollState();
|
||||||
},
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll up/down in response to a scroll key
|
* Scroll up/down in response to a scroll key
|
||||||
* @param {object} ev the keyboard event
|
* @param {object} ev the keyboard event
|
||||||
*/
|
*/
|
||||||
handleScrollKey: function(ev) {
|
handleScrollKey = ev => {
|
||||||
switch (ev.key) {
|
switch (ev.key) {
|
||||||
case Key.PAGE_UP:
|
case Key.PAGE_UP:
|
||||||
if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
|
||||||
|
@ -561,7 +555,7 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
/* Scroll the panel to bring the DOM node with the scroll token
|
/* Scroll the panel to bring the DOM node with the scroll token
|
||||||
* `scrollToken` into view.
|
* `scrollToken` into view.
|
||||||
|
@ -574,7 +568,7 @@ export default createReactClass({
|
||||||
* node (specifically, the bottom of it) will be positioned. If omitted, it
|
* node (specifically, the bottom of it) will be positioned. If omitted, it
|
||||||
* defaults to 0.
|
* defaults to 0.
|
||||||
*/
|
*/
|
||||||
scrollToToken: function(scrollToken, pixelOffset, offsetBase) {
|
scrollToToken = (scrollToken, pixelOffset, offsetBase) => {
|
||||||
pixelOffset = pixelOffset || 0;
|
pixelOffset = pixelOffset || 0;
|
||||||
offsetBase = offsetBase || 0;
|
offsetBase = offsetBase || 0;
|
||||||
|
|
||||||
|
@ -596,9 +590,9 @@ export default createReactClass({
|
||||||
scrollNode.scrollTop = (trackedNode.offsetTop - (scrollNode.clientHeight * offsetBase)) + pixelOffset;
|
scrollNode.scrollTop = (trackedNode.offsetTop - (scrollNode.clientHeight * offsetBase)) + pixelOffset;
|
||||||
this._saveScrollState();
|
this._saveScrollState();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_saveScrollState: function() {
|
_saveScrollState() {
|
||||||
if (this.props.stickyBottom && this.isAtBottom()) {
|
if (this.props.stickyBottom && this.isAtBottom()) {
|
||||||
this.scrollState = { stuckAtBottom: true };
|
this.scrollState = { stuckAtBottom: true };
|
||||||
debuglog("saved stuckAtBottom state");
|
debuglog("saved stuckAtBottom state");
|
||||||
|
@ -641,9 +635,9 @@ export default createReactClass({
|
||||||
bottomOffset: bottomOffset,
|
bottomOffset: bottomOffset,
|
||||||
pixelOffset: bottomOffset - viewportBottom, //needed for restoring the scroll position when coming back to the room
|
pixelOffset: bottomOffset - viewportBottom, //needed for restoring the scroll position when coming back to the room
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
_restoreSavedScrollState: async function() {
|
async _restoreSavedScrollState() {
|
||||||
const scrollState = this.scrollState;
|
const scrollState = this.scrollState;
|
||||||
|
|
||||||
if (scrollState.stuckAtBottom) {
|
if (scrollState.stuckAtBottom) {
|
||||||
|
@ -676,7 +670,8 @@ export default createReactClass({
|
||||||
} else {
|
} else {
|
||||||
debuglog("not updating height because request already in progress");
|
debuglog("not updating height because request already in progress");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// need a better name that also indicates this will change scrollTop? Rebalance height? Reveal content?
|
// need a better name that also indicates this will change scrollTop? Rebalance height? Reveal content?
|
||||||
async _updateHeight() {
|
async _updateHeight() {
|
||||||
// wait until user has stopped scrolling
|
// wait until user has stopped scrolling
|
||||||
|
@ -731,7 +726,7 @@ export default createReactClass({
|
||||||
debuglog("updateHeight to", {newHeight, topDiff});
|
debuglog("updateHeight to", {newHeight, topDiff});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_getTrackedNode() {
|
_getTrackedNode() {
|
||||||
const scrollState = this.scrollState;
|
const scrollState = this.scrollState;
|
||||||
|
@ -764,11 +759,11 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return scrollState.trackedNode;
|
return scrollState.trackedNode;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getListHeight() {
|
_getListHeight() {
|
||||||
return this._bottomGrowth + (this._pages * PAGE_SIZE);
|
return this._bottomGrowth + (this._pages * PAGE_SIZE);
|
||||||
},
|
}
|
||||||
|
|
||||||
_getMessagesHeight() {
|
_getMessagesHeight() {
|
||||||
const itemlist = this._itemlist.current;
|
const itemlist = this._itemlist.current;
|
||||||
|
@ -777,17 +772,17 @@ export default createReactClass({
|
||||||
const firstNodeTop = itemlist.firstElementChild ? itemlist.firstElementChild.offsetTop : 0;
|
const firstNodeTop = itemlist.firstElementChild ? itemlist.firstElementChild.offsetTop : 0;
|
||||||
// 18 is itemlist padding
|
// 18 is itemlist padding
|
||||||
return lastNodeBottom - firstNodeTop + (18 * 2);
|
return lastNodeBottom - firstNodeTop + (18 * 2);
|
||||||
},
|
}
|
||||||
|
|
||||||
_topFromBottom(node) {
|
_topFromBottom(node) {
|
||||||
// current capped height - distance from top = distance from bottom of container to top of tracked element
|
// current capped height - distance from top = distance from bottom of container to top of tracked element
|
||||||
return this._itemlist.current.clientHeight - node.offsetTop;
|
return this._itemlist.current.clientHeight - node.offsetTop;
|
||||||
},
|
}
|
||||||
|
|
||||||
/* get the DOM node which has the scrollTop property we care about for our
|
/* get the DOM node which has the scrollTop property we care about for our
|
||||||
* message panel.
|
* message panel.
|
||||||
*/
|
*/
|
||||||
_getScrollNode: function() {
|
_getScrollNode() {
|
||||||
if (this.unmounted) {
|
if (this.unmounted) {
|
||||||
// this shouldn't happen, but when it does, turn the NPE into
|
// this shouldn't happen, but when it does, turn the NPE into
|
||||||
// something more meaningful.
|
// something more meaningful.
|
||||||
|
@ -801,18 +796,18 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._divScroll;
|
return this._divScroll;
|
||||||
},
|
}
|
||||||
|
|
||||||
_collectScroll: function(divScroll) {
|
_collectScroll = divScroll => {
|
||||||
this._divScroll = divScroll;
|
this._divScroll = divScroll;
|
||||||
},
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Mark the bottom offset of the last tile so we can balance it out when
|
Mark the bottom offset of the last tile so we can balance it out when
|
||||||
anything below it changes, by calling updatePreventShrinking, to keep
|
anything below it changes, by calling updatePreventShrinking, to keep
|
||||||
the same minimum bottom offset, effectively preventing the timeline to shrink.
|
the same minimum bottom offset, effectively preventing the timeline to shrink.
|
||||||
*/
|
*/
|
||||||
preventShrinking: function() {
|
preventShrinking = () => {
|
||||||
const messageList = this._itemlist.current;
|
const messageList = this._itemlist.current;
|
||||||
const tiles = messageList && messageList.children;
|
const tiles = messageList && messageList.children;
|
||||||
if (!messageList) {
|
if (!messageList) {
|
||||||
|
@ -836,16 +831,16 @@ export default createReactClass({
|
||||||
offsetNode: lastTileNode,
|
offsetNode: lastTileNode,
|
||||||
};
|
};
|
||||||
debuglog("prevent shrinking, last tile ", offsetFromBottom, "px from bottom");
|
debuglog("prevent shrinking, last tile ", offsetFromBottom, "px from bottom");
|
||||||
},
|
};
|
||||||
|
|
||||||
/** Clear shrinking prevention. Used internally, and when the timeline is reloaded. */
|
/** Clear shrinking prevention. Used internally, and when the timeline is reloaded. */
|
||||||
clearPreventShrinking: function() {
|
clearPreventShrinking = () => {
|
||||||
const messageList = this._itemlist.current;
|
const messageList = this._itemlist.current;
|
||||||
const balanceElement = messageList && messageList.parentElement;
|
const balanceElement = messageList && messageList.parentElement;
|
||||||
if (balanceElement) balanceElement.style.paddingBottom = null;
|
if (balanceElement) balanceElement.style.paddingBottom = null;
|
||||||
this.preventShrinkingState = null;
|
this.preventShrinkingState = null;
|
||||||
debuglog("prevent shrinking cleared");
|
debuglog("prevent shrinking cleared");
|
||||||
},
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
update the container padding to balance
|
update the container padding to balance
|
||||||
|
@ -855,7 +850,7 @@ export default createReactClass({
|
||||||
from the bottom of the marked tile grows larger than
|
from the bottom of the marked tile grows larger than
|
||||||
what it was when marking.
|
what it was when marking.
|
||||||
*/
|
*/
|
||||||
updatePreventShrinking: function() {
|
updatePreventShrinking = () => {
|
||||||
if (this.preventShrinkingState) {
|
if (this.preventShrinkingState) {
|
||||||
const sn = this._getScrollNode();
|
const sn = this._getScrollNode();
|
||||||
const scrollState = this.scrollState;
|
const scrollState = this.scrollState;
|
||||||
|
@ -885,9 +880,9 @@ export default createReactClass({
|
||||||
this.clearPreventShrinking();
|
this.clearPreventShrinking();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
// TODO: the classnames on the div and ol could do with being updated to
|
// TODO: the classnames on the div and ol could do with being updated to
|
||||||
// reflect the fact that we don't necessarily contain a list of messages.
|
// reflect the fact that we don't necessarily contain a list of messages.
|
||||||
// it's not obvious why we have a separate div and ol anyway.
|
// it's not obvious why we have a separate div and ol anyway.
|
||||||
|
@ -905,5 +900,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</AutoHideScrollbar>
|
</AutoHideScrollbar>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Key } from '../../Keyboard';
|
import { Key } from '../../Keyboard';
|
||||||
import dis from '../../dispatcher/dispatcher';
|
import dis from '../../dispatcher/dispatcher';
|
||||||
|
@ -24,10 +23,8 @@ import { throttle } from 'lodash';
|
||||||
import AccessibleButton from '../../components/views/elements/AccessibleButton';
|
import AccessibleButton from '../../components/views/elements/AccessibleButton';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class SearchBox extends React.Component {
|
||||||
displayName: 'SearchBox',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
onSearch: PropTypes.func,
|
onSearch: PropTypes.func,
|
||||||
onCleared: PropTypes.func,
|
onCleared: PropTypes.func,
|
||||||
onKeyDown: PropTypes.func,
|
onKeyDown: PropTypes.func,
|
||||||
|
@ -38,35 +35,32 @@ export default createReactClass({
|
||||||
// on room search focus action (it would be nicer to take
|
// on room search focus action (it would be nicer to take
|
||||||
// this functionality out, but not obvious how that would work)
|
// this functionality out, but not obvious how that would work)
|
||||||
enableRoomSearchFocus: PropTypes.bool,
|
enableRoomSearchFocus: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
enableRoomSearchFocus: false,
|
||||||
enableRoomSearchFocus: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this._search = createRef();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
searchTerm: "",
|
searchTerm: "",
|
||||||
blurred: true,
|
blurred: true,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
componentDidMount() {
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._search = createRef();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
},
|
}
|
||||||
|
|
||||||
onAction: function(payload) {
|
onAction = payload => {
|
||||||
if (!this.props.enableRoomSearchFocus) return;
|
if (!this.props.enableRoomSearchFocus) return;
|
||||||
|
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
|
@ -81,51 +75,51 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onChange: function() {
|
onChange = () => {
|
||||||
if (!this._search.current) return;
|
if (!this._search.current) return;
|
||||||
this.setState({ searchTerm: this._search.current.value });
|
this.setState({ searchTerm: this._search.current.value });
|
||||||
this.onSearch();
|
this.onSearch();
|
||||||
},
|
};
|
||||||
|
|
||||||
onSearch: throttle(function() {
|
onSearch = throttle(() => {
|
||||||
this.props.onSearch(this._search.current.value);
|
this.props.onSearch(this._search.current.value);
|
||||||
}, 200, {trailing: true, leading: true}),
|
}, 200, {trailing: true, leading: true});
|
||||||
|
|
||||||
_onKeyDown: function(ev) {
|
_onKeyDown = ev => {
|
||||||
switch (ev.key) {
|
switch (ev.key) {
|
||||||
case Key.ESCAPE:
|
case Key.ESCAPE:
|
||||||
this._clearSearch("keyboard");
|
this._clearSearch("keyboard");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (this.props.onKeyDown) this.props.onKeyDown(ev);
|
if (this.props.onKeyDown) this.props.onKeyDown(ev);
|
||||||
},
|
};
|
||||||
|
|
||||||
_onFocus: function(ev) {
|
_onFocus = ev => {
|
||||||
this.setState({blurred: false});
|
this.setState({blurred: false});
|
||||||
ev.target.select();
|
ev.target.select();
|
||||||
if (this.props.onFocus) {
|
if (this.props.onFocus) {
|
||||||
this.props.onFocus(ev);
|
this.props.onFocus(ev);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_onBlur: function(ev) {
|
_onBlur = ev => {
|
||||||
this.setState({blurred: true});
|
this.setState({blurred: true});
|
||||||
if (this.props.onBlur) {
|
if (this.props.onBlur) {
|
||||||
this.props.onBlur(ev);
|
this.props.onBlur(ev);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_clearSearch: function(source) {
|
_clearSearch(source) {
|
||||||
this._search.current.value = "";
|
this._search.current.value = "";
|
||||||
this.onChange();
|
this.onChange();
|
||||||
if (this.props.onCleared) {
|
if (this.props.onCleared) {
|
||||||
this.props.onCleared(source);
|
this.props.onCleared(source);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
// check for collapsed here and
|
// check for collapsed here and
|
||||||
// not at parent so we keep
|
// not at parent so we keep
|
||||||
// searchTerm in our state
|
// searchTerm in our state
|
||||||
|
@ -166,5 +160,5 @@ export default createReactClass({
|
||||||
{ clearButton }
|
{ clearButton }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import TagOrderStore from '../../stores/TagOrderStore';
|
import TagOrderStore from '../../stores/TagOrderStore';
|
||||||
|
|
||||||
import GroupActions from '../../actions/GroupActions';
|
import GroupActions from '../../actions/GroupActions';
|
||||||
|
@ -32,21 +31,15 @@ import AutoHideScrollbar from "./AutoHideScrollbar";
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import UserTagTile from "../views/elements/UserTagTile";
|
import UserTagTile from "../views/elements/UserTagTile";
|
||||||
|
|
||||||
const TagPanel = createReactClass({
|
class TagPanel extends React.Component {
|
||||||
displayName: 'TagPanel',
|
static contextType = MatrixClientContext;
|
||||||
|
|
||||||
statics: {
|
state = {
|
||||||
contextType: MatrixClientContext,
|
orderedTags: [],
|
||||||
},
|
selectedTags: [],
|
||||||
|
};
|
||||||
|
|
||||||
getInitialState() {
|
componentDidMount() {
|
||||||
return {
|
|
||||||
orderedTags: [],
|
|
||||||
selectedTags: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.unmounted = false;
|
this.unmounted = false;
|
||||||
this.context.on("Group.myMembership", this._onGroupMyMembership);
|
this.context.on("Group.myMembership", this._onGroupMyMembership);
|
||||||
this.context.on("sync", this._onClientSync);
|
this.context.on("sync", this._onClientSync);
|
||||||
|
@ -62,7 +55,7 @@ const TagPanel = createReactClass({
|
||||||
});
|
});
|
||||||
// This could be done by anything with a matrix client
|
// This could be done by anything with a matrix client
|
||||||
dis.dispatch(GroupActions.fetchJoinedGroups(this.context));
|
dis.dispatch(GroupActions.fetchJoinedGroups(this.context));
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
|
@ -71,14 +64,14 @@ const TagPanel = createReactClass({
|
||||||
if (this._tagOrderStoreToken) {
|
if (this._tagOrderStoreToken) {
|
||||||
this._tagOrderStoreToken.remove();
|
this._tagOrderStoreToken.remove();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_onGroupMyMembership() {
|
_onGroupMyMembership = () => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
dis.dispatch(GroupActions.fetchJoinedGroups(this.context));
|
dis.dispatch(GroupActions.fetchJoinedGroups(this.context));
|
||||||
},
|
};
|
||||||
|
|
||||||
_onClientSync(syncState, prevState) {
|
_onClientSync = (syncState, prevState) => {
|
||||||
// Consider the client reconnected if there is no error with syncing.
|
// Consider the client reconnected if there is no error with syncing.
|
||||||
// This means the state could be RECONNECTING, SYNCING, PREPARED or CATCHUP.
|
// This means the state could be RECONNECTING, SYNCING, PREPARED or CATCHUP.
|
||||||
const reconnected = syncState !== "ERROR" && prevState !== syncState;
|
const reconnected = syncState !== "ERROR" && prevState !== syncState;
|
||||||
|
@ -86,18 +79,18 @@ const TagPanel = createReactClass({
|
||||||
// Load joined groups
|
// Load joined groups
|
||||||
dis.dispatch(GroupActions.fetchJoinedGroups(this.context));
|
dis.dispatch(GroupActions.fetchJoinedGroups(this.context));
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onMouseDown(e) {
|
onMouseDown = e => {
|
||||||
// only dispatch if its not a no-op
|
// only dispatch if its not a no-op
|
||||||
if (this.state.selectedTags.length > 0) {
|
if (this.state.selectedTags.length > 0) {
|
||||||
dis.dispatch({action: 'deselect_tags'});
|
dis.dispatch({action: 'deselect_tags'});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onClearFilterClick(ev) {
|
onClearFilterClick = ev => {
|
||||||
dis.dispatch({action: 'deselect_tags'});
|
dis.dispatch({action: 'deselect_tags'});
|
||||||
},
|
};
|
||||||
|
|
||||||
renderGlobalIcon() {
|
renderGlobalIcon() {
|
||||||
if (!SettingsStore.getValue("feature_communities_v2_prototypes")) return null;
|
if (!SettingsStore.getValue("feature_communities_v2_prototypes")) return null;
|
||||||
|
@ -108,7 +101,7 @@ const TagPanel = createReactClass({
|
||||||
<hr className="mx_TagPanel_divider" />
|
<hr className="mx_TagPanel_divider" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const DNDTagTile = sdk.getComponent('elements.DNDTagTile');
|
const DNDTagTile = sdk.getComponent('elements.DNDTagTile');
|
||||||
|
@ -173,6 +166,6 @@ const TagPanel = createReactClass({
|
||||||
</Droppable>
|
</Droppable>
|
||||||
</AutoHideScrollbar>
|
</AutoHideScrollbar>
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
export default TagPanel;
|
export default TagPanel;
|
||||||
|
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
||||||
|
|
||||||
import SettingsStore from "../../settings/SettingsStore";
|
import SettingsStore from "../../settings/SettingsStore";
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {EventTimeline} from "matrix-js-sdk";
|
import {EventTimeline} from "matrix-js-sdk";
|
||||||
|
@ -54,10 +53,8 @@ if (DEBUG) {
|
||||||
*
|
*
|
||||||
* Also responsible for handling and sending read receipts.
|
* Also responsible for handling and sending read receipts.
|
||||||
*/
|
*/
|
||||||
const TimelinePanel = createReactClass({
|
class TimelinePanel extends React.Component {
|
||||||
displayName: 'TimelinePanel',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// The js-sdk EventTimelineSet object for the timeline sequence we are
|
// The js-sdk EventTimelineSet object for the timeline sequence we are
|
||||||
// representing. This may or may not have a room, depending on what it's
|
// representing. This may or may not have a room, depending on what it's
|
||||||
// a timeline representing. If it has a room, we maintain RRs etc for
|
// a timeline representing. If it has a room, we maintain RRs etc for
|
||||||
|
@ -115,23 +112,35 @@ const TimelinePanel = createReactClass({
|
||||||
|
|
||||||
// whether to use the irc layout
|
// whether to use the irc layout
|
||||||
useIRCLayout: PropTypes.bool,
|
useIRCLayout: PropTypes.bool,
|
||||||
},
|
}
|
||||||
|
|
||||||
statics: {
|
// a map from room id to read marker event timestamp
|
||||||
// a map from room id to read marker event timestamp
|
static roomReadMarkerTsMap = {};
|
||||||
roomReadMarkerTsMap: {},
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
// By default, disable the timelineCap in favour of unpaginating based on
|
||||||
// By default, disable the timelineCap in favour of unpaginating based on
|
// event tile heights. (See _unpaginateEvents)
|
||||||
// event tile heights. (See _unpaginateEvents)
|
timelineCap: Number.MAX_VALUE,
|
||||||
timelineCap: Number.MAX_VALUE,
|
className: 'mx_RoomView_messagePanel',
|
||||||
className: 'mx_RoomView_messagePanel',
|
};
|
||||||
};
|
|
||||||
},
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
debuglog("TimelinePanel: mounting");
|
||||||
|
|
||||||
|
this.lastRRSentEventId = undefined;
|
||||||
|
this.lastRMSentEventId = undefined;
|
||||||
|
|
||||||
|
this._messagePanel = createRef();
|
||||||
|
|
||||||
|
if (this.props.manageReadReceipts) {
|
||||||
|
this.updateReadReceiptOnUserActivity();
|
||||||
|
}
|
||||||
|
if (this.props.manageReadMarkers) {
|
||||||
|
this.updateReadMarkerOnUserActivity();
|
||||||
|
}
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
// XXX: we could track RM per TimelineSet rather than per Room.
|
// XXX: we could track RM per TimelineSet rather than per Room.
|
||||||
// but for now we just do it per room for simplicity.
|
// but for now we just do it per room for simplicity.
|
||||||
let initialReadMarker = null;
|
let initialReadMarker = null;
|
||||||
|
@ -144,7 +153,7 @@ const TimelinePanel = createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
this.state = {
|
||||||
events: [],
|
events: [],
|
||||||
liveEvents: [],
|
liveEvents: [],
|
||||||
timelineLoading: true, // track whether our room timeline is loading
|
timelineLoading: true, // track whether our room timeline is loading
|
||||||
|
@ -203,24 +212,6 @@ const TimelinePanel = createReactClass({
|
||||||
// how long to show the RM for when it's scrolled off-screen
|
// how long to show the RM for when it's scrolled off-screen
|
||||||
readMarkerOutOfViewThresholdMs: SettingsStore.getValue("readMarkerOutOfViewThresholdMs"),
|
readMarkerOutOfViewThresholdMs: SettingsStore.getValue("readMarkerOutOfViewThresholdMs"),
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
debuglog("TimelinePanel: mounting");
|
|
||||||
|
|
||||||
this.lastRRSentEventId = undefined;
|
|
||||||
this.lastRMSentEventId = undefined;
|
|
||||||
|
|
||||||
this._messagePanel = createRef();
|
|
||||||
|
|
||||||
if (this.props.manageReadReceipts) {
|
|
||||||
this.updateReadReceiptOnUserActivity();
|
|
||||||
}
|
|
||||||
if (this.props.manageReadMarkers) {
|
|
||||||
this.updateReadMarkerOnUserActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
||||||
|
@ -236,10 +227,10 @@ const TimelinePanel = createReactClass({
|
||||||
MatrixClientPeg.get().on("sync", this.onSync);
|
MatrixClientPeg.get().on("sync", this.onSync);
|
||||||
|
|
||||||
this._initTimeline(this.props);
|
this._initTimeline(this.props);
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
UNSAFE_componentWillReceiveProps: function(newProps) {
|
UNSAFE_componentWillReceiveProps(newProps) {
|
||||||
if (newProps.timelineSet !== this.props.timelineSet) {
|
if (newProps.timelineSet !== this.props.timelineSet) {
|
||||||
// throw new Error("changing timelineSet on a TimelinePanel is not supported");
|
// throw new Error("changing timelineSet on a TimelinePanel is not supported");
|
||||||
|
|
||||||
|
@ -260,9 +251,9 @@ const TimelinePanel = createReactClass({
|
||||||
" (was " + this.props.eventId + ")");
|
" (was " + this.props.eventId + ")");
|
||||||
return this._initTimeline(newProps);
|
return this._initTimeline(newProps);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldComponentUpdate: function(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
if (!ObjectUtils.shallowEqual(this.props, nextProps)) {
|
if (!ObjectUtils.shallowEqual(this.props, nextProps)) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
console.group("Timeline.shouldComponentUpdate: props change");
|
console.group("Timeline.shouldComponentUpdate: props change");
|
||||||
|
@ -284,9 +275,9 @@ const TimelinePanel = createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
// set a boolean to say we've been unmounted, which any pending
|
// set a boolean to say we've been unmounted, which any pending
|
||||||
// promises can use to throw away their results.
|
// promises can use to throw away their results.
|
||||||
//
|
//
|
||||||
|
@ -316,9 +307,9 @@ const TimelinePanel = createReactClass({
|
||||||
client.removeListener("Event.replaced", this.onEventReplaced);
|
client.removeListener("Event.replaced", this.onEventReplaced);
|
||||||
client.removeListener("sync", this.onSync);
|
client.removeListener("sync", this.onSync);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onMessageListUnfillRequest: function(backwards, scrollToken) {
|
onMessageListUnfillRequest = (backwards, scrollToken) => {
|
||||||
// If backwards, unpaginate from the back (i.e. the start of the timeline)
|
// If backwards, unpaginate from the back (i.e. the start of the timeline)
|
||||||
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
|
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
|
||||||
debuglog("TimelinePanel: unpaginating events in direction", dir);
|
debuglog("TimelinePanel: unpaginating events in direction", dir);
|
||||||
|
@ -349,18 +340,18 @@ const TimelinePanel = createReactClass({
|
||||||
firstVisibleEventIndex,
|
firstVisibleEventIndex,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onPaginationRequest(timelineWindow, direction, size) {
|
onPaginationRequest = (timelineWindow, direction, size) => {
|
||||||
if (this.props.onPaginationRequest) {
|
if (this.props.onPaginationRequest) {
|
||||||
return this.props.onPaginationRequest(timelineWindow, direction, size);
|
return this.props.onPaginationRequest(timelineWindow, direction, size);
|
||||||
} else {
|
} else {
|
||||||
return timelineWindow.paginate(direction, size);
|
return timelineWindow.paginate(direction, size);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
// set off a pagination request.
|
// set off a pagination request.
|
||||||
onMessageListFillRequest: function(backwards) {
|
onMessageListFillRequest = backwards => {
|
||||||
if (!this._shouldPaginate()) return Promise.resolve(false);
|
if (!this._shouldPaginate()) return Promise.resolve(false);
|
||||||
|
|
||||||
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
|
const dir = backwards ? EventTimeline.BACKWARDS : EventTimeline.FORWARDS;
|
||||||
|
@ -425,9 +416,9 @@ const TimelinePanel = createReactClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onMessageListScroll: function(e) {
|
onMessageListScroll = e => {
|
||||||
if (this.props.onScroll) {
|
if (this.props.onScroll) {
|
||||||
this.props.onScroll(e);
|
this.props.onScroll(e);
|
||||||
}
|
}
|
||||||
|
@ -447,9 +438,9 @@ const TimelinePanel = createReactClass({
|
||||||
// NO-OP when timeout already has set to the given value
|
// NO-OP when timeout already has set to the given value
|
||||||
this._readMarkerActivityTimer.changeTimeout(timeout);
|
this._readMarkerActivityTimer.changeTimeout(timeout);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onAction: function(payload) {
|
onAction = payload => {
|
||||||
if (payload.action === 'ignore_state_changed') {
|
if (payload.action === 'ignore_state_changed') {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
|
@ -463,9 +454,9 @@ const TimelinePanel = createReactClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomTimeline: function(ev, room, toStartOfTimeline, removed, data) {
|
onRoomTimeline = (ev, room, toStartOfTimeline, removed, data) => {
|
||||||
// ignore events for other timeline sets
|
// ignore events for other timeline sets
|
||||||
if (data.timeline.getTimelineSet() !== this.props.timelineSet) return;
|
if (data.timeline.getTimelineSet() !== this.props.timelineSet) return;
|
||||||
|
|
||||||
|
@ -537,21 +528,19 @@ const TimelinePanel = createReactClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomTimelineReset: function(room, timelineSet) {
|
onRoomTimelineReset = (room, timelineSet) => {
|
||||||
if (timelineSet !== this.props.timelineSet) return;
|
if (timelineSet !== this.props.timelineSet) return;
|
||||||
|
|
||||||
if (this._messagePanel.current && this._messagePanel.current.isAtBottom()) {
|
if (this._messagePanel.current && this._messagePanel.current.isAtBottom()) {
|
||||||
this._loadTimeline();
|
this._loadTimeline();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
canResetTimeline: function() {
|
canResetTimeline = () => this._messagePanel.current && this._messagePanel.current.isAtBottom();
|
||||||
return this._messagePanel.current && this._messagePanel.current.isAtBottom();
|
|
||||||
},
|
|
||||||
|
|
||||||
onRoomRedaction: function(ev, room) {
|
onRoomRedaction = (ev, room) => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
// ignore events for other rooms
|
// ignore events for other rooms
|
||||||
|
@ -560,9 +549,9 @@ const TimelinePanel = createReactClass({
|
||||||
// we could skip an update if the event isn't in our timeline,
|
// we could skip an update if the event isn't in our timeline,
|
||||||
// but that's probably an early optimisation.
|
// but that's probably an early optimisation.
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
onEventReplaced: function(replacedEvent, room) {
|
onEventReplaced = (replacedEvent, room) => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
// ignore events for other rooms
|
// ignore events for other rooms
|
||||||
|
@ -571,27 +560,27 @@ const TimelinePanel = createReactClass({
|
||||||
// we could skip an update if the event isn't in our timeline,
|
// we could skip an update if the event isn't in our timeline,
|
||||||
// but that's probably an early optimisation.
|
// but that's probably an early optimisation.
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomReceipt: function(ev, room) {
|
onRoomReceipt = (ev, room) => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
// ignore events for other rooms
|
// ignore events for other rooms
|
||||||
if (room !== this.props.timelineSet.room) return;
|
if (room !== this.props.timelineSet.room) return;
|
||||||
|
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
onLocalEchoUpdated: function(ev, room, oldEventId) {
|
onLocalEchoUpdated = (ev, room, oldEventId) => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
// ignore events for other rooms
|
// ignore events for other rooms
|
||||||
if (room !== this.props.timelineSet.room) return;
|
if (room !== this.props.timelineSet.room) return;
|
||||||
|
|
||||||
this._reloadEvents();
|
this._reloadEvents();
|
||||||
},
|
};
|
||||||
|
|
||||||
onAccountData: function(ev, room) {
|
onAccountData = (ev, room) => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
// ignore events for other rooms
|
// ignore events for other rooms
|
||||||
|
@ -605,9 +594,9 @@ const TimelinePanel = createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
readMarkerEventId: ev.getContent().event_id,
|
readMarkerEventId: ev.getContent().event_id,
|
||||||
}, this.props.onReadMarkerUpdated);
|
}, this.props.onReadMarkerUpdated);
|
||||||
},
|
};
|
||||||
|
|
||||||
onEventDecrypted: function(ev) {
|
onEventDecrypted = ev => {
|
||||||
// Can be null for the notification timeline, etc.
|
// Can be null for the notification timeline, etc.
|
||||||
if (!this.props.timelineSet.room) return;
|
if (!this.props.timelineSet.room) return;
|
||||||
|
|
||||||
|
@ -620,19 +609,19 @@ const TimelinePanel = createReactClass({
|
||||||
if (ev.getRoomId() === this.props.timelineSet.room.roomId) {
|
if (ev.getRoomId() === this.props.timelineSet.room.roomId) {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onSync: function(state, prevState, data) {
|
onSync = (state, prevState, data) => {
|
||||||
this.setState({clientSyncState: state});
|
this.setState({clientSyncState: state});
|
||||||
},
|
};
|
||||||
|
|
||||||
_readMarkerTimeout(readMarkerPosition) {
|
_readMarkerTimeout(readMarkerPosition) {
|
||||||
return readMarkerPosition === 0 ?
|
return readMarkerPosition === 0 ?
|
||||||
this.state.readMarkerInViewThresholdMs :
|
this.state.readMarkerInViewThresholdMs :
|
||||||
this.state.readMarkerOutOfViewThresholdMs;
|
this.state.readMarkerOutOfViewThresholdMs;
|
||||||
},
|
}
|
||||||
|
|
||||||
updateReadMarkerOnUserActivity: async function() {
|
async updateReadMarkerOnUserActivity() {
|
||||||
const initialTimeout = this._readMarkerTimeout(this.getReadMarkerPosition());
|
const initialTimeout = this._readMarkerTimeout(this.getReadMarkerPosition());
|
||||||
this._readMarkerActivityTimer = new Timer(initialTimeout);
|
this._readMarkerActivityTimer = new Timer(initialTimeout);
|
||||||
|
|
||||||
|
@ -644,9 +633,9 @@ const TimelinePanel = createReactClass({
|
||||||
// outside of try/catch to not swallow errors
|
// outside of try/catch to not swallow errors
|
||||||
this.updateReadMarker();
|
this.updateReadMarker();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
updateReadReceiptOnUserActivity: async function() {
|
async updateReadReceiptOnUserActivity() {
|
||||||
this._readReceiptActivityTimer = new Timer(READ_RECEIPT_INTERVAL_MS);
|
this._readReceiptActivityTimer = new Timer(READ_RECEIPT_INTERVAL_MS);
|
||||||
while (this._readReceiptActivityTimer) { //unset on unmount
|
while (this._readReceiptActivityTimer) { //unset on unmount
|
||||||
UserActivity.sharedInstance().timeWhileActiveNow(this._readReceiptActivityTimer);
|
UserActivity.sharedInstance().timeWhileActiveNow(this._readReceiptActivityTimer);
|
||||||
|
@ -656,9 +645,9 @@ const TimelinePanel = createReactClass({
|
||||||
// outside of try/catch to not swallow errors
|
// outside of try/catch to not swallow errors
|
||||||
this.sendReadReceipt();
|
this.sendReadReceipt();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
sendReadReceipt: function() {
|
sendReadReceipt = () => {
|
||||||
if (SettingsStore.getValue("lowBandwidth")) return;
|
if (SettingsStore.getValue("lowBandwidth")) return;
|
||||||
|
|
||||||
if (!this._messagePanel.current) return;
|
if (!this._messagePanel.current) return;
|
||||||
|
@ -766,11 +755,11 @@ const TimelinePanel = createReactClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
// if the read marker is on the screen, we can now assume we've caught up to the end
|
// if the read marker is on the screen, we can now assume we've caught up to the end
|
||||||
// of the screen, so move the marker down to the bottom of the screen.
|
// of the screen, so move the marker down to the bottom of the screen.
|
||||||
updateReadMarker: function() {
|
updateReadMarker = () => {
|
||||||
if (!this.props.manageReadMarkers) return;
|
if (!this.props.manageReadMarkers) return;
|
||||||
if (this.getReadMarkerPosition() === 1) {
|
if (this.getReadMarkerPosition() === 1) {
|
||||||
// the read marker is at an event below the viewport,
|
// the read marker is at an event below the viewport,
|
||||||
|
@ -801,11 +790,11 @@ const TimelinePanel = createReactClass({
|
||||||
|
|
||||||
// Send the updated read marker (along with read receipt) to the server
|
// Send the updated read marker (along with read receipt) to the server
|
||||||
this.sendReadReceipt();
|
this.sendReadReceipt();
|
||||||
},
|
};
|
||||||
|
|
||||||
|
|
||||||
// advance the read marker past any events we sent ourselves.
|
// advance the read marker past any events we sent ourselves.
|
||||||
_advanceReadMarkerPastMyEvents: function() {
|
_advanceReadMarkerPastMyEvents() {
|
||||||
if (!this.props.manageReadMarkers) return;
|
if (!this.props.manageReadMarkers) return;
|
||||||
|
|
||||||
// we call `_timelineWindow.getEvents()` rather than using
|
// we call `_timelineWindow.getEvents()` rather than using
|
||||||
|
@ -837,11 +826,11 @@ const TimelinePanel = createReactClass({
|
||||||
|
|
||||||
const ev = events[i];
|
const ev = events[i];
|
||||||
this._setReadMarker(ev.getId(), ev.getTs());
|
this._setReadMarker(ev.getId(), ev.getTs());
|
||||||
},
|
}
|
||||||
|
|
||||||
/* jump down to the bottom of this room, where new events are arriving
|
/* jump down to the bottom of this room, where new events are arriving
|
||||||
*/
|
*/
|
||||||
jumpToLiveTimeline: function() {
|
jumpToLiveTimeline = () => {
|
||||||
// if we can't forward-paginate the existing timeline, then there
|
// if we can't forward-paginate the existing timeline, then there
|
||||||
// is no point reloading it - just jump straight to the bottom.
|
// is no point reloading it - just jump straight to the bottom.
|
||||||
//
|
//
|
||||||
|
@ -854,12 +843,12 @@ const TimelinePanel = createReactClass({
|
||||||
this._messagePanel.current.scrollToBottom();
|
this._messagePanel.current.scrollToBottom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
/* scroll to show the read-up-to marker. We put it 1/3 of the way down
|
/* scroll to show the read-up-to marker. We put it 1/3 of the way down
|
||||||
* the container.
|
* the container.
|
||||||
*/
|
*/
|
||||||
jumpToReadMarker: function() {
|
jumpToReadMarker = () => {
|
||||||
if (!this.props.manageReadMarkers) return;
|
if (!this.props.manageReadMarkers) return;
|
||||||
if (!this._messagePanel.current) return;
|
if (!this._messagePanel.current) return;
|
||||||
if (!this.state.readMarkerEventId) return;
|
if (!this.state.readMarkerEventId) return;
|
||||||
|
@ -883,11 +872,11 @@ const TimelinePanel = createReactClass({
|
||||||
// As with jumpToLiveTimeline, we want to reload the timeline around the
|
// As with jumpToLiveTimeline, we want to reload the timeline around the
|
||||||
// read-marker.
|
// read-marker.
|
||||||
this._loadTimeline(this.state.readMarkerEventId, 0, 1/3);
|
this._loadTimeline(this.state.readMarkerEventId, 0, 1/3);
|
||||||
},
|
};
|
||||||
|
|
||||||
/* update the read-up-to marker to match the read receipt
|
/* update the read-up-to marker to match the read receipt
|
||||||
*/
|
*/
|
||||||
forgetReadMarker: function() {
|
forgetReadMarker = () => {
|
||||||
if (!this.props.manageReadMarkers) return;
|
if (!this.props.manageReadMarkers) return;
|
||||||
|
|
||||||
const rmId = this._getCurrentReadReceipt();
|
const rmId = this._getCurrentReadReceipt();
|
||||||
|
@ -903,17 +892,17 @@ const TimelinePanel = createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this._setReadMarker(rmId, rmTs);
|
this._setReadMarker(rmId, rmTs);
|
||||||
},
|
};
|
||||||
|
|
||||||
/* return true if the content is fully scrolled down and we are
|
/* return true if the content is fully scrolled down and we are
|
||||||
* at the end of the live timeline.
|
* at the end of the live timeline.
|
||||||
*/
|
*/
|
||||||
isAtEndOfLiveTimeline: function() {
|
isAtEndOfLiveTimeline = () => {
|
||||||
return this._messagePanel.current
|
return this._messagePanel.current
|
||||||
&& this._messagePanel.current.isAtBottom()
|
&& this._messagePanel.current.isAtBottom()
|
||||||
&& this._timelineWindow
|
&& this._timelineWindow
|
||||||
&& !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
|
&& !this._timelineWindow.canPaginate(EventTimeline.FORWARDS);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
|
||||||
/* get the current scroll state. See ScrollPanel.getScrollState for
|
/* get the current scroll state. See ScrollPanel.getScrollState for
|
||||||
|
@ -921,10 +910,10 @@ const TimelinePanel = createReactClass({
|
||||||
*
|
*
|
||||||
* returns null if we are not mounted.
|
* returns null if we are not mounted.
|
||||||
*/
|
*/
|
||||||
getScrollState: function() {
|
getScrollState = () => {
|
||||||
if (!this._messagePanel.current) { return null; }
|
if (!this._messagePanel.current) { return null; }
|
||||||
return this._messagePanel.current.getScrollState();
|
return this._messagePanel.current.getScrollState();
|
||||||
},
|
};
|
||||||
|
|
||||||
// returns one of:
|
// returns one of:
|
||||||
//
|
//
|
||||||
|
@ -932,7 +921,7 @@ const TimelinePanel = createReactClass({
|
||||||
// -1: read marker is above the window
|
// -1: read marker is above the window
|
||||||
// 0: read marker is visible
|
// 0: read marker is visible
|
||||||
// +1: read marker is below the window
|
// +1: read marker is below the window
|
||||||
getReadMarkerPosition: function() {
|
getReadMarkerPosition = () => {
|
||||||
if (!this.props.manageReadMarkers) return null;
|
if (!this.props.manageReadMarkers) return null;
|
||||||
if (!this._messagePanel.current) return null;
|
if (!this._messagePanel.current) return null;
|
||||||
|
|
||||||
|
@ -953,9 +942,9 @@ const TimelinePanel = createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
};
|
||||||
|
|
||||||
canJumpToReadMarker: function() {
|
canJumpToReadMarker = () => {
|
||||||
// 1. Do not show jump bar if neither the RM nor the RR are set.
|
// 1. Do not show jump bar if neither the RM nor the RR are set.
|
||||||
// 3. We want to show the bar if the read-marker is off the top of the screen.
|
// 3. We want to show the bar if the read-marker is off the top of the screen.
|
||||||
// 4. Also, if pos === null, the event might not be paginated - show the unread bar
|
// 4. Also, if pos === null, the event might not be paginated - show the unread bar
|
||||||
|
@ -963,14 +952,14 @@ const TimelinePanel = createReactClass({
|
||||||
const ret = this.state.readMarkerEventId !== null && // 1.
|
const ret = this.state.readMarkerEventId !== null && // 1.
|
||||||
(pos < 0 || pos === null); // 3., 4.
|
(pos < 0 || pos === null); // 3., 4.
|
||||||
return ret;
|
return ret;
|
||||||
},
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* called by the parent component when PageUp/Down/etc is pressed.
|
* called by the parent component when PageUp/Down/etc is pressed.
|
||||||
*
|
*
|
||||||
* We pass it down to the scroll panel.
|
* We pass it down to the scroll panel.
|
||||||
*/
|
*/
|
||||||
handleScrollKey: function(ev) {
|
handleScrollKey = ev => {
|
||||||
if (!this._messagePanel.current) { return; }
|
if (!this._messagePanel.current) { return; }
|
||||||
|
|
||||||
// jump to the live timeline on ctrl-end, rather than the end of the
|
// jump to the live timeline on ctrl-end, rather than the end of the
|
||||||
|
@ -980,9 +969,9 @@ const TimelinePanel = createReactClass({
|
||||||
} else {
|
} else {
|
||||||
this._messagePanel.current.handleScrollKey(ev);
|
this._messagePanel.current.handleScrollKey(ev);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_initTimeline: function(props) {
|
_initTimeline(props) {
|
||||||
const initialEvent = props.eventId;
|
const initialEvent = props.eventId;
|
||||||
const pixelOffset = props.eventPixelOffset;
|
const pixelOffset = props.eventPixelOffset;
|
||||||
|
|
||||||
|
@ -994,7 +983,7 @@ const TimelinePanel = createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._loadTimeline(initialEvent, pixelOffset, offsetBase);
|
return this._loadTimeline(initialEvent, pixelOffset, offsetBase);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* (re)-load the event timeline, and initialise the scroll state, centered
|
* (re)-load the event timeline, and initialise the scroll state, centered
|
||||||
|
@ -1012,7 +1001,7 @@ const TimelinePanel = createReactClass({
|
||||||
*
|
*
|
||||||
* returns a promise which will resolve when the load completes.
|
* returns a promise which will resolve when the load completes.
|
||||||
*/
|
*/
|
||||||
_loadTimeline: function(eventId, pixelOffset, offsetBase) {
|
_loadTimeline(eventId, pixelOffset, offsetBase) {
|
||||||
this._timelineWindow = new Matrix.TimelineWindow(
|
this._timelineWindow = new Matrix.TimelineWindow(
|
||||||
MatrixClientPeg.get(), this.props.timelineSet,
|
MatrixClientPeg.get(), this.props.timelineSet,
|
||||||
{windowLimit: this.props.timelineCap});
|
{windowLimit: this.props.timelineCap});
|
||||||
|
@ -1122,21 +1111,21 @@ const TimelinePanel = createReactClass({
|
||||||
});
|
});
|
||||||
prom.then(onLoaded, onError);
|
prom.then(onLoaded, onError);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// handle the completion of a timeline load or localEchoUpdate, by
|
// handle the completion of a timeline load or localEchoUpdate, by
|
||||||
// reloading the events from the timelinewindow and pending event list into
|
// reloading the events from the timelinewindow and pending event list into
|
||||||
// the state.
|
// the state.
|
||||||
_reloadEvents: function() {
|
_reloadEvents() {
|
||||||
// we might have switched rooms since the load started - just bin
|
// we might have switched rooms since the load started - just bin
|
||||||
// the results if so.
|
// the results if so.
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
|
||||||
this.setState(this._getEvents());
|
this.setState(this._getEvents());
|
||||||
},
|
}
|
||||||
|
|
||||||
// get the list of events from the timeline window and the pending event list
|
// get the list of events from the timeline window and the pending event list
|
||||||
_getEvents: function() {
|
_getEvents() {
|
||||||
const events = this._timelineWindow.getEvents();
|
const events = this._timelineWindow.getEvents();
|
||||||
const firstVisibleEventIndex = this._checkForPreJoinUISI(events);
|
const firstVisibleEventIndex = this._checkForPreJoinUISI(events);
|
||||||
|
|
||||||
|
@ -1154,7 +1143,7 @@ const TimelinePanel = createReactClass({
|
||||||
liveEvents,
|
liveEvents,
|
||||||
firstVisibleEventIndex,
|
firstVisibleEventIndex,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for undecryptable messages that were sent while the user was not in
|
* Check for undecryptable messages that were sent while the user was not in
|
||||||
|
@ -1166,7 +1155,7 @@ const TimelinePanel = createReactClass({
|
||||||
* undecryptable event that was sent while the user was not in the room. If no
|
* undecryptable event that was sent while the user was not in the room. If no
|
||||||
* such events were found, then it returns 0.
|
* such events were found, then it returns 0.
|
||||||
*/
|
*/
|
||||||
_checkForPreJoinUISI: function(events) {
|
_checkForPreJoinUISI(events) {
|
||||||
const room = this.props.timelineSet.room;
|
const room = this.props.timelineSet.room;
|
||||||
|
|
||||||
if (events.length === 0 || !room ||
|
if (events.length === 0 || !room ||
|
||||||
|
@ -1228,18 +1217,18 @@ const TimelinePanel = createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
},
|
}
|
||||||
|
|
||||||
_indexForEventId: function(evId) {
|
_indexForEventId(evId) {
|
||||||
for (let i = 0; i < this.state.events.length; ++i) {
|
for (let i = 0; i < this.state.events.length; ++i) {
|
||||||
if (evId == this.state.events[i].getId()) {
|
if (evId == this.state.events[i].getId()) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getLastDisplayedEventIndex: function(opts) {
|
_getLastDisplayedEventIndex(opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
const ignoreOwn = opts.ignoreOwn || false;
|
const ignoreOwn = opts.ignoreOwn || false;
|
||||||
const allowPartial = opts.allowPartial || false;
|
const allowPartial = opts.allowPartial || false;
|
||||||
|
@ -1313,7 +1302,7 @@ const TimelinePanel = createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the id of the event corresponding to our user's latest read-receipt.
|
* Get the id of the event corresponding to our user's latest read-receipt.
|
||||||
|
@ -1324,7 +1313,7 @@ const TimelinePanel = createReactClass({
|
||||||
* SDK.
|
* SDK.
|
||||||
* @return {String} the event ID
|
* @return {String} the event ID
|
||||||
*/
|
*/
|
||||||
_getCurrentReadReceipt: function(ignoreSynthesized) {
|
_getCurrentReadReceipt(ignoreSynthesized) {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
// the client can be null on logout
|
// the client can be null on logout
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
|
@ -1333,9 +1322,9 @@ const TimelinePanel = createReactClass({
|
||||||
|
|
||||||
const myUserId = client.credentials.userId;
|
const myUserId = client.credentials.userId;
|
||||||
return this.props.timelineSet.room.getEventReadUpTo(myUserId, ignoreSynthesized);
|
return this.props.timelineSet.room.getEventReadUpTo(myUserId, ignoreSynthesized);
|
||||||
},
|
}
|
||||||
|
|
||||||
_setReadMarker: function(eventId, eventTs, inhibitSetState) {
|
_setReadMarker(eventId, eventTs, inhibitSetState) {
|
||||||
const roomId = this.props.timelineSet.room.roomId;
|
const roomId = this.props.timelineSet.room.roomId;
|
||||||
|
|
||||||
// don't update the state (and cause a re-render) if there is
|
// don't update the state (and cause a re-render) if there is
|
||||||
|
@ -1358,9 +1347,9 @@ const TimelinePanel = createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
readMarkerEventId: eventId,
|
readMarkerEventId: eventId,
|
||||||
}, this.props.onReadMarkerUpdated);
|
}, this.props.onReadMarkerUpdated);
|
||||||
},
|
}
|
||||||
|
|
||||||
_shouldPaginate: function() {
|
_shouldPaginate() {
|
||||||
// don't try to paginate while events in the timeline are
|
// don't try to paginate while events in the timeline are
|
||||||
// still being decrypted. We don't render events while they're
|
// still being decrypted. We don't render events while they're
|
||||||
// being decrypted, so they don't take up space in the timeline.
|
// being decrypted, so they don't take up space in the timeline.
|
||||||
|
@ -1369,13 +1358,13 @@ const TimelinePanel = createReactClass({
|
||||||
return !this.state.events.some((e) => {
|
return !this.state.events.some((e) => {
|
||||||
return e.isBeingDecrypted();
|
return e.isBeingDecrypted();
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getRelationsForEvent(...args) {
|
getRelationsForEvent(...args) {
|
||||||
return this.props.timelineSet.getRelationsForEvent(...args);
|
return this.props.timelineSet.getRelationsForEvent(...args);
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const MessagePanel = sdk.getComponent("structures.MessagePanel");
|
const MessagePanel = sdk.getComponent("structures.MessagePanel");
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
|
|
||||||
|
@ -1456,7 +1445,7 @@ const TimelinePanel = createReactClass({
|
||||||
useIRCLayout={this.props.useIRCLayout}
|
useIRCLayout={this.props.useIRCLayout}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export default TimelinePanel;
|
export default TimelinePanel;
|
||||||
|
|
|
@ -16,30 +16,28 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ContentMessages from '../../ContentMessages';
|
import ContentMessages from '../../ContentMessages';
|
||||||
import dis from "../../dispatcher/dispatcher";
|
import dis from "../../dispatcher/dispatcher";
|
||||||
import filesize from "filesize";
|
import filesize from "filesize";
|
||||||
import { _t } from '../../languageHandler';
|
import { _t } from '../../languageHandler';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class UploadBar extends React.Component {
|
||||||
displayName: 'UploadBar',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
room: PropTypes.object,
|
room: PropTypes.object,
|
||||||
},
|
};
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
this.mounted = true;
|
this.mounted = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
},
|
}
|
||||||
|
|
||||||
onAction: function(payload) {
|
onAction = payload => {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'upload_progress':
|
case 'upload_progress':
|
||||||
case 'upload_finished':
|
case 'upload_finished':
|
||||||
|
@ -48,9 +46,9 @@ export default createReactClass({
|
||||||
if (this.mounted) this.forceUpdate();
|
if (this.mounted) this.forceUpdate();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const uploads = ContentMessages.sharedInstance().getCurrentUploads();
|
const uploads = ContentMessages.sharedInstance().getCurrentUploads();
|
||||||
|
|
||||||
// for testing UI... - also fix up the ContentMessages.getCurrentUploads().length
|
// for testing UI... - also fix up the ContentMessages.getCurrentUploads().length
|
||||||
|
@ -105,5 +103,5 @@ export default createReactClass({
|
||||||
<div className="mx_UploadBar_uploadFilename">{ uploadText }</div>
|
<div className="mx_UploadBar_uploadFilename">{ uploadText }</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,24 +17,21 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import SyntaxHighlight from '../views/elements/SyntaxHighlight';
|
import SyntaxHighlight from '../views/elements/SyntaxHighlight';
|
||||||
import {_t} from "../../languageHandler";
|
import {_t} from "../../languageHandler";
|
||||||
import * as sdk from "../../index";
|
import * as sdk from "../../index";
|
||||||
|
|
||||||
|
|
||||||
export default createReactClass({
|
export default class ViewSource extends React.Component {
|
||||||
displayName: 'ViewSource',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
content: PropTypes.object.isRequired,
|
content: PropTypes.object.isRequired,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
roomId: PropTypes.string.isRequired,
|
roomId: PropTypes.string.isRequired,
|
||||||
eventId: PropTypes.string.isRequired,
|
eventId: PropTypes.string.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
return (
|
return (
|
||||||
<BaseDialog className="mx_ViewSource" onFinished={this.props.onFinished} title={_t('View Source')}>
|
<BaseDialog className="mx_ViewSource" onFinished={this.props.onFinished} title={_t('View Source')}>
|
||||||
|
@ -49,5 +46,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
@ -40,50 +39,47 @@ const PHASE_EMAIL_SENT = 3;
|
||||||
// User has clicked the link in email and completed reset
|
// User has clicked the link in email and completed reset
|
||||||
const PHASE_DONE = 4;
|
const PHASE_DONE = 4;
|
||||||
|
|
||||||
export default createReactClass({
|
export default class ForgotPassword extends React.Component {
|
||||||
displayName: 'ForgotPassword',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||||
onServerConfigChange: PropTypes.func.isRequired,
|
onServerConfigChange: PropTypes.func.isRequired,
|
||||||
onLoginClick: PropTypes.func,
|
onLoginClick: PropTypes.func,
|
||||||
onComplete: PropTypes.func.isRequired,
|
onComplete: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
phase: PHASE_FORGOT,
|
||||||
phase: PHASE_FORGOT,
|
email: "",
|
||||||
email: "",
|
password: "",
|
||||||
password: "",
|
password2: "",
|
||||||
password2: "",
|
errorText: null,
|
||||||
errorText: null,
|
|
||||||
|
|
||||||
// We perform liveliness checks later, but for now suppress the errors.
|
// We perform liveliness checks later, but for now suppress the errors.
|
||||||
// We also track the server dead errors independently of the regular errors so
|
// We also track the server dead errors independently of the regular errors so
|
||||||
// that we can render it differently, and override any other error the user may
|
// that we can render it differently, and override any other error the user may
|
||||||
// be seeing.
|
// be seeing.
|
||||||
serverIsAlive: true,
|
serverIsAlive: true,
|
||||||
serverErrorIsFatal: false,
|
serverErrorIsFatal: false,
|
||||||
serverDeadError: "",
|
serverDeadError: "",
|
||||||
serverRequiresIdServer: null,
|
serverRequiresIdServer: null,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this.reset = null;
|
this.reset = null;
|
||||||
this._checkServerLiveliness(this.props.serverConfig);
|
this._checkServerLiveliness(this.props.serverConfig);
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
UNSAFE_componentWillReceiveProps: function(newProps) {
|
// eslint-disable-next-line camelcase
|
||||||
|
UNSAFE_componentWillReceiveProps(newProps) {
|
||||||
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
|
if (newProps.serverConfig.hsUrl === this.props.serverConfig.hsUrl &&
|
||||||
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
|
newProps.serverConfig.isUrl === this.props.serverConfig.isUrl) return;
|
||||||
|
|
||||||
// Do a liveliness check on the new URLs
|
// Do a liveliness check on the new URLs
|
||||||
this._checkServerLiveliness(newProps.serverConfig);
|
this._checkServerLiveliness(newProps.serverConfig);
|
||||||
},
|
}
|
||||||
|
|
||||||
_checkServerLiveliness: async function(serverConfig) {
|
async _checkServerLiveliness(serverConfig) {
|
||||||
try {
|
try {
|
||||||
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
|
await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
|
||||||
serverConfig.hsUrl,
|
serverConfig.hsUrl,
|
||||||
|
@ -100,9 +96,9 @@ export default createReactClass({
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState(AutoDiscoveryUtils.authComponentStateForError(e, "forgot_password"));
|
this.setState(AutoDiscoveryUtils.authComponentStateForError(e, "forgot_password"));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
submitPasswordReset: function(email, password) {
|
submitPasswordReset(email, password) {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_SENDING_EMAIL,
|
phase: PHASE_SENDING_EMAIL,
|
||||||
});
|
});
|
||||||
|
@ -117,9 +113,9 @@ export default createReactClass({
|
||||||
phase: PHASE_FORGOT,
|
phase: PHASE_FORGOT,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onVerify: async function(ev) {
|
onVerify = async ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
if (!this.reset) {
|
if (!this.reset) {
|
||||||
console.error("onVerify called before submitPasswordReset!");
|
console.error("onVerify called before submitPasswordReset!");
|
||||||
|
@ -131,9 +127,9 @@ export default createReactClass({
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.showErrorDialog(err.message);
|
this.showErrorDialog(err.message);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onSubmitForm: async function(ev) {
|
onSubmitForm = async ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
// refresh the server errors, just in case the server came back online
|
// refresh the server errors, just in case the server came back online
|
||||||
|
@ -166,41 +162,41 @@ export default createReactClass({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onInputChanged: function(stateKey, ev) {
|
onInputChanged = (stateKey, ev) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
[stateKey]: ev.target.value,
|
[stateKey]: ev.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
async onServerDetailsNextPhaseClick() {
|
onServerDetailsNextPhaseClick = async () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_FORGOT,
|
phase: PHASE_FORGOT,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onEditServerDetailsClick(ev) {
|
onEditServerDetailsClick = ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_SERVER_DETAILS,
|
phase: PHASE_SERVER_DETAILS,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onLoginClick: function(ev) {
|
onLoginClick = ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.props.onLoginClick();
|
this.props.onLoginClick();
|
||||||
},
|
};
|
||||||
|
|
||||||
showErrorDialog: function(body, title) {
|
showErrorDialog(body, title) {
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
|
Modal.createTrackedDialog('Forgot Password Error', '', ErrorDialog, {
|
||||||
title: title,
|
title: title,
|
||||||
description: body,
|
description: body,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
renderServerDetails() {
|
renderServerDetails() {
|
||||||
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
||||||
|
@ -218,7 +214,7 @@ export default createReactClass({
|
||||||
submitText={_t("Next")}
|
submitText={_t("Next")}
|
||||||
submitClass="mx_Login_submit"
|
submitClass="mx_Login_submit"
|
||||||
/>;
|
/>;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderForgot() {
|
renderForgot() {
|
||||||
const Field = sdk.getComponent('elements.Field');
|
const Field = sdk.getComponent('elements.Field');
|
||||||
|
@ -335,12 +331,12 @@ export default createReactClass({
|
||||||
{_t('Sign in instead')}
|
{_t('Sign in instead')}
|
||||||
</a>
|
</a>
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderSendingEmail() {
|
renderSendingEmail() {
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
return <Spinner />;
|
return <Spinner />;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderEmailSent() {
|
renderEmailSent() {
|
||||||
return <div>
|
return <div>
|
||||||
|
@ -350,7 +346,7 @@ export default createReactClass({
|
||||||
<input className="mx_Login_submit" type="button" onClick={this.onVerify}
|
<input className="mx_Login_submit" type="button" onClick={this.onVerify}
|
||||||
value={_t('I have verified my email address')} />
|
value={_t('I have verified my email address')} />
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderDone() {
|
renderDone() {
|
||||||
return <div>
|
return <div>
|
||||||
|
@ -363,9 +359,9 @@ export default createReactClass({
|
||||||
<input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
|
<input className="mx_Login_submit" type="button" onClick={this.props.onComplete}
|
||||||
value={_t('Return to login screen')} />
|
value={_t('Return to login screen')} />
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
||||||
const AuthBody = sdk.getComponent("auth.AuthBody");
|
const AuthBody = sdk.getComponent("auth.AuthBody");
|
||||||
|
|
||||||
|
@ -397,5 +393,5 @@ export default createReactClass({
|
||||||
</AuthBody>
|
</AuthBody>
|
||||||
</AuthPage>
|
</AuthPage>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {_t, _td} from '../../../languageHandler';
|
import {_t, _td} from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
@ -56,10 +55,8 @@ _td("General failure");
|
||||||
/**
|
/**
|
||||||
* A wire component which glues together login UI components and Login logic
|
* A wire component which glues together login UI components and Login logic
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class Login extends React.Component {
|
||||||
displayName: 'Login',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// Called when the user has logged in. Params:
|
// Called when the user has logged in. Params:
|
||||||
// - The object returned by the login API
|
// - The object returned by the login API
|
||||||
// - The user's password, if applicable, (may be cached in memory for a
|
// - The user's password, if applicable, (may be cached in memory for a
|
||||||
|
@ -85,10 +82,14 @@ export default createReactClass({
|
||||||
|
|
||||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||||
isSyncing: PropTypes.bool,
|
isSyncing: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this._unmounted = false;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
busy: false,
|
busy: false,
|
||||||
busyLoggingIn: null,
|
busyLoggingIn: null,
|
||||||
errorText: null,
|
errorText: null,
|
||||||
|
@ -113,11 +114,6 @@ export default createReactClass({
|
||||||
serverErrorIsFatal: false,
|
serverErrorIsFatal: false,
|
||||||
serverDeadError: "",
|
serverDeadError: "",
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Move this to constructor
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._unmounted = false;
|
|
||||||
|
|
||||||
// map from login step type to a function which will render a control
|
// map from login step type to a function which will render a control
|
||||||
// letting you do that login type
|
// letting you do that login type
|
||||||
|
@ -130,11 +126,11 @@ export default createReactClass({
|
||||||
};
|
};
|
||||||
|
|
||||||
this._initLoginLogic();
|
this._initLoginLogic();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
UNSAFE_componentWillReceiveProps(newProps) {
|
UNSAFE_componentWillReceiveProps(newProps) {
|
||||||
|
@ -143,20 +139,18 @@ export default createReactClass({
|
||||||
|
|
||||||
// Ensure that we end up actually logging in to the right place
|
// Ensure that we end up actually logging in to the right place
|
||||||
this._initLoginLogic(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl);
|
this._initLoginLogic(newProps.serverConfig.hsUrl, newProps.serverConfig.isUrl);
|
||||||
},
|
}
|
||||||
|
|
||||||
onPasswordLoginError: function(errorText) {
|
onPasswordLoginError = errorText => {
|
||||||
this.setState({
|
this.setState({
|
||||||
errorText,
|
errorText,
|
||||||
loginIncorrect: Boolean(errorText),
|
loginIncorrect: Boolean(errorText),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
isBusy: function() {
|
isBusy = () => this.state.busy || this.props.busy;
|
||||||
return this.state.busy || this.props.busy;
|
|
||||||
},
|
|
||||||
|
|
||||||
onPasswordLogin: async function(username, phoneCountry, phoneNumber, password) {
|
onPasswordLogin = async (username, phoneCountry, phoneNumber, password) => {
|
||||||
if (!this.state.serverIsAlive) {
|
if (!this.state.serverIsAlive) {
|
||||||
this.setState({busy: true});
|
this.setState({busy: true});
|
||||||
// Do a quick liveliness check on the URLs
|
// Do a quick liveliness check on the URLs
|
||||||
|
@ -263,13 +257,13 @@ export default createReactClass({
|
||||||
loginIncorrect: error.httpStatus === 401 || error.httpStatus === 403,
|
loginIncorrect: error.httpStatus === 401 || error.httpStatus === 403,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onUsernameChanged: function(username) {
|
onUsernameChanged = username => {
|
||||||
this.setState({ username: username });
|
this.setState({ username: username });
|
||||||
},
|
};
|
||||||
|
|
||||||
onUsernameBlur: async function(username) {
|
onUsernameBlur = async username => {
|
||||||
const doWellknownLookup = username[0] === "@";
|
const doWellknownLookup = username[0] === "@";
|
||||||
this.setState({
|
this.setState({
|
||||||
username: username,
|
username: username,
|
||||||
|
@ -314,19 +308,19 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onPhoneCountryChanged: function(phoneCountry) {
|
onPhoneCountryChanged = phoneCountry => {
|
||||||
this.setState({ phoneCountry: phoneCountry });
|
this.setState({ phoneCountry: phoneCountry });
|
||||||
},
|
};
|
||||||
|
|
||||||
onPhoneNumberChanged: function(phoneNumber) {
|
onPhoneNumberChanged = phoneNumber => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phoneNumber: phoneNumber,
|
phoneNumber: phoneNumber,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onPhoneNumberBlur: function(phoneNumber) {
|
onPhoneNumberBlur = phoneNumber => {
|
||||||
// Validate the phone number entered
|
// Validate the phone number entered
|
||||||
if (!PHONE_NUMBER_REGEX.test(phoneNumber)) {
|
if (!PHONE_NUMBER_REGEX.test(phoneNumber)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -339,15 +333,15 @@ export default createReactClass({
|
||||||
canTryLogin: true,
|
canTryLogin: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRegisterClick: function(ev) {
|
onRegisterClick = ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.props.onRegisterClick();
|
this.props.onRegisterClick();
|
||||||
},
|
};
|
||||||
|
|
||||||
onTryRegisterClick: function(ev) {
|
onTryRegisterClick = ev => {
|
||||||
const step = this._getCurrentFlowStep();
|
const step = this._getCurrentFlowStep();
|
||||||
if (step === 'm.login.sso' || step === 'm.login.cas') {
|
if (step === 'm.login.sso' || step === 'm.login.cas') {
|
||||||
// If we're showing SSO it means that registration is also probably disabled,
|
// If we're showing SSO it means that registration is also probably disabled,
|
||||||
|
@ -361,23 +355,23 @@ export default createReactClass({
|
||||||
// Don't intercept - just go through to the register page
|
// Don't intercept - just go through to the register page
|
||||||
this.onRegisterClick(ev);
|
this.onRegisterClick(ev);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
async onServerDetailsNextPhaseClick() {
|
onServerDetailsNextPhaseClick = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_LOGIN,
|
phase: PHASE_LOGIN,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onEditServerDetailsClick(ev) {
|
onEditServerDetailsClick = ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_SERVER_DETAILS,
|
phase: PHASE_SERVER_DETAILS,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_initLoginLogic: async function(hsUrl, isUrl) {
|
async _initLoginLogic(hsUrl, isUrl) {
|
||||||
hsUrl = hsUrl || this.props.serverConfig.hsUrl;
|
hsUrl = hsUrl || this.props.serverConfig.hsUrl;
|
||||||
isUrl = isUrl || this.props.serverConfig.isUrl;
|
isUrl = isUrl || this.props.serverConfig.isUrl;
|
||||||
|
|
||||||
|
@ -465,9 +459,9 @@ export default createReactClass({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_isSupportedFlow: function(flow) {
|
_isSupportedFlow(flow) {
|
||||||
// technically the flow can have multiple steps, but no one does this
|
// technically the flow can have multiple steps, but no one does this
|
||||||
// for login and loginLogic doesn't support it so we can ignore it.
|
// for login and loginLogic doesn't support it so we can ignore it.
|
||||||
if (!this._stepRendererMap[flow.type]) {
|
if (!this._stepRendererMap[flow.type]) {
|
||||||
|
@ -475,11 +469,11 @@ export default createReactClass({
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getCurrentFlowStep: function() {
|
_getCurrentFlowStep() {
|
||||||
return this._loginLogic ? this._loginLogic.getCurrentFlowStep() : null;
|
return this._loginLogic ? this._loginLogic.getCurrentFlowStep() : null;
|
||||||
},
|
}
|
||||||
|
|
||||||
_errorTextFromError(err) {
|
_errorTextFromError(err) {
|
||||||
let errCode = err.errcode;
|
let errCode = err.errcode;
|
||||||
|
@ -526,7 +520,7 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorText;
|
return errorText;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderServerComponent() {
|
renderServerComponent() {
|
||||||
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
const ServerConfig = sdk.getComponent("auth.ServerConfig");
|
||||||
|
@ -552,7 +546,7 @@ export default createReactClass({
|
||||||
delayTimeMs={250}
|
delayTimeMs={250}
|
||||||
{...serverDetailsProps}
|
{...serverDetailsProps}
|
||||||
/>;
|
/>;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderLoginComponentForStep() {
|
renderLoginComponentForStep() {
|
||||||
if (PHASES_ENABLED && this.state.phase !== PHASE_LOGIN) {
|
if (PHASES_ENABLED && this.state.phase !== PHASE_LOGIN) {
|
||||||
|
@ -572,9 +566,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
}
|
||||||
|
|
||||||
_renderPasswordStep: function() {
|
_renderPasswordStep() {
|
||||||
const PasswordLogin = sdk.getComponent('auth.PasswordLogin');
|
const PasswordLogin = sdk.getComponent('auth.PasswordLogin');
|
||||||
|
|
||||||
let onEditServerDetailsClick = null;
|
let onEditServerDetailsClick = null;
|
||||||
|
@ -603,9 +597,9 @@ export default createReactClass({
|
||||||
busy={this.props.isSyncing || this.state.busyLoggingIn}
|
busy={this.props.isSyncing || this.state.busyLoggingIn}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
_renderSsoStep: function(loginType) {
|
_renderSsoStep(loginType) {
|
||||||
const SignInToText = sdk.getComponent('views.auth.SignInToText');
|
const SignInToText = sdk.getComponent('views.auth.SignInToText');
|
||||||
|
|
||||||
let onEditServerDetailsClick = null;
|
let onEditServerDetailsClick = null;
|
||||||
|
@ -634,9 +628,9 @@ export default createReactClass({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
const InlineSpinner = sdk.getComponent("elements.InlineSpinner");
|
const InlineSpinner = sdk.getComponent("elements.InlineSpinner");
|
||||||
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
const AuthHeader = sdk.getComponent("auth.AuthHeader");
|
||||||
|
@ -704,5 +698,5 @@ export default createReactClass({
|
||||||
</AuthBody>
|
</AuthBody>
|
||||||
</AuthPage>
|
</AuthPage>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -15,29 +15,24 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import AuthPage from "../../views/auth/AuthPage";
|
import AuthPage from "../../views/auth/AuthPage";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class PostRegistration extends React.Component {
|
||||||
displayName: 'PostRegistration',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
onComplete: PropTypes.func.isRequired,
|
onComplete: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
avatarUrl: null,
|
||||||
avatarUrl: null,
|
errorString: null,
|
||||||
errorString: null,
|
busy: false,
|
||||||
busy: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
// There is some assymetry between ChangeDisplayName and ChangeAvatar,
|
// There is some assymetry between ChangeDisplayName and ChangeAvatar,
|
||||||
// as ChangeDisplayName will auto-get the name but ChangeAvatar expects
|
// as ChangeDisplayName will auto-get the name but ChangeAvatar expects
|
||||||
// the URL to be passed to you (because it's also used for room avatars).
|
// the URL to be passed to you (because it's also used for room avatars).
|
||||||
|
@ -55,9 +50,9 @@ export default createReactClass({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
|
const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName');
|
||||||
const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
|
const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar');
|
||||||
const AuthHeader = sdk.getComponent('auth.AuthHeader');
|
const AuthHeader = sdk.getComponent('auth.AuthHeader');
|
||||||
|
@ -78,5 +73,5 @@ export default createReactClass({
|
||||||
</AuthBody>
|
</AuthBody>
|
||||||
</AuthPage>
|
</AuthPage>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
||||||
|
|
||||||
import Matrix from 'matrix-js-sdk';
|
import Matrix from 'matrix-js-sdk';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t, _td } from '../../../languageHandler';
|
import { _t, _td } from '../../../languageHandler';
|
||||||
|
@ -43,10 +42,8 @@ const PHASE_REGISTRATION = 1;
|
||||||
// Enable phases for registration
|
// Enable phases for registration
|
||||||
const PHASES_ENABLED = true;
|
const PHASES_ENABLED = true;
|
||||||
|
|
||||||
export default createReactClass({
|
export default class Registration extends React.Component {
|
||||||
displayName: 'Registration',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// Called when the user has logged in. Params:
|
// Called when the user has logged in. Params:
|
||||||
// - object with userId, deviceId, homeserverUrl, identityServerUrl, accessToken
|
// - object with userId, deviceId, homeserverUrl, identityServerUrl, accessToken
|
||||||
// - The user's password, if available and applicable (may be cached in memory
|
// - The user's password, if available and applicable (may be cached in memory
|
||||||
|
@ -65,12 +62,13 @@ export default createReactClass({
|
||||||
onLoginClick: PropTypes.func.isRequired,
|
onLoginClick: PropTypes.func.isRequired,
|
||||||
onServerConfigChange: PropTypes.func.isRequired,
|
onServerConfigChange: PropTypes.func.isRequired,
|
||||||
defaultDeviceDisplayName: PropTypes.string,
|
defaultDeviceDisplayName: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
const serverType = ServerType.getTypeFromServerConfig(this.props.serverConfig);
|
const serverType = ServerType.getTypeFromServerConfig(this.props.serverConfig);
|
||||||
|
this.state = {
|
||||||
return {
|
|
||||||
busy: false,
|
busy: false,
|
||||||
errorText: null,
|
errorText: null,
|
||||||
// We remember the values entered by the user because
|
// We remember the values entered by the user because
|
||||||
|
@ -118,12 +116,12 @@ export default createReactClass({
|
||||||
// this is the user ID that's logged in.
|
// this is the user ID that's logged in.
|
||||||
differentLoggedInUserId: null,
|
differentLoggedInUserId: null,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
this._replaceClient();
|
this._replaceClient();
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
UNSAFE_componentWillReceiveProps(newProps) {
|
UNSAFE_componentWillReceiveProps(newProps) {
|
||||||
|
@ -142,7 +140,7 @@ export default createReactClass({
|
||||||
phase: this.getDefaultPhaseForServerType(serverType),
|
phase: this.getDefaultPhaseForServerType(serverType),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
getDefaultPhaseForServerType(type) {
|
getDefaultPhaseForServerType(type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -155,9 +153,9 @@ export default createReactClass({
|
||||||
case ServerType.ADVANCED:
|
case ServerType.ADVANCED:
|
||||||
return PHASE_SERVER_DETAILS;
|
return PHASE_SERVER_DETAILS;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onServerTypeChange(type) {
|
onServerTypeChange = type => {
|
||||||
this.setState({
|
this.setState({
|
||||||
serverType: type,
|
serverType: type,
|
||||||
});
|
});
|
||||||
|
@ -184,9 +182,9 @@ export default createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: this.getDefaultPhaseForServerType(type),
|
phase: this.getDefaultPhaseForServerType(type),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_replaceClient: async function(serverConfig) {
|
async _replaceClient(serverConfig) {
|
||||||
this.setState({
|
this.setState({
|
||||||
errorText: null,
|
errorText: null,
|
||||||
serverDeadError: null,
|
serverDeadError: null,
|
||||||
|
@ -286,18 +284,18 @@ export default createReactClass({
|
||||||
showGenericError(e);
|
showGenericError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onFormSubmit: function(formVals) {
|
onFormSubmit = formVals => {
|
||||||
this.setState({
|
this.setState({
|
||||||
errorText: "",
|
errorText: "",
|
||||||
busy: true,
|
busy: true,
|
||||||
formVals: formVals,
|
formVals: formVals,
|
||||||
doingUIAuth: true,
|
doingUIAuth: true,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_requestEmailToken: function(emailAddress, clientSecret, sendAttempt, sessionId) {
|
_requestEmailToken = (emailAddress, clientSecret, sendAttempt, sessionId) => {
|
||||||
return this.state.matrixClient.requestRegisterEmailToken(
|
return this.state.matrixClient.requestRegisterEmailToken(
|
||||||
emailAddress,
|
emailAddress,
|
||||||
clientSecret,
|
clientSecret,
|
||||||
|
@ -309,9 +307,9 @@ export default createReactClass({
|
||||||
session_id: sessionId,
|
session_id: sessionId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
_onUIAuthFinished: async function(success, response, extra) {
|
_onUIAuthFinished = async (success, response, extra) => {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
let msg = response.message || response.toString();
|
let msg = response.message || response.toString();
|
||||||
// can we give a better error message?
|
// can we give a better error message?
|
||||||
|
@ -395,9 +393,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
},
|
};
|
||||||
|
|
||||||
_setupPushers: function() {
|
_setupPushers() {
|
||||||
if (!this.props.brand) {
|
if (!this.props.brand) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -418,15 +416,15 @@ export default createReactClass({
|
||||||
}, (error) => {
|
}, (error) => {
|
||||||
console.error("Couldn't get pushers: " + error);
|
console.error("Couldn't get pushers: " + error);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onLoginClick: function(ev) {
|
onLoginClick = ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.props.onLoginClick();
|
this.props.onLoginClick();
|
||||||
},
|
};
|
||||||
|
|
||||||
onGoToFormClicked(ev) {
|
onGoToFormClicked = ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this._replaceClient();
|
this._replaceClient();
|
||||||
|
@ -435,23 +433,23 @@ export default createReactClass({
|
||||||
doingUIAuth: false,
|
doingUIAuth: false,
|
||||||
phase: PHASE_REGISTRATION,
|
phase: PHASE_REGISTRATION,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
async onServerDetailsNextPhaseClick() {
|
onServerDetailsNextPhaseClick = async () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_REGISTRATION,
|
phase: PHASE_REGISTRATION,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onEditServerDetailsClick(ev) {
|
onEditServerDetailsClick = ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: PHASE_SERVER_DETAILS,
|
phase: PHASE_SERVER_DETAILS,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_makeRegisterRequest: function(auth) {
|
_makeRegisterRequest = auth => {
|
||||||
// We inhibit login if we're trying to register with an email address: this
|
// We inhibit login if we're trying to register with an email address: this
|
||||||
// avoids a lot of complex race conditions that can occur if we try to log
|
// avoids a lot of complex race conditions that can occur if we try to log
|
||||||
// the user in one one or both of the tabs they might end up with after
|
// the user in one one or both of the tabs they might end up with after
|
||||||
|
@ -471,20 +469,20 @@ export default createReactClass({
|
||||||
if (auth) registerParams.auth = auth;
|
if (auth) registerParams.auth = auth;
|
||||||
if (inhibitLogin !== undefined && inhibitLogin !== null) registerParams.inhibit_login = inhibitLogin;
|
if (inhibitLogin !== undefined && inhibitLogin !== null) registerParams.inhibit_login = inhibitLogin;
|
||||||
return this.state.matrixClient.registerRequest(registerParams);
|
return this.state.matrixClient.registerRequest(registerParams);
|
||||||
},
|
};
|
||||||
|
|
||||||
_getUIAuthInputs: function() {
|
_getUIAuthInputs() {
|
||||||
return {
|
return {
|
||||||
emailAddress: this.state.formVals.email,
|
emailAddress: this.state.formVals.email,
|
||||||
phoneCountry: this.state.formVals.phoneCountry,
|
phoneCountry: this.state.formVals.phoneCountry,
|
||||||
phoneNumber: this.state.formVals.phoneNumber,
|
phoneNumber: this.state.formVals.phoneNumber,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
// Links to the login page shown after registration is completed are routed through this
|
// Links to the login page shown after registration is completed are routed through this
|
||||||
// which checks the user hasn't already logged in somewhere else (perhaps we should do
|
// which checks the user hasn't already logged in somewhere else (perhaps we should do
|
||||||
// this more generally?)
|
// this more generally?)
|
||||||
_onLoginClickWithCheck: async function(ev) {
|
_onLoginClickWithCheck = async ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
const sessionLoaded = await Lifecycle.loadSession({ignoreGuest: true});
|
const sessionLoaded = await Lifecycle.loadSession({ignoreGuest: true});
|
||||||
|
@ -492,7 +490,7 @@ export default createReactClass({
|
||||||
// ok fine, there's still no session: really go to the login page
|
// ok fine, there's still no session: really go to the login page
|
||||||
this.props.onLoginClick();
|
this.props.onLoginClick();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
renderServerComponent() {
|
renderServerComponent() {
|
||||||
const ServerTypeSelector = sdk.getComponent("auth.ServerTypeSelector");
|
const ServerTypeSelector = sdk.getComponent("auth.ServerTypeSelector");
|
||||||
|
@ -553,7 +551,7 @@ export default createReactClass({
|
||||||
/>
|
/>
|
||||||
{serverDetails}
|
{serverDetails}
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderRegisterComponent() {
|
renderRegisterComponent() {
|
||||||
if (PHASES_ENABLED && this.state.phase !== PHASE_REGISTRATION) {
|
if (PHASES_ENABLED && this.state.phase !== PHASE_REGISTRATION) {
|
||||||
|
@ -608,9 +606,9 @@ export default createReactClass({
|
||||||
serverRequiresIdServer={this.state.serverRequiresIdServer}
|
serverRequiresIdServer={this.state.serverRequiresIdServer}
|
||||||
/>;
|
/>;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const AuthHeader = sdk.getComponent('auth.AuthHeader');
|
const AuthHeader = sdk.getComponent('auth.AuthHeader');
|
||||||
const AuthBody = sdk.getComponent("auth.AuthBody");
|
const AuthBody = sdk.getComponent("auth.AuthBody");
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
@ -706,5 +704,5 @@ export default createReactClass({
|
||||||
</AuthBody>
|
</AuthBody>
|
||||||
</AuthPage>
|
</AuthPage>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,16 +18,13 @@ limitations under the License.
|
||||||
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
|
|
||||||
export default createReactClass({
|
export default class AuthFooter extends React.Component {
|
||||||
displayName: 'AuthFooter',
|
render() {
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_AuthFooter">
|
<div className="mx_AuthFooter">
|
||||||
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">{ _t("powered by Matrix") }</a>
|
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">{ _t("powered by Matrix") }</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,17 +17,14 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class AuthHeader extends React.Component {
|
||||||
displayName: 'AuthHeader',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
disableLanguageSelector: PropTypes.bool,
|
disableLanguageSelector: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const AuthHeaderLogo = sdk.getComponent('auth.AuthHeaderLogo');
|
const AuthHeaderLogo = sdk.getComponent('auth.AuthHeaderLogo');
|
||||||
const LanguageSelector = sdk.getComponent('views.auth.LanguageSelector');
|
const LanguageSelector = sdk.getComponent('views.auth.LanguageSelector');
|
||||||
|
|
||||||
|
@ -37,5 +34,5 @@ export default createReactClass({
|
||||||
<LanguageSelector disabled={this.props.disableLanguageSelector} />
|
<LanguageSelector disabled={this.props.disableLanguageSelector} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
|
@ -24,36 +23,31 @@ const DIV_ID = 'mx_recaptcha';
|
||||||
/**
|
/**
|
||||||
* A pure UI component which displays a captcha form.
|
* A pure UI component which displays a captcha form.
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class CaptchaForm extends React.Component {
|
||||||
displayName: 'CaptchaForm',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
sitePublicKey: PropTypes.string,
|
sitePublicKey: PropTypes.string,
|
||||||
|
|
||||||
// called with the captcha response
|
// called with the captcha response
|
||||||
onCaptchaResponse: PropTypes.func,
|
onCaptchaResponse: PropTypes.func,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
onCaptchaResponse: () => {},
|
||||||
onCaptchaResponse: () => {},
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
errorText: null,
|
errorText: null,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._captchaWidgetId = null;
|
this._captchaWidgetId = null;
|
||||||
|
|
||||||
this._recaptchaContainer = createRef();
|
this._recaptchaContainer = createRef();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
// Just putting a script tag into the returned jsx doesn't work, annoyingly,
|
// Just putting a script tag into the returned jsx doesn't work, annoyingly,
|
||||||
// so we do this instead.
|
// so we do this instead.
|
||||||
if (global.grecaptcha) {
|
if (global.grecaptcha) {
|
||||||
|
@ -68,13 +62,13 @@ export default createReactClass({
|
||||||
);
|
);
|
||||||
this._recaptchaContainer.current.appendChild(scriptTag);
|
this._recaptchaContainer.current.appendChild(scriptTag);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
this._resetRecaptcha();
|
this._resetRecaptcha();
|
||||||
},
|
}
|
||||||
|
|
||||||
_renderRecaptcha: function(divId) {
|
_renderRecaptcha(divId) {
|
||||||
if (!global.grecaptcha) {
|
if (!global.grecaptcha) {
|
||||||
console.error("grecaptcha not loaded!");
|
console.error("grecaptcha not loaded!");
|
||||||
throw new Error("Recaptcha did not load successfully");
|
throw new Error("Recaptcha did not load successfully");
|
||||||
|
@ -93,15 +87,15 @@ export default createReactClass({
|
||||||
sitekey: publicKey,
|
sitekey: publicKey,
|
||||||
callback: this.props.onCaptchaResponse,
|
callback: this.props.onCaptchaResponse,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_resetRecaptcha: function() {
|
_resetRecaptcha() {
|
||||||
if (this._captchaWidgetId !== null) {
|
if (this._captchaWidgetId !== null) {
|
||||||
global.grecaptcha.reset(this._captchaWidgetId);
|
global.grecaptcha.reset(this._captchaWidgetId);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_onCaptchaLoaded: function() {
|
_onCaptchaLoaded() {
|
||||||
console.log("Loaded recaptcha script.");
|
console.log("Loaded recaptcha script.");
|
||||||
try {
|
try {
|
||||||
this._renderRecaptcha(DIV_ID);
|
this._renderRecaptcha(DIV_ID);
|
||||||
|
@ -110,9 +104,9 @@ export default createReactClass({
|
||||||
errorText: e.toString(),
|
errorText: e.toString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let error = null;
|
let error = null;
|
||||||
if (this.state.errorText) {
|
if (this.state.errorText) {
|
||||||
error = (
|
error = (
|
||||||
|
@ -131,5 +125,5 @@ export default createReactClass({
|
||||||
{ error }
|
{ error }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,14 +16,11 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class CustomServerDialog extends React.Component {
|
||||||
displayName: 'CustomServerDialog',
|
render() {
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
return (
|
return (
|
||||||
<div className="mx_ErrorDialog">
|
<div className="mx_ErrorDialog">
|
||||||
|
@ -46,5 +43,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
@ -75,14 +74,10 @@ import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
|
||||||
export const DEFAULT_PHASE = 0;
|
export const DEFAULT_PHASE = 0;
|
||||||
|
|
||||||
export const PasswordAuthEntry = createReactClass({
|
export class PasswordAuthEntry extends React.Component {
|
||||||
displayName: 'PasswordAuthEntry',
|
static LOGIN_TYPE = "m.login.password";
|
||||||
|
|
||||||
statics: {
|
static propTypes = {
|
||||||
LOGIN_TYPE: "m.login.password",
|
|
||||||
},
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
matrixClient: PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
submitAuthDict: PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
errorText: PropTypes.string,
|
errorText: PropTypes.string,
|
||||||
|
@ -90,19 +85,17 @@ export const PasswordAuthEntry = createReactClass({
|
||||||
// happen?
|
// happen?
|
||||||
busy: PropTypes.bool,
|
busy: PropTypes.bool,
|
||||||
onPhaseChange: PropTypes.func.isRequired,
|
onPhaseChange: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this.props.onPhaseChange(DEFAULT_PHASE);
|
this.props.onPhaseChange(DEFAULT_PHASE);
|
||||||
},
|
}
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
password: "",
|
||||||
password: "",
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_onSubmit: function(e) {
|
_onSubmit = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.props.busy) return;
|
if (this.props.busy) return;
|
||||||
|
|
||||||
|
@ -117,16 +110,16 @@ export const PasswordAuthEntry = createReactClass({
|
||||||
},
|
},
|
||||||
password: this.state.password,
|
password: this.state.password,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onPasswordFieldChange: function(ev) {
|
_onPasswordFieldChange = ev => {
|
||||||
// enable the submit button iff the password is non-empty
|
// enable the submit button iff the password is non-empty
|
||||||
this.setState({
|
this.setState({
|
||||||
password: ev.target.value,
|
password: ev.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const passwordBoxClass = classnames({
|
const passwordBoxClass = classnames({
|
||||||
"error": this.props.errorText,
|
"error": this.props.errorText,
|
||||||
});
|
});
|
||||||
|
@ -176,36 +169,32 @@ export const PasswordAuthEntry = createReactClass({
|
||||||
{ errorSection }
|
{ errorSection }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export const RecaptchaAuthEntry = createReactClass({
|
export class RecaptchaAuthEntry extends React.Component {
|
||||||
displayName: 'RecaptchaAuthEntry',
|
static LOGIN_TYPE = "m.login.recaptcha";
|
||||||
|
|
||||||
statics: {
|
static propTypes = {
|
||||||
LOGIN_TYPE: "m.login.recaptcha",
|
|
||||||
},
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
submitAuthDict: PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
stageParams: PropTypes.object.isRequired,
|
stageParams: PropTypes.object.isRequired,
|
||||||
errorText: PropTypes.string,
|
errorText: PropTypes.string,
|
||||||
busy: PropTypes.bool,
|
busy: PropTypes.bool,
|
||||||
onPhaseChange: PropTypes.func.isRequired,
|
onPhaseChange: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this.props.onPhaseChange(DEFAULT_PHASE);
|
this.props.onPhaseChange(DEFAULT_PHASE);
|
||||||
},
|
}
|
||||||
|
|
||||||
_onCaptchaResponse: function(response) {
|
_onCaptchaResponse = response => {
|
||||||
this.props.submitAuthDict({
|
this.props.submitAuthDict({
|
||||||
type: RecaptchaAuthEntry.LOGIN_TYPE,
|
type: RecaptchaAuthEntry.LOGIN_TYPE,
|
||||||
response: response,
|
response: response,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
if (this.props.busy) {
|
if (this.props.busy) {
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
|
@ -241,31 +230,24 @@ export const RecaptchaAuthEntry = createReactClass({
|
||||||
{ errorSection }
|
{ errorSection }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export const TermsAuthEntry = createReactClass({
|
export class TermsAuthEntry extends React.Component {
|
||||||
displayName: 'TermsAuthEntry',
|
static LOGIN_TYPE = "m.login.terms";
|
||||||
|
|
||||||
statics: {
|
static propTypes = {
|
||||||
LOGIN_TYPE: "m.login.terms",
|
|
||||||
},
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
submitAuthDict: PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
stageParams: PropTypes.object.isRequired,
|
stageParams: PropTypes.object.isRequired,
|
||||||
errorText: PropTypes.string,
|
errorText: PropTypes.string,
|
||||||
busy: PropTypes.bool,
|
busy: PropTypes.bool,
|
||||||
showContinue: PropTypes.bool,
|
showContinue: PropTypes.bool,
|
||||||
onPhaseChange: PropTypes.func.isRequired,
|
onPhaseChange: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
componentDidMount: function() {
|
constructor(props) {
|
||||||
this.props.onPhaseChange(DEFAULT_PHASE);
|
super(props);
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Move this to constructor
|
|
||||||
componentWillMount: function() {
|
|
||||||
// example stageParams:
|
// example stageParams:
|
||||||
//
|
//
|
||||||
// {
|
// {
|
||||||
|
@ -310,17 +292,22 @@ export const TermsAuthEntry = createReactClass({
|
||||||
pickedPolicies.push(langPolicy);
|
pickedPolicies.push(langPolicy);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.state = {
|
||||||
"toggledPolicies": initToggles,
|
toggledPolicies: initToggles,
|
||||||
"policies": pickedPolicies,
|
policies: pickedPolicies,
|
||||||
});
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
tryContinue: function() {
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.onPhaseChange(DEFAULT_PHASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
tryContinue = () => {
|
||||||
this._trySubmit();
|
this._trySubmit();
|
||||||
},
|
};
|
||||||
|
|
||||||
_togglePolicy: function(policyId) {
|
_togglePolicy(policyId) {
|
||||||
const newToggles = {};
|
const newToggles = {};
|
||||||
for (const policy of this.state.policies) {
|
for (const policy of this.state.policies) {
|
||||||
let checked = this.state.toggledPolicies[policy.id];
|
let checked = this.state.toggledPolicies[policy.id];
|
||||||
|
@ -329,9 +316,9 @@ export const TermsAuthEntry = createReactClass({
|
||||||
newToggles[policy.id] = checked;
|
newToggles[policy.id] = checked;
|
||||||
}
|
}
|
||||||
this.setState({"toggledPolicies": newToggles});
|
this.setState({"toggledPolicies": newToggles});
|
||||||
},
|
}
|
||||||
|
|
||||||
_trySubmit: function() {
|
_trySubmit = () => {
|
||||||
let allChecked = true;
|
let allChecked = true;
|
||||||
for (const policy of this.state.policies) {
|
for (const policy of this.state.policies) {
|
||||||
const checked = this.state.toggledPolicies[policy.id];
|
const checked = this.state.toggledPolicies[policy.id];
|
||||||
|
@ -340,9 +327,9 @@ export const TermsAuthEntry = createReactClass({
|
||||||
|
|
||||||
if (allChecked) this.props.submitAuthDict({type: TermsAuthEntry.LOGIN_TYPE});
|
if (allChecked) this.props.submitAuthDict({type: TermsAuthEntry.LOGIN_TYPE});
|
||||||
else this.setState({errorText: _t("Please review and accept all of the homeserver's policies")});
|
else this.setState({errorText: _t("Please review and accept all of the homeserver's policies")});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
if (this.props.busy) {
|
if (this.props.busy) {
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
|
@ -387,17 +374,13 @@ export const TermsAuthEntry = createReactClass({
|
||||||
{ submitButton }
|
{ submitButton }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export const EmailIdentityAuthEntry = createReactClass({
|
export class EmailIdentityAuthEntry extends React.Component {
|
||||||
displayName: 'EmailIdentityAuthEntry',
|
static LOGIN_TYPE = "m.login.email.identity";
|
||||||
|
|
||||||
statics: {
|
static propTypes = {
|
||||||
LOGIN_TYPE: "m.login.email.identity",
|
|
||||||
},
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
matrixClient: PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
submitAuthDict: PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
authSessionId: PropTypes.string.isRequired,
|
authSessionId: PropTypes.string.isRequired,
|
||||||
|
@ -407,13 +390,13 @@ export const EmailIdentityAuthEntry = createReactClass({
|
||||||
fail: PropTypes.func.isRequired,
|
fail: PropTypes.func.isRequired,
|
||||||
setEmailSid: PropTypes.func.isRequired,
|
setEmailSid: PropTypes.func.isRequired,
|
||||||
onPhaseChange: PropTypes.func.isRequired,
|
onPhaseChange: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this.props.onPhaseChange(DEFAULT_PHASE);
|
this.props.onPhaseChange(DEFAULT_PHASE);
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
// This component is now only displayed once the token has been requested,
|
// This component is now only displayed once the token has been requested,
|
||||||
// so we know the email has been sent. It can also get loaded after the user
|
// so we know the email has been sent. It can also get loaded after the user
|
||||||
// has clicked the validation link if the server takes a while to propagate
|
// has clicked the validation link if the server takes a while to propagate
|
||||||
|
@ -434,17 +417,13 @@ export const EmailIdentityAuthEntry = createReactClass({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export const MsisdnAuthEntry = createReactClass({
|
export class MsisdnAuthEntry extends React.Component {
|
||||||
displayName: 'MsisdnAuthEntry',
|
static LOGIN_TYPE = "m.login.msisdn";
|
||||||
|
|
||||||
statics: {
|
static propTypes = {
|
||||||
LOGIN_TYPE: "m.login.msisdn",
|
|
||||||
},
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
inputs: PropTypes.shape({
|
inputs: PropTypes.shape({
|
||||||
phoneCountry: PropTypes.string,
|
phoneCountry: PropTypes.string,
|
||||||
phoneNumber: PropTypes.string,
|
phoneNumber: PropTypes.string,
|
||||||
|
@ -454,16 +433,14 @@ export const MsisdnAuthEntry = createReactClass({
|
||||||
submitAuthDict: PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
matrixClient: PropTypes.object,
|
matrixClient: PropTypes.object,
|
||||||
onPhaseChange: PropTypes.func.isRequired,
|
onPhaseChange: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
token: '',
|
||||||
token: '',
|
requestingToken: false,
|
||||||
requestingToken: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this.props.onPhaseChange(DEFAULT_PHASE);
|
this.props.onPhaseChange(DEFAULT_PHASE);
|
||||||
|
|
||||||
this._submitUrl = null;
|
this._submitUrl = null;
|
||||||
|
@ -477,12 +454,12 @@ export const MsisdnAuthEntry = createReactClass({
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.setState({requestingToken: false});
|
this.setState({requestingToken: false});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Requests a verification token by SMS.
|
* Requests a verification token by SMS.
|
||||||
*/
|
*/
|
||||||
_requestMsisdnToken: function() {
|
_requestMsisdnToken() {
|
||||||
return this.props.matrixClient.requestRegisterMsisdnToken(
|
return this.props.matrixClient.requestRegisterMsisdnToken(
|
||||||
this.props.inputs.phoneCountry,
|
this.props.inputs.phoneCountry,
|
||||||
this.props.inputs.phoneNumber,
|
this.props.inputs.phoneNumber,
|
||||||
|
@ -493,15 +470,15 @@ export const MsisdnAuthEntry = createReactClass({
|
||||||
this._sid = result.sid;
|
this._sid = result.sid;
|
||||||
this._msisdn = result.msisdn;
|
this._msisdn = result.msisdn;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_onTokenChange: function(e) {
|
_onTokenChange = e => {
|
||||||
this.setState({
|
this.setState({
|
||||||
token: e.target.value,
|
token: e.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onFormSubmit: async function(e) {
|
_onFormSubmit = async e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (this.state.token == '') return;
|
if (this.state.token == '') return;
|
||||||
|
|
||||||
|
@ -552,9 +529,9 @@ export const MsisdnAuthEntry = createReactClass({
|
||||||
this.props.fail(e);
|
this.props.fail(e);
|
||||||
console.log("Failed to submit msisdn token");
|
console.log("Failed to submit msisdn token");
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
if (this.state.requestingToken) {
|
if (this.state.requestingToken) {
|
||||||
const Loader = sdk.getComponent("elements.Spinner");
|
const Loader = sdk.getComponent("elements.Spinner");
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
|
@ -598,8 +575,8 @@ export const MsisdnAuthEntry = createReactClass({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export class SSOAuthEntry extends React.Component {
|
export class SSOAuthEntry extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -686,46 +663,46 @@ export class SSOAuthEntry extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FallbackAuthEntry = createReactClass({
|
export class FallbackAuthEntry extends React.Component {
|
||||||
displayName: 'FallbackAuthEntry',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
matrixClient: PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
authSessionId: PropTypes.string.isRequired,
|
authSessionId: PropTypes.string.isRequired,
|
||||||
loginType: PropTypes.string.isRequired,
|
loginType: PropTypes.string.isRequired,
|
||||||
submitAuthDict: PropTypes.func.isRequired,
|
submitAuthDict: PropTypes.func.isRequired,
|
||||||
errorText: PropTypes.string,
|
errorText: PropTypes.string,
|
||||||
onPhaseChange: PropTypes.func.isRequired,
|
onPhaseChange: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
componentDidMount: function() {
|
constructor(props) {
|
||||||
this.props.onPhaseChange(DEFAULT_PHASE);
|
super(props);
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
// we have to make the user click a button, as browsers will block
|
// we have to make the user click a button, as browsers will block
|
||||||
// the popup if we open it immediately.
|
// the popup if we open it immediately.
|
||||||
this._popupWindow = null;
|
this._popupWindow = null;
|
||||||
window.addEventListener("message", this._onReceiveMessage);
|
window.addEventListener("message", this._onReceiveMessage);
|
||||||
|
|
||||||
this._fallbackButton = createRef();
|
this._fallbackButton = createRef();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.onPhaseChange(DEFAULT_PHASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
window.removeEventListener("message", this._onReceiveMessage);
|
window.removeEventListener("message", this._onReceiveMessage);
|
||||||
if (this._popupWindow) {
|
if (this._popupWindow) {
|
||||||
this._popupWindow.close();
|
this._popupWindow.close();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
focus: function() {
|
focus = () => {
|
||||||
if (this._fallbackButton.current) {
|
if (this._fallbackButton.current) {
|
||||||
this._fallbackButton.current.focus();
|
this._fallbackButton.current.focus();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_onShowFallbackClick: function(e) {
|
_onShowFallbackClick = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
|
@ -735,18 +712,18 @@ export const FallbackAuthEntry = createReactClass({
|
||||||
);
|
);
|
||||||
this._popupWindow = window.open(url);
|
this._popupWindow = window.open(url);
|
||||||
this._popupWindow.opener = null;
|
this._popupWindow.opener = null;
|
||||||
},
|
};
|
||||||
|
|
||||||
_onReceiveMessage: function(event) {
|
_onReceiveMessage = event => {
|
||||||
if (
|
if (
|
||||||
event.data === "authDone" &&
|
event.data === "authDone" &&
|
||||||
event.origin === this.props.matrixClient.getHomeserverUrl()
|
event.origin === this.props.matrixClient.getHomeserverUrl()
|
||||||
) {
|
) {
|
||||||
this.props.submitAuthDict({});
|
this.props.submitAuthDict({});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let errorSection;
|
let errorSection;
|
||||||
if (this.props.errorText) {
|
if (this.props.errorText) {
|
||||||
errorSection = (
|
errorSection = (
|
||||||
|
@ -761,8 +738,8 @@ export const FallbackAuthEntry = createReactClass({
|
||||||
{errorSection}
|
{errorSection}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
const AuthEntryComponents = [
|
const AuthEntryComponents = [
|
||||||
PasswordAuthEntry,
|
PasswordAuthEntry,
|
||||||
|
|
|
@ -18,7 +18,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import * as Email from '../../../email';
|
import * as Email from '../../../email';
|
||||||
|
@ -42,10 +41,8 @@ const PASSWORD_MIN_SCORE = 3; // safely unguessable: moderate protection from of
|
||||||
/**
|
/**
|
||||||
* A pure UI component which displays a registration form.
|
* A pure UI component which displays a registration form.
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class RegistrationForm extends React.Component {
|
||||||
displayName: 'RegistrationForm',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// Values pre-filled in the input boxes when the component loads
|
// Values pre-filled in the input boxes when the component loads
|
||||||
defaultEmail: PropTypes.string,
|
defaultEmail: PropTypes.string,
|
||||||
defaultPhoneCountry: PropTypes.string,
|
defaultPhoneCountry: PropTypes.string,
|
||||||
|
@ -58,17 +55,17 @@ export default createReactClass({
|
||||||
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
serverConfig: PropTypes.instanceOf(ValidatedServerConfig).isRequired,
|
||||||
canSubmit: PropTypes.bool,
|
canSubmit: PropTypes.bool,
|
||||||
serverRequiresIdServer: PropTypes.bool,
|
serverRequiresIdServer: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
onValidationChange: console.error,
|
||||||
onValidationChange: console.error,
|
canSubmit: true,
|
||||||
canSubmit: true,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
// Field error codes by field ID
|
// Field error codes by field ID
|
||||||
fieldValid: {},
|
fieldValid: {},
|
||||||
// The ISO2 country code selected in the phone number entry
|
// The ISO2 country code selected in the phone number entry
|
||||||
|
@ -80,9 +77,9 @@ export default createReactClass({
|
||||||
passwordConfirm: this.props.defaultPassword || "",
|
passwordConfirm: this.props.defaultPassword || "",
|
||||||
passwordComplexity: null,
|
passwordComplexity: null,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
onSubmit: async function(ev) {
|
onSubmit = async ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
if (!this.props.canSubmit) return;
|
if (!this.props.canSubmit) return;
|
||||||
|
@ -118,7 +115,7 @@ export default createReactClass({
|
||||||
title: _t("Warning!"),
|
title: _t("Warning!"),
|
||||||
description: desc,
|
description: desc,
|
||||||
button: _t("Continue"),
|
button: _t("Continue"),
|
||||||
onFinished: function(confirmed) {
|
onFinished(confirmed) {
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
self._doSubmit(ev);
|
self._doSubmit(ev);
|
||||||
}
|
}
|
||||||
|
@ -127,9 +124,9 @@ export default createReactClass({
|
||||||
} else {
|
} else {
|
||||||
self._doSubmit(ev);
|
self._doSubmit(ev);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_doSubmit: function(ev) {
|
_doSubmit(ev) {
|
||||||
const email = this.state.email.trim();
|
const email = this.state.email.trim();
|
||||||
const promise = this.props.onRegisterClick({
|
const promise = this.props.onRegisterClick({
|
||||||
username: this.state.username.trim(),
|
username: this.state.username.trim(),
|
||||||
|
@ -145,7 +142,7 @@ export default createReactClass({
|
||||||
ev.target.disabled = false;
|
ev.target.disabled = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
async verifyFieldsBeforeSubmit() {
|
async verifyFieldsBeforeSubmit() {
|
||||||
// Blur the active element if any, so we first run its blur validation,
|
// Blur the active element if any, so we first run its blur validation,
|
||||||
|
@ -196,12 +193,12 @@ export default createReactClass({
|
||||||
invalidField.focus();
|
invalidField.focus();
|
||||||
invalidField.validate({ allowEmpty: false, focused: true });
|
invalidField.validate({ allowEmpty: false, focused: true });
|
||||||
return false;
|
return false;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {boolean} true if all fields were valid last time they were validated.
|
* @returns {boolean} true if all fields were valid last time they were validated.
|
||||||
*/
|
*/
|
||||||
allFieldsValid: function() {
|
allFieldsValid() {
|
||||||
const keys = Object.keys(this.state.fieldValid);
|
const keys = Object.keys(this.state.fieldValid);
|
||||||
for (let i = 0; i < keys.length; ++i) {
|
for (let i = 0; i < keys.length; ++i) {
|
||||||
if (!this.state.fieldValid[keys[i]]) {
|
if (!this.state.fieldValid[keys[i]]) {
|
||||||
|
@ -209,7 +206,7 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
|
|
||||||
findFirstInvalidField(fieldIDs) {
|
findFirstInvalidField(fieldIDs) {
|
||||||
for (const fieldID of fieldIDs) {
|
for (const fieldID of fieldIDs) {
|
||||||
|
@ -218,34 +215,34 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
}
|
||||||
|
|
||||||
markFieldValid: function(fieldID, valid) {
|
markFieldValid(fieldID, valid) {
|
||||||
const { fieldValid } = this.state;
|
const { fieldValid } = this.state;
|
||||||
fieldValid[fieldID] = valid;
|
fieldValid[fieldID] = valid;
|
||||||
this.setState({
|
this.setState({
|
||||||
fieldValid,
|
fieldValid,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onEmailChange(ev) {
|
onEmailChange = ev => {
|
||||||
this.setState({
|
this.setState({
|
||||||
email: ev.target.value,
|
email: ev.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
async onEmailValidate(fieldState) {
|
onEmailValidate = async fieldState => {
|
||||||
const result = await this.validateEmailRules(fieldState);
|
const result = await RegistrationForm.validateEmailRules(fieldState);
|
||||||
this.markFieldValid(FIELD_EMAIL, result.valid);
|
this.markFieldValid(FIELD_EMAIL, result.valid);
|
||||||
return result;
|
return result;
|
||||||
},
|
};
|
||||||
|
|
||||||
validateEmailRules: withValidation({
|
static validateEmailRules = withValidation({
|
||||||
description: () => _t("Use an email address to recover your account"),
|
description: () => _t("Use an email address to recover your account"),
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
key: "required",
|
key: "required",
|
||||||
test: function({ value, allowEmpty }) {
|
test({ value, allowEmpty }) {
|
||||||
return allowEmpty || !this._authStepIsRequired('m.login.email.identity') || !!value;
|
return allowEmpty || !this._authStepIsRequired('m.login.email.identity') || !!value;
|
||||||
},
|
},
|
||||||
invalid: () => _t("Enter email address (required on this homeserver)"),
|
invalid: () => _t("Enter email address (required on this homeserver)"),
|
||||||
|
@ -256,31 +253,31 @@ export default createReactClass({
|
||||||
invalid: () => _t("Doesn't look like a valid email address"),
|
invalid: () => _t("Doesn't look like a valid email address"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
});
|
||||||
|
|
||||||
onPasswordChange(ev) {
|
onPasswordChange = ev => {
|
||||||
this.setState({
|
this.setState({
|
||||||
password: ev.target.value,
|
password: ev.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onPasswordValidate(result) {
|
onPasswordValidate = result => {
|
||||||
this.markFieldValid(FIELD_PASSWORD, result.valid);
|
this.markFieldValid(FIELD_PASSWORD, result.valid);
|
||||||
},
|
};
|
||||||
|
|
||||||
onPasswordConfirmChange(ev) {
|
onPasswordConfirmChange = ev => {
|
||||||
this.setState({
|
this.setState({
|
||||||
passwordConfirm: ev.target.value,
|
passwordConfirm: ev.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
async onPasswordConfirmValidate(fieldState) {
|
onPasswordConfirmValidate = async fieldState => {
|
||||||
const result = await this.validatePasswordConfirmRules(fieldState);
|
const result = await RegistrationForm.validatePasswordConfirmRules(fieldState);
|
||||||
this.markFieldValid(FIELD_PASSWORD_CONFIRM, result.valid);
|
this.markFieldValid(FIELD_PASSWORD_CONFIRM, result.valid);
|
||||||
return result;
|
return result;
|
||||||
},
|
};
|
||||||
|
|
||||||
validatePasswordConfirmRules: withValidation({
|
static validatePasswordConfirmRules = withValidation({
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
key: "required",
|
key: "required",
|
||||||
|
@ -289,39 +286,39 @@ export default createReactClass({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "match",
|
key: "match",
|
||||||
test: function({ value }) {
|
test({ value }) {
|
||||||
return !value || value === this.state.password;
|
return !value || value === this.state.password;
|
||||||
},
|
},
|
||||||
invalid: () => _t("Passwords don't match"),
|
invalid: () => _t("Passwords don't match"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
});
|
||||||
|
|
||||||
onPhoneCountryChange(newVal) {
|
onPhoneCountryChange = newVal => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phoneCountry: newVal.iso2,
|
phoneCountry: newVal.iso2,
|
||||||
phonePrefix: newVal.prefix,
|
phonePrefix: newVal.prefix,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onPhoneNumberChange(ev) {
|
onPhoneNumberChange = ev => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phoneNumber: ev.target.value,
|
phoneNumber: ev.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
async onPhoneNumberValidate(fieldState) {
|
onPhoneNumberValidate = async fieldState => {
|
||||||
const result = await this.validatePhoneNumberRules(fieldState);
|
const result = await RegistrationForm.validatePhoneNumberRules(fieldState);
|
||||||
this.markFieldValid(FIELD_PHONE_NUMBER, result.valid);
|
this.markFieldValid(FIELD_PHONE_NUMBER, result.valid);
|
||||||
return result;
|
return result;
|
||||||
},
|
};
|
||||||
|
|
||||||
validatePhoneNumberRules: withValidation({
|
static validatePhoneNumberRules = withValidation({
|
||||||
description: () => _t("Other users can invite you to rooms using your contact details"),
|
description: () => _t("Other users can invite you to rooms using your contact details"),
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
key: "required",
|
key: "required",
|
||||||
test: function({ value, allowEmpty }) {
|
test({ value, allowEmpty }) {
|
||||||
return allowEmpty || !this._authStepIsRequired('m.login.msisdn') || !!value;
|
return allowEmpty || !this._authStepIsRequired('m.login.msisdn') || !!value;
|
||||||
},
|
},
|
||||||
invalid: () => _t("Enter phone number (required on this homeserver)"),
|
invalid: () => _t("Enter phone number (required on this homeserver)"),
|
||||||
|
@ -332,21 +329,21 @@ export default createReactClass({
|
||||||
invalid: () => _t("Doesn't look like a valid phone number"),
|
invalid: () => _t("Doesn't look like a valid phone number"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
});
|
||||||
|
|
||||||
onUsernameChange(ev) {
|
onUsernameChange = ev => {
|
||||||
this.setState({
|
this.setState({
|
||||||
username: ev.target.value,
|
username: ev.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
async onUsernameValidate(fieldState) {
|
onUsernameValidate = async fieldState => {
|
||||||
const result = await this.validateUsernameRules(fieldState);
|
const result = await RegistrationForm.validateUsernameRules(fieldState);
|
||||||
this.markFieldValid(FIELD_USERNAME, result.valid);
|
this.markFieldValid(FIELD_USERNAME, result.valid);
|
||||||
return result;
|
return result;
|
||||||
},
|
};
|
||||||
|
|
||||||
validateUsernameRules: withValidation({
|
static validateUsernameRules = withValidation({
|
||||||
description: () => _t("Use lowercase letters, numbers, dashes and underscores only"),
|
description: () => _t("Use lowercase letters, numbers, dashes and underscores only"),
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
@ -360,7 +357,7 @@ export default createReactClass({
|
||||||
invalid: () => _t("Some characters not allowed"),
|
invalid: () => _t("Some characters not allowed"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A step is required if all flows include that step.
|
* A step is required if all flows include that step.
|
||||||
|
@ -372,7 +369,7 @@ export default createReactClass({
|
||||||
return this.props.flows.every((flow) => {
|
return this.props.flows.every((flow) => {
|
||||||
return flow.stages.includes(step);
|
return flow.stages.includes(step);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A step is used if any flows include that step.
|
* A step is used if any flows include that step.
|
||||||
|
@ -384,7 +381,7 @@ export default createReactClass({
|
||||||
return this.props.flows.some((flow) => {
|
return this.props.flows.some((flow) => {
|
||||||
return flow.stages.includes(step);
|
return flow.stages.includes(step);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_showEmail() {
|
_showEmail() {
|
||||||
const haveIs = Boolean(this.props.serverConfig.isUrl);
|
const haveIs = Boolean(this.props.serverConfig.isUrl);
|
||||||
|
@ -395,7 +392,7 @@ export default createReactClass({
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
|
|
||||||
_showPhoneNumber() {
|
_showPhoneNumber() {
|
||||||
const threePidLogin = !SdkConfig.get().disable_3pid_login;
|
const threePidLogin = !SdkConfig.get().disable_3pid_login;
|
||||||
|
@ -408,7 +405,7 @@ export default createReactClass({
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderEmail() {
|
renderEmail() {
|
||||||
if (!this._showEmail()) {
|
if (!this._showEmail()) {
|
||||||
|
@ -426,7 +423,7 @@ export default createReactClass({
|
||||||
onChange={this.onEmailChange}
|
onChange={this.onEmailChange}
|
||||||
onValidate={this.onEmailValidate}
|
onValidate={this.onEmailValidate}
|
||||||
/>;
|
/>;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderPassword() {
|
renderPassword() {
|
||||||
return <PassphraseField
|
return <PassphraseField
|
||||||
|
@ -437,7 +434,7 @@ export default createReactClass({
|
||||||
onChange={this.onPasswordChange}
|
onChange={this.onPasswordChange}
|
||||||
onValidate={this.onPasswordValidate}
|
onValidate={this.onPasswordValidate}
|
||||||
/>;
|
/>;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderPasswordConfirm() {
|
renderPasswordConfirm() {
|
||||||
const Field = sdk.getComponent('elements.Field');
|
const Field = sdk.getComponent('elements.Field');
|
||||||
|
@ -451,7 +448,7 @@ export default createReactClass({
|
||||||
onChange={this.onPasswordConfirmChange}
|
onChange={this.onPasswordConfirmChange}
|
||||||
onValidate={this.onPasswordConfirmValidate}
|
onValidate={this.onPasswordConfirmValidate}
|
||||||
/>;
|
/>;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderPhoneNumber() {
|
renderPhoneNumber() {
|
||||||
if (!this._showPhoneNumber()) {
|
if (!this._showPhoneNumber()) {
|
||||||
|
@ -477,7 +474,7 @@ export default createReactClass({
|
||||||
onChange={this.onPhoneNumberChange}
|
onChange={this.onPhoneNumberChange}
|
||||||
onValidate={this.onPhoneNumberValidate}
|
onValidate={this.onPhoneNumberValidate}
|
||||||
/>;
|
/>;
|
||||||
},
|
}
|
||||||
|
|
||||||
renderUsername() {
|
renderUsername() {
|
||||||
const Field = sdk.getComponent('elements.Field');
|
const Field = sdk.getComponent('elements.Field');
|
||||||
|
@ -491,9 +488,9 @@ export default createReactClass({
|
||||||
onChange={this.onUsernameChange}
|
onChange={this.onUsernameChange}
|
||||||
onValidate={this.onUsernameValidate}
|
onValidate={this.onUsernameValidate}
|
||||||
/>;
|
/>;
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let yourMatrixAccountText = _t('Create your Matrix account on %(serverName)s', {
|
let yourMatrixAccountText = _t('Create your Matrix account on %(serverName)s', {
|
||||||
serverName: this.props.serverConfig.hsName,
|
serverName: this.props.serverConfig.hsName,
|
||||||
});
|
});
|
||||||
|
@ -578,5 +575,5 @@ export default createReactClass({
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {EventStatus} from 'matrix-js-sdk';
|
import {EventStatus} from 'matrix-js-sdk';
|
||||||
|
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
|
@ -37,10 +36,8 @@ function canCancel(eventStatus) {
|
||||||
return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
|
return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createReactClass({
|
export default class MessageContextMenu extends React.Component {
|
||||||
displayName: 'MessageContextMenu',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
/* the MatrixEvent associated with the context menu */
|
/* the MatrixEvent associated with the context menu */
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
|
|
||||||
|
@ -52,28 +49,26 @@ export default createReactClass({
|
||||||
|
|
||||||
/* callback called when the menu is dismissed */
|
/* callback called when the menu is dismissed */
|
||||||
onFinished: PropTypes.func,
|
onFinished: PropTypes.func,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
canRedact: false,
|
||||||
canRedact: false,
|
canPin: false,
|
||||||
canPin: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
MatrixClientPeg.get().on('RoomMember.powerLevel', this._checkPermissions);
|
MatrixClientPeg.get().on('RoomMember.powerLevel', this._checkPermissions);
|
||||||
this._checkPermissions();
|
this._checkPermissions();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli) {
|
if (cli) {
|
||||||
cli.removeListener('RoomMember.powerLevel', this._checkPermissions);
|
cli.removeListener('RoomMember.powerLevel', this._checkPermissions);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_checkPermissions: function() {
|
_checkPermissions = () => {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
||||||
|
|
||||||
|
@ -84,47 +79,47 @@ export default createReactClass({
|
||||||
if (!SettingsStore.getValue("feature_pinning")) canPin = false;
|
if (!SettingsStore.getValue("feature_pinning")) canPin = false;
|
||||||
|
|
||||||
this.setState({canRedact, canPin});
|
this.setState({canRedact, canPin});
|
||||||
},
|
};
|
||||||
|
|
||||||
_isPinned: function() {
|
_isPinned() {
|
||||||
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId());
|
||||||
const pinnedEvent = room.currentState.getStateEvents('m.room.pinned_events', '');
|
const pinnedEvent = room.currentState.getStateEvents('m.room.pinned_events', '');
|
||||||
if (!pinnedEvent) return false;
|
if (!pinnedEvent) return false;
|
||||||
const content = pinnedEvent.getContent();
|
const content = pinnedEvent.getContent();
|
||||||
return content.pinned && Array.isArray(content.pinned) && content.pinned.includes(this.props.mxEvent.getId());
|
return content.pinned && Array.isArray(content.pinned) && content.pinned.includes(this.props.mxEvent.getId());
|
||||||
},
|
}
|
||||||
|
|
||||||
onResendClick: function() {
|
onResendClick = () => {
|
||||||
Resend.resend(this.props.mxEvent);
|
Resend.resend(this.props.mxEvent);
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onResendEditClick: function() {
|
onResendEditClick = () => {
|
||||||
Resend.resend(this.props.mxEvent.replacingEvent());
|
Resend.resend(this.props.mxEvent.replacingEvent());
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onResendRedactionClick: function() {
|
onResendRedactionClick = () => {
|
||||||
Resend.resend(this.props.mxEvent.localRedactionEvent());
|
Resend.resend(this.props.mxEvent.localRedactionEvent());
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onResendReactionsClick: function() {
|
onResendReactionsClick = () => {
|
||||||
for (const reaction of this._getUnsentReactions()) {
|
for (const reaction of this._getUnsentReactions()) {
|
||||||
Resend.resend(reaction);
|
Resend.resend(reaction);
|
||||||
}
|
}
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onReportEventClick: function() {
|
onReportEventClick = () => {
|
||||||
const ReportEventDialog = sdk.getComponent("dialogs.ReportEventDialog");
|
const ReportEventDialog = sdk.getComponent("dialogs.ReportEventDialog");
|
||||||
Modal.createTrackedDialog('Report Event', '', ReportEventDialog, {
|
Modal.createTrackedDialog('Report Event', '', ReportEventDialog, {
|
||||||
mxEvent: this.props.mxEvent,
|
mxEvent: this.props.mxEvent,
|
||||||
}, 'mx_Dialog_reportEvent');
|
}, 'mx_Dialog_reportEvent');
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onViewSourceClick: function() {
|
onViewSourceClick = () => {
|
||||||
const ev = this.props.mxEvent.replacingEvent() || this.props.mxEvent;
|
const ev = this.props.mxEvent.replacingEvent() || this.props.mxEvent;
|
||||||
const ViewSource = sdk.getComponent('structures.ViewSource');
|
const ViewSource = sdk.getComponent('structures.ViewSource');
|
||||||
Modal.createTrackedDialog('View Event Source', '', ViewSource, {
|
Modal.createTrackedDialog('View Event Source', '', ViewSource, {
|
||||||
|
@ -133,9 +128,9 @@ export default createReactClass({
|
||||||
content: ev.event,
|
content: ev.event,
|
||||||
}, 'mx_Dialog_viewsource');
|
}, 'mx_Dialog_viewsource');
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onViewClearSourceClick: function() {
|
onViewClearSourceClick = () => {
|
||||||
const ev = this.props.mxEvent.replacingEvent() || this.props.mxEvent;
|
const ev = this.props.mxEvent.replacingEvent() || this.props.mxEvent;
|
||||||
const ViewSource = sdk.getComponent('structures.ViewSource');
|
const ViewSource = sdk.getComponent('structures.ViewSource');
|
||||||
Modal.createTrackedDialog('View Clear Event Source', '', ViewSource, {
|
Modal.createTrackedDialog('View Clear Event Source', '', ViewSource, {
|
||||||
|
@ -145,9 +140,9 @@ export default createReactClass({
|
||||||
content: ev._clearEvent,
|
content: ev._clearEvent,
|
||||||
}, 'mx_Dialog_viewsource');
|
}, 'mx_Dialog_viewsource');
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onRedactClick: function() {
|
onRedactClick = () => {
|
||||||
const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog");
|
const ConfirmRedactDialog = sdk.getComponent("dialogs.ConfirmRedactDialog");
|
||||||
Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, {
|
Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, {
|
||||||
onFinished: async (proceed) => {
|
onFinished: async (proceed) => {
|
||||||
|
@ -176,9 +171,9 @@ export default createReactClass({
|
||||||
},
|
},
|
||||||
}, 'mx_Dialog_confirmredact');
|
}, 'mx_Dialog_confirmredact');
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onCancelSendClick: function() {
|
onCancelSendClick = () => {
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
const editEvent = mxEvent.replacingEvent();
|
const editEvent = mxEvent.replacingEvent();
|
||||||
const redactEvent = mxEvent.localRedactionEvent();
|
const redactEvent = mxEvent.localRedactionEvent();
|
||||||
|
@ -199,17 +194,17 @@ export default createReactClass({
|
||||||
Resend.removeFromQueue(this.props.mxEvent);
|
Resend.removeFromQueue(this.props.mxEvent);
|
||||||
}
|
}
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onForwardClick: function() {
|
onForwardClick = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'forward_event',
|
action: 'forward_event',
|
||||||
event: this.props.mxEvent,
|
event: this.props.mxEvent,
|
||||||
});
|
});
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onPinClick: function() {
|
onPinClick = () => {
|
||||||
MatrixClientPeg.get().getStateEvent(this.props.mxEvent.getRoomId(), 'm.room.pinned_events', '')
|
MatrixClientPeg.get().getStateEvent(this.props.mxEvent.getRoomId(), 'm.room.pinned_events', '')
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
// Intercept the Event Not Found error and fall through the promise chain with no event.
|
// Intercept the Event Not Found error and fall through the promise chain with no event.
|
||||||
|
@ -230,28 +225,28 @@ export default createReactClass({
|
||||||
cli.sendStateEvent(this.props.mxEvent.getRoomId(), 'm.room.pinned_events', {pinned: eventIds}, '');
|
cli.sendStateEvent(this.props.mxEvent.getRoomId(), 'm.room.pinned_events', {pinned: eventIds}, '');
|
||||||
});
|
});
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
closeMenu: function() {
|
closeMenu = () => {
|
||||||
if (this.props.onFinished) this.props.onFinished();
|
if (this.props.onFinished) this.props.onFinished();
|
||||||
},
|
};
|
||||||
|
|
||||||
onUnhidePreviewClick: function() {
|
onUnhidePreviewClick = () => {
|
||||||
if (this.props.eventTileOps) {
|
if (this.props.eventTileOps) {
|
||||||
this.props.eventTileOps.unhideWidget();
|
this.props.eventTileOps.unhideWidget();
|
||||||
}
|
}
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onQuoteClick: function() {
|
onQuoteClick = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'quote',
|
action: 'quote',
|
||||||
event: this.props.mxEvent,
|
event: this.props.mxEvent,
|
||||||
});
|
});
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onPermalinkClick: function(e: Event) {
|
onPermalinkClick = (e: Event) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
||||||
Modal.createTrackedDialog('share room message dialog', '', ShareDialog, {
|
Modal.createTrackedDialog('share room message dialog', '', ShareDialog, {
|
||||||
|
@ -259,12 +254,12 @@ export default createReactClass({
|
||||||
permalinkCreator: this.props.permalinkCreator,
|
permalinkCreator: this.props.permalinkCreator,
|
||||||
});
|
});
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
onCollapseReplyThreadClick: function() {
|
onCollapseReplyThreadClick = () => {
|
||||||
this.props.collapseReplyThread();
|
this.props.collapseReplyThread();
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
_getReactions(filter) {
|
_getReactions(filter) {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
@ -277,17 +272,17 @@ export default createReactClass({
|
||||||
relation.event_id === eventId &&
|
relation.event_id === eventId &&
|
||||||
filter(e);
|
filter(e);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_getPendingReactions() {
|
_getPendingReactions() {
|
||||||
return this._getReactions(e => canCancel(e.status));
|
return this._getReactions(e => canCancel(e.status));
|
||||||
},
|
}
|
||||||
|
|
||||||
_getUnsentReactions() {
|
_getUnsentReactions() {
|
||||||
return this._getReactions(e => e.status === EventStatus.NOT_SENT);
|
return this._getReactions(e => e.status === EventStatus.NOT_SENT);
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const me = cli.getUserId();
|
const me = cli.getUserId();
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
|
@ -489,5 +484,5 @@ export default createReactClass({
|
||||||
{ reportEventButton }
|
{ reportEventButton }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
|
|
||||||
import { _t, _td } from '../../../languageHandler';
|
import { _t, _td } from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
@ -45,10 +44,8 @@ const addressTypeName = {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default createReactClass({
|
export default class AddressPickerDialog extends React.Component {
|
||||||
displayName: "AddressPickerDialog",
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
description: PropTypes.node,
|
description: PropTypes.node,
|
||||||
// Extra node inserted after picker input, dropdown and errors
|
// Extra node inserted after picker input, dropdown and errors
|
||||||
|
@ -66,26 +63,28 @@ export default createReactClass({
|
||||||
// Whether the current user should be included in the addresses returned. Only
|
// Whether the current user should be included in the addresses returned. Only
|
||||||
// applicable when pickerType is `user`. Default: false.
|
// applicable when pickerType is `user`. Default: false.
|
||||||
includeSelf: PropTypes.bool,
|
includeSelf: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
value: "",
|
||||||
value: "",
|
focus: true,
|
||||||
focus: true,
|
validAddressTypes: addressTypes,
|
||||||
validAddressTypes: addressTypes,
|
pickerType: 'user',
|
||||||
pickerType: 'user',
|
includeSelf: false,
|
||||||
includeSelf: false,
|
};
|
||||||
};
|
|
||||||
},
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._textinput = createRef();
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
let validAddressTypes = this.props.validAddressTypes;
|
let validAddressTypes = this.props.validAddressTypes;
|
||||||
// Remove email from validAddressTypes if no IS is configured. It may be added at a later stage by the user
|
// Remove email from validAddressTypes if no IS is configured. It may be added at a later stage by the user
|
||||||
if (!MatrixClientPeg.get().getIdentityServerUrl() && validAddressTypes.includes("email")) {
|
if (!MatrixClientPeg.get().getIdentityServerUrl() && validAddressTypes.includes("email")) {
|
||||||
validAddressTypes = validAddressTypes.filter(type => type !== "email");
|
validAddressTypes = validAddressTypes.filter(type => type !== "email");
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
this.state = {
|
||||||
// Whether to show an error message because of an invalid address
|
// Whether to show an error message because of an invalid address
|
||||||
invalidAddressError: false,
|
invalidAddressError: false,
|
||||||
// List of UserAddressType objects representing
|
// List of UserAddressType objects representing
|
||||||
|
@ -106,19 +105,14 @@ export default createReactClass({
|
||||||
// dialog is open and represents the supported list of address types at this time.
|
// dialog is open and represents the supported list of address types at this time.
|
||||||
validAddressTypes,
|
validAddressTypes,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
componentDidMount() {
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._textinput = createRef();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
if (this.props.focus) {
|
if (this.props.focus) {
|
||||||
// Set the cursor at the end of the text input
|
// Set the cursor at the end of the text input
|
||||||
this._textinput.current.value = this.props.value;
|
this._textinput.current.value = this.props.value;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
getPlaceholder() {
|
getPlaceholder() {
|
||||||
const { placeholder } = this.props;
|
const { placeholder } = this.props;
|
||||||
|
@ -127,9 +121,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
// Otherwise it's a function, as checked by prop types.
|
// Otherwise it's a function, as checked by prop types.
|
||||||
return placeholder(this.state.validAddressTypes);
|
return placeholder(this.state.validAddressTypes);
|
||||||
},
|
}
|
||||||
|
|
||||||
onButtonClick: function() {
|
onButtonClick = () => {
|
||||||
let selectedList = this.state.selectedList.slice();
|
let selectedList = this.state.selectedList.slice();
|
||||||
// Check the text input field to see if user has an unconverted address
|
// Check the text input field to see if user has an unconverted address
|
||||||
// If there is and it's valid add it to the local selectedList
|
// If there is and it's valid add it to the local selectedList
|
||||||
|
@ -138,13 +132,13 @@ export default createReactClass({
|
||||||
if (selectedList === null) return;
|
if (selectedList === null) return;
|
||||||
}
|
}
|
||||||
this.props.onFinished(true, selectedList);
|
this.props.onFinished(true, selectedList);
|
||||||
},
|
};
|
||||||
|
|
||||||
onCancel: function() {
|
onCancel = () => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
onKeyDown: function(e) {
|
onKeyDown = e => {
|
||||||
const textInput = this._textinput.current ? this._textinput.current.value : undefined;
|
const textInput = this._textinput.current ? this._textinput.current.value : undefined;
|
||||||
|
|
||||||
if (e.key === Key.ESCAPE) {
|
if (e.key === Key.ESCAPE) {
|
||||||
|
@ -181,9 +175,9 @@ export default createReactClass({
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this._addAddressesToList([textInput]);
|
this._addAddressesToList([textInput]);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onQueryChanged: function(ev) {
|
onQueryChanged = ev => {
|
||||||
const query = ev.target.value;
|
const query = ev.target.value;
|
||||||
if (this.queryChangedDebouncer) {
|
if (this.queryChangedDebouncer) {
|
||||||
clearTimeout(this.queryChangedDebouncer);
|
clearTimeout(this.queryChangedDebouncer);
|
||||||
|
@ -216,28 +210,24 @@ export default createReactClass({
|
||||||
searchError: null,
|
searchError: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onDismissed: function(index) {
|
onDismissed = index => () => {
|
||||||
return () => {
|
const selectedList = this.state.selectedList.slice();
|
||||||
const selectedList = this.state.selectedList.slice();
|
selectedList.splice(index, 1);
|
||||||
selectedList.splice(index, 1);
|
this.setState({
|
||||||
this.setState({
|
selectedList,
|
||||||
selectedList,
|
suggestedList: [],
|
||||||
suggestedList: [],
|
query: "",
|
||||||
query: "",
|
});
|
||||||
});
|
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
||||||
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onClick: function(index) {
|
onClick = index => () => {
|
||||||
return () => {
|
this.onSelected(index);
|
||||||
this.onSelected(index);
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onSelected: function(index) {
|
onSelected = index => {
|
||||||
const selectedList = this.state.selectedList.slice();
|
const selectedList = this.state.selectedList.slice();
|
||||||
selectedList.push(this._getFilteredSuggestions()[index]);
|
selectedList.push(this._getFilteredSuggestions()[index]);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -246,9 +236,9 @@ export default createReactClass({
|
||||||
query: "",
|
query: "",
|
||||||
});
|
});
|
||||||
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
||||||
},
|
};
|
||||||
|
|
||||||
_doNaiveGroupSearch: function(query) {
|
_doNaiveGroupSearch(query) {
|
||||||
const lowerCaseQuery = query.toLowerCase();
|
const lowerCaseQuery = query.toLowerCase();
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
|
@ -280,9 +270,9 @@ export default createReactClass({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_doNaiveGroupRoomSearch: function(query) {
|
_doNaiveGroupRoomSearch(query) {
|
||||||
const lowerCaseQuery = query.toLowerCase();
|
const lowerCaseQuery = query.toLowerCase();
|
||||||
const results = [];
|
const results = [];
|
||||||
GroupStore.getGroupRooms(this.props.groupId).forEach((r) => {
|
GroupStore.getGroupRooms(this.props.groupId).forEach((r) => {
|
||||||
|
@ -302,9 +292,9 @@ export default createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_doRoomSearch: function(query) {
|
_doRoomSearch(query) {
|
||||||
const lowerCaseQuery = query.toLowerCase();
|
const lowerCaseQuery = query.toLowerCase();
|
||||||
const rooms = MatrixClientPeg.get().getRooms();
|
const rooms = MatrixClientPeg.get().getRooms();
|
||||||
const results = [];
|
const results = [];
|
||||||
|
@ -359,9 +349,9 @@ export default createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_doUserDirectorySearch: function(query) {
|
_doUserDirectorySearch(query) {
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
query,
|
query,
|
||||||
|
@ -393,9 +383,9 @@ export default createReactClass({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_doLocalSearch: function(query) {
|
_doLocalSearch(query) {
|
||||||
this.setState({
|
this.setState({
|
||||||
query,
|
query,
|
||||||
searchError: null,
|
searchError: null,
|
||||||
|
@ -417,9 +407,9 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._processResults(results, query);
|
this._processResults(results, query);
|
||||||
},
|
}
|
||||||
|
|
||||||
_processResults: function(results, query) {
|
_processResults(results, query) {
|
||||||
const suggestedList = [];
|
const suggestedList = [];
|
||||||
results.forEach((result) => {
|
results.forEach((result) => {
|
||||||
if (result.room_id) {
|
if (result.room_id) {
|
||||||
|
@ -485,9 +475,9 @@ export default createReactClass({
|
||||||
}, () => {
|
}, () => {
|
||||||
if (this.addressSelector) this.addressSelector.moveSelectionTop();
|
if (this.addressSelector) this.addressSelector.moveSelectionTop();
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_addAddressesToList: function(addressTexts) {
|
_addAddressesToList(addressTexts) {
|
||||||
const selectedList = this.state.selectedList.slice();
|
const selectedList = this.state.selectedList.slice();
|
||||||
|
|
||||||
let hasError = false;
|
let hasError = false;
|
||||||
|
@ -529,9 +519,9 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
if (this._cancelThreepidLookup) this._cancelThreepidLookup();
|
||||||
return hasError ? null : selectedList;
|
return hasError ? null : selectedList;
|
||||||
},
|
}
|
||||||
|
|
||||||
_lookupThreepid: async function(medium, address) {
|
async _lookupThreepid(medium, address) {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
// Note that we can't safely remove this after we're done
|
// Note that we can't safely remove this after we're done
|
||||||
// because we don't know that it's the same one, so we just
|
// because we don't know that it's the same one, so we just
|
||||||
|
@ -577,9 +567,9 @@ export default createReactClass({
|
||||||
searchError: _t('Something went wrong!'),
|
searchError: _t('Something went wrong!'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_getFilteredSuggestions: function() {
|
_getFilteredSuggestions() {
|
||||||
// map addressType => set of addresses to avoid O(n*m) operation
|
// map addressType => set of addresses to avoid O(n*m) operation
|
||||||
const selectedAddresses = {};
|
const selectedAddresses = {};
|
||||||
this.state.selectedList.forEach(({address, addressType}) => {
|
this.state.selectedList.forEach(({address, addressType}) => {
|
||||||
|
@ -591,17 +581,17 @@ export default createReactClass({
|
||||||
return this.state.suggestedList.filter(({address, addressType}) => {
|
return this.state.suggestedList.filter(({address, addressType}) => {
|
||||||
return !(selectedAddresses[addressType] && selectedAddresses[addressType].has(address));
|
return !(selectedAddresses[addressType] && selectedAddresses[addressType].has(address));
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_onPaste: function(e) {
|
_onPaste = e => {
|
||||||
// Prevent the text being pasted into the textarea
|
// Prevent the text being pasted into the textarea
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const text = e.clipboardData.getData("text");
|
const text = e.clipboardData.getData("text");
|
||||||
// Process it as a list of addresses to add instead
|
// Process it as a list of addresses to add instead
|
||||||
this._addAddressesToList(text.split(/[\s,]+/));
|
this._addAddressesToList(text.split(/[\s,]+/));
|
||||||
},
|
};
|
||||||
|
|
||||||
onUseDefaultIdentityServerClick(e) {
|
onUseDefaultIdentityServerClick = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
// Update the IS in account data. Actually using it may trigger terms.
|
// Update the IS in account data. Actually using it may trigger terms.
|
||||||
|
@ -612,15 +602,15 @@ export default createReactClass({
|
||||||
const { validAddressTypes } = this.state;
|
const { validAddressTypes } = this.state;
|
||||||
validAddressTypes.push('email');
|
validAddressTypes.push('email');
|
||||||
this.setState({ validAddressTypes });
|
this.setState({ validAddressTypes });
|
||||||
},
|
};
|
||||||
|
|
||||||
onManageSettingsClick(e) {
|
onManageSettingsClick = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
dis.fire(Action.ViewUserSettings);
|
dis.fire(Action.ViewUserSettings);
|
||||||
this.onCancel();
|
this.onCancel();
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
const AddressSelector = sdk.getComponent("elements.AddressSelector");
|
const AddressSelector = sdk.getComponent("elements.AddressSelector");
|
||||||
|
@ -738,5 +728,5 @@ export default createReactClass({
|
||||||
onCancel={this.onCancel} />
|
onCancel={this.onCancel} />
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,37 +16,36 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import {SettingLevel} from "../../../settings/SettingLevel";
|
import {SettingLevel} from "../../../settings/SettingLevel";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class AskInviteAnywayDialog extends React.Component {
|
||||||
propTypes: {
|
static propTypes = {
|
||||||
unknownProfileUsers: PropTypes.array.isRequired, // [ {userId, errorText}... ]
|
unknownProfileUsers: PropTypes.array.isRequired, // [ {userId, errorText}... ]
|
||||||
onInviteAnyways: PropTypes.func.isRequired,
|
onInviteAnyways: PropTypes.func.isRequired,
|
||||||
onGiveUp: PropTypes.func.isRequired,
|
onGiveUp: PropTypes.func.isRequired,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
_onInviteClicked: function() {
|
_onInviteClicked = () => {
|
||||||
this.props.onInviteAnyways();
|
this.props.onInviteAnyways();
|
||||||
this.props.onFinished(true);
|
this.props.onFinished(true);
|
||||||
},
|
};
|
||||||
|
|
||||||
_onInviteNeverWarnClicked: function() {
|
_onInviteNeverWarnClicked = () => {
|
||||||
SettingsStore.setValue("promptBeforeInviteUnknownUsers", null, SettingLevel.ACCOUNT, false);
|
SettingsStore.setValue("promptBeforeInviteUnknownUsers", null, SettingLevel.ACCOUNT, false);
|
||||||
this.props.onInviteAnyways();
|
this.props.onInviteAnyways();
|
||||||
this.props.onFinished(true);
|
this.props.onFinished(true);
|
||||||
},
|
};
|
||||||
|
|
||||||
_onGiveUpClicked: function() {
|
_onGiveUpClicked = () => {
|
||||||
this.props.onGiveUp();
|
this.props.onGiveUp();
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
|
|
||||||
const errorList = this.props.unknownProfileUsers
|
const errorList = this.props.unknownProfileUsers
|
||||||
|
@ -78,5 +77,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import FocusLock from 'react-focus-lock';
|
import FocusLock from 'react-focus-lock';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -34,10 +33,8 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
* Includes a div for the title, and a keypress handler which cancels the
|
* Includes a div for the title, and a keypress handler which cancels the
|
||||||
* dialog on escape.
|
* dialog on escape.
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class BaseDialog extends React.Component {
|
||||||
displayName: 'BaseDialog',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// onFinished callback to call when Escape is pressed
|
// onFinished callback to call when Escape is pressed
|
||||||
// Take a boolean which is true if the dialog was dismissed
|
// Take a boolean which is true if the dialog was dismissed
|
||||||
// with a positive / confirm action or false if it was
|
// with a positive / confirm action or false if it was
|
||||||
|
@ -81,21 +78,20 @@ export default createReactClass({
|
||||||
PropTypes.object,
|
PropTypes.object,
|
||||||
PropTypes.arrayOf(PropTypes.string),
|
PropTypes.arrayOf(PropTypes.string),
|
||||||
]),
|
]),
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
hasCancel: true,
|
||||||
hasCancel: true,
|
fixedWidth: true,
|
||||||
fixedWidth: true,
|
};
|
||||||
};
|
|
||||||
},
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Move this to constructor
|
|
||||||
UNSAFE_componentWillMount() {
|
|
||||||
this._matrixClient = MatrixClientPeg.get();
|
this._matrixClient = MatrixClientPeg.get();
|
||||||
},
|
}
|
||||||
|
|
||||||
_onKeyDown: function(e) {
|
_onKeyDown = (e) => {
|
||||||
if (this.props.onKeyDown) {
|
if (this.props.onKeyDown) {
|
||||||
this.props.onKeyDown(e);
|
this.props.onKeyDown(e);
|
||||||
}
|
}
|
||||||
|
@ -104,13 +100,13 @@ export default createReactClass({
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_onCancelClick: function(e) {
|
_onCancelClick = (e) => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let cancelButton;
|
let cancelButton;
|
||||||
if (this.props.hasCancel) {
|
if (this.props.hasCancel) {
|
||||||
cancelButton = (
|
cancelButton = (
|
||||||
|
@ -161,5 +157,5 @@ export default createReactClass({
|
||||||
</FocusLock>
|
</FocusLock>
|
||||||
</MatrixClientContext.Provider>
|
</MatrixClientContext.Provider>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -15,17 +15,14 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A dialog for confirming a redaction.
|
* A dialog for confirming a redaction.
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class ConfirmRedactDialog extends React.Component {
|
||||||
displayName: 'ConfirmRedactDialog',
|
render() {
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog');
|
const QuestionDialog = sdk.getComponent('views.dialogs.QuestionDialog');
|
||||||
return (
|
return (
|
||||||
<QuestionDialog onFinished={this.props.onFinished}
|
<QuestionDialog onFinished={this.props.onFinished}
|
||||||
|
@ -36,5 +33,5 @@ export default createReactClass({
|
||||||
button={_t("Remove")}>
|
button={_t("Remove")}>
|
||||||
</QuestionDialog>
|
</QuestionDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { MatrixClient } from 'matrix-js-sdk';
|
import { MatrixClient } from 'matrix-js-sdk';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
@ -30,9 +29,8 @@ import { GroupMemberType } from '../../../groups';
|
||||||
* to make it obvious what is going to happen.
|
* to make it obvious what is going to happen.
|
||||||
* Also tweaks the style for 'dangerous' actions (albeit only with colour)
|
* Also tweaks the style for 'dangerous' actions (albeit only with colour)
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class ConfirmUserActionDialog extends React.Component {
|
||||||
displayName: 'ConfirmUserActionDialog',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
// matrix-js-sdk (room) member object. Supply either this or 'groupMember'
|
// matrix-js-sdk (room) member object. Supply either this or 'groupMember'
|
||||||
member: PropTypes.object,
|
member: PropTypes.object,
|
||||||
// group member object. Supply either this or 'member'
|
// group member object. Supply either this or 'member'
|
||||||
|
@ -48,35 +46,36 @@ export default createReactClass({
|
||||||
askReason: PropTypes.bool,
|
askReason: PropTypes.bool,
|
||||||
danger: PropTypes.bool,
|
danger: PropTypes.bool,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: () => ({
|
static defaultProps = {
|
||||||
danger: false,
|
danger: false,
|
||||||
askReason: false,
|
askReason: false,
|
||||||
}),
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Move this to constructor
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._reasonField = null;
|
this._reasonField = null;
|
||||||
},
|
}
|
||||||
|
|
||||||
onOk: function() {
|
onOk = () => {
|
||||||
let reason;
|
let reason;
|
||||||
if (this._reasonField) {
|
if (this._reasonField) {
|
||||||
reason = this._reasonField.value;
|
reason = this._reasonField.value;
|
||||||
}
|
}
|
||||||
this.props.onFinished(true, reason);
|
this.props.onFinished(true, reason);
|
||||||
},
|
};
|
||||||
|
|
||||||
onCancel: function() {
|
onCancel = () => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
_collectReasonField: function(e) {
|
_collectReasonField = e => {
|
||||||
this._reasonField = e;
|
this._reasonField = e;
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar");
|
const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar");
|
||||||
|
@ -134,5 +133,5 @@ export default createReactClass({
|
||||||
onCancel={this.onCancel} />
|
onCancel={this.onCancel} />
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -15,46 +15,42 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class CreateGroupDialog extends React.Component {
|
||||||
displayName: 'CreateGroupDialog',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
groupName: '',
|
||||||
groupName: '',
|
groupId: '',
|
||||||
groupId: '',
|
groupError: null,
|
||||||
groupError: null,
|
creating: false,
|
||||||
creating: false,
|
createError: null,
|
||||||
createError: null,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_onGroupNameChange: function(e) {
|
_onGroupNameChange = e => {
|
||||||
this.setState({
|
this.setState({
|
||||||
groupName: e.target.value,
|
groupName: e.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onGroupIdChange: function(e) {
|
_onGroupIdChange = e => {
|
||||||
this.setState({
|
this.setState({
|
||||||
groupId: e.target.value,
|
groupId: e.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onGroupIdBlur: function(e) {
|
_onGroupIdBlur = e => {
|
||||||
this._checkGroupId();
|
this._checkGroupId();
|
||||||
},
|
};
|
||||||
|
|
||||||
_checkGroupId: function(e) {
|
_checkGroupId(e) {
|
||||||
let error = null;
|
let error = null;
|
||||||
if (!this.state.groupId) {
|
if (!this.state.groupId) {
|
||||||
error = _t("Community IDs cannot be empty.");
|
error = _t("Community IDs cannot be empty.");
|
||||||
|
@ -67,9 +63,9 @@ export default createReactClass({
|
||||||
createError: null,
|
createError: null,
|
||||||
});
|
});
|
||||||
return error;
|
return error;
|
||||||
},
|
}
|
||||||
|
|
||||||
_onFormSubmit: function(e) {
|
_onFormSubmit = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
if (this._checkGroupId()) return;
|
if (this._checkGroupId()) return;
|
||||||
|
@ -94,13 +90,13 @@ export default createReactClass({
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.setState({creating: false});
|
this.setState({creating: false});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onCancel: function() {
|
_onCancel = () => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const Spinner = sdk.getComponent('elements.Spinner');
|
const Spinner = sdk.getComponent('elements.Spinner');
|
||||||
|
|
||||||
|
@ -171,5 +167,5 @@ export default createReactClass({
|
||||||
</form>
|
</form>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
|
@ -28,16 +27,17 @@ import {privateShouldBeEncrypted} from "../../../createRoom";
|
||||||
import TagOrderStore from "../../../stores/TagOrderStore";
|
import TagOrderStore from "../../../stores/TagOrderStore";
|
||||||
import GroupStore from "../../../stores/GroupStore";
|
import GroupStore from "../../../stores/GroupStore";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class CreateRoomDialog extends React.Component {
|
||||||
displayName: 'CreateRoomDialog',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
defaultPublic: PropTypes.bool,
|
defaultPublic: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
const config = SdkConfig.get();
|
const config = SdkConfig.get();
|
||||||
return {
|
this.state = {
|
||||||
isPublic: this.props.defaultPublic || false,
|
isPublic: this.props.defaultPublic || false,
|
||||||
isEncrypted: privateShouldBeEncrypted(),
|
isEncrypted: privateShouldBeEncrypted(),
|
||||||
name: "",
|
name: "",
|
||||||
|
@ -47,7 +47,7 @@ export default createReactClass({
|
||||||
noFederate: config.default_federate === false,
|
noFederate: config.default_federate === false,
|
||||||
nameIsValid: false,
|
nameIsValid: false,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
_roomCreateOptions() {
|
_roomCreateOptions() {
|
||||||
const opts = {};
|
const opts = {};
|
||||||
|
@ -77,27 +77,27 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return opts;
|
return opts;
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this._detailsRef.addEventListener("toggle", this.onDetailsToggled);
|
this._detailsRef.addEventListener("toggle", this.onDetailsToggled);
|
||||||
// move focus to first field when showing dialog
|
// move focus to first field when showing dialog
|
||||||
this._nameFieldRef.focus();
|
this._nameFieldRef.focus();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this._detailsRef.removeEventListener("toggle", this.onDetailsToggled);
|
this._detailsRef.removeEventListener("toggle", this.onDetailsToggled);
|
||||||
},
|
}
|
||||||
|
|
||||||
_onKeyDown: function(event) {
|
_onKeyDown = event => {
|
||||||
if (event.key === Key.ENTER) {
|
if (event.key === Key.ENTER) {
|
||||||
this.onOk();
|
this.onOk();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onOk: async function() {
|
onOk = async () => {
|
||||||
const activeElement = document.activeElement;
|
const activeElement = document.activeElement;
|
||||||
if (activeElement) {
|
if (activeElement) {
|
||||||
activeElement.blur();
|
activeElement.blur();
|
||||||
|
@ -123,51 +123,51 @@ export default createReactClass({
|
||||||
field.validate({ allowEmpty: false, focused: true });
|
field.validate({ allowEmpty: false, focused: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onCancel: function() {
|
onCancel = () => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
onNameChange(ev) {
|
onNameChange = ev => {
|
||||||
this.setState({name: ev.target.value});
|
this.setState({name: ev.target.value});
|
||||||
},
|
};
|
||||||
|
|
||||||
onTopicChange(ev) {
|
onTopicChange = ev => {
|
||||||
this.setState({topic: ev.target.value});
|
this.setState({topic: ev.target.value});
|
||||||
},
|
};
|
||||||
|
|
||||||
onPublicChange(isPublic) {
|
onPublicChange = isPublic => {
|
||||||
this.setState({isPublic});
|
this.setState({isPublic});
|
||||||
},
|
};
|
||||||
|
|
||||||
onEncryptedChange(isEncrypted) {
|
onEncryptedChange = isEncrypted => {
|
||||||
this.setState({isEncrypted});
|
this.setState({isEncrypted});
|
||||||
},
|
};
|
||||||
|
|
||||||
onAliasChange(alias) {
|
onAliasChange = alias => {
|
||||||
this.setState({alias});
|
this.setState({alias});
|
||||||
},
|
};
|
||||||
|
|
||||||
onDetailsToggled(ev) {
|
onDetailsToggled = ev => {
|
||||||
this.setState({detailsOpen: ev.target.open});
|
this.setState({detailsOpen: ev.target.open});
|
||||||
},
|
};
|
||||||
|
|
||||||
onNoFederateChange(noFederate) {
|
onNoFederateChange = noFederate => {
|
||||||
this.setState({noFederate});
|
this.setState({noFederate});
|
||||||
},
|
};
|
||||||
|
|
||||||
collectDetailsRef(ref) {
|
collectDetailsRef = ref => {
|
||||||
this._detailsRef = ref;
|
this._detailsRef = ref;
|
||||||
},
|
};
|
||||||
|
|
||||||
async onNameValidate(fieldState) {
|
onNameValidate = async fieldState => {
|
||||||
const result = await this._validateRoomName(fieldState);
|
const result = await CreateRoomDialog._validateRoomName(fieldState);
|
||||||
this.setState({nameIsValid: result.valid});
|
this.setState({nameIsValid: result.valid});
|
||||||
return result;
|
return result;
|
||||||
},
|
};
|
||||||
|
|
||||||
_validateRoomName: withValidation({
|
static _validateRoomName = withValidation({
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
key: "required",
|
key: "required",
|
||||||
|
@ -175,9 +175,9 @@ export default createReactClass({
|
||||||
invalid: () => _t("Please enter a name for the room"),
|
invalid: () => _t("Please enter a name for the room"),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}),
|
});
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
const Field = sdk.getComponent('views.elements.Field');
|
const Field = sdk.getComponent('views.elements.Field');
|
||||||
|
@ -275,5 +275,5 @@ export default createReactClass({
|
||||||
onCancel={this.onCancel} />
|
onCancel={this.onCancel} />
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -26,14 +26,12 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class ErrorDialog extends React.Component {
|
||||||
displayName: 'ErrorDialog',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
description: PropTypes.oneOfType([
|
description: PropTypes.oneOfType([
|
||||||
PropTypes.element,
|
PropTypes.element,
|
||||||
|
@ -43,18 +41,16 @@ export default createReactClass({
|
||||||
focus: PropTypes.bool,
|
focus: PropTypes.bool,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
headerImage: PropTypes.string,
|
headerImage: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
focus: true,
|
||||||
focus: true,
|
title: null,
|
||||||
title: null,
|
description: null,
|
||||||
description: null,
|
button: null,
|
||||||
button: null,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
return (
|
return (
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
|
@ -74,5 +70,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,15 +17,13 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class InfoDialog extends React.Component {
|
||||||
displayName: 'InfoDialog',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
description: PropTypes.node,
|
description: PropTypes.node,
|
||||||
|
@ -33,21 +31,19 @@ export default createReactClass({
|
||||||
onFinished: PropTypes.func,
|
onFinished: PropTypes.func,
|
||||||
hasCloseButton: PropTypes.bool,
|
hasCloseButton: PropTypes.bool,
|
||||||
onKeyDown: PropTypes.func,
|
onKeyDown: PropTypes.func,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
title: '',
|
||||||
title: '',
|
description: '',
|
||||||
description: '',
|
hasCloseButton: false,
|
||||||
hasCloseButton: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onFinished: function() {
|
onFinished = () => {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
return (
|
return (
|
||||||
|
@ -69,5 +65,5 @@ export default createReactClass({
|
||||||
</DialogButtons>
|
</DialogButtons>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
@ -27,10 +26,8 @@ import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import {ERROR_USER_CANCELLED} from "../../structures/InteractiveAuth";
|
import {ERROR_USER_CANCELLED} from "../../structures/InteractiveAuth";
|
||||||
import {SSOAuthEntry} from "../auth/InteractiveAuthEntryComponents";
|
import {SSOAuthEntry} from "../auth/InteractiveAuthEntryComponents";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class InteractiveAuthDialog extends React.Component {
|
||||||
displayName: 'InteractiveAuthDialog',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// matrix client to use for UI auth requests
|
// matrix client to use for UI auth requests
|
||||||
matrixClient: PropTypes.object.isRequired,
|
matrixClient: PropTypes.object.isRequired,
|
||||||
|
|
||||||
|
@ -70,19 +67,17 @@ export default createReactClass({
|
||||||
//
|
//
|
||||||
// Default is defined in _getDefaultDialogAesthetics()
|
// Default is defined in _getDefaultDialogAesthetics()
|
||||||
aestheticsForStagePhases: PropTypes.object,
|
aestheticsForStagePhases: PropTypes.object,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
authError: null,
|
||||||
authError: null,
|
|
||||||
|
|
||||||
// See _onUpdateStagePhase()
|
// See _onUpdateStagePhase()
|
||||||
uiaStage: null,
|
uiaStage: null,
|
||||||
uiaStagePhase: null,
|
uiaStagePhase: null,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
_getDefaultDialogAesthetics: function() {
|
_getDefaultDialogAesthetics() {
|
||||||
const ssoAesthetics = {
|
const ssoAesthetics = {
|
||||||
[SSOAuthEntry.PHASE_PREAUTH]: {
|
[SSOAuthEntry.PHASE_PREAUTH]: {
|
||||||
title: _t("Use Single Sign On to continue"),
|
title: _t("Use Single Sign On to continue"),
|
||||||
|
@ -102,9 +97,9 @@ export default createReactClass({
|
||||||
[SSOAuthEntry.LOGIN_TYPE]: ssoAesthetics,
|
[SSOAuthEntry.LOGIN_TYPE]: ssoAesthetics,
|
||||||
[SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: ssoAesthetics,
|
[SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: ssoAesthetics,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
_onAuthFinished: function(success, result) {
|
_onAuthFinished = (success, result) => {
|
||||||
if (success) {
|
if (success) {
|
||||||
this.props.onFinished(true, result);
|
this.props.onFinished(true, result);
|
||||||
} else {
|
} else {
|
||||||
|
@ -116,18 +111,18 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_onUpdateStagePhase: function(newStage, newPhase) {
|
_onUpdateStagePhase = (newStage, newPhase) => {
|
||||||
// We copy the stage and stage phase params into state for title selection in render()
|
// We copy the stage and stage phase params into state for title selection in render()
|
||||||
this.setState({uiaStage: newStage, uiaStagePhase: newPhase});
|
this.setState({uiaStage: newStage, uiaStagePhase: newPhase});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onDismissClick: function() {
|
_onDismissClick = () => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const InteractiveAuth = sdk.getComponent("structures.InteractiveAuth");
|
const InteractiveAuth = sdk.getComponent("structures.InteractiveAuth");
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
|
|
||||||
|
@ -190,5 +185,5 @@ export default createReactClass({
|
||||||
{ content }
|
{ content }
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,14 +16,12 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class QuestionDialog extends React.Component {
|
||||||
displayName: 'QuestionDialog',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
description: PropTypes.node,
|
description: PropTypes.node,
|
||||||
extraButtons: PropTypes.node,
|
extraButtons: PropTypes.node,
|
||||||
|
@ -34,29 +32,27 @@ export default createReactClass({
|
||||||
headerImage: PropTypes.string,
|
headerImage: PropTypes.string,
|
||||||
quitOnly: PropTypes.bool, // quitOnly doesn't show the cancel button just the quit [x].
|
quitOnly: PropTypes.bool, // quitOnly doesn't show the cancel button just the quit [x].
|
||||||
fixedWidth: PropTypes.bool,
|
fixedWidth: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
title: "",
|
||||||
title: "",
|
description: "",
|
||||||
description: "",
|
extraButtons: null,
|
||||||
extraButtons: null,
|
focus: true,
|
||||||
focus: true,
|
hasCancelButton: true,
|
||||||
hasCancelButton: true,
|
danger: false,
|
||||||
danger: false,
|
quitOnly: false,
|
||||||
quitOnly: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onOk: function() {
|
onOk = () => {
|
||||||
this.props.onFinished(true);
|
this.props.onFinished(true);
|
||||||
},
|
};
|
||||||
|
|
||||||
onCancel: function() {
|
onCancel = () => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
let primaryButtonClass = "";
|
let primaryButtonClass = "";
|
||||||
|
@ -88,5 +84,5 @@ export default createReactClass({
|
||||||
</DialogButtons>
|
</DialogButtons>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -15,38 +15,33 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomUpgradeDialog extends React.Component {
|
||||||
displayName: 'RoomUpgradeDialog',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
room: PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
componentDidMount: async function() {
|
state = {
|
||||||
|
busy: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
async componentDidMount() {
|
||||||
const recommended = await this.props.room.getRecommendedVersion();
|
const recommended = await this.props.room.getRecommendedVersion();
|
||||||
this._targetVersion = recommended.version;
|
this._targetVersion = recommended.version;
|
||||||
this.setState({busy: false});
|
this.setState({busy: false});
|
||||||
},
|
}
|
||||||
|
|
||||||
getInitialState: function() {
|
_onCancelClick = () => {
|
||||||
return {
|
|
||||||
busy: true,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCancelClick: function() {
|
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
_onUpgradeClick: function() {
|
_onUpgradeClick = () => {
|
||||||
this.setState({busy: true});
|
this.setState({busy: true});
|
||||||
MatrixClientPeg.get().upgradeRoom(this.props.room.roomId, this._targetVersion).then(() => {
|
MatrixClientPeg.get().upgradeRoom(this.props.room.roomId, this._targetVersion).then(() => {
|
||||||
this.props.onFinished(true);
|
this.props.onFinished(true);
|
||||||
|
@ -59,9 +54,9 @@ export default createReactClass({
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.setState({busy: false});
|
this.setState({busy: false});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
const Spinner = sdk.getComponent('views.elements.Spinner');
|
const Spinner = sdk.getComponent('views.elements.Spinner');
|
||||||
|
@ -106,5 +101,5 @@ export default createReactClass({
|
||||||
{buttons}
|
{buttons}
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
|
@ -25,20 +24,18 @@ import Modal from '../../../Modal';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
|
|
||||||
export default createReactClass({
|
export default class SessionRestoreErrorDialog extends React.Component {
|
||||||
displayName: 'SessionRestoreErrorDialog',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
error: PropTypes.string.isRequired,
|
error: PropTypes.string.isRequired,
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
_sendBugReport: function() {
|
_sendBugReport = () => {
|
||||||
const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog");
|
const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog");
|
||||||
Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, {});
|
Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, {});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onClearStorageClick: function() {
|
_onClearStorageClick = () => {
|
||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
Modal.createTrackedDialog('Session Restore Confirm Logout', '', QuestionDialog, {
|
Modal.createTrackedDialog('Session Restore Confirm Logout', '', QuestionDialog, {
|
||||||
title: _t("Sign out"),
|
title: _t("Sign out"),
|
||||||
|
@ -48,15 +45,15 @@ export default createReactClass({
|
||||||
danger: true,
|
danger: true,
|
||||||
onFinished: this.props.onFinished,
|
onFinished: this.props.onFinished,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onRefreshClick: function() {
|
_onRefreshClick = () => {
|
||||||
// Is this likely to help? Probably not, but giving only one button
|
// Is this likely to help? Probably not, but giving only one button
|
||||||
// that clears your storage seems awful.
|
// that clears your storage seems awful.
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
|
@ -110,5 +107,5 @@ export default createReactClass({
|
||||||
{ dialogButtons }
|
{ dialogButtons }
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import * as Email from '../../../email';
|
import * as Email from '../../../email';
|
||||||
|
@ -30,26 +29,23 @@ import Modal from '../../../Modal';
|
||||||
*
|
*
|
||||||
* On success, `onFinished(true)` is called.
|
* On success, `onFinished(true)` is called.
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class SetEmailDialog extends React.Component {
|
||||||
displayName: 'SetEmailDialog',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
emailAddress: '',
|
||||||
emailAddress: '',
|
emailBusy: false,
|
||||||
emailBusy: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onEmailAddressChanged: function(value) {
|
onEmailAddressChanged = value => {
|
||||||
this.setState({
|
this.setState({
|
||||||
emailAddress: value,
|
emailAddress: value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onSubmit: function() {
|
onSubmit = () => {
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
|
|
||||||
|
@ -81,21 +77,21 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.setState({emailBusy: true});
|
this.setState({emailBusy: true});
|
||||||
},
|
};
|
||||||
|
|
||||||
onCancelled: function() {
|
onCancelled = () => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
onEmailDialogFinished: function(ok) {
|
onEmailDialogFinished = ok => {
|
||||||
if (ok) {
|
if (ok) {
|
||||||
this.verifyEmailAddress();
|
this.verifyEmailAddress();
|
||||||
} else {
|
} else {
|
||||||
this.setState({emailBusy: false});
|
this.setState({emailBusy: false});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
verifyEmailAddress: function() {
|
verifyEmailAddress() {
|
||||||
this._addThreepid.checkEmailLinkClicked().then(() => {
|
this._addThreepid.checkEmailLinkClicked().then(() => {
|
||||||
this.props.onFinished(true);
|
this.props.onFinished(true);
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
|
@ -119,9 +115,9 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const Spinner = sdk.getComponent('elements.Spinner');
|
const Spinner = sdk.getComponent('elements.Spinner');
|
||||||
const EditableText = sdk.getComponent('elements.EditableText');
|
const EditableText = sdk.getComponent('elements.EditableText');
|
||||||
|
@ -161,5 +157,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
|
@ -34,18 +33,22 @@ const USERNAME_CHECK_DEBOUNCE_MS = 250;
|
||||||
*
|
*
|
||||||
* On success, `onFinished(true, newDisplayName)` is called.
|
* On success, `onFinished(true, newDisplayName)` is called.
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class SetMxIdDialog extends React.Component {
|
||||||
displayName: 'SetMxIdDialog',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
// Called when the user requests to register with a different homeserver
|
// Called when the user requests to register with a different homeserver
|
||||||
onDifferentServerClicked: PropTypes.func.isRequired,
|
onDifferentServerClicked: PropTypes.func.isRequired,
|
||||||
// Called if the user wants to switch to login instead
|
// Called if the user wants to switch to login instead
|
||||||
onLoginClick: PropTypes.func.isRequired,
|
onLoginClick: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this._input_value = createRef();
|
||||||
|
this._uiAuth = createRef();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
// The entered username
|
// The entered username
|
||||||
username: '',
|
username: '',
|
||||||
// Indicate ongoing work on the username
|
// Indicate ongoing work on the username
|
||||||
|
@ -60,21 +63,15 @@ export default createReactClass({
|
||||||
// Indicate error with auth
|
// Indicate error with auth
|
||||||
authError: '',
|
authError: '',
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
componentDidMount() {
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._input_value = createRef();
|
|
||||||
this._uiAuth = createRef();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this._input_value.current.select();
|
this._input_value.current.select();
|
||||||
|
|
||||||
this._matrixClient = MatrixClientPeg.get();
|
this._matrixClient = MatrixClientPeg.get();
|
||||||
},
|
}
|
||||||
|
|
||||||
onValueChange: function(ev) {
|
onValueChange = ev => {
|
||||||
this.setState({
|
this.setState({
|
||||||
username: ev.target.value,
|
username: ev.target.value,
|
||||||
usernameBusy: true,
|
usernameBusy: true,
|
||||||
|
@ -99,24 +96,24 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
}, USERNAME_CHECK_DEBOUNCE_MS);
|
}, USERNAME_CHECK_DEBOUNCE_MS);
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onKeyUp: function(ev) {
|
onKeyUp = ev => {
|
||||||
if (ev.key === Key.ENTER) {
|
if (ev.key === Key.ENTER) {
|
||||||
this.onSubmit();
|
this.onSubmit();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onSubmit: function(ev) {
|
onSubmit = ev => {
|
||||||
if (this._uiAuth.current) {
|
if (this._uiAuth.current) {
|
||||||
this._uiAuth.current.tryContinue();
|
this._uiAuth.current.tryContinue();
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
doingUIAuth: true,
|
doingUIAuth: true,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_doUsernameCheck: function() {
|
_doUsernameCheck() {
|
||||||
// We do a quick check ahead of the username availability API to ensure the
|
// We do a quick check ahead of the username availability API to ensure the
|
||||||
// user ID roughly looks okay from a Matrix perspective.
|
// user ID roughly looks okay from a Matrix perspective.
|
||||||
if (!SAFE_LOCALPART_REGEX.test(this.state.username)) {
|
if (!SAFE_LOCALPART_REGEX.test(this.state.username)) {
|
||||||
|
@ -167,13 +164,13 @@ export default createReactClass({
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
_generatePassword: function() {
|
_generatePassword() {
|
||||||
return Math.random().toString(36).slice(2);
|
return Math.random().toString(36).slice(2);
|
||||||
},
|
}
|
||||||
|
|
||||||
_makeRegisterRequest: function(auth) {
|
_makeRegisterRequest = auth => {
|
||||||
// Not upgrading - changing mxids
|
// Not upgrading - changing mxids
|
||||||
const guestAccessToken = null;
|
const guestAccessToken = null;
|
||||||
if (!this._generatedPassword) {
|
if (!this._generatedPassword) {
|
||||||
|
@ -187,9 +184,9 @@ export default createReactClass({
|
||||||
{},
|
{},
|
||||||
guestAccessToken,
|
guestAccessToken,
|
||||||
);
|
);
|
||||||
},
|
};
|
||||||
|
|
||||||
_onUIAuthFinished: function(success, response) {
|
_onUIAuthFinished = (success, response) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
doingUIAuth: false,
|
doingUIAuth: false,
|
||||||
});
|
});
|
||||||
|
@ -207,9 +204,9 @@ export default createReactClass({
|
||||||
accessToken: response.access_token,
|
accessToken: response.access_token,
|
||||||
password: this._generatedPassword,
|
password: this._generatedPassword,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const InteractiveAuth = sdk.getComponent('structures.InteractiveAuth');
|
const InteractiveAuth = sdk.getComponent('structures.InteractiveAuth');
|
||||||
|
|
||||||
|
@ -303,5 +300,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -63,32 +62,25 @@ const WarmFuzzy = function(props) {
|
||||||
*
|
*
|
||||||
* On success, `onFinished()` when finished
|
* On success, `onFinished()` when finished
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class SetPasswordDialog extends React.Component {
|
||||||
displayName: 'SetPasswordDialog',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
error: null,
|
||||||
error: null,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
_onPasswordChanged = res => {
|
||||||
console.info('SetPasswordDialog component did mount');
|
|
||||||
},
|
|
||||||
|
|
||||||
_onPasswordChanged: function(res) {
|
|
||||||
Modal.createDialog(WarmFuzzy, {
|
Modal.createDialog(WarmFuzzy, {
|
||||||
didSetEmail: res.didSetEmail,
|
didSetEmail: res.didSetEmail,
|
||||||
onFinished: () => {
|
onFinished: () => {
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onPasswordChangeError: function(err) {
|
_onPasswordChangeError = err => {
|
||||||
let errMsg = err.error || "";
|
let errMsg = err.error || "";
|
||||||
if (err.httpStatus === 403) {
|
if (err.httpStatus === 403) {
|
||||||
errMsg = _t('Failed to change password. Is your password correct?');
|
errMsg = _t('Failed to change password. Is your password correct?');
|
||||||
|
@ -101,9 +93,9 @@ export default createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
error: errMsg,
|
error: errMsg,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const ChangePassword = sdk.getComponent('views.settings.ChangePassword');
|
const ChangePassword = sdk.getComponent('views.settings.ChangePassword');
|
||||||
|
|
||||||
|
@ -132,5 +124,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -15,14 +15,12 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import Field from "../elements/Field";
|
import Field from "../elements/Field";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class TextInputDialog extends React.Component {
|
||||||
displayName: 'TextInputDialog',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
description: PropTypes.oneOfType([
|
description: PropTypes.oneOfType([
|
||||||
PropTypes.element,
|
PropTypes.element,
|
||||||
|
@ -36,39 +34,36 @@ export default createReactClass({
|
||||||
hasCancel: PropTypes.bool,
|
hasCancel: PropTypes.bool,
|
||||||
validator: PropTypes.func, // result of withValidation
|
validator: PropTypes.func, // result of withValidation
|
||||||
fixedWidth: PropTypes.bool,
|
fixedWidth: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
title: "",
|
||||||
title: "",
|
value: "",
|
||||||
value: "",
|
description: "",
|
||||||
description: "",
|
focus: true,
|
||||||
focus: true,
|
hasCancel: true,
|
||||||
hasCancel: true,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this._field = createRef();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
value: this.props.value,
|
value: this.props.value,
|
||||||
valid: false,
|
valid: false,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
componentDidMount() {
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._field = createRef();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
if (this.props.focus) {
|
if (this.props.focus) {
|
||||||
// Set the cursor at the end of the text input
|
// Set the cursor at the end of the text input
|
||||||
// this._field.current.value = this.props.value;
|
// this._field.current.value = this.props.value;
|
||||||
this._field.current.focus();
|
this._field.current.focus();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onOk: async function(ev) {
|
onOk = async ev => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
if (this.props.validator) {
|
if (this.props.validator) {
|
||||||
await this._field.current.validate({ allowEmpty: false });
|
await this._field.current.validate({ allowEmpty: false });
|
||||||
|
@ -80,27 +75,27 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.props.onFinished(true, this.state.value);
|
this.props.onFinished(true, this.state.value);
|
||||||
},
|
};
|
||||||
|
|
||||||
onCancel: function() {
|
onCancel = () => {
|
||||||
this.props.onFinished(false);
|
this.props.onFinished(false);
|
||||||
},
|
};
|
||||||
|
|
||||||
onChange: function(ev) {
|
onChange = ev => {
|
||||||
this.setState({
|
this.setState({
|
||||||
value: ev.target.value,
|
value: ev.target.value,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onValidate: async function(fieldState) {
|
onValidate = async fieldState => {
|
||||||
const result = await this.props.validator(fieldState);
|
const result = await this.props.validator(fieldState);
|
||||||
this.setState({
|
this.setState({
|
||||||
valid: result.valid,
|
valid: result.valid,
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||||
return (
|
return (
|
||||||
|
@ -137,5 +132,5 @@ export default createReactClass({
|
||||||
/>
|
/>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,16 +16,13 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import AccessibleButton from './AccessibleButton';
|
import AccessibleButton from './AccessibleButton';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import Analytics from '../../../Analytics';
|
import Analytics from '../../../Analytics';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class ActionButton extends React.Component {
|
||||||
displayName: 'RoleButton',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
size: PropTypes.string,
|
size: PropTypes.string,
|
||||||
tooltip: PropTypes.bool,
|
tooltip: PropTypes.bool,
|
||||||
action: PropTypes.string.isRequired,
|
action: PropTypes.string.isRequired,
|
||||||
|
@ -33,39 +30,35 @@ export default createReactClass({
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
iconPath: PropTypes.string,
|
iconPath: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
size: "25",
|
||||||
size: "25",
|
tooltip: false,
|
||||||
tooltip: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
showTooltip: false,
|
||||||
showTooltip: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_onClick: function(ev) {
|
_onClick = (ev) => {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
Analytics.trackEvent('Action Button', 'click', this.props.action);
|
Analytics.trackEvent('Action Button', 'click', this.props.action);
|
||||||
dis.dispatch({action: this.props.action});
|
dis.dispatch({action: this.props.action});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onMouseEnter: function() {
|
_onMouseEnter = () => {
|
||||||
if (this.props.tooltip) this.setState({showTooltip: true});
|
if (this.props.tooltip) this.setState({showTooltip: true});
|
||||||
if (this.props.mouseOverAction) {
|
if (this.props.mouseOverAction) {
|
||||||
dis.dispatch({action: this.props.mouseOverAction});
|
dis.dispatch({action: this.props.mouseOverAction});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_onMouseLeave: function() {
|
_onMouseLeave = () => {
|
||||||
this.setState({showTooltip: false});
|
this.setState({showTooltip: false});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
|
|
||||||
let tooltip;
|
let tooltip;
|
||||||
|
@ -94,5 +87,5 @@ export default createReactClass({
|
||||||
{ tooltip }
|
{ tooltip }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,15 +17,12 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { UserAddressType } from '../../../UserAddress';
|
import { UserAddressType } from '../../../UserAddress';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class AddressSelector extends React.Component {
|
||||||
displayName: 'AddressSelector',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
onSelected: PropTypes.func.isRequired,
|
onSelected: PropTypes.func.isRequired,
|
||||||
|
|
||||||
// List of the addresses to display
|
// List of the addresses to display
|
||||||
|
@ -37,90 +34,91 @@ export default createReactClass({
|
||||||
|
|
||||||
// Element to put as a header on top of the list
|
// Element to put as a header on top of the list
|
||||||
header: PropTypes.node,
|
header: PropTypes.node,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
selected: this.props.selected === undefined ? 0 : this.props.selected,
|
selected: this.props.selected === undefined ? 0 : this.props.selected,
|
||||||
hover: false,
|
hover: false,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
UNSAFE_componentWillReceiveProps: function(props) {
|
UNSAFE_componentWillReceiveProps(props) {
|
||||||
// Make sure the selected item isn't outside the list bounds
|
// Make sure the selected item isn't outside the list bounds
|
||||||
const selected = this.state.selected;
|
const selected = this.state.selected;
|
||||||
const maxSelected = this._maxSelected(props.addressList);
|
const maxSelected = this._maxSelected(props.addressList);
|
||||||
if (selected > maxSelected) {
|
if (selected > maxSelected) {
|
||||||
this.setState({ selected: maxSelected });
|
this.setState({ selected: maxSelected });
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate() {
|
||||||
// As the user scrolls with the arrow keys keep the selected item
|
// As the user scrolls with the arrow keys keep the selected item
|
||||||
// at the top of the window.
|
// at the top of the window.
|
||||||
if (this.scrollElement && this.props.addressList.length > 0 && !this.state.hover) {
|
if (this.scrollElement && this.props.addressList.length > 0 && !this.state.hover) {
|
||||||
const elementHeight = this.addressListElement.getBoundingClientRect().height;
|
const elementHeight = this.addressListElement.getBoundingClientRect().height;
|
||||||
this.scrollElement.scrollTop = (this.state.selected * elementHeight) - elementHeight;
|
this.scrollElement.scrollTop = (this.state.selected * elementHeight) - elementHeight;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
moveSelectionTop: function() {
|
moveSelectionTop = () => {
|
||||||
if (this.state.selected > 0) {
|
if (this.state.selected > 0) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selected: 0,
|
selected: 0,
|
||||||
hover: false,
|
hover: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
moveSelectionUp: function() {
|
moveSelectionUp = () => {
|
||||||
if (this.state.selected > 0) {
|
if (this.state.selected > 0) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selected: this.state.selected - 1,
|
selected: this.state.selected - 1,
|
||||||
hover: false,
|
hover: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
moveSelectionDown: function() {
|
moveSelectionDown = () => {
|
||||||
if (this.state.selected < this._maxSelected(this.props.addressList)) {
|
if (this.state.selected < this._maxSelected(this.props.addressList)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
selected: this.state.selected + 1,
|
selected: this.state.selected + 1,
|
||||||
hover: false,
|
hover: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
chooseSelection: function() {
|
chooseSelection = () => {
|
||||||
this.selectAddress(this.state.selected);
|
this.selectAddress(this.state.selected);
|
||||||
},
|
};
|
||||||
|
|
||||||
onClick: function(index) {
|
onClick = index => {
|
||||||
this.selectAddress(index);
|
this.selectAddress(index);
|
||||||
},
|
};
|
||||||
|
|
||||||
onMouseEnter: function(index) {
|
onMouseEnter = index => {
|
||||||
this.setState({
|
this.setState({
|
||||||
selected: index,
|
selected: index,
|
||||||
hover: true,
|
hover: true,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onMouseLeave: function() {
|
onMouseLeave = () => {
|
||||||
this.setState({ hover: false });
|
this.setState({ hover: false });
|
||||||
},
|
};
|
||||||
|
|
||||||
selectAddress: function(index) {
|
selectAddress = index => {
|
||||||
// Only try to select an address if one exists
|
// Only try to select an address if one exists
|
||||||
if (this.props.addressList.length !== 0) {
|
if (this.props.addressList.length !== 0) {
|
||||||
this.props.onSelected(index);
|
this.props.onSelected(index);
|
||||||
this.setState({ hover: false });
|
this.setState({ hover: false });
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
createAddressListTiles: function() {
|
createAddressListTiles() {
|
||||||
const self = this;
|
|
||||||
const AddressTile = sdk.getComponent("elements.AddressTile");
|
const AddressTile = sdk.getComponent("elements.AddressTile");
|
||||||
const maxSelected = this._maxSelected(this.props.addressList);
|
const maxSelected = this._maxSelected(this.props.addressList);
|
||||||
const addressList = [];
|
const addressList = [];
|
||||||
|
@ -157,15 +155,15 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return addressList;
|
return addressList;
|
||||||
},
|
}
|
||||||
|
|
||||||
_maxSelected: function(list) {
|
_maxSelected(list) {
|
||||||
const listSize = list.length === 0 ? 0 : list.length - 1;
|
const listSize = list.length === 0 ? 0 : list.length - 1;
|
||||||
const maxSelected = listSize > (this.props.truncateAt - 1) ? (this.props.truncateAt - 1) : listSize;
|
const maxSelected = listSize > (this.props.truncateAt - 1) ? (this.props.truncateAt - 1) : listSize;
|
||||||
return maxSelected;
|
return maxSelected;
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const classes = classNames({
|
const classes = classNames({
|
||||||
"mx_AddressSelector": true,
|
"mx_AddressSelector": true,
|
||||||
"mx_AddressSelector_empty": this.props.addressList.length === 0,
|
"mx_AddressSelector_empty": this.props.addressList.length === 0,
|
||||||
|
@ -177,5 +175,5 @@ export default createReactClass({
|
||||||
{ this.createAddressListTiles() }
|
{ this.createAddressListTiles() }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
|
@ -25,25 +24,21 @@ import { _t } from '../../../languageHandler';
|
||||||
import { UserAddressType } from '../../../UserAddress.js';
|
import { UserAddressType } from '../../../UserAddress.js';
|
||||||
|
|
||||||
|
|
||||||
export default createReactClass({
|
export default class AddressTile extends React.Component {
|
||||||
displayName: 'AddressTile',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
address: UserAddressType.isRequired,
|
address: UserAddressType.isRequired,
|
||||||
canDismiss: PropTypes.bool,
|
canDismiss: PropTypes.bool,
|
||||||
onDismissed: PropTypes.func,
|
onDismissed: PropTypes.func,
|
||||||
justified: PropTypes.bool,
|
justified: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
canDismiss: false,
|
||||||
canDismiss: false,
|
onDismissed: function() {}, // NOP
|
||||||
onDismissed: function() {}, // NOP
|
justified: false,
|
||||||
justified: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const address = this.props.address;
|
const address = this.props.address;
|
||||||
const name = address.displayName || address.address;
|
const name = address.displayName || address.address;
|
||||||
|
|
||||||
|
@ -144,5 +139,5 @@ export default createReactClass({
|
||||||
{ dismiss }
|
{ dismiss }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,16 +18,13 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic container for buttons in modal dialogs.
|
* Basic container for buttons in modal dialogs.
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class DialogButtons extends React.Component {
|
||||||
displayName: "DialogButtons",
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// The primary button which is styled differently and has default focus.
|
// The primary button which is styled differently and has default focus.
|
||||||
primaryButton: PropTypes.node.isRequired,
|
primaryButton: PropTypes.node.isRequired,
|
||||||
|
|
||||||
|
@ -57,20 +54,18 @@ export default createReactClass({
|
||||||
|
|
||||||
// disables only the primary button
|
// disables only the primary button
|
||||||
primaryDisabled: PropTypes.bool,
|
primaryDisabled: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
hasCancel: true,
|
||||||
hasCancel: true,
|
disabled: false,
|
||||||
disabled: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_onCancelClick: function() {
|
_onCancelClick = () => {
|
||||||
this.props.onCancel();
|
this.props.onCancel();
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let primaryButtonClassName = "mx_Dialog_primary";
|
let primaryButtonClassName = "mx_Dialog_primary";
|
||||||
if (this.props.primaryButtonClass) {
|
if (this.props.primaryButtonClass) {
|
||||||
primaryButtonClassName += " " + this.props.primaryButtonClass;
|
primaryButtonClassName += " " + this.props.primaryButtonClass;
|
||||||
|
@ -104,5 +99,5 @@ export default createReactClass({
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,13 +17,10 @@ limitations under the License.
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {Key} from "../../../Keyboard";
|
import {Key} from "../../../Keyboard";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class EditableText extends React.Component {
|
||||||
displayName: 'EditableText',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
onValueChanged: PropTypes.func,
|
onValueChanged: PropTypes.func,
|
||||||
initialValue: PropTypes.string,
|
initialValue: PropTypes.string,
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
|
@ -36,60 +33,62 @@ export default createReactClass({
|
||||||
// Will cause onValueChanged(value, true) to fire on blur
|
// Will cause onValueChanged(value, true) to fire on blur
|
||||||
blurToSubmit: PropTypes.bool,
|
blurToSubmit: PropTypes.bool,
|
||||||
editable: PropTypes.bool,
|
editable: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
Phases: {
|
static Phases = {
|
||||||
Display: "display",
|
Display: "display",
|
||||||
Edit: "edit",
|
Edit: "edit",
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
onValueChanged() {},
|
||||||
onValueChanged: function() {},
|
initialValue: '',
|
||||||
initialValue: '',
|
label: '',
|
||||||
label: '',
|
placeholder: '',
|
||||||
placeholder: '',
|
editable: true,
|
||||||
editable: true,
|
className: "mx_EditableText",
|
||||||
className: "mx_EditableText",
|
placeholderClassName: "mx_EditableText_placeholder",
|
||||||
placeholderClassName: "mx_EditableText_placeholder",
|
blurToSubmit: false,
|
||||||
blurToSubmit: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
phase: this.Phases.Display,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
|
||||||
UNSAFE_componentWillReceiveProps: function(nextProps) {
|
|
||||||
if (nextProps.initialValue !== this.props.initialValue) {
|
|
||||||
this.value = nextProps.initialValue;
|
|
||||||
if (this._editable_div.current) {
|
|
||||||
this.showPlaceholder(!this.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
// we track value as an JS object field rather than in React state
|
// we track value as an JS object field rather than in React state
|
||||||
// as React doesn't play nice with contentEditable.
|
// as React doesn't play nice with contentEditable.
|
||||||
this.value = '';
|
this.value = '';
|
||||||
this.placeholder = false;
|
this.placeholder = false;
|
||||||
|
|
||||||
this._editable_div = createRef();
|
this._editable_div = createRef();
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
this.state = {
|
||||||
|
phase: EditableText.Phases.Display,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
state = {
|
||||||
|
phase: EditableText.Phases.Display,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.initialValue !== this.props.initialValue) {
|
||||||
|
this.value = nextProps.initialValue;
|
||||||
|
if (this._editable_div.current) {
|
||||||
|
this.showPlaceholder(!this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
this.value = this.props.initialValue;
|
this.value = this.props.initialValue;
|
||||||
if (this._editable_div.current) {
|
if (this._editable_div.current) {
|
||||||
this.showPlaceholder(!this.value);
|
this.showPlaceholder(!this.value);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
showPlaceholder: function(show) {
|
showPlaceholder = show => {
|
||||||
if (show) {
|
if (show) {
|
||||||
this._editable_div.current.textContent = this.props.placeholder;
|
this._editable_div.current.textContent = this.props.placeholder;
|
||||||
this._editable_div.current.setAttribute("class", this.props.className
|
this._editable_div.current.setAttribute("class", this.props.className
|
||||||
|
@ -101,38 +100,36 @@ export default createReactClass({
|
||||||
this._editable_div.current.setAttribute("class", this.props.className);
|
this._editable_div.current.setAttribute("class", this.props.className);
|
||||||
this.placeholder = false;
|
this.placeholder = false;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
getValue: function() {
|
getValue = () => this.value;
|
||||||
return this.value;
|
|
||||||
},
|
|
||||||
|
|
||||||
setValue: function(value) {
|
setValue = value => {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.showPlaceholder(!this.value);
|
this.showPlaceholder(!this.value);
|
||||||
},
|
};
|
||||||
|
|
||||||
edit: function() {
|
edit = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: this.Phases.Edit,
|
phase: EditableText.Phases.Edit,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
cancelEdit: function() {
|
cancelEdit = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: this.Phases.Display,
|
phase: EditableText.Phases.Display,
|
||||||
});
|
});
|
||||||
this.value = this.props.initialValue;
|
this.value = this.props.initialValue;
|
||||||
this.showPlaceholder(!this.value);
|
this.showPlaceholder(!this.value);
|
||||||
this.onValueChanged(false);
|
this.onValueChanged(false);
|
||||||
this._editable_div.current.blur();
|
this._editable_div.current.blur();
|
||||||
},
|
};
|
||||||
|
|
||||||
onValueChanged: function(shouldSubmit) {
|
onValueChanged = shouldSubmit => {
|
||||||
this.props.onValueChanged(this.value, shouldSubmit);
|
this.props.onValueChanged(this.value, shouldSubmit);
|
||||||
},
|
};
|
||||||
|
|
||||||
onKeyDown: function(ev) {
|
onKeyDown = ev => {
|
||||||
// console.log("keyDown: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder);
|
// console.log("keyDown: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder);
|
||||||
|
|
||||||
if (this.placeholder) {
|
if (this.placeholder) {
|
||||||
|
@ -145,9 +142,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("keyDown: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder);
|
// console.log("keyDown: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder);
|
||||||
},
|
};
|
||||||
|
|
||||||
onKeyUp: function(ev) {
|
onKeyUp = ev => {
|
||||||
// console.log("keyUp: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder);
|
// console.log("keyUp: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder);
|
||||||
|
|
||||||
if (!ev.target.textContent) {
|
if (!ev.target.textContent) {
|
||||||
|
@ -163,17 +160,17 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.log("keyUp: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder);
|
// console.log("keyUp: textContent=" + ev.target.textContent + ", value=" + this.value + ", placeholder=" + this.placeholder);
|
||||||
},
|
};
|
||||||
|
|
||||||
onClickDiv: function(ev) {
|
onClickDiv = ev => {
|
||||||
if (!this.props.editable) return;
|
if (!this.props.editable) return;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: this.Phases.Edit,
|
phase: EditableText.Phases.Edit,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onFocus: function(ev) {
|
onFocus = ev => {
|
||||||
//ev.target.setSelectionRange(0, ev.target.textContent.length);
|
//ev.target.setSelectionRange(0, ev.target.textContent.length);
|
||||||
|
|
||||||
const node = ev.target.childNodes[0];
|
const node = ev.target.childNodes[0];
|
||||||
|
@ -186,21 +183,21 @@ export default createReactClass({
|
||||||
sel.removeAllRanges();
|
sel.removeAllRanges();
|
||||||
sel.addRange(range);
|
sel.addRange(range);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onFinish: function(ev, shouldSubmit) {
|
onFinish = (ev, shouldSubmit) => {
|
||||||
const self = this;
|
const self = this;
|
||||||
const submit = (ev.key === Key.ENTER) || shouldSubmit;
|
const submit = (ev.key === Key.ENTER) || shouldSubmit;
|
||||||
this.setState({
|
this.setState({
|
||||||
phase: this.Phases.Display,
|
phase: EditableText.Phases.Display,
|
||||||
}, () => {
|
}, () => {
|
||||||
if (this.value !== this.props.initialValue) {
|
if (this.value !== this.props.initialValue) {
|
||||||
self.onValueChanged(submit);
|
self.onValueChanged(submit);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onBlur: function(ev) {
|
onBlur = ev => {
|
||||||
const sel = window.getSelection();
|
const sel = window.getSelection();
|
||||||
sel.removeAllRanges();
|
sel.removeAllRanges();
|
||||||
|
|
||||||
|
@ -211,13 +208,13 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showPlaceholder(!this.value);
|
this.showPlaceholder(!this.value);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const {className, editable, initialValue, label, labelClassName} = this.props;
|
const {className, editable, initialValue, label, labelClassName} = this.props;
|
||||||
let editableEl;
|
let editableEl;
|
||||||
|
|
||||||
if (!editable || (this.state.phase === this.Phases.Display && (label || labelClassName) && !this.value)) {
|
if (!editable || (this.state.phase === EditableText.Phases.Display && (label || labelClassName) && !this.value)) {
|
||||||
// show the label
|
// show the label
|
||||||
editableEl = <div className={className + " " + labelClassName} onClick={this.onClickDiv}>
|
editableEl = <div className={className + " " + labelClassName} onClick={this.onClickDiv}>
|
||||||
{ label || initialValue }
|
{ label || initialValue }
|
||||||
|
@ -234,5 +231,5 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return editableEl;
|
return editableEl;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -15,14 +15,11 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class InlineSpinner extends React.Component {
|
||||||
displayName: 'InlineSpinner',
|
render() {
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const w = this.props.w || 16;
|
const w = this.props.w || 16;
|
||||||
const h = this.props.h || 16;
|
const h = this.props.h || 16;
|
||||||
const imgClass = this.props.imgClassName || "";
|
const imgClass = this.props.imgClassName || "";
|
||||||
|
@ -45,5 +42,5 @@ export default createReactClass({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,17 +18,14 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { formatCommaSeparatedList } from '../../../utils/FormattingUtils';
|
import { formatCommaSeparatedList } from '../../../utils/FormattingUtils';
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import {MatrixEvent} from "matrix-js-sdk";
|
import {MatrixEvent} from "matrix-js-sdk";
|
||||||
import {isValid3pidInvite} from "../../../RoomInvite";
|
import {isValid3pidInvite} from "../../../RoomInvite";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class MemberEventListSummary extends React.Component {
|
||||||
displayName: 'MemberEventListSummary',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// An array of member events to summarise
|
// An array of member events to summarise
|
||||||
events: PropTypes.arrayOf(PropTypes.instanceOf(MatrixEvent)).isRequired,
|
events: PropTypes.arrayOf(PropTypes.instanceOf(MatrixEvent)).isRequired,
|
||||||
// An array of EventTiles to render when expanded
|
// An array of EventTiles to render when expanded
|
||||||
|
@ -43,17 +40,15 @@ export default createReactClass({
|
||||||
onToggle: PropTypes.func,
|
onToggle: PropTypes.func,
|
||||||
// Whether or not to begin with state.expanded=true
|
// Whether or not to begin with state.expanded=true
|
||||||
startExpanded: PropTypes.bool,
|
startExpanded: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
summaryLength: 1,
|
||||||
summaryLength: 1,
|
threshold: 3,
|
||||||
threshold: 3,
|
avatarsMaxLength: 5,
|
||||||
avatarsMaxLength: 5,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldComponentUpdate: function(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
// Update if
|
// Update if
|
||||||
// - The number of summarised events has changed
|
// - The number of summarised events has changed
|
||||||
// - or if the summary is about to toggle to become collapsed
|
// - or if the summary is about to toggle to become collapsed
|
||||||
|
@ -62,7 +57,7 @@ export default createReactClass({
|
||||||
nextProps.events.length !== this.props.events.length ||
|
nextProps.events.length !== this.props.events.length ||
|
||||||
nextProps.events.length < this.props.threshold
|
nextProps.events.length < this.props.threshold
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the text for users aggregated by their transition sequences (`eventAggregates`) where
|
* Generate the text for users aggregated by their transition sequences (`eventAggregates`) where
|
||||||
|
@ -73,7 +68,7 @@ export default createReactClass({
|
||||||
* `Object.keys(eventAggregates)`.
|
* `Object.keys(eventAggregates)`.
|
||||||
* @returns {string} the textual summary of the aggregated events that occurred.
|
* @returns {string} the textual summary of the aggregated events that occurred.
|
||||||
*/
|
*/
|
||||||
_generateSummary: function(eventAggregates, orderedTransitionSequences) {
|
_generateSummary(eventAggregates, orderedTransitionSequences) {
|
||||||
const summaries = orderedTransitionSequences.map((transitions) => {
|
const summaries = orderedTransitionSequences.map((transitions) => {
|
||||||
const userNames = eventAggregates[transitions];
|
const userNames = eventAggregates[transitions];
|
||||||
const nameList = this._renderNameList(userNames);
|
const nameList = this._renderNameList(userNames);
|
||||||
|
@ -105,7 +100,7 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return summaries.join(", ");
|
return summaries.join(", ");
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string[]} users an array of user display names or user IDs.
|
* @param {string[]} users an array of user display names or user IDs.
|
||||||
|
@ -113,9 +108,9 @@ export default createReactClass({
|
||||||
* more items in `users` than `this.props.summaryLength`, which is the number of names
|
* more items in `users` than `this.props.summaryLength`, which is the number of names
|
||||||
* included before "and [n] others".
|
* included before "and [n] others".
|
||||||
*/
|
*/
|
||||||
_renderNameList: function(users) {
|
_renderNameList(users) {
|
||||||
return formatCommaSeparatedList(users, this.props.summaryLength);
|
return formatCommaSeparatedList(users, this.props.summaryLength);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Canonicalise an array of transitions such that some pairs of transitions become
|
* Canonicalise an array of transitions such that some pairs of transitions become
|
||||||
|
@ -124,7 +119,7 @@ export default createReactClass({
|
||||||
* @param {string[]} transitions an array of transitions.
|
* @param {string[]} transitions an array of transitions.
|
||||||
* @returns {string[]} an array of transitions.
|
* @returns {string[]} an array of transitions.
|
||||||
*/
|
*/
|
||||||
_getCanonicalTransitions: function(transitions) {
|
_getCanonicalTransitions(transitions) {
|
||||||
const modMap = {
|
const modMap = {
|
||||||
'joined': {
|
'joined': {
|
||||||
'after': 'left',
|
'after': 'left',
|
||||||
|
@ -155,7 +150,7 @@ export default createReactClass({
|
||||||
res.push(transition);
|
res.push(transition);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transform an array of transitions into an array of transitions and how many times
|
* Transform an array of transitions into an array of transitions and how many times
|
||||||
|
@ -171,7 +166,7 @@ export default createReactClass({
|
||||||
* @param {string[]} transitions the array of transitions to transform.
|
* @param {string[]} transitions the array of transitions to transform.
|
||||||
* @returns {object[]} an array of coalesced transitions.
|
* @returns {object[]} an array of coalesced transitions.
|
||||||
*/
|
*/
|
||||||
_coalesceRepeatedTransitions: function(transitions) {
|
_coalesceRepeatedTransitions(transitions) {
|
||||||
const res = [];
|
const res = [];
|
||||||
for (let i = 0; i < transitions.length; i++) {
|
for (let i = 0; i < transitions.length; i++) {
|
||||||
if (res.length > 0 && res[res.length - 1].transitionType === transitions[i]) {
|
if (res.length > 0 && res[res.length - 1].transitionType === transitions[i]) {
|
||||||
|
@ -184,7 +179,7 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For a certain transition, t, describe what happened to the users that
|
* For a certain transition, t, describe what happened to the users that
|
||||||
|
@ -268,11 +263,11 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getTransitionSequence: function(events) {
|
_getTransitionSequence(events) {
|
||||||
return events.map(this._getTransition);
|
return events.map(this._getTransition);
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label a given membership event, `e`, where `getContent().membership` has
|
* Label a given membership event, `e`, where `getContent().membership` has
|
||||||
|
@ -282,7 +277,7 @@ export default createReactClass({
|
||||||
* @returns {string?} the transition type given to this event. This defaults to `null`
|
* @returns {string?} the transition type given to this event. This defaults to `null`
|
||||||
* if a transition is not recognised.
|
* if a transition is not recognised.
|
||||||
*/
|
*/
|
||||||
_getTransition: function(e) {
|
_getTransition(e) {
|
||||||
if (e.mxEvent.getType() === 'm.room.third_party_invite') {
|
if (e.mxEvent.getType() === 'm.room.third_party_invite') {
|
||||||
// Handle 3pid invites the same as invites so they get bundled together
|
// Handle 3pid invites the same as invites so they get bundled together
|
||||||
if (!isValid3pidInvite(e.mxEvent)) {
|
if (!isValid3pidInvite(e.mxEvent)) {
|
||||||
|
@ -323,9 +318,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_getAggregate: function(userEvents) {
|
_getAggregate(userEvents) {
|
||||||
// A map of aggregate type to arrays of display names. Each aggregate type
|
// A map of aggregate type to arrays of display names. Each aggregate type
|
||||||
// is a comma-delimited string of transitions, e.g. "joined,left,kicked".
|
// is a comma-delimited string of transitions, e.g. "joined,left,kicked".
|
||||||
// The array of display names is the array of users who went through that
|
// The array of display names is the array of users who went through that
|
||||||
|
@ -364,9 +359,9 @@ export default createReactClass({
|
||||||
names: aggregate,
|
names: aggregate,
|
||||||
indices: aggregateIndices,
|
indices: aggregateIndices,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const eventsToRender = this.props.events;
|
const eventsToRender = this.props.events;
|
||||||
|
|
||||||
// Map user IDs to an array of objects:
|
// Map user IDs to an array of objects:
|
||||||
|
@ -420,5 +415,5 @@ export default createReactClass({
|
||||||
children={this.props.children}
|
children={this.props.children}
|
||||||
summaryMembers={avatarMembers}
|
summaryMembers={avatarMembers}
|
||||||
summaryText={this._generateSummary(aggregate.names, orderedTransitionSequences)} />;
|
summaryText={this._generateSummary(aggregate.names, orderedTransitionSequences)} />;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,49 +16,44 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||||
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
|
||||||
import WidgetUtils from '../../../utils/WidgetUtils';
|
import WidgetUtils from '../../../utils/WidgetUtils';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class PersistentApp extends React.Component {
|
||||||
displayName: 'PersistentApp',
|
state = {
|
||||||
|
roomId: RoomViewStore.getRoomId(),
|
||||||
|
persistentWidgetId: ActiveWidgetStore.getPersistentWidgetId(),
|
||||||
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
componentDidMount() {
|
||||||
return {
|
|
||||||
roomId: RoomViewStore.getRoomId(),
|
|
||||||
persistentWidgetId: ActiveWidgetStore.getPersistentWidgetId(),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
|
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
|
||||||
ActiveWidgetStore.on('update', this._onActiveWidgetStoreUpdate);
|
ActiveWidgetStore.on('update', this._onActiveWidgetStoreUpdate);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
if (this._roomStoreToken) {
|
if (this._roomStoreToken) {
|
||||||
this._roomStoreToken.remove();
|
this._roomStoreToken.remove();
|
||||||
}
|
}
|
||||||
ActiveWidgetStore.removeListener('update', this._onActiveWidgetStoreUpdate);
|
ActiveWidgetStore.removeListener('update', this._onActiveWidgetStoreUpdate);
|
||||||
},
|
}
|
||||||
|
|
||||||
_onRoomViewStoreUpdate: function(payload) {
|
_onRoomViewStoreUpdate = payload => {
|
||||||
if (RoomViewStore.getRoomId() === this.state.roomId) return;
|
if (RoomViewStore.getRoomId() === this.state.roomId) return;
|
||||||
this.setState({
|
this.setState({
|
||||||
roomId: RoomViewStore.getRoomId(),
|
roomId: RoomViewStore.getRoomId(),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onActiveWidgetStoreUpdate: function() {
|
_onActiveWidgetStoreUpdate = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
persistentWidgetId: ActiveWidgetStore.getPersistentWidgetId(),
|
persistentWidgetId: ActiveWidgetStore.getPersistentWidgetId(),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
if (this.state.persistentWidgetId) {
|
if (this.state.persistentWidgetId) {
|
||||||
const persistentWidgetInRoomId = ActiveWidgetStore.getRoomId(this.state.persistentWidgetId);
|
const persistentWidgetInRoomId = ActiveWidgetStore.getRoomId(this.state.persistentWidgetId);
|
||||||
if (this.state.roomId !== persistentWidgetInRoomId) {
|
if (this.state.roomId !== persistentWidgetInRoomId) {
|
||||||
|
@ -91,6 +86,6 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
@ -32,27 +31,29 @@ import {Action} from "../../../dispatcher/actions";
|
||||||
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
|
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
|
||||||
const REGEX_LOCAL_PERMALINK = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/;
|
const REGEX_LOCAL_PERMALINK = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/;
|
||||||
|
|
||||||
const Pill = createReactClass({
|
class Pill extends React.Component {
|
||||||
statics: {
|
static isPillUrl(url) {
|
||||||
isPillUrl: (url) => {
|
return !!getPrimaryPermalinkEntity(url);
|
||||||
return !!getPrimaryPermalinkEntity(url);
|
}
|
||||||
},
|
|
||||||
isMessagePillUrl: (url) => {
|
|
||||||
return !!REGEX_LOCAL_PERMALINK.exec(url);
|
|
||||||
},
|
|
||||||
roomNotifPos: (text) => {
|
|
||||||
return text.indexOf("@room");
|
|
||||||
},
|
|
||||||
roomNotifLen: () => {
|
|
||||||
return "@room".length;
|
|
||||||
},
|
|
||||||
TYPE_USER_MENTION: 'TYPE_USER_MENTION',
|
|
||||||
TYPE_ROOM_MENTION: 'TYPE_ROOM_MENTION',
|
|
||||||
TYPE_GROUP_MENTION: 'TYPE_GROUP_MENTION',
|
|
||||||
TYPE_AT_ROOM_MENTION: 'TYPE_AT_ROOM_MENTION', // '@room' mention
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
static isMessagePillUrl(url) {
|
||||||
|
return !!REGEX_LOCAL_PERMALINK.exec(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
static roomNotifPos(text) {
|
||||||
|
return text.indexOf("@room");
|
||||||
|
}
|
||||||
|
|
||||||
|
static roomNotifLen() {
|
||||||
|
return "@room".length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TYPE_USER_MENTION = 'TYPE_USER_MENTION';
|
||||||
|
static TYPE_ROOM_MENTION = 'TYPE_ROOM_MENTION';
|
||||||
|
static TYPE_GROUP_MENTION = 'TYPE_GROUP_MENTION';
|
||||||
|
static TYPE_AT_ROOM_MENTION = 'TYPE_AT_ROOM_MENTION'; // '@room' mention
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
// The Type of this Pill. If url is given, this is auto-detected.
|
// The Type of this Pill. If url is given, this is auto-detected.
|
||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
// The URL to pillify (no validation is done, see isPillUrl and isMessagePillUrl)
|
// The URL to pillify (no validation is done, see isPillUrl and isMessagePillUrl)
|
||||||
|
@ -65,23 +66,21 @@ const Pill = createReactClass({
|
||||||
shouldShowPillAvatar: PropTypes.bool,
|
shouldShowPillAvatar: PropTypes.bool,
|
||||||
// Whether to render this pill as if it were highlit by a selection
|
// Whether to render this pill as if it were highlit by a selection
|
||||||
isSelected: PropTypes.bool,
|
isSelected: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState() {
|
state = {
|
||||||
return {
|
// ID/alias of the room/user
|
||||||
// ID/alias of the room/user
|
resourceId: null,
|
||||||
resourceId: null,
|
// Type of pill
|
||||||
// Type of pill
|
pillType: null,
|
||||||
pillType: null,
|
|
||||||
|
|
||||||
// The member related to the user pill
|
// The member related to the user pill
|
||||||
member: null,
|
member: null,
|
||||||
// The group related to the group pill
|
// The group related to the group pill
|
||||||
group: null,
|
group: null,
|
||||||
// The room related to the room pill
|
// The room related to the room pill
|
||||||
room: null,
|
room: null,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
async UNSAFE_componentWillReceiveProps(nextProps) {
|
async UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
|
@ -155,7 +154,7 @@ const Pill = createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.setState({resourceId, pillType, member, group, room});
|
this.setState({resourceId, pillType, member, group, room});
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
|
@ -163,13 +162,13 @@ const Pill = createReactClass({
|
||||||
|
|
||||||
// eslint-disable-next-line new-cap
|
// eslint-disable-next-line new-cap
|
||||||
this.UNSAFE_componentWillReceiveProps(this.props); // HACK: We shouldn't be calling lifecycle functions ourselves.
|
this.UNSAFE_componentWillReceiveProps(this.props); // HACK: We shouldn't be calling lifecycle functions ourselves.
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
doProfileLookup: function(userId, member) {
|
doProfileLookup(userId, member) {
|
||||||
MatrixClientPeg.get().getProfileInfo(userId).then((resp) => {
|
MatrixClientPeg.get().getProfileInfo(userId).then((resp) => {
|
||||||
if (this._unmounted) {
|
if (this._unmounted) {
|
||||||
return;
|
return;
|
||||||
|
@ -188,15 +187,16 @@ const Pill = createReactClass({
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error('Could not retrieve profile data for ' + userId + ':', err);
|
console.error('Could not retrieve profile data for ' + userId + ':', err);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onUserPillClicked: function() {
|
onUserPillClicked = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: Action.ViewUser,
|
action: Action.ViewUser,
|
||||||
member: this.state.member,
|
member: this.state.member,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
render: function() {
|
|
||||||
|
render() {
|
||||||
const BaseAvatar = sdk.getComponent('views.avatars.BaseAvatar');
|
const BaseAvatar = sdk.getComponent('views.avatars.BaseAvatar');
|
||||||
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||||
const RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
|
const RoomAvatar = sdk.getComponent('avatars.RoomAvatar');
|
||||||
|
@ -285,7 +285,7 @@ const Pill = createReactClass({
|
||||||
// Deliberately render nothing if the URL isn't recognised
|
// Deliberately render nothing if the URL isn't recognised
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export default Pill;
|
export default Pill;
|
||||||
|
|
|
@ -16,16 +16,13 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as Roles from '../../../Roles';
|
import * as Roles from '../../../Roles';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import Field from "./Field";
|
import Field from "./Field";
|
||||||
import {Key} from "../../../Keyboard";
|
import {Key} from "../../../Keyboard";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class PowerSelector extends React.Component {
|
||||||
displayName: 'PowerSelector',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
value: PropTypes.number.isRequired,
|
value: PropTypes.number.isRequired,
|
||||||
// The maximum value that can be set with the power selector
|
// The maximum value that can be set with the power selector
|
||||||
maxValue: PropTypes.number.isRequired,
|
maxValue: PropTypes.number.isRequired,
|
||||||
|
@ -42,10 +39,17 @@ export default createReactClass({
|
||||||
|
|
||||||
// The name to annotate the selector with
|
// The name to annotate the selector with
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
},
|
}
|
||||||
|
|
||||||
getInitialState: function() {
|
static defaultProps = {
|
||||||
return {
|
maxValue: Infinity,
|
||||||
|
usersDefault: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
levelRoleMap: {},
|
levelRoleMap: {},
|
||||||
// List of power levels to show in the drop-down
|
// List of power levels to show in the drop-down
|
||||||
options: [],
|
options: [],
|
||||||
|
@ -53,26 +57,16 @@ export default createReactClass({
|
||||||
customValue: this.props.value,
|
customValue: this.props.value,
|
||||||
selectValue: 0,
|
selectValue: 0,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
maxValue: Infinity,
|
|
||||||
usersDefault: 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
// TODO: [REACT-WARNING] Move this to class constructor
|
|
||||||
this._initStateFromProps(this.props);
|
this._initStateFromProps(this.props);
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
UNSAFE_componentWillReceiveProps: function(newProps) {
|
UNSAFE_componentWillReceiveProps(newProps) {
|
||||||
this._initStateFromProps(newProps);
|
this._initStateFromProps(newProps);
|
||||||
},
|
}
|
||||||
|
|
||||||
_initStateFromProps: function(newProps) {
|
_initStateFromProps(newProps) {
|
||||||
// This needs to be done now because levelRoleMap has translated strings
|
// This needs to be done now because levelRoleMap has translated strings
|
||||||
const levelRoleMap = Roles.levelRoleMap(newProps.usersDefault);
|
const levelRoleMap = Roles.levelRoleMap(newProps.usersDefault);
|
||||||
const options = Object.keys(levelRoleMap).filter(level => {
|
const options = Object.keys(levelRoleMap).filter(level => {
|
||||||
|
@ -92,9 +86,9 @@ export default createReactClass({
|
||||||
customLevel: newProps.value,
|
customLevel: newProps.value,
|
||||||
selectValue: isCustom ? "SELECT_VALUE_CUSTOM" : newProps.value,
|
selectValue: isCustom ? "SELECT_VALUE_CUSTOM" : newProps.value,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onSelectChange: function(event) {
|
onSelectChange = event => {
|
||||||
const isCustom = event.target.value === "SELECT_VALUE_CUSTOM";
|
const isCustom = event.target.value === "SELECT_VALUE_CUSTOM";
|
||||||
if (isCustom) {
|
if (isCustom) {
|
||||||
this.setState({custom: true});
|
this.setState({custom: true});
|
||||||
|
@ -102,20 +96,20 @@ export default createReactClass({
|
||||||
this.props.onChange(event.target.value, this.props.powerLevelKey);
|
this.props.onChange(event.target.value, this.props.powerLevelKey);
|
||||||
this.setState({selectValue: event.target.value});
|
this.setState({selectValue: event.target.value});
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onCustomChange: function(event) {
|
onCustomChange = event => {
|
||||||
this.setState({customValue: event.target.value});
|
this.setState({customValue: event.target.value});
|
||||||
},
|
};
|
||||||
|
|
||||||
onCustomBlur: function(event) {
|
onCustomBlur = event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
this.props.onChange(parseInt(this.state.customValue), this.props.powerLevelKey);
|
this.props.onChange(parseInt(this.state.customValue), this.props.powerLevelKey);
|
||||||
},
|
};
|
||||||
|
|
||||||
onCustomKeyDown: function(event) {
|
onCustomKeyDown = event => {
|
||||||
if (event.key === Key.ENTER) {
|
if (event.key === Key.ENTER) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
@ -127,9 +121,9 @@ export default createReactClass({
|
||||||
// handle the onBlur safely.
|
// handle the onBlur safely.
|
||||||
event.target.blur();
|
event.target.blur();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let picker;
|
let picker;
|
||||||
const label = typeof this.props.label === "undefined" ? _t("Power level") : this.props.label;
|
const label = typeof this.props.label === "undefined" ? _t("Power level") : this.props.label;
|
||||||
if (this.state.custom) {
|
if (this.state.custom) {
|
||||||
|
@ -166,5 +160,5 @@ export default createReactClass({
|
||||||
{ picker }
|
{ picker }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
|
@ -37,10 +36,8 @@ import SettingsStore from "../../../settings/SettingsStore";
|
||||||
// - Rooms that are part of the group
|
// - Rooms that are part of the group
|
||||||
// - Direct messages with members of the group
|
// - Direct messages with members of the group
|
||||||
// with the intention that this could be expanded to arbitrary tags in future.
|
// with the intention that this could be expanded to arbitrary tags in future.
|
||||||
export default createReactClass({
|
export default class TagTile extends React.Component {
|
||||||
displayName: 'TagTile',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// A string tag such as "m.favourite" or a group ID such as "+groupid:domain.bla"
|
// A string tag such as "m.favourite" or a group ID such as "+groupid:domain.bla"
|
||||||
// For now, only group IDs are handled.
|
// For now, only group IDs are handled.
|
||||||
tag: PropTypes.string,
|
tag: PropTypes.string,
|
||||||
|
@ -48,20 +45,16 @@ export default createReactClass({
|
||||||
openMenu: PropTypes.func,
|
openMenu: PropTypes.func,
|
||||||
menuDisplayed: PropTypes.bool,
|
menuDisplayed: PropTypes.bool,
|
||||||
selected: PropTypes.bool,
|
selected: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
statics: {
|
static contextType = MatrixClientContext;
|
||||||
contextType: MatrixClientContext,
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
state = {
|
||||||
return {
|
// Whether the mouse is over the tile
|
||||||
// Whether the mouse is over the tile
|
hover: false,
|
||||||
hover: false,
|
// The profile data of the group if this.props.tag is a group ID
|
||||||
// The profile data of the group if this.props.tag is a group ID
|
profile: null,
|
||||||
profile: null,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.unmounted = false;
|
this.unmounted = false;
|
||||||
|
@ -71,16 +64,16 @@ export default createReactClass({
|
||||||
// New rooms or members may have been added to the group, fetch async
|
// New rooms or members may have been added to the group, fetch async
|
||||||
this._refreshGroup(this.props.tag);
|
this._refreshGroup(this.props.tag);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
if (this.props.tag[0] === '+') {
|
if (this.props.tag[0] === '+') {
|
||||||
FlairStore.removeListener('updateGroupProfile', this._onFlairStoreUpdated);
|
FlairStore.removeListener('updateGroupProfile', this._onFlairStoreUpdated);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_onFlairStoreUpdated() {
|
_onFlairStoreUpdated = () => {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
FlairStore.getGroupProfileCached(
|
FlairStore.getGroupProfileCached(
|
||||||
this.context,
|
this.context,
|
||||||
|
@ -91,14 +84,14 @@ export default createReactClass({
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.warn('Could not fetch group profile for ' + this.props.tag, err);
|
console.warn('Could not fetch group profile for ' + this.props.tag, err);
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_refreshGroup(groupId) {
|
_refreshGroup(groupId) {
|
||||||
GroupStore.refreshGroupRooms(groupId);
|
GroupStore.refreshGroupRooms(groupId);
|
||||||
GroupStore.refreshGroupMembers(groupId);
|
GroupStore.refreshGroupMembers(groupId);
|
||||||
},
|
}
|
||||||
|
|
||||||
onClick: function(e) {
|
onClick = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
|
@ -111,27 +104,27 @@ export default createReactClass({
|
||||||
// New rooms or members may have been added to the group, fetch async
|
// New rooms or members may have been added to the group, fetch async
|
||||||
this._refreshGroup(this.props.tag);
|
this._refreshGroup(this.props.tag);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onMouseOver: function() {
|
onMouseOver = () => {
|
||||||
if (SettingsStore.getValue("feature_communities_v2_prototypes")) return;
|
if (SettingsStore.getValue("feature_communities_v2_prototypes")) return;
|
||||||
this.setState({ hover: true });
|
this.setState({ hover: true });
|
||||||
},
|
};
|
||||||
|
|
||||||
onMouseLeave: function() {
|
onMouseLeave = () => {
|
||||||
this.setState({ hover: false });
|
this.setState({ hover: false });
|
||||||
},
|
};
|
||||||
|
|
||||||
openMenu: function(e) {
|
openMenu = e => {
|
||||||
// Prevent the TagTile onClick event firing as well
|
// Prevent the TagTile onClick event firing as well
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (SettingsStore.getValue("feature_communities_v2_prototypes")) return;
|
if (SettingsStore.getValue("feature_communities_v2_prototypes")) return;
|
||||||
this.setState({ hover: false });
|
this.setState({ hover: false });
|
||||||
this.props.openMenu();
|
this.props.openMenu();
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
const profile = this.state.profile || {};
|
const profile = this.state.profile || {};
|
||||||
const name = profile.name || this.props.tag;
|
const name = profile.name || this.props.tag;
|
||||||
|
@ -192,5 +185,5 @@ export default createReactClass({
|
||||||
{badgeElement}
|
{badgeElement}
|
||||||
</div>
|
</div>
|
||||||
</AccessibleTooltipButton>;
|
</AccessibleTooltipButton>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,49 +17,44 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import Tinter from "../../../Tinter";
|
import Tinter from "../../../Tinter";
|
||||||
|
|
||||||
const TintableSvg = createReactClass({
|
class TintableSvg extends React.Component {
|
||||||
displayName: 'TintableSvg',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
src: PropTypes.string.isRequired,
|
src: PropTypes.string.isRequired,
|
||||||
width: PropTypes.string.isRequired,
|
width: PropTypes.string.isRequired,
|
||||||
height: PropTypes.string.isRequired,
|
height: PropTypes.string.isRequired,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
statics: {
|
// list of currently mounted TintableSvgs
|
||||||
// list of currently mounted TintableSvgs
|
static mounts = {};
|
||||||
mounts: {},
|
static idSequence = 0;
|
||||||
idSequence: 0,
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this.fixups = [];
|
this.fixups = [];
|
||||||
|
|
||||||
this.id = TintableSvg.idSequence++;
|
this.id = TintableSvg.idSequence++;
|
||||||
TintableSvg.mounts[this.id] = this;
|
TintableSvg.mounts[this.id] = this;
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
delete TintableSvg.mounts[this.id];
|
delete TintableSvg.mounts[this.id];
|
||||||
},
|
}
|
||||||
|
|
||||||
tint: function() {
|
tint = () => {
|
||||||
// TODO: only bother running this if the global tint settings have changed
|
// TODO: only bother running this if the global tint settings have changed
|
||||||
// since we loaded!
|
// since we loaded!
|
||||||
Tinter.applySvgFixups(this.fixups);
|
Tinter.applySvgFixups(this.fixups);
|
||||||
},
|
};
|
||||||
|
|
||||||
onLoad: function(event) {
|
onLoad = event => {
|
||||||
// console.log("TintableSvg.onLoad for " + this.props.src);
|
// console.log("TintableSvg.onLoad for " + this.props.src);
|
||||||
this.fixups = Tinter.calcSvgFixups([event.target]);
|
this.fixups = Tinter.calcSvgFixups([event.target]);
|
||||||
Tinter.applySvgFixups(this.fixups);
|
Tinter.applySvgFixups(this.fixups);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<object className={"mx_TintableSvg " + (this.props.className ? this.props.className : "")}
|
<object className={"mx_TintableSvg " + (this.props.className ? this.props.className : "")}
|
||||||
type="image/svg+xml"
|
type="image/svg+xml"
|
||||||
|
@ -70,8 +65,8 @@ const TintableSvg = createReactClass({
|
||||||
tabIndex="-1"
|
tabIndex="-1"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Register with the Tinter so that we will be told if the tint changes
|
// Register with the Tinter so that we will be told if the tint changes
|
||||||
Tinter.registerTintable(function() {
|
Tinter.registerTintable(function() {
|
||||||
|
|
|
@ -16,31 +16,26 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class TooltipButton extends React.Component {
|
||||||
displayName: 'TooltipButton',
|
state = {
|
||||||
|
hover: false,
|
||||||
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
onMouseOver = () => {
|
||||||
return {
|
|
||||||
hover: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onMouseOver: function() {
|
|
||||||
this.setState({
|
this.setState({
|
||||||
hover: true,
|
hover: true,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onMouseLeave: function() {
|
onMouseLeave = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
hover: false,
|
hover: false,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const Tooltip = sdk.getComponent("elements.Tooltip");
|
const Tooltip = sdk.getComponent("elements.Tooltip");
|
||||||
const tip = this.state.hover ? <Tooltip
|
const tip = this.state.hover ? <Tooltip
|
||||||
className="mx_TooltipButton_container"
|
className="mx_TooltipButton_container"
|
||||||
|
@ -53,5 +48,5 @@ export default createReactClass({
|
||||||
{ tip }
|
{ tip }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,13 +17,10 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class TruncatedList extends React.Component {
|
||||||
displayName: 'TruncatedList',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// The number of elements to show before truncating. If negative, no truncation is done.
|
// The number of elements to show before truncating. If negative, no truncation is done.
|
||||||
truncateAt: PropTypes.number,
|
truncateAt: PropTypes.number,
|
||||||
// The className to apply to the wrapping div
|
// The className to apply to the wrapping div
|
||||||
|
@ -40,20 +37,18 @@ export default createReactClass({
|
||||||
// A function which will be invoked when an overflow element is required.
|
// A function which will be invoked when an overflow element is required.
|
||||||
// This will be inserted after the children.
|
// This will be inserted after the children.
|
||||||
createOverflowElement: PropTypes.func,
|
createOverflowElement: PropTypes.func,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps ={
|
||||||
return {
|
truncateAt: 2,
|
||||||
truncateAt: 2,
|
createOverflowElement(overflowCount, totalCount) {
|
||||||
createOverflowElement: function(overflowCount, totalCount) {
|
return (
|
||||||
return (
|
<div>{ _t("And %(count)s more...", {count: overflowCount}) }</div>
|
||||||
<div>{ _t("And %(count)s more...", {count: overflowCount}) }</div>
|
);
|
||||||
);
|
},
|
||||||
},
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
_getChildren: function(start, end) {
|
_getChildren(start, end) {
|
||||||
if (this.props.getChildren && this.props.getChildCount) {
|
if (this.props.getChildren && this.props.getChildCount) {
|
||||||
return this.props.getChildren(start, end);
|
return this.props.getChildren(start, end);
|
||||||
} else {
|
} else {
|
||||||
|
@ -64,9 +59,9 @@ export default createReactClass({
|
||||||
return c != null;
|
return c != null;
|
||||||
}).slice(start, end);
|
}).slice(start, end);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_getChildCount: function() {
|
_getChildCount() {
|
||||||
if (this.props.getChildren && this.props.getChildCount) {
|
if (this.props.getChildren && this.props.getChildCount) {
|
||||||
return this.props.getChildCount();
|
return this.props.getChildCount();
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,9 +69,9 @@ export default createReactClass({
|
||||||
return c != null;
|
return c != null;
|
||||||
}).length;
|
}).length;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let overflowNode = null;
|
let overflowNode = null;
|
||||||
|
|
||||||
const totalChildren = this._getChildCount();
|
const totalChildren = this._getChildCount();
|
||||||
|
@ -98,5 +93,5 @@ export default createReactClass({
|
||||||
{ overflowNode }
|
{ overflowNode }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import {_t} from '../../../languageHandler';
|
import {_t} from '../../../languageHandler';
|
||||||
|
@ -29,50 +28,48 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import {RovingTabIndexWrapper} from "../../../accessibility/RovingTabIndex";
|
import {RovingTabIndexWrapper} from "../../../accessibility/RovingTabIndex";
|
||||||
|
|
||||||
// XXX this class copies a lot from RoomTile.js
|
// XXX this class copies a lot from RoomTile.js
|
||||||
export default createReactClass({
|
export default class GroupInviteTile extends React.Component {
|
||||||
displayName: 'GroupInviteTile',
|
static propTypes: {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
group: PropTypes.object.isRequired,
|
group: PropTypes.object.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
statics: {
|
static contextType = MatrixClientContext;
|
||||||
contextType: MatrixClientContext,
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return ({
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
hover: false,
|
hover: false,
|
||||||
badgeHover: false,
|
badgeHover: false,
|
||||||
menuDisplayed: false,
|
menuDisplayed: false,
|
||||||
selected: this.props.group.groupId === null, // XXX: this needs linking to LoggedInView/GroupView state
|
selected: this.props.group.groupId === null, // XXX: this needs linking to LoggedInView/GroupView state
|
||||||
});
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
onClick: function(e) {
|
onClick = e => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_group',
|
action: 'view_group',
|
||||||
group_id: this.props.group.groupId,
|
group_id: this.props.group.groupId,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onMouseEnter: function() {
|
onMouseEnter = () => {
|
||||||
const state = {hover: true};
|
const state = {hover: true};
|
||||||
// Only allow non-guests to access the context menu
|
// Only allow non-guests to access the context menu
|
||||||
if (!this.context.isGuest()) {
|
if (!this.context.isGuest()) {
|
||||||
state.badgeHover = true;
|
state.badgeHover = true;
|
||||||
}
|
}
|
||||||
this.setState(state);
|
this.setState(state);
|
||||||
},
|
};
|
||||||
|
|
||||||
onMouseLeave: function() {
|
onMouseLeave = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
badgeHover: false,
|
badgeHover: false,
|
||||||
hover: false,
|
hover: false,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_showContextMenu: function(boundingClientRect) {
|
_showContextMenu(boundingClientRect) {
|
||||||
// Only allow non-guests to access the context menu
|
// Only allow non-guests to access the context menu
|
||||||
if (MatrixClientPeg.get().isGuest()) return;
|
if (MatrixClientPeg.get().isGuest()) return;
|
||||||
|
|
||||||
|
@ -86,17 +83,17 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(state);
|
this.setState(state);
|
||||||
},
|
}
|
||||||
|
|
||||||
onContextMenuButtonClick: function(e) {
|
onContextMenuButtonClick = e => {
|
||||||
// Prevent the RoomTile onClick event firing as well
|
// Prevent the RoomTile onClick event firing as well
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
this._showContextMenu(e.target.getBoundingClientRect());
|
this._showContextMenu(e.target.getBoundingClientRect());
|
||||||
},
|
};
|
||||||
|
|
||||||
onContextMenu: function(e) {
|
onContextMenu = e => {
|
||||||
// Prevent the native context menu
|
// Prevent the native context menu
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
@ -105,15 +102,15 @@ export default createReactClass({
|
||||||
top: e.clientY,
|
top: e.clientY,
|
||||||
height: 0,
|
height: 0,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
closeMenu: function() {
|
closeMenu = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
contextMenuPosition: null,
|
contextMenuPosition: null,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
|
|
||||||
|
@ -197,5 +194,5 @@ export default createReactClass({
|
||||||
|
|
||||||
{ contextMenu }
|
{ contextMenu }
|
||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
|
@ -30,33 +29,29 @@ import {Action} from "../../../dispatcher/actions";
|
||||||
|
|
||||||
const INITIAL_LOAD_NUM_MEMBERS = 30;
|
const INITIAL_LOAD_NUM_MEMBERS = 30;
|
||||||
|
|
||||||
export default createReactClass({
|
export default class GroupMemberList extends React.Component {
|
||||||
displayName: 'GroupMemberList',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
groupId: PropTypes.string.isRequired,
|
groupId: PropTypes.string.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
members: null,
|
||||||
members: null,
|
membersError: null,
|
||||||
membersError: null,
|
invitedMembers: null,
|
||||||
invitedMembers: null,
|
invitedMembersError: null,
|
||||||
invitedMembersError: null,
|
truncateAt: INITIAL_LOAD_NUM_MEMBERS,
|
||||||
truncateAt: INITIAL_LOAD_NUM_MEMBERS,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
this._initGroupStore(this.props.groupId);
|
this._initGroupStore(this.props.groupId);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
_initGroupStore: function(groupId) {
|
_initGroupStore(groupId) {
|
||||||
GroupStore.registerListener(groupId, () => {
|
GroupStore.registerListener(groupId, () => {
|
||||||
this._fetchMembers();
|
this._fetchMembers();
|
||||||
});
|
});
|
||||||
|
@ -73,17 +68,17 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_fetchMembers: function() {
|
_fetchMembers() {
|
||||||
if (this._unmounted) return;
|
if (this._unmounted) return;
|
||||||
this.setState({
|
this.setState({
|
||||||
members: GroupStore.getGroupMembers(this.props.groupId),
|
members: GroupStore.getGroupMembers(this.props.groupId),
|
||||||
invitedMembers: GroupStore.getGroupInvitedMembers(this.props.groupId),
|
invitedMembers: GroupStore.getGroupInvitedMembers(this.props.groupId),
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_createOverflowTile: function(overflowCount, totalCount) {
|
_createOverflowTile = (overflowCount, totalCount) => {
|
||||||
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
||||||
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
||||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||||
|
@ -94,19 +89,19 @@ export default createReactClass({
|
||||||
} name={text} presenceState="online" suppressOnHover={true}
|
} name={text} presenceState="online" suppressOnHover={true}
|
||||||
onClick={this._showFullMemberList} />
|
onClick={this._showFullMemberList} />
|
||||||
);
|
);
|
||||||
},
|
};
|
||||||
|
|
||||||
_showFullMemberList: function() {
|
_showFullMemberList = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
truncateAt: -1,
|
truncateAt: -1,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onSearchQueryChanged: function(ev) {
|
onSearchQueryChanged = ev => {
|
||||||
this.setState({ searchQuery: ev.target.value });
|
this.setState({ searchQuery: ev.target.value });
|
||||||
},
|
};
|
||||||
|
|
||||||
makeGroupMemberTiles: function(query, memberList, memberListError) {
|
makeGroupMemberTiles(query, memberList, memberListError) {
|
||||||
if (memberListError) {
|
if (memberListError) {
|
||||||
return <div className="warning">{ _t("Failed to load group members") }</div>;
|
return <div className="warning">{ _t("Failed to load group members") }</div>;
|
||||||
}
|
}
|
||||||
|
@ -160,9 +155,9 @@ export default createReactClass({
|
||||||
>
|
>
|
||||||
{ memberTiles }
|
{ memberTiles }
|
||||||
</TruncatedList>;
|
</TruncatedList>;
|
||||||
},
|
}
|
||||||
|
|
||||||
onInviteToGroupButtonClick() {
|
onInviteToGroupButtonClick = () => {
|
||||||
showGroupInviteDialog(this.props.groupId).then(() => {
|
showGroupInviteDialog(this.props.groupId).then(() => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: Action.SetRightPanelPhase,
|
action: Action.SetRightPanelPhase,
|
||||||
|
@ -170,9 +165,9 @@ export default createReactClass({
|
||||||
refireParams: { groupId: this.props.groupId },
|
refireParams: { groupId: this.props.groupId },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
if (this.state.fetching || this.state.fetchingInvitedMembers) {
|
if (this.state.fetching || this.state.fetchingInvitedMembers) {
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
return (<div className="mx_MemberList">
|
return (<div className="mx_MemberList">
|
||||||
|
@ -230,5 +225,5 @@ export default createReactClass({
|
||||||
{ inputBox }
|
{ inputBox }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,37 +18,28 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import { GroupMemberType } from '../../../groups';
|
import { GroupMemberType } from '../../../groups';
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class GroupMemberTile extends React.Component {
|
||||||
displayName: 'GroupMemberTile',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
groupId: PropTypes.string.isRequired,
|
groupId: PropTypes.string.isRequired,
|
||||||
member: GroupMemberType.isRequired,
|
member: GroupMemberType.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
static contextType = MatrixClientContext;
|
||||||
return {};
|
|
||||||
},
|
|
||||||
|
|
||||||
statics: {
|
onClick = e => {
|
||||||
contextType: MatrixClientContext,
|
|
||||||
},
|
|
||||||
|
|
||||||
onClick: function(e) {
|
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_group_user',
|
action: 'view_group_user',
|
||||||
member: this.props.member,
|
member: this.props.member,
|
||||||
groupId: this.props.groupId,
|
groupId: this.props.groupId,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
const EntityTile = sdk.getComponent('rooms.EntityTile');
|
const EntityTile = sdk.getComponent('rooms.EntityTile');
|
||||||
|
|
||||||
|
@ -74,5 +65,5 @@ export default createReactClass({
|
||||||
powerStatus={this.props.member.isPrivileged ? EntityTile.POWER_STATUS_ADMIN : null}
|
powerStatus={this.props.member.isPrivileged ? EntityTile.POWER_STATUS_ADMIN : null}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,44 +16,39 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import GroupStore from '../../../stores/GroupStore';
|
import GroupStore from '../../../stores/GroupStore';
|
||||||
import ToggleSwitch from "../elements/ToggleSwitch";
|
import ToggleSwitch from "../elements/ToggleSwitch";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class GroupPublicityToggle extends React.Component {
|
||||||
displayName: 'GroupPublicityToggle',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
groupId: PropTypes.string.isRequired,
|
groupId: PropTypes.string.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState() {
|
state = {
|
||||||
return {
|
busy: false,
|
||||||
busy: false,
|
ready: false,
|
||||||
ready: false,
|
isGroupPublicised: false, // assume false as <ToggleSwitch /> expects a boolean
|
||||||
isGroupPublicised: false, // assume false as <ToggleSwitch /> expects a boolean
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this._initGroupStore(this.props.groupId);
|
this._initGroupStore(this.props.groupId);
|
||||||
},
|
}
|
||||||
|
|
||||||
_initGroupStore: function(groupId) {
|
_initGroupStore(groupId) {
|
||||||
this._groupStoreToken = GroupStore.registerListener(groupId, () => {
|
this._groupStoreToken = GroupStore.registerListener(groupId, () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isGroupPublicised: Boolean(GroupStore.getGroupPublicity(groupId)),
|
isGroupPublicised: Boolean(GroupStore.getGroupPublicity(groupId)),
|
||||||
ready: GroupStore.isStateReady(groupId, GroupStore.STATE_KEY.Summary),
|
ready: GroupStore.isStateReady(groupId, GroupStore.STATE_KEY.Summary),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (this._groupStoreToken) this._groupStoreToken.unregister();
|
if (this._groupStoreToken) this._groupStoreToken.unregister();
|
||||||
},
|
}
|
||||||
|
|
||||||
_onPublicityToggle: function() {
|
_onPublicityToggle = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
busy: true,
|
busy: true,
|
||||||
// Optimistic early update
|
// Optimistic early update
|
||||||
|
@ -64,7 +59,7 @@ export default createReactClass({
|
||||||
busy: false,
|
busy: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const GroupTile = sdk.getComponent('groups.GroupTile');
|
const GroupTile = sdk.getComponent('groups.GroupTile');
|
||||||
|
@ -76,5 +71,5 @@ export default createReactClass({
|
||||||
disabled={!this.state.ready || this.state.busy}
|
disabled={!this.state.ready || this.state.busy}
|
||||||
onChange={this._onPublicityToggle} />
|
onChange={this._onPublicityToggle} />
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
@ -26,30 +25,24 @@ import GroupStore from '../../../stores/GroupStore';
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class GroupRoomInfo extends React.Component {
|
||||||
displayName: 'GroupRoomInfo',
|
static contextType = MatrixClientContext;
|
||||||
|
|
||||||
statics: {
|
static propTypes = {
|
||||||
contextType: MatrixClientContext,
|
|
||||||
},
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
groupId: PropTypes.string,
|
groupId: PropTypes.string,
|
||||||
groupRoomId: PropTypes.string,
|
groupRoomId: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
isUserPrivilegedInGroup: null,
|
||||||
isUserPrivilegedInGroup: null,
|
groupRoom: null,
|
||||||
groupRoom: null,
|
groupRoomPublicityLoading: false,
|
||||||
groupRoomPublicityLoading: false,
|
groupRoomRemoveLoading: false,
|
||||||
groupRoomRemoveLoading: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this._initGroupStore(this.props.groupId);
|
this._initGroupStore(this.props.groupId);
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
UNSAFE_componentWillReceiveProps(newProps) {
|
UNSAFE_componentWillReceiveProps(newProps) {
|
||||||
|
@ -57,19 +50,19 @@ export default createReactClass({
|
||||||
this._unregisterGroupStore(this.props.groupId);
|
this._unregisterGroupStore(this.props.groupId);
|
||||||
this._initGroupStore(newProps.groupId);
|
this._initGroupStore(newProps.groupId);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this._unregisterGroupStore(this.props.groupId);
|
this._unregisterGroupStore(this.props.groupId);
|
||||||
},
|
}
|
||||||
|
|
||||||
_initGroupStore(groupId) {
|
_initGroupStore(groupId) {
|
||||||
GroupStore.registerListener(groupId, this.onGroupStoreUpdated);
|
GroupStore.registerListener(groupId, this.onGroupStoreUpdated);
|
||||||
},
|
}
|
||||||
|
|
||||||
_unregisterGroupStore(groupId) {
|
_unregisterGroupStore(groupId) {
|
||||||
GroupStore.unregisterListener(this.onGroupStoreUpdated);
|
GroupStore.unregisterListener(this.onGroupStoreUpdated);
|
||||||
},
|
}
|
||||||
|
|
||||||
_updateGroupRoom() {
|
_updateGroupRoom() {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -77,16 +70,16 @@ export default createReactClass({
|
||||||
(r) => r.roomId === this.props.groupRoomId,
|
(r) => r.roomId === this.props.groupRoomId,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onGroupStoreUpdated: function() {
|
onGroupStoreUpdated = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isUserPrivilegedInGroup: GroupStore.isUserPrivileged(this.props.groupId),
|
isUserPrivilegedInGroup: GroupStore.isUserPrivileged(this.props.groupId),
|
||||||
});
|
});
|
||||||
this._updateGroupRoom();
|
this._updateGroupRoom();
|
||||||
},
|
};
|
||||||
|
|
||||||
_onRemove: function(e) {
|
_onRemove = e => {
|
||||||
const groupId = this.props.groupId;
|
const groupId = this.props.groupId;
|
||||||
const roomName = this.state.groupRoom.displayname;
|
const roomName = this.state.groupRoom.displayname;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -119,15 +112,15 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onCancel: function(e) {
|
_onCancel = e => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: "view_group_room_list",
|
action: "view_group_room_list",
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_changeGroupRoomPublicity(e) {
|
_changeGroupRoomPublicity = e => {
|
||||||
const isPublic = e.target.value === "public";
|
const isPublic = e.target.value === "public";
|
||||||
this.setState({
|
this.setState({
|
||||||
groupRoomPublicityLoading: true,
|
groupRoomPublicityLoading: true,
|
||||||
|
@ -150,9 +143,9 @@ export default createReactClass({
|
||||||
groupRoomPublicityLoading: false,
|
groupRoomPublicityLoading: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const InlineSpinner = sdk.getComponent('elements.InlineSpinner');
|
const InlineSpinner = sdk.getComponent('elements.InlineSpinner');
|
||||||
if (this.state.groupRoomRemoveLoading || !this.state.groupRoom) {
|
if (this.state.groupRoomRemoveLoading || !this.state.groupRoom) {
|
||||||
|
@ -235,5 +228,5 @@ export default createReactClass({
|
||||||
</AutoHideScrollbar>
|
</AutoHideScrollbar>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import GroupStore from '../../../stores/GroupStore';
|
import GroupStore from '../../../stores/GroupStore';
|
||||||
|
@ -25,34 +24,32 @@ import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
|
|
||||||
const INITIAL_LOAD_NUM_ROOMS = 30;
|
const INITIAL_LOAD_NUM_ROOMS = 30;
|
||||||
|
|
||||||
export default createReactClass({
|
export default class GroupRoomList extends React.Component {
|
||||||
propTypes: {
|
static propTypes = {
|
||||||
groupId: PropTypes.string.isRequired,
|
groupId: PropTypes.string.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
rooms: null,
|
||||||
rooms: null,
|
truncateAt: INITIAL_LOAD_NUM_ROOMS,
|
||||||
truncateAt: INITIAL_LOAD_NUM_ROOMS,
|
searchQuery: "",
|
||||||
searchQuery: "",
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
this._initGroupStore(this.props.groupId);
|
this._initGroupStore(this.props.groupId);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
this._unregisterGroupStore();
|
this._unregisterGroupStore();
|
||||||
},
|
}
|
||||||
|
|
||||||
_unregisterGroupStore() {
|
_unregisterGroupStore() {
|
||||||
GroupStore.unregisterListener(this.onGroupStoreUpdated);
|
GroupStore.unregisterListener(this.onGroupStoreUpdated);
|
||||||
},
|
}
|
||||||
|
|
||||||
_initGroupStore: function(groupId) {
|
_initGroupStore(groupId) {
|
||||||
GroupStore.registerListener(groupId, this.onGroupStoreUpdated);
|
GroupStore.registerListener(groupId, this.onGroupStoreUpdated);
|
||||||
// XXX: This should be more fluxy - let's get the error from GroupStore .getError or something
|
// XXX: This should be more fluxy - let's get the error from GroupStore .getError or something
|
||||||
// XXX: This is also leaked - we should remove it when unmounting
|
// XXX: This is also leaked - we should remove it when unmounting
|
||||||
|
@ -62,16 +59,16 @@ export default createReactClass({
|
||||||
rooms: null,
|
rooms: null,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onGroupStoreUpdated: function() {
|
onGroupStoreUpdated = () => {
|
||||||
if (this._unmounted) return;
|
if (this._unmounted) return;
|
||||||
this.setState({
|
this.setState({
|
||||||
rooms: GroupStore.getGroupRooms(this.props.groupId),
|
rooms: GroupStore.getGroupRooms(this.props.groupId),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_createOverflowTile: function(overflowCount, totalCount) {
|
_createOverflowTile = (overflowCount, totalCount) => {
|
||||||
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
||||||
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
||||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||||
|
@ -82,25 +79,25 @@ export default createReactClass({
|
||||||
} name={text} presenceState="online" suppressOnHover={true}
|
} name={text} presenceState="online" suppressOnHover={true}
|
||||||
onClick={this._showFullRoomList} />
|
onClick={this._showFullRoomList} />
|
||||||
);
|
);
|
||||||
},
|
};
|
||||||
|
|
||||||
_showFullRoomList: function() {
|
_showFullRoomList = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
truncateAt: -1,
|
truncateAt: -1,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onSearchQueryChanged: function(ev) {
|
onSearchQueryChanged = ev => {
|
||||||
this.setState({ searchQuery: ev.target.value });
|
this.setState({ searchQuery: ev.target.value });
|
||||||
},
|
};
|
||||||
|
|
||||||
onAddRoomToGroupButtonClick() {
|
onAddRoomToGroupButtonClick = () => {
|
||||||
showGroupAddRoomDialog(this.props.groupId).then(() => {
|
showGroupAddRoomDialog(this.props.groupId).then(() => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
makeGroupRoomTiles: function(query) {
|
makeGroupRoomTiles(query) {
|
||||||
const GroupRoomTile = sdk.getComponent("groups.GroupRoomTile");
|
const GroupRoomTile = sdk.getComponent("groups.GroupRoomTile");
|
||||||
query = (query || "").toLowerCase();
|
query = (query || "").toLowerCase();
|
||||||
|
|
||||||
|
@ -123,9 +120,9 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
return roomList;
|
return roomList;
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
if (this.state.rooms === null) {
|
if (this.state.rooms === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -160,5 +157,5 @@ export default createReactClass({
|
||||||
{ inputBox }
|
{ inputBox }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,29 +16,28 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import { GroupRoomType } from '../../../groups';
|
import { GroupRoomType } from '../../../groups';
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
const GroupRoomTile = createReactClass({
|
class GroupRoomTile extends React.Component {
|
||||||
displayName: 'GroupRoomTile',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
groupId: PropTypes.string.isRequired,
|
groupId: PropTypes.string.isRequired,
|
||||||
groupRoom: GroupRoomType.isRequired,
|
groupRoom: GroupRoomType.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
onClick: function(e) {
|
static contextType = MatrixClientContext
|
||||||
|
|
||||||
|
onClick = e => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_group_room',
|
action: 'view_group_room',
|
||||||
groupId: this.props.groupId,
|
groupId: this.props.groupId,
|
||||||
groupRoomId: this.props.groupRoom.roomId,
|
groupRoomId: this.props.groupRoom.roomId,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const avatarUrl = this.context.mxcUrlToHttp(
|
const avatarUrl = this.context.mxcUrlToHttp(
|
||||||
|
@ -63,10 +62,7 @@ const GroupRoomTile = createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
GroupRoomTile.contextType = MatrixClientContext;
|
|
||||||
|
|
||||||
|
|
||||||
export default GroupRoomTile;
|
export default GroupRoomTile;
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { Draggable, Droppable } from 'react-beautiful-dnd';
|
import { Draggable, Droppable } from 'react-beautiful-dnd';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
|
@ -25,53 +24,45 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
function nop() {}
|
function nop() {}
|
||||||
|
|
||||||
const GroupTile = createReactClass({
|
class GroupTile extends React.Component {
|
||||||
displayName: 'GroupTile',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
groupId: PropTypes.string.isRequired,
|
groupId: PropTypes.string.isRequired,
|
||||||
// Whether to show the short description of the group on the tile
|
// Whether to show the short description of the group on the tile
|
||||||
showDescription: PropTypes.bool,
|
showDescription: PropTypes.bool,
|
||||||
// Height of the group avatar in pixels
|
// Height of the group avatar in pixels
|
||||||
avatarHeight: PropTypes.number,
|
avatarHeight: PropTypes.number,
|
||||||
draggable: PropTypes.bool,
|
draggable: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
statics: {
|
static contextType = MatrixClientContext;
|
||||||
contextType: MatrixClientContext,
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
static defaultProps = {
|
||||||
return {
|
showDescription: true,
|
||||||
profile: null,
|
avatarHeight: 50,
|
||||||
};
|
draggable: true,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps() {
|
state = {
|
||||||
return {
|
profile: null,
|
||||||
showDescription: true,
|
};
|
||||||
avatarHeight: 50,
|
|
||||||
draggable: true,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
FlairStore.getGroupProfileCached(this.context, this.props.groupId).then((profile) => {
|
FlairStore.getGroupProfileCached(this.context, this.props.groupId).then((profile) => {
|
||||||
this.setState({profile});
|
this.setState({profile});
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.error('Error whilst getting cached profile for GroupTile', err);
|
console.error('Error whilst getting cached profile for GroupTile', err);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onMouseDown: function(e) {
|
onMouseDown = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_group',
|
action: 'view_group',
|
||||||
group_id: this.props.groupId,
|
group_id: this.props.groupId,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const profile = this.state.profile || {};
|
const profile = this.state.profile || {};
|
||||||
|
@ -135,7 +126,7 @@ const GroupTile = createReactClass({
|
||||||
<div className="mx_GroupTile_groupId">{ this.props.groupId }</div>
|
<div className="mx_GroupTile_groupId">{ this.props.groupId }</div>
|
||||||
</div>
|
</div>
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export default GroupTile;
|
export default GroupTile;
|
||||||
|
|
|
@ -15,33 +15,26 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class GroupUserSettings extends React.Component {
|
||||||
displayName: 'GroupUserSettings',
|
static contextType = MatrixClientContext;
|
||||||
|
|
||||||
statics: {
|
state = {
|
||||||
contextType: MatrixClientContext,
|
error: null,
|
||||||
},
|
groups: null,
|
||||||
|
};
|
||||||
|
|
||||||
getInitialState() {
|
componentDidMount() {
|
||||||
return {
|
|
||||||
error: null,
|
|
||||||
groups: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.context.getJoinedGroups().then((result) => {
|
this.context.getJoinedGroups().then((result) => {
|
||||||
this.setState({groups: result.groups || [], error: null});
|
this.setState({groups: result.groups || [], error: null});
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
this.setState({groups: null, error: err});
|
this.setState({groups: null, error: err});
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let text = "";
|
let text = "";
|
||||||
|
@ -70,5 +63,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import filesize from 'filesize';
|
import filesize from 'filesize';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
@ -117,16 +116,8 @@ function computedStyle(element) {
|
||||||
return cssText;
|
return cssText;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createReactClass({
|
export default class MFileBody extends React.Component {
|
||||||
displayName: 'MFileBody',
|
static propTypes = {
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
decryptedBlob: (this.props.decryptedBlob ? this.props.decryptedBlob : null),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
/* already decrypted blob */
|
/* already decrypted blob */
|
||||||
|
@ -135,7 +126,19 @@ export default createReactClass({
|
||||||
onHeightChanged: PropTypes.func,
|
onHeightChanged: PropTypes.func,
|
||||||
/* the shape of the tile, used */
|
/* the shape of the tile, used */
|
||||||
tileShape: PropTypes.string,
|
tileShape: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
decryptedBlob: (this.props.decryptedBlob ? this.props.decryptedBlob : null),
|
||||||
|
};
|
||||||
|
|
||||||
|
this._iframe = createRef();
|
||||||
|
this._dummyLink = createRef();
|
||||||
|
this._downloadImage = createRef();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts a human readable label for the file attachment to use as
|
* Extracts a human readable label for the file attachment to use as
|
||||||
|
@ -144,7 +147,7 @@ export default createReactClass({
|
||||||
* @params {Object} content The "content" key of the matrix event.
|
* @params {Object} content The "content" key of the matrix event.
|
||||||
* @return {string} the human readable link text for the attachment.
|
* @return {string} the human readable link text for the attachment.
|
||||||
*/
|
*/
|
||||||
presentableTextForFile: function(content) {
|
presentableTextForFile(content) {
|
||||||
let linkText = _t("Attachment");
|
let linkText = _t("Attachment");
|
||||||
if (content.body && content.body.length > 0) {
|
if (content.body && content.body.length > 0) {
|
||||||
// The content body should be the name of the file including a
|
// The content body should be the name of the file including a
|
||||||
|
@ -163,40 +166,33 @@ export default createReactClass({
|
||||||
linkText += ' (' + filesize(content.info.size) + ')';
|
linkText += ' (' + filesize(content.info.size) + ')';
|
||||||
}
|
}
|
||||||
return linkText;
|
return linkText;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getContentUrl: function() {
|
_getContentUrl() {
|
||||||
const content = this.props.mxEvent.getContent();
|
const content = this.props.mxEvent.getContent();
|
||||||
return MatrixClientPeg.get().mxcUrlToHttp(content.url);
|
return MatrixClientPeg.get().mxcUrlToHttp(content.url);
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
componentDidMount() {
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._iframe = createRef();
|
|
||||||
this._dummyLink = createRef();
|
|
||||||
this._downloadImage = createRef();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
// Add this to the list of mounted components to receive notifications
|
// Add this to the list of mounted components to receive notifications
|
||||||
// when the tint changes.
|
// when the tint changes.
|
||||||
this.id = nextMountId++;
|
this.id = nextMountId++;
|
||||||
mounts[this.id] = this;
|
mounts[this.id] = this;
|
||||||
this.tint();
|
this.tint();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
if (this.props.onHeightChanged && !prevState.decryptedBlob && this.state.decryptedBlob) {
|
if (this.props.onHeightChanged && !prevState.decryptedBlob && this.state.decryptedBlob) {
|
||||||
this.props.onHeightChanged();
|
this.props.onHeightChanged();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
// Remove this from the list of mounted components
|
// Remove this from the list of mounted components
|
||||||
delete mounts[this.id];
|
delete mounts[this.id];
|
||||||
},
|
}
|
||||||
|
|
||||||
tint: function() {
|
tint = () => {
|
||||||
// Update our tinted copy of require("../../../../res/img/download.svg")
|
// Update our tinted copy of require("../../../../res/img/download.svg")
|
||||||
if (this._downloadImage.current) {
|
if (this._downloadImage.current) {
|
||||||
this._downloadImage.current.src = tintedDownloadImageURL;
|
this._downloadImage.current.src = tintedDownloadImageURL;
|
||||||
|
@ -210,9 +206,9 @@ export default createReactClass({
|
||||||
style: computedStyle(this._dummyLink.current),
|
style: computedStyle(this._dummyLink.current),
|
||||||
}, "*");
|
}, "*");
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const content = this.props.mxEvent.getContent();
|
const content = this.props.mxEvent.getContent();
|
||||||
const text = this.presentableTextForFile(content);
|
const text = this.presentableTextForFile(content);
|
||||||
const isEncrypted = content.file !== undefined;
|
const isEncrypted = content.file !== undefined;
|
||||||
|
@ -378,5 +374,5 @@ export default createReactClass({
|
||||||
{ _t("Invalid file%(extra)s", { extra: extra }) }
|
{ _t("Invalid file%(extra)s", { extra: extra }) }
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import MFileBody from './MFileBody';
|
import MFileBody from './MFileBody';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import { decryptFile } from '../../../utils/DecryptFile';
|
import { decryptFile } from '../../../utils/DecryptFile';
|
||||||
|
@ -25,27 +24,23 @@ import { _t } from '../../../languageHandler';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import InlineSpinner from '../elements/InlineSpinner';
|
import InlineSpinner from '../elements/InlineSpinner';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class MVideoBody extends React.Component {
|
||||||
displayName: 'MVideoBody',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
|
|
||||||
/* called when the video has loaded */
|
/* called when the video has loaded */
|
||||||
onHeightChanged: PropTypes.func.isRequired,
|
onHeightChanged: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
decryptedUrl: null,
|
||||||
decryptedUrl: null,
|
decryptedThumbnailUrl: null,
|
||||||
decryptedThumbnailUrl: null,
|
decryptedBlob: null,
|
||||||
decryptedBlob: null,
|
error: null,
|
||||||
error: null,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
thumbScale: function(fullWidth, fullHeight, thumbWidth, thumbHeight) {
|
thumbScale(fullWidth, fullHeight, thumbWidth, thumbHeight) {
|
||||||
if (!fullWidth || !fullHeight) {
|
if (!fullWidth || !fullHeight) {
|
||||||
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
|
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
|
||||||
// log this because it's spammy
|
// log this because it's spammy
|
||||||
|
@ -64,18 +59,18 @@ export default createReactClass({
|
||||||
// height is the dominant dimension so scaling will be fixed on that
|
// height is the dominant dimension so scaling will be fixed on that
|
||||||
return heightMulti;
|
return heightMulti;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_getContentUrl: function() {
|
_getContentUrl() {
|
||||||
const content = this.props.mxEvent.getContent();
|
const content = this.props.mxEvent.getContent();
|
||||||
if (content.file !== undefined) {
|
if (content.file !== undefined) {
|
||||||
return this.state.decryptedUrl;
|
return this.state.decryptedUrl;
|
||||||
} else {
|
} else {
|
||||||
return MatrixClientPeg.get().mxcUrlToHttp(content.url);
|
return MatrixClientPeg.get().mxcUrlToHttp(content.url);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_getThumbUrl: function() {
|
_getThumbUrl() {
|
||||||
const content = this.props.mxEvent.getContent();
|
const content = this.props.mxEvent.getContent();
|
||||||
if (content.file !== undefined) {
|
if (content.file !== undefined) {
|
||||||
return this.state.decryptedThumbnailUrl;
|
return this.state.decryptedThumbnailUrl;
|
||||||
|
@ -84,9 +79,9 @@ export default createReactClass({
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
const content = this.props.mxEvent.getContent();
|
const content = this.props.mxEvent.getContent();
|
||||||
if (content.file !== undefined && this.state.decryptedUrl === null) {
|
if (content.file !== undefined && this.state.decryptedUrl === null) {
|
||||||
let thumbnailPromise = Promise.resolve(null);
|
let thumbnailPromise = Promise.resolve(null);
|
||||||
|
@ -118,18 +113,18 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
if (this.state.decryptedUrl) {
|
if (this.state.decryptedUrl) {
|
||||||
URL.revokeObjectURL(this.state.decryptedUrl);
|
URL.revokeObjectURL(this.state.decryptedUrl);
|
||||||
}
|
}
|
||||||
if (this.state.decryptedThumbnailUrl) {
|
if (this.state.decryptedThumbnailUrl) {
|
||||||
URL.revokeObjectURL(this.state.decryptedThumbnailUrl);
|
URL.revokeObjectURL(this.state.decryptedThumbnailUrl);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const content = this.props.mxEvent.getContent();
|
const content = this.props.mxEvent.getContent();
|
||||||
|
|
||||||
if (this.state.error !== null) {
|
if (this.state.error !== null) {
|
||||||
|
@ -182,5 +177,5 @@ export default createReactClass({
|
||||||
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} />
|
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,17 +16,14 @@ limitations under the License.
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import {Mjolnir} from "../../../mjolnir/Mjolnir";
|
import {Mjolnir} from "../../../mjolnir/Mjolnir";
|
||||||
import RedactedBody from "./RedactedBody";
|
import RedactedBody from "./RedactedBody";
|
||||||
import UnknownBody from "./UnknownBody";
|
import UnknownBody from "./UnknownBody";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class MessageEvent extends React.Component {
|
||||||
displayName: 'MessageEvent',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
|
|
||||||
|
@ -47,22 +44,23 @@ export default createReactClass({
|
||||||
|
|
||||||
/* the maximum image height to use, if the event is an image */
|
/* the maximum image height to use, if the event is an image */
|
||||||
maxImageHeight: PropTypes.number,
|
maxImageHeight: PropTypes.number,
|
||||||
},
|
};
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._body = createRef();
|
this._body = createRef();
|
||||||
},
|
}
|
||||||
|
|
||||||
getEventTileOps: function() {
|
getEventTileOps = () => {
|
||||||
return this._body.current && this._body.current.getEventTileOps ? this._body.current.getEventTileOps() : null;
|
return this._body.current && this._body.current.getEventTileOps ? this._body.current.getEventTileOps() : null;
|
||||||
},
|
};
|
||||||
|
|
||||||
onTileUpdate: function() {
|
onTileUpdate = () => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const bodyTypes = {
|
const bodyTypes = {
|
||||||
'm.text': sdk.getComponent('messages.TextualBody'),
|
'm.text': sdk.getComponent('messages.TextualBody'),
|
||||||
'm.notice': sdk.getComponent('messages.TextualBody'),
|
'm.notice': sdk.getComponent('messages.TextualBody'),
|
||||||
|
@ -123,5 +121,5 @@ export default createReactClass({
|
||||||
onHeightChanged={this.props.onHeightChanged}
|
onHeightChanged={this.props.onHeightChanged}
|
||||||
onMessageAllowed={this.onTileUpdate}
|
onMessageAllowed={this.onTileUpdate}
|
||||||
/>;
|
/>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,22 +18,19 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomAvatarEvent extends React.Component {
|
||||||
displayName: 'RoomAvatarEvent',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
onAvatarClick: function() {
|
onAvatarClick = () => {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const ev = this.props.mxEvent;
|
const ev = this.props.mxEvent;
|
||||||
const httpUrl = cli.mxcUrlToHttp(ev.getContent().url);
|
const httpUrl = cli.mxcUrlToHttp(ev.getContent().url);
|
||||||
|
@ -50,9 +47,9 @@ export default createReactClass({
|
||||||
name: text,
|
name: text,
|
||||||
};
|
};
|
||||||
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
|
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const ev = this.props.mxEvent;
|
const ev = this.props.mxEvent;
|
||||||
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
|
||||||
const RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
|
const RoomAvatar = sdk.getComponent("avatars.RoomAvatar");
|
||||||
|
@ -86,5 +83,5 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,22 +17,19 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
|
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomCreate extends React.Component {
|
||||||
displayName: 'RoomCreate',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
_onLinkClicked: function(e) {
|
_onLinkClicked = e => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const predecessor = this.props.mxEvent.getContent()['predecessor'];
|
const predecessor = this.props.mxEvent.getContent()['predecessor'];
|
||||||
|
@ -43,9 +40,9 @@ export default createReactClass({
|
||||||
highlighted: true,
|
highlighted: true,
|
||||||
room_id: predecessor['room_id'],
|
room_id: predecessor['room_id'],
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const predecessor = this.props.mxEvent.getContent()['predecessor'];
|
const predecessor = this.props.mxEvent.getContent()['predecessor'];
|
||||||
if (predecessor === undefined) {
|
if (predecessor === undefined) {
|
||||||
return <div />; // We should never have been instaniated in this case
|
return <div />; // We should never have been instaniated in this case
|
||||||
|
@ -66,5 +63,5 @@ export default createReactClass({
|
||||||
{_t("Click here to see older messages.")}
|
{_t("Click here to see older messages.")}
|
||||||
</a>
|
</a>
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,31 +16,25 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import Flair from '../elements/Flair.js';
|
import Flair from '../elements/Flair.js';
|
||||||
import FlairStore from '../../../stores/FlairStore';
|
import FlairStore from '../../../stores/FlairStore';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {getUserNameColorClass} from '../../../utils/FormattingUtils';
|
import {getUserNameColorClass} from '../../../utils/FormattingUtils';
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class SenderProfile extends React.Component {
|
||||||
displayName: 'SenderProfile',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
mxEvent: PropTypes.object.isRequired, // event whose sender we're showing
|
mxEvent: PropTypes.object.isRequired, // event whose sender we're showing
|
||||||
text: PropTypes.string, // Text to show. Defaults to sender name
|
text: PropTypes.string, // Text to show. Defaults to sender name
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
},
|
};
|
||||||
|
|
||||||
statics: {
|
static contextType = MatrixClientContext;
|
||||||
contextType: MatrixClientContext,
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState() {
|
state = {
|
||||||
return {
|
userGroups: null,
|
||||||
userGroups: null,
|
relatedGroups: [],
|
||||||
relatedGroups: [],
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.unmounted = false;
|
this.unmounted = false;
|
||||||
|
@ -54,20 +48,20 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
this.context.on('RoomState.events', this.onRoomStateEvents);
|
this.context.on('RoomState.events', this.onRoomStateEvents);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
this.context.removeListener('RoomState.events', this.onRoomStateEvents);
|
this.context.removeListener('RoomState.events', this.onRoomStateEvents);
|
||||||
},
|
}
|
||||||
|
|
||||||
onRoomStateEvents(event) {
|
onRoomStateEvents = event => {
|
||||||
if (event.getType() === 'm.room.related_groups' &&
|
if (event.getType() === 'm.room.related_groups' &&
|
||||||
event.getRoomId() === this.props.mxEvent.getRoomId()
|
event.getRoomId() === this.props.mxEvent.getRoomId()
|
||||||
) {
|
) {
|
||||||
this._updateRelatedGroups();
|
this._updateRelatedGroups();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_updateRelatedGroups() {
|
_updateRelatedGroups() {
|
||||||
if (this.unmounted) return;
|
if (this.unmounted) return;
|
||||||
|
@ -78,7 +72,7 @@ export default createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
relatedGroups: relatedGroupsEvent ? relatedGroupsEvent.getContent().groups || [] : [],
|
relatedGroups: relatedGroupsEvent ? relatedGroupsEvent.getContent().groups || [] : [],
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_getDisplayedGroups(userGroups, relatedGroups) {
|
_getDisplayedGroups(userGroups, relatedGroups) {
|
||||||
let displayedGroups = userGroups || [];
|
let displayedGroups = userGroups || [];
|
||||||
|
@ -90,7 +84,7 @@ export default createReactClass({
|
||||||
displayedGroups = [];
|
displayedGroups = [];
|
||||||
}
|
}
|
||||||
return displayedGroups;
|
return displayedGroups;
|
||||||
},
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {mxEvent} = this.props;
|
const {mxEvent} = this.props;
|
||||||
|
@ -138,5 +132,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import highlight from 'highlight.js';
|
import highlight from 'highlight.js';
|
||||||
import * as HtmlUtils from '../../../HtmlUtils';
|
import * as HtmlUtils from '../../../HtmlUtils';
|
||||||
import {formatDate} from '../../../DateUtils';
|
import {formatDate} from '../../../DateUtils';
|
||||||
|
@ -37,10 +36,8 @@ import {toRightOf} from "../../structures/ContextMenu";
|
||||||
import {copyPlaintext} from "../../../utils/strings";
|
import {copyPlaintext} from "../../../utils/strings";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class TextualBody extends React.Component {
|
||||||
displayName: 'TextualBody',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
|
|
||||||
|
@ -58,10 +55,14 @@ export default createReactClass({
|
||||||
|
|
||||||
/* the shape of the tile, used */
|
/* the shape of the tile, used */
|
||||||
tileShape: PropTypes.string,
|
tileShape: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this._content = createRef();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
// the URLs (if any) to be previewed with a LinkPreviewWidget
|
// the URLs (if any) to be previewed with a LinkPreviewWidget
|
||||||
// inside this TextualBody.
|
// inside this TextualBody.
|
||||||
links: [],
|
links: [],
|
||||||
|
@ -69,20 +70,15 @@ export default createReactClass({
|
||||||
// track whether the preview widget is hidden
|
// track whether the preview widget is hidden
|
||||||
widgetHidden: false,
|
widgetHidden: false,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
componentDidMount() {
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._content = createRef();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this._unmounted = false;
|
this._unmounted = false;
|
||||||
this._pills = [];
|
this._pills = [];
|
||||||
if (!this.props.editState) {
|
if (!this.props.editState) {
|
||||||
this._applyFormatting();
|
this._applyFormatting();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_applyFormatting() {
|
_applyFormatting() {
|
||||||
this.activateSpoilers([this._content.current]);
|
this.activateSpoilers([this._content.current]);
|
||||||
|
@ -119,9 +115,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
this._addCodeCopyButton();
|
this._addCodeCopyButton();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
if (!this.props.editState) {
|
if (!this.props.editState) {
|
||||||
const stoppedEditing = prevProps.editState && !this.props.editState;
|
const stoppedEditing = prevProps.editState && !this.props.editState;
|
||||||
const messageWasEdited = prevProps.replacingEventId !== this.props.replacingEventId;
|
const messageWasEdited = prevProps.replacingEventId !== this.props.replacingEventId;
|
||||||
|
@ -129,14 +125,14 @@ export default createReactClass({
|
||||||
this._applyFormatting();
|
this._applyFormatting();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
this._unmounted = true;
|
this._unmounted = true;
|
||||||
unmountPills(this._pills);
|
unmountPills(this._pills);
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldComponentUpdate: function(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
//console.info("shouldComponentUpdate: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
|
//console.info("shouldComponentUpdate: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
|
||||||
|
|
||||||
// exploit that events are immutable :)
|
// exploit that events are immutable :)
|
||||||
|
@ -148,9 +144,9 @@ export default createReactClass({
|
||||||
nextProps.editState !== this.props.editState ||
|
nextProps.editState !== this.props.editState ||
|
||||||
nextState.links !== this.state.links ||
|
nextState.links !== this.state.links ||
|
||||||
nextState.widgetHidden !== this.state.widgetHidden);
|
nextState.widgetHidden !== this.state.widgetHidden);
|
||||||
},
|
}
|
||||||
|
|
||||||
calculateUrlPreview: function() {
|
calculateUrlPreview() {
|
||||||
//console.info("calculateUrlPreview: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
|
//console.info("calculateUrlPreview: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
|
||||||
|
|
||||||
if (this.props.showUrlPreview) {
|
if (this.props.showUrlPreview) {
|
||||||
|
@ -176,9 +172,9 @@ export default createReactClass({
|
||||||
this.setState({ links: [] });
|
this.setState({ links: [] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
activateSpoilers: function(nodes) {
|
activateSpoilers(nodes) {
|
||||||
let node = nodes[0];
|
let node = nodes[0];
|
||||||
while (node) {
|
while (node) {
|
||||||
if (node.tagName === "SPAN" && typeof node.getAttribute("data-mx-spoiler") === "string") {
|
if (node.tagName === "SPAN" && typeof node.getAttribute("data-mx-spoiler") === "string") {
|
||||||
|
@ -204,9 +200,9 @@ export default createReactClass({
|
||||||
|
|
||||||
node = node.nextSibling;
|
node = node.nextSibling;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
findLinks: function(nodes) {
|
findLinks(nodes) {
|
||||||
let links = [];
|
let links = [];
|
||||||
|
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
@ -223,9 +219,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return links;
|
return links;
|
||||||
},
|
}
|
||||||
|
|
||||||
isLinkPreviewable: function(node) {
|
isLinkPreviewable(node) {
|
||||||
// don't try to preview relative links
|
// don't try to preview relative links
|
||||||
if (!node.getAttribute("href").startsWith("http://") &&
|
if (!node.getAttribute("href").startsWith("http://") &&
|
||||||
!node.getAttribute("href").startsWith("https://")) {
|
!node.getAttribute("href").startsWith("https://")) {
|
||||||
|
@ -256,7 +252,7 @@ export default createReactClass({
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_addCodeCopyButton() {
|
_addCodeCopyButton() {
|
||||||
// Add 'copy' buttons to pre blocks
|
// Add 'copy' buttons to pre blocks
|
||||||
|
@ -288,41 +284,39 @@ export default createReactClass({
|
||||||
div.appendChild(p);
|
div.appendChild(p);
|
||||||
div.appendChild(button);
|
div.appendChild(button);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onCancelClick: function(event) {
|
onCancelClick = event => {
|
||||||
this.setState({ widgetHidden: true });
|
this.setState({ widgetHidden: true });
|
||||||
// FIXME: persist this somewhere smarter than local storage
|
// FIXME: persist this somewhere smarter than local storage
|
||||||
if (global.localStorage) {
|
if (global.localStorage) {
|
||||||
global.localStorage.setItem("hide_preview_" + this.props.mxEvent.getId(), "1");
|
global.localStorage.setItem("hide_preview_" + this.props.mxEvent.getId(), "1");
|
||||||
}
|
}
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
onEmoteSenderClick: function(event) {
|
onEmoteSenderClick = event => {
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'insert_mention',
|
action: 'insert_mention',
|
||||||
user_id: mxEvent.getSender(),
|
user_id: mxEvent.getSender(),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
getEventTileOps: function() {
|
getEventTileOps = () => ({
|
||||||
return {
|
isWidgetHidden: () => {
|
||||||
isWidgetHidden: () => {
|
return this.state.widgetHidden;
|
||||||
return this.state.widgetHidden;
|
},
|
||||||
},
|
|
||||||
|
|
||||||
unhideWidget: () => {
|
unhideWidget: () => {
|
||||||
this.setState({ widgetHidden: false });
|
this.setState({widgetHidden: false});
|
||||||
if (global.localStorage) {
|
if (global.localStorage) {
|
||||||
global.localStorage.removeItem("hide_preview_" + this.props.mxEvent.getId());
|
global.localStorage.removeItem("hide_preview_" + this.props.mxEvent.getId());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
},
|
|
||||||
|
|
||||||
onStarterLinkClick: function(starterLink, ev) {
|
onStarterLinkClick = (starterLink, ev) => {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
// We need to add on our scalar token to the starter link, but we may not have one!
|
// We need to add on our scalar token to the starter link, but we may not have one!
|
||||||
// In addition, we can't fetch one on click and then go to it immediately as that
|
// In addition, we can't fetch one on click and then go to it immediately as that
|
||||||
|
@ -353,7 +347,7 @@ export default createReactClass({
|
||||||
"Do you wish to continue?", { integrationsUrl: integrationsUrl }) }
|
"Do you wish to continue?", { integrationsUrl: integrationsUrl }) }
|
||||||
</div>,
|
</div>,
|
||||||
button: _t("Continue"),
|
button: _t("Continue"),
|
||||||
onFinished: function(confirmed) {
|
onFinished(confirmed) {
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -367,14 +361,14 @@ export default createReactClass({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_openHistoryDialog: async function() {
|
_openHistoryDialog = async () => {
|
||||||
const MessageEditHistoryDialog = sdk.getComponent("views.dialogs.MessageEditHistoryDialog");
|
const MessageEditHistoryDialog = sdk.getComponent("views.dialogs.MessageEditHistoryDialog");
|
||||||
Modal.createDialog(MessageEditHistoryDialog, {mxEvent: this.props.mxEvent});
|
Modal.createDialog(MessageEditHistoryDialog, {mxEvent: this.props.mxEvent});
|
||||||
},
|
};
|
||||||
|
|
||||||
_renderEditedMarker: function() {
|
_renderEditedMarker() {
|
||||||
const date = this.props.mxEvent.replacingEventDate();
|
const date = this.props.mxEvent.replacingEventDate();
|
||||||
const dateString = date && formatDate(date);
|
const dateString = date && formatDate(date);
|
||||||
|
|
||||||
|
@ -397,9 +391,9 @@ export default createReactClass({
|
||||||
<span>{`(${_t("edited")})`}</span>
|
<span>{`(${_t("edited")})`}</span>
|
||||||
</AccessibleTooltipButton>
|
</AccessibleTooltipButton>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
if (this.props.editState) {
|
if (this.props.editState) {
|
||||||
const EditMessageComposer = sdk.getComponent('rooms.EditMessageComposer');
|
const EditMessageComposer = sdk.getComponent('rooms.EditMessageComposer');
|
||||||
return <EditMessageComposer editState={this.props.editState} className="mx_EventTile_content" />;
|
return <EditMessageComposer editState={this.props.editState} className="mx_EventTile_content" />;
|
||||||
|
@ -468,5 +462,5 @@ export default createReactClass({
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,22 +17,19 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as TextForEvent from "../../../TextForEvent";
|
import * as TextForEvent from "../../../TextForEvent";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class TextualEvent extends React.Component {
|
||||||
displayName: 'TextualEvent',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const text = TextForEvent.textForEvent(this.props.mxEvent);
|
const text = TextForEvent.textForEvent(this.props.mxEvent);
|
||||||
if (text == null || text.length === 0) return null;
|
if (text == null || text.length === 0) return null;
|
||||||
return (
|
return (
|
||||||
<div className="mx_TextualEvent">{ text }</div>
|
<div className="mx_TextualEvent">{ text }</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import { _t, _td } from '../../../languageHandler';
|
import { _t, _td } from '../../../languageHandler';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
@ -29,20 +28,18 @@ import {Action} from "../../../dispatcher/actions";
|
||||||
import {SettingLevel} from "../../../settings/SettingLevel";
|
import {SettingLevel} from "../../../settings/SettingLevel";
|
||||||
|
|
||||||
|
|
||||||
export default createReactClass({
|
export default class UrlPreviewSettings extends React.Component {
|
||||||
displayName: 'UrlPreviewSettings',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
room: PropTypes.object,
|
room: PropTypes.object,
|
||||||
},
|
};
|
||||||
|
|
||||||
_onClickUserSettings: (e) => {
|
_onClickUserSettings = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
dis.fire(Action.ViewUserSettings);
|
dis.fire(Action.ViewUserSettings);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
const SettingsFlag = sdk.getComponent("elements.SettingsFlag");
|
||||||
const roomId = this.props.room.roomId;
|
const roomId = this.props.room.roomId;
|
||||||
const isEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId);
|
const isEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId);
|
||||||
|
@ -110,5 +107,5 @@ export default createReactClass({
|
||||||
<label>{ previewsForRoomAccount }</label>
|
<label>{ previewsForRoomAccount }</label>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import AppTile from '../elements/AppTile';
|
import AppTile from '../elements/AppTile';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
@ -34,50 +33,50 @@ import SettingsStore from "../../../settings/SettingsStore";
|
||||||
// The maximum number of widgets that can be added in a room
|
// The maximum number of widgets that can be added in a room
|
||||||
const MAX_WIDGETS = 2;
|
const MAX_WIDGETS = 2;
|
||||||
|
|
||||||
export default createReactClass({
|
export default class AppsDrawer extends React.Component {
|
||||||
displayName: 'AppsDrawer',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
userId: PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
room: PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
showApps: PropTypes.bool, // Should apps be rendered
|
showApps: PropTypes.bool, // Should apps be rendered
|
||||||
hide: PropTypes.bool, // If rendered, should apps drawer be visible
|
hide: PropTypes.bool, // If rendered, should apps drawer be visible
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: () => ({
|
static defaultProps = {
|
||||||
showApps: true,
|
showApps: true,
|
||||||
hide: false,
|
hide: false,
|
||||||
}),
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
apps: this._getApps(),
|
apps: this._getApps(),
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
ScalarMessaging.startListening();
|
ScalarMessaging.startListening();
|
||||||
MatrixClientPeg.get().on('RoomState.events', this.onRoomStateEvents);
|
MatrixClientPeg.get().on('RoomState.events', this.onRoomStateEvents);
|
||||||
WidgetEchoStore.on('update', this._updateApps);
|
WidgetEchoStore.on('update', this._updateApps);
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
ScalarMessaging.stopListening();
|
ScalarMessaging.stopListening();
|
||||||
if (MatrixClientPeg.get()) {
|
if (MatrixClientPeg.get()) {
|
||||||
MatrixClientPeg.get().removeListener('RoomState.events', this.onRoomStateEvents);
|
MatrixClientPeg.get().removeListener('RoomState.events', this.onRoomStateEvents);
|
||||||
}
|
}
|
||||||
WidgetEchoStore.removeListener('update', this._updateApps);
|
WidgetEchoStore.removeListener('update', this._updateApps);
|
||||||
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
|
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
UNSAFE_componentWillReceiveProps(newProps) {
|
UNSAFE_componentWillReceiveProps(newProps) {
|
||||||
// Room has changed probably, update apps
|
// Room has changed probably, update apps
|
||||||
this._updateApps();
|
this._updateApps();
|
||||||
},
|
}
|
||||||
|
|
||||||
onAction: function(action) {
|
onAction = (action) => {
|
||||||
const hideWidgetKey = this.props.room.roomId + '_hide_widget_drawer';
|
const hideWidgetKey = this.props.room.roomId + '_hide_widget_drawer';
|
||||||
switch (action.action) {
|
switch (action.action) {
|
||||||
case 'appsDrawer':
|
case 'appsDrawer':
|
||||||
|
@ -93,16 +92,16 @@ export default createReactClass({
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomStateEvents: function(ev, state) {
|
onRoomStateEvents = (ev, state) => {
|
||||||
if (ev.getRoomId() !== this.props.room.roomId || ev.getType() !== 'im.vector.modular.widgets') {
|
if (ev.getRoomId() !== this.props.room.roomId || ev.getType() !== 'im.vector.modular.widgets') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._updateApps();
|
this._updateApps();
|
||||||
},
|
};
|
||||||
|
|
||||||
_getApps: function() {
|
_getApps() {
|
||||||
const widgets = WidgetEchoStore.getEchoedRoomWidgets(
|
const widgets = WidgetEchoStore.getEchoedRoomWidgets(
|
||||||
this.props.room.roomId, WidgetUtils.getRoomWidgets(this.props.room),
|
this.props.room.roomId, WidgetUtils.getRoomWidgets(this.props.room),
|
||||||
);
|
);
|
||||||
|
@ -111,33 +110,33 @@ export default createReactClass({
|
||||||
ev.getStateKey(), ev.getContent(), ev.getSender(), ev.getRoomId(), ev.getId(),
|
ev.getStateKey(), ev.getContent(), ev.getSender(), ev.getRoomId(), ev.getId(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_updateApps: function() {
|
_updateApps = () => {
|
||||||
const apps = this._getApps();
|
const apps = this._getApps();
|
||||||
this.setState({
|
this.setState({
|
||||||
apps: apps,
|
apps: apps,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_canUserModify: function() {
|
_canUserModify() {
|
||||||
try {
|
try {
|
||||||
return WidgetUtils.canUserModifyWidgets(this.props.room.roomId);
|
return WidgetUtils.canUserModifyWidgets(this.props.room.roomId);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_launchManageIntegrations: function() {
|
_launchManageIntegrations() {
|
||||||
if (SettingsStore.getValue("feature_many_integration_managers")) {
|
if (SettingsStore.getValue("feature_many_integration_managers")) {
|
||||||
IntegrationManagers.sharedInstance().openAll();
|
IntegrationManagers.sharedInstance().openAll();
|
||||||
} else {
|
} else {
|
||||||
IntegrationManagers.sharedInstance().getPrimaryManager().open(this.props.room, 'add_integ');
|
IntegrationManagers.sharedInstance().getPrimaryManager().open(this.props.room, 'add_integ');
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onClickAddWidget: function(e) {
|
onClickAddWidget = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
// Display a warning dialog if the max number of widgets have already been added to the room
|
// Display a warning dialog if the max number of widgets have already been added to the room
|
||||||
const apps = this._getApps();
|
const apps = this._getApps();
|
||||||
|
@ -152,9 +151,9 @@ export default createReactClass({
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._launchManageIntegrations();
|
this._launchManageIntegrations();
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const apps = this.state.apps.map((app, index, arr) => {
|
const apps = this.state.apps.map((app, index, arr) => {
|
||||||
const capWhitelist = WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, this.props.room.roomId);
|
const capWhitelist = WidgetUtils.getCapWhitelistForAppTypeInRoomId(app.type, this.props.room.roomId);
|
||||||
|
|
||||||
|
@ -211,5 +210,5 @@ export default createReactClass({
|
||||||
{ this._canUserModify() && addWidget }
|
{ this._canUserModify() && addWidget }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
|
@ -31,10 +30,8 @@ import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
import CallView from "../voip/CallView";
|
import CallView from "../voip/CallView";
|
||||||
|
|
||||||
|
|
||||||
export default createReactClass({
|
export default class AuxPanel extends React.Component {
|
||||||
displayName: 'AuxPanel',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// js-sdk room object
|
// js-sdk room object
|
||||||
room: PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
userId: PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
|
@ -58,42 +55,46 @@ export default createReactClass({
|
||||||
// content in a way that is likely to make it change size.
|
// content in a way that is likely to make it change size.
|
||||||
onResize: PropTypes.func,
|
onResize: PropTypes.func,
|
||||||
fullHeight: PropTypes.bool,
|
fullHeight: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: () => ({
|
static defaultProps = {
|
||||||
showApps: true,
|
showApps: true,
|
||||||
hideAppsDrawer: false,
|
hideAppsDrawer: false,
|
||||||
}),
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return { counters: this._computeCounters() };
|
super(props);
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
this.state = {
|
||||||
|
counters: this._computeCounters(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
cli.on("RoomState.events", this._rateLimitedUpdate);
|
cli.on("RoomState.events", this._rateLimitedUpdate);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli) {
|
if (cli) {
|
||||||
cli.removeListener("RoomState.events", this._rateLimitedUpdate);
|
cli.removeListener("RoomState.events", this._rateLimitedUpdate);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldComponentUpdate: function(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
return (!ObjectUtils.shallowEqual(this.props, nextProps) ||
|
return (!ObjectUtils.shallowEqual(this.props, nextProps) ||
|
||||||
!ObjectUtils.shallowEqual(this.state, nextState));
|
!ObjectUtils.shallowEqual(this.state, nextState));
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
// most changes are likely to cause a resize
|
// most changes are likely to cause a resize
|
||||||
if (this.props.onResize) {
|
if (this.props.onResize) {
|
||||||
this.props.onResize();
|
this.props.onResize();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onConferenceNotificationClick: function(ev, type) {
|
onConferenceNotificationClick = (ev, type) => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'place_call',
|
action: 'place_call',
|
||||||
type: type,
|
type: type,
|
||||||
|
@ -101,15 +102,15 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
},
|
};
|
||||||
|
|
||||||
_rateLimitedUpdate: new RateLimitedFunc(function() {
|
_rateLimitedUpdate = new RateLimitedFunc(() => {
|
||||||
if (SettingsStore.getValue("feature_state_counters")) {
|
if (SettingsStore.getValue("feature_state_counters")) {
|
||||||
this.setState({counters: this._computeCounters()});
|
this.setState({counters: this._computeCounters()});
|
||||||
}
|
}
|
||||||
}, 500),
|
}, 500);
|
||||||
|
|
||||||
_computeCounters: function() {
|
_computeCounters() {
|
||||||
let counters = [];
|
let counters = [];
|
||||||
|
|
||||||
if (this.props.room && SettingsStore.getValue("feature_state_counters")) {
|
if (this.props.room && SettingsStore.getValue("feature_state_counters")) {
|
||||||
|
@ -140,9 +141,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return counters;
|
return counters;
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
|
|
||||||
let fileDropTarget = null;
|
let fileDropTarget = null;
|
||||||
|
@ -274,5 +275,5 @@ export default createReactClass({
|
||||||
{ this.props.children }
|
{ this.props.children }
|
||||||
</AutoHideScrollbar>
|
</AutoHideScrollbar>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -51,10 +50,8 @@ function presenceClassForMember(presenceState, lastActiveAgo, showPresence) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const EntityTile = createReactClass({
|
class EntityTile extends React.Component {
|
||||||
displayName: 'EntityTile',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
name: PropTypes.string,
|
name: PropTypes.string,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
avatarJsx: PropTypes.any, // <BaseAvatar />
|
avatarJsx: PropTypes.any, // <BaseAvatar />
|
||||||
|
@ -70,33 +67,29 @@ const EntityTile = createReactClass({
|
||||||
showPresence: PropTypes.bool,
|
showPresence: PropTypes.bool,
|
||||||
subtextLabel: PropTypes.string,
|
subtextLabel: PropTypes.string,
|
||||||
e2eStatus: PropTypes.string,
|
e2eStatus: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
shouldComponentUpdate: function(nextProps, nextState) { return true; },
|
||||||
shouldComponentUpdate: function(nextProps, nextState) { return true; },
|
onClick: function() {},
|
||||||
onClick: function() {},
|
presenceState: "offline",
|
||||||
presenceState: "offline",
|
presenceLastActiveAgo: 0,
|
||||||
presenceLastActiveAgo: 0,
|
presenceLastTs: 0,
|
||||||
presenceLastTs: 0,
|
showInviteButton: false,
|
||||||
showInviteButton: false,
|
suppressOnHover: false,
|
||||||
suppressOnHover: false,
|
showPresence: true,
|
||||||
showPresence: true,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
hover: false,
|
||||||
hover: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldComponentUpdate: function(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
if (this.state.hover !== nextState.hover) return true;
|
if (this.state.hover !== nextState.hover) return true;
|
||||||
return this.props.shouldComponentUpdate(nextProps, nextState);
|
return this.props.shouldComponentUpdate(nextProps, nextState);
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const mainClassNames = {
|
const mainClassNames = {
|
||||||
"mx_EntityTile": true,
|
"mx_EntityTile": true,
|
||||||
"mx_EntityTile_noHover": this.props.suppressOnHover,
|
"mx_EntityTile_noHover": this.props.suppressOnHover,
|
||||||
|
@ -193,8 +186,8 @@ const EntityTile = createReactClass({
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
EntityTile.POWER_STATUS_MODERATOR = "moderator";
|
EntityTile.POWER_STATUS_MODERATOR = "moderator";
|
||||||
EntityTile.POWER_STATUS_ADMIN = "admin";
|
EntityTile.POWER_STATUS_ADMIN = "admin";
|
||||||
|
|
|
@ -20,7 +20,6 @@ limitations under the License.
|
||||||
import ReplyThread from "../elements/ReplyThread";
|
import ReplyThread from "../elements/ReplyThread";
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { _t, _td } from '../../../languageHandler';
|
import { _t, _td } from '../../../languageHandler';
|
||||||
import * as TextForEvent from "../../../TextForEvent";
|
import * as TextForEvent from "../../../TextForEvent";
|
||||||
|
@ -127,10 +126,8 @@ const MAX_READ_AVATARS = 5;
|
||||||
// | '--------------------------------------' |
|
// | '--------------------------------------' |
|
||||||
// '----------------------------------------------------------'
|
// '----------------------------------------------------------'
|
||||||
|
|
||||||
export default createReactClass({
|
export default class EventTile extends React.Component {
|
||||||
displayName: 'EventTile',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
/* the MatrixEvent to show */
|
/* the MatrixEvent to show */
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
|
|
||||||
|
@ -209,17 +206,19 @@ export default createReactClass({
|
||||||
|
|
||||||
// whether to use the irc layout
|
// whether to use the irc layout
|
||||||
useIRCLayout: PropTypes.bool,
|
useIRCLayout: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
// no-op function because onHeightChanged is optional yet some sub-components assume its existence
|
||||||
// no-op function because onHeightChanged is optional yet some sub-components assume its existence
|
onHeightChanged: function() {},
|
||||||
onHeightChanged: function() {},
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
static contextType = MatrixClientContext;
|
||||||
return {
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
// Whether the action bar is focused.
|
// Whether the action bar is focused.
|
||||||
actionBarFocused: false,
|
actionBarFocused: false,
|
||||||
// Whether all read receipts are being displayed. If not, only display
|
// Whether all read receipts are being displayed. If not, only display
|
||||||
|
@ -232,23 +231,16 @@ export default createReactClass({
|
||||||
// The Relations model from the JS SDK for reactions to `mxEvent`
|
// The Relations model from the JS SDK for reactions to `mxEvent`
|
||||||
reactions: this.getReactions(),
|
reactions: this.getReactions(),
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
statics: {
|
|
||||||
contextType: MatrixClientContext,
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
// don't do RR animations until we are mounted
|
// don't do RR animations until we are mounted
|
||||||
this._suppressReadReceiptAnimation = true;
|
this._suppressReadReceiptAnimation = true;
|
||||||
this._verifyEvent(this.props.mxEvent);
|
this._verifyEvent(this.props.mxEvent);
|
||||||
|
|
||||||
this._tile = createRef();
|
this._tile = createRef();
|
||||||
this._replyThread = createRef();
|
this._replyThread = createRef();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this._suppressReadReceiptAnimation = false;
|
this._suppressReadReceiptAnimation = false;
|
||||||
const client = this.context;
|
const client = this.context;
|
||||||
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||||
|
@ -257,26 +249,26 @@ export default createReactClass({
|
||||||
if (this.props.showReactions) {
|
if (this.props.showReactions) {
|
||||||
this.props.mxEvent.on("Event.relationsCreated", this._onReactionsCreated);
|
this.props.mxEvent.on("Event.relationsCreated", this._onReactionsCreated);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
|
||||||
UNSAFE_componentWillReceiveProps: function(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||||
// re-check the sender verification as outgoing events progress through
|
// re-check the sender verification as outgoing events progress through
|
||||||
// the send process.
|
// the send process.
|
||||||
if (nextProps.eventSendStatus !== this.props.eventSendStatus) {
|
if (nextProps.eventSendStatus !== this.props.eventSendStatus) {
|
||||||
this._verifyEvent(nextProps.mxEvent);
|
this._verifyEvent(nextProps.mxEvent);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldComponentUpdate: function(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
if (!ObjectUtils.shallowEqual(this.state, nextState)) {
|
if (!ObjectUtils.shallowEqual(this.state, nextState)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !this._propsEqual(this.props, nextProps);
|
return !this._propsEqual(this.props, nextProps);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
const client = this.context;
|
const client = this.context;
|
||||||
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
client.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||||
client.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
|
client.removeListener("userTrustStatusChanged", this.onUserVerificationChanged);
|
||||||
|
@ -284,31 +276,31 @@ export default createReactClass({
|
||||||
if (this.props.showReactions) {
|
if (this.props.showReactions) {
|
||||||
this.props.mxEvent.removeListener("Event.relationsCreated", this._onReactionsCreated);
|
this.props.mxEvent.removeListener("Event.relationsCreated", this._onReactionsCreated);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
/** called when the event is decrypted after we show it.
|
/** called when the event is decrypted after we show it.
|
||||||
*/
|
*/
|
||||||
_onDecrypted: function() {
|
_onDecrypted = () => {
|
||||||
// we need to re-verify the sending device.
|
// we need to re-verify the sending device.
|
||||||
// (we call onHeightChanged in _verifyEvent to handle the case where decryption
|
// (we call onHeightChanged in _verifyEvent to handle the case where decryption
|
||||||
// has caused a change in size of the event tile)
|
// has caused a change in size of the event tile)
|
||||||
this._verifyEvent(this.props.mxEvent);
|
this._verifyEvent(this.props.mxEvent);
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
onDeviceVerificationChanged: function(userId, device) {
|
onDeviceVerificationChanged = (userId, device) => {
|
||||||
if (userId === this.props.mxEvent.getSender()) {
|
if (userId === this.props.mxEvent.getSender()) {
|
||||||
this._verifyEvent(this.props.mxEvent);
|
this._verifyEvent(this.props.mxEvent);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onUserVerificationChanged: function(userId, _trustStatus) {
|
onUserVerificationChanged = (userId, _trustStatus) => {
|
||||||
if (userId === this.props.mxEvent.getSender()) {
|
if (userId === this.props.mxEvent.getSender()) {
|
||||||
this._verifyEvent(this.props.mxEvent);
|
this._verifyEvent(this.props.mxEvent);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_verifyEvent: async function(mxEvent) {
|
async _verifyEvent(mxEvent) {
|
||||||
if (!mxEvent.isEncrypted()) {
|
if (!mxEvent.isEncrypted()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -360,9 +352,9 @@ export default createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
verified: E2E_STATE.VERIFIED,
|
verified: E2E_STATE.VERIFIED,
|
||||||
}, this.props.onHeightChanged); // Decryption may have caused a change in size
|
}, this.props.onHeightChanged); // Decryption may have caused a change in size
|
||||||
},
|
}
|
||||||
|
|
||||||
_propsEqual: function(objA, objB) {
|
_propsEqual(objA, objB) {
|
||||||
const keysA = Object.keys(objA);
|
const keysA = Object.keys(objA);
|
||||||
const keysB = Object.keys(objB);
|
const keysB = Object.keys(objB);
|
||||||
|
|
||||||
|
@ -408,9 +400,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
|
|
||||||
shouldHighlight: function() {
|
shouldHighlight() {
|
||||||
const actions = this.context.getPushActionsForEvent(this.props.mxEvent.replacingEvent() || this.props.mxEvent);
|
const actions = this.context.getPushActionsForEvent(this.props.mxEvent.replacingEvent() || this.props.mxEvent);
|
||||||
if (!actions || !actions.tweaks) { return false; }
|
if (!actions || !actions.tweaks) { return false; }
|
||||||
|
|
||||||
|
@ -420,15 +412,15 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return actions.tweaks.highlight;
|
return actions.tweaks.highlight;
|
||||||
},
|
}
|
||||||
|
|
||||||
toggleAllReadAvatars: function() {
|
toggleAllReadAvatars = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
allReadAvatars: !this.state.allReadAvatars,
|
allReadAvatars: !this.state.allReadAvatars,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
getReadAvatars: function() {
|
getReadAvatars() {
|
||||||
// return early if there are no read receipts
|
// return early if there are no read receipts
|
||||||
if (!this.props.readReceipts || this.props.readReceipts.length === 0) {
|
if (!this.props.readReceipts || this.props.readReceipts.length === 0) {
|
||||||
return (<span className="mx_EventTile_readAvatars" />);
|
return (<span className="mx_EventTile_readAvatars" />);
|
||||||
|
@ -494,17 +486,17 @@ export default createReactClass({
|
||||||
{ remText }
|
{ remText }
|
||||||
{ avatars }
|
{ avatars }
|
||||||
</span>;
|
</span>;
|
||||||
},
|
}
|
||||||
|
|
||||||
onSenderProfileClick: function(event) {
|
onSenderProfileClick = event => {
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'insert_mention',
|
action: 'insert_mention',
|
||||||
user_id: mxEvent.getSender(),
|
user_id: mxEvent.getSender(),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onRequestKeysClick: function() {
|
onRequestKeysClick = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
// Indicate in the UI that the keys have been requested (this is expected to
|
// Indicate in the UI that the keys have been requested (this is expected to
|
||||||
// be reset if the component is mounted in the future).
|
// be reset if the component is mounted in the future).
|
||||||
|
@ -515,9 +507,9 @@ export default createReactClass({
|
||||||
// is received for the request with the required keys, the event could be
|
// is received for the request with the required keys, the event could be
|
||||||
// decrypted successfully.
|
// decrypted successfully.
|
||||||
this.context.cancelAndResendEventRoomKeyRequest(this.props.mxEvent);
|
this.context.cancelAndResendEventRoomKeyRequest(this.props.mxEvent);
|
||||||
},
|
};
|
||||||
|
|
||||||
onPermalinkClicked: function(e) {
|
onPermalinkClicked = e => {
|
||||||
// This allows the permalink to be opened in a new tab/window or copied as
|
// This allows the permalink to be opened in a new tab/window or copied as
|
||||||
// matrix.to, but also for it to enable routing within Element when clicked.
|
// matrix.to, but also for it to enable routing within Element when clicked.
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
@ -527,9 +519,9 @@ export default createReactClass({
|
||||||
highlighted: true,
|
highlighted: true,
|
||||||
room_id: this.props.mxEvent.getRoomId(),
|
room_id: this.props.mxEvent.getRoomId(),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_renderE2EPadlock: function() {
|
_renderE2EPadlock() {
|
||||||
const ev = this.props.mxEvent;
|
const ev = this.props.mxEvent;
|
||||||
|
|
||||||
// event could not be decrypted
|
// event could not be decrypted
|
||||||
|
@ -570,23 +562,19 @@ export default createReactClass({
|
||||||
|
|
||||||
// no padlock needed
|
// no padlock needed
|
||||||
return null;
|
return null;
|
||||||
},
|
}
|
||||||
|
|
||||||
onActionBarFocusChange(focused) {
|
onActionBarFocusChange = focused => {
|
||||||
this.setState({
|
this.setState({
|
||||||
actionBarFocused: focused,
|
actionBarFocused: focused,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
getTile() {
|
getTile = () => this._tile.current;
|
||||||
return this._tile.current;
|
|
||||||
},
|
|
||||||
|
|
||||||
getReplyThread() {
|
getReplyThread = () => this._replyThread.current;
|
||||||
return this._replyThread.current;
|
|
||||||
},
|
|
||||||
|
|
||||||
getReactions() {
|
getReactions = () => {
|
||||||
if (
|
if (
|
||||||
!this.props.showReactions ||
|
!this.props.showReactions ||
|
||||||
!this.props.getRelationsForEvent
|
!this.props.getRelationsForEvent
|
||||||
|
@ -602,9 +590,9 @@ export default createReactClass({
|
||||||
console.trace("Stacktrace for https://github.com/vector-im/element-web/issues/11120");
|
console.trace("Stacktrace for https://github.com/vector-im/element-web/issues/11120");
|
||||||
}
|
}
|
||||||
return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction");
|
return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction");
|
||||||
},
|
};
|
||||||
|
|
||||||
_onReactionsCreated(relationType, eventType) {
|
_onReactionsCreated = (relationType, eventType) => {
|
||||||
if (relationType !== "m.annotation" || eventType !== "m.reaction") {
|
if (relationType !== "m.annotation" || eventType !== "m.reaction") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -612,9 +600,9 @@ export default createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
reactions: this.getReactions(),
|
reactions: this.getReactions(),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const MessageTimestamp = sdk.getComponent('messages.MessageTimestamp');
|
const MessageTimestamp = sdk.getComponent('messages.MessageTimestamp');
|
||||||
const SenderProfile = sdk.getComponent('messages.SenderProfile');
|
const SenderProfile = sdk.getComponent('messages.SenderProfile');
|
||||||
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||||
|
@ -947,8 +935,8 @@ export default createReactClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// XXX this'll eventually be dynamic based on the fields once we have extensible event types
|
// XXX this'll eventually be dynamic based on the fields once we have extensible event types
|
||||||
const messageTypes = ['m.room.message', 'm.sticker'];
|
const messageTypes = ['m.room.message', 'm.sticker'];
|
||||||
|
|
|
@ -17,49 +17,46 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import {Key} from '../../../Keyboard';
|
import {Key} from '../../../Keyboard';
|
||||||
|
|
||||||
|
|
||||||
export default createReactClass({
|
export default class ForwardMessage extends React.Component {
|
||||||
displayName: 'ForwardMessage',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
onCancelClick: PropTypes.func.isRequired,
|
onCancelClick: PropTypes.func.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'panel_disable',
|
action: 'panel_disable',
|
||||||
middleDisabled: true,
|
middleDisabled: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('keydown', this._onKeyDown);
|
document.addEventListener('keydown', this._onKeyDown);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'panel_disable',
|
action: 'panel_disable',
|
||||||
middleDisabled: false,
|
middleDisabled: false,
|
||||||
});
|
});
|
||||||
document.removeEventListener('keydown', this._onKeyDown);
|
document.removeEventListener('keydown', this._onKeyDown);
|
||||||
},
|
}
|
||||||
|
|
||||||
_onKeyDown: function(ev) {
|
_onKeyDown = ev => {
|
||||||
switch (ev.key) {
|
switch (ev.key) {
|
||||||
case Key.ESCAPE:
|
case Key.ESCAPE:
|
||||||
this.props.onCancelClick();
|
this.props.onCancelClick();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="mx_ForwardMessage">
|
<div className="mx_ForwardMessage">
|
||||||
<h1>{ _t('Please select the destination room for this message') }</h1>
|
<h1>{ _t('Please select the destination room for this message') }</h1>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { AllHtmlEntities } from 'html-entities';
|
import { AllHtmlEntities } from 'html-entities';
|
||||||
import {linkifyElement} from '../../../HtmlUtils';
|
import {linkifyElement} from '../../../HtmlUtils';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
@ -27,24 +26,21 @@ import Modal from "../../../Modal";
|
||||||
import * as ImageUtils from "../../../ImageUtils";
|
import * as ImageUtils from "../../../ImageUtils";
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class LinkPreviewWidget extends React.Component {
|
||||||
displayName: 'LinkPreviewWidget',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
link: PropTypes.string.isRequired, // the URL being previewed
|
link: PropTypes.string.isRequired, // the URL being previewed
|
||||||
mxEvent: PropTypes.object.isRequired, // the Event associated with the preview
|
mxEvent: PropTypes.object.isRequired, // the Event associated with the preview
|
||||||
onCancelClick: PropTypes.func, // called when the preview's cancel ('hide') button is clicked
|
onCancelClick: PropTypes.func, // called when the preview's cancel ('hide') button is clicked
|
||||||
onHeightChanged: PropTypes.func, // called when the preview's contents has loaded
|
onHeightChanged: PropTypes.func, // called when the preview's contents has loaded
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
preview: null,
|
preview: null,
|
||||||
};
|
};
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this.unmounted = false;
|
this.unmounted = false;
|
||||||
MatrixClientPeg.get().getUrlPreview(this.props.link, this.props.mxEvent.getTs()).then((res)=>{
|
MatrixClientPeg.get().getUrlPreview(this.props.link, this.props.mxEvent.getTs()).then((res)=>{
|
||||||
if (this.unmounted) {
|
if (this.unmounted) {
|
||||||
|
@ -59,25 +55,25 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
this._description = createRef();
|
this._description = createRef();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
if (this._description.current) {
|
if (this._description.current) {
|
||||||
linkifyElement(this._description.current);
|
linkifyElement(this._description.current);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate() {
|
||||||
if (this._description.current) {
|
if (this._description.current) {
|
||||||
linkifyElement(this._description.current);
|
linkifyElement(this._description.current);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
},
|
}
|
||||||
|
|
||||||
onImageClick: function(ev) {
|
onImageClick = ev => {
|
||||||
const p = this.state.preview;
|
const p = this.state.preview;
|
||||||
if (ev.button != 0 || ev.metaKey) return;
|
if (ev.button != 0 || ev.metaKey) return;
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
@ -98,9 +94,9 @@ export default createReactClass({
|
||||||
};
|
};
|
||||||
|
|
||||||
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
|
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox");
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const p = this.state.preview;
|
const p = this.state.preview;
|
||||||
if (!p || Object.keys(p).length === 0) {
|
if (!p || Object.keys(p).length === 0) {
|
||||||
return <div />;
|
return <div />;
|
||||||
|
@ -149,5 +145,5 @@ export default createReactClass({
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import SdkConfig from '../../../SdkConfig';
|
import SdkConfig from '../../../SdkConfig';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
|
@ -36,23 +35,19 @@ const SHOW_MORE_INCREMENT = 100;
|
||||||
// matches all ASCII punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
|
// matches all ASCII punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
|
||||||
const SORT_REGEX = /[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]+/g;
|
const SORT_REGEX = /[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]+/g;
|
||||||
|
|
||||||
export default createReactClass({
|
export default class MemberList extends React.Component {
|
||||||
displayName: 'MemberList',
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli.hasLazyLoadMembersEnabled()) {
|
if (cli.hasLazyLoadMembersEnabled()) {
|
||||||
// show an empty list
|
// show an empty list
|
||||||
return this._getMembersState([]);
|
this.state = this._getMembersState([]);
|
||||||
} else {
|
} else {
|
||||||
return this._getMembersState(this.roomMembers());
|
this.state = this._getMembersState(this.roomMembers());
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Move this to constructor
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._mounted = true;
|
this._mounted = true;
|
||||||
const cli = MatrixClientPeg.get();
|
|
||||||
if (cli.hasLazyLoadMembersEnabled()) {
|
if (cli.hasLazyLoadMembersEnabled()) {
|
||||||
this._showMembersAccordingToMembershipWithLL();
|
this._showMembersAccordingToMembershipWithLL();
|
||||||
cli.on("Room.myMembership", this.onMyMembership);
|
cli.on("Room.myMembership", this.onMyMembership);
|
||||||
|
@ -66,9 +61,9 @@ export default createReactClass({
|
||||||
if (enablePresenceByHsUrl && enablePresenceByHsUrl[hsUrl] !== undefined) {
|
if (enablePresenceByHsUrl && enablePresenceByHsUrl[hsUrl] !== undefined) {
|
||||||
this._showPresence = enablePresenceByHsUrl[hsUrl];
|
this._showPresence = enablePresenceByHsUrl[hsUrl];
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_listenForMembersChanges: function() {
|
_listenForMembersChanges() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
cli.on("RoomState.members", this.onRoomStateMember);
|
cli.on("RoomState.members", this.onRoomStateMember);
|
||||||
cli.on("RoomMember.name", this.onRoomMemberName);
|
cli.on("RoomMember.name", this.onRoomMemberName);
|
||||||
|
@ -80,9 +75,9 @@ export default createReactClass({
|
||||||
cli.on("User.presence", this.onUserPresenceChange);
|
cli.on("User.presence", this.onUserPresenceChange);
|
||||||
cli.on("User.currentlyActive", this.onUserPresenceChange);
|
cli.on("User.currentlyActive", this.onUserPresenceChange);
|
||||||
// cli.on("Room.timeline", this.onRoomTimeline);
|
// cli.on("Room.timeline", this.onRoomTimeline);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
this._mounted = false;
|
this._mounted = false;
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli) {
|
if (cli) {
|
||||||
|
@ -98,14 +93,14 @@ export default createReactClass({
|
||||||
|
|
||||||
// cancel any pending calls to the rate_limited_funcs
|
// cancel any pending calls to the rate_limited_funcs
|
||||||
this._updateList.cancelPendingCall();
|
this._updateList.cancelPendingCall();
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If lazy loading is enabled, either:
|
* If lazy loading is enabled, either:
|
||||||
* show a spinner and load the members if the user is joined,
|
* show a spinner and load the members if the user is joined,
|
||||||
* or show the members available so far if the user is invited
|
* or show the members available so far if the user is invited
|
||||||
*/
|
*/
|
||||||
_showMembersAccordingToMembershipWithLL: async function() {
|
async _showMembersAccordingToMembershipWithLL() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
if (cli.hasLazyLoadMembersEnabled()) {
|
if (cli.hasLazyLoadMembersEnabled()) {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
@ -125,9 +120,9 @@ export default createReactClass({
|
||||||
this.setState(this._getMembersState(this.roomMembers()));
|
this.setState(this._getMembersState(this.roomMembers()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_getMembersState: function(members) {
|
_getMembersState(members) {
|
||||||
// set the state after determining _showPresence to make sure it's
|
// set the state after determining _showPresence to make sure it's
|
||||||
// taken into account while rerendering
|
// taken into account while rerendering
|
||||||
return {
|
return {
|
||||||
|
@ -142,9 +137,9 @@ export default createReactClass({
|
||||||
truncateAtInvited: INITIAL_LOAD_NUM_INVITED,
|
truncateAtInvited: INITIAL_LOAD_NUM_INVITED,
|
||||||
searchQuery: "",
|
searchQuery: "",
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
onUserPresenceChange(event, user) {
|
onUserPresenceChange = (event, user) => {
|
||||||
// Attach a SINGLE listener for global presence changes then locate the
|
// Attach a SINGLE listener for global presence changes then locate the
|
||||||
// member tile and re-render it. This is more efficient than every tile
|
// member tile and re-render it. This is more efficient than every tile
|
||||||
// ever attaching their own listener.
|
// ever attaching their own listener.
|
||||||
|
@ -153,9 +148,9 @@ export default createReactClass({
|
||||||
if (tile) {
|
if (tile) {
|
||||||
this._updateList(); // reorder the membership list
|
this._updateList(); // reorder the membership list
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoom: function(room) {
|
onRoom = room => {
|
||||||
if (room.roomId !== this.props.roomId) {
|
if (room.roomId !== this.props.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -163,40 +158,40 @@ export default createReactClass({
|
||||||
// we need to wait till the room is fully populated with state
|
// we need to wait till the room is fully populated with state
|
||||||
// before refreshing the member list else we get a stale list.
|
// before refreshing the member list else we get a stale list.
|
||||||
this._showMembersAccordingToMembershipWithLL();
|
this._showMembersAccordingToMembershipWithLL();
|
||||||
},
|
};
|
||||||
|
|
||||||
onMyMembership: function(room, membership, oldMembership) {
|
onMyMembership = (room, membership, oldMembership) => {
|
||||||
if (room.roomId === this.props.roomId && membership === "join") {
|
if (room.roomId === this.props.roomId && membership === "join") {
|
||||||
this._showMembersAccordingToMembershipWithLL();
|
this._showMembersAccordingToMembershipWithLL();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomStateMember: function(ev, state, member) {
|
onRoomStateMember = (ev, state, member) => {
|
||||||
if (member.roomId !== this.props.roomId) {
|
if (member.roomId !== this.props.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._updateList();
|
this._updateList();
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomMemberName: function(ev, member) {
|
onRoomMemberName = (ev, member) => {
|
||||||
if (member.roomId !== this.props.roomId) {
|
if (member.roomId !== this.props.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._updateList();
|
this._updateList();
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomStateEvent: function(event, state) {
|
onRoomStateEvent = (event, state) => {
|
||||||
if (event.getRoomId() === this.props.roomId &&
|
if (event.getRoomId() === this.props.roomId &&
|
||||||
event.getType() === "m.room.third_party_invite") {
|
event.getType() === "m.room.third_party_invite") {
|
||||||
this._updateList();
|
this._updateList();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_updateList: rate_limited_func(function() {
|
_updateList = rate_limited_func(() => {
|
||||||
this._updateListNow();
|
this._updateListNow();
|
||||||
}, 500),
|
}, 500);
|
||||||
|
|
||||||
_updateListNow: function() {
|
_updateListNow() {
|
||||||
// console.log("Updating memberlist");
|
// console.log("Updating memberlist");
|
||||||
const newState = {
|
const newState = {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
@ -205,9 +200,9 @@ export default createReactClass({
|
||||||
newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join', this.state.searchQuery);
|
newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join', this.state.searchQuery);
|
||||||
newState.filteredInvitedMembers = this._filterMembers(newState.members, 'invite', this.state.searchQuery);
|
newState.filteredInvitedMembers = this._filterMembers(newState.members, 'invite', this.state.searchQuery);
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
},
|
}
|
||||||
|
|
||||||
getMembersWithUser: function() {
|
getMembersWithUser() {
|
||||||
if (!this.props.roomId) return [];
|
if (!this.props.roomId) return [];
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const room = cli.getRoom(this.props.roomId);
|
const room = cli.getRoom(this.props.roomId);
|
||||||
|
@ -228,9 +223,9 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
|
|
||||||
return allMembers;
|
return allMembers;
|
||||||
},
|
}
|
||||||
|
|
||||||
roomMembers: function() {
|
roomMembers() {
|
||||||
const ConferenceHandler = CallHandler.getConferenceHandler();
|
const ConferenceHandler = CallHandler.getConferenceHandler();
|
||||||
|
|
||||||
const allMembers = this.getMembersWithUser();
|
const allMembers = this.getMembersWithUser();
|
||||||
|
@ -244,17 +239,17 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
filteredAndSortedMembers.sort(this.memberSort);
|
filteredAndSortedMembers.sort(this.memberSort);
|
||||||
return filteredAndSortedMembers;
|
return filteredAndSortedMembers;
|
||||||
},
|
}
|
||||||
|
|
||||||
_createOverflowTileJoined: function(overflowCount, totalCount) {
|
_createOverflowTileJoined(overflowCount, totalCount) {
|
||||||
return this._createOverflowTile(overflowCount, totalCount, this._showMoreJoinedMemberList);
|
return this._createOverflowTile(overflowCount, totalCount, this._showMoreJoinedMemberList);
|
||||||
},
|
}
|
||||||
|
|
||||||
_createOverflowTileInvited: function(overflowCount, totalCount) {
|
_createOverflowTileInvited(overflowCount, totalCount) {
|
||||||
return this._createOverflowTile(overflowCount, totalCount, this._showMoreInvitedMemberList);
|
return this._createOverflowTile(overflowCount, totalCount, this._showMoreInvitedMemberList);
|
||||||
},
|
}
|
||||||
|
|
||||||
_createOverflowTile: function(overflowCount, totalCount, onClick) {
|
_createOverflowTile(overflowCount, totalCount, onClick) {
|
||||||
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
// For now we'll pretend this is any entity. It should probably be a separate tile.
|
||||||
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
||||||
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
const BaseAvatar = sdk.getComponent("avatars.BaseAvatar");
|
||||||
|
@ -265,33 +260,33 @@ export default createReactClass({
|
||||||
} name={text} presenceState="online" suppressOnHover={true}
|
} name={text} presenceState="online" suppressOnHover={true}
|
||||||
onClick={onClick} />
|
onClick={onClick} />
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
_showMoreJoinedMemberList: function() {
|
_showMoreJoinedMemberList = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
truncateAtJoined: this.state.truncateAtJoined + SHOW_MORE_INCREMENT,
|
truncateAtJoined: this.state.truncateAtJoined + SHOW_MORE_INCREMENT,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_showMoreInvitedMemberList: function() {
|
_showMoreInvitedMemberList = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
truncateAtInvited: this.state.truncateAtInvited + SHOW_MORE_INCREMENT,
|
truncateAtInvited: this.state.truncateAtInvited + SHOW_MORE_INCREMENT,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
memberString: function(member) {
|
memberString(member) {
|
||||||
if (!member) {
|
if (!member) {
|
||||||
return "(null)";
|
return "(null)";
|
||||||
} else {
|
} else {
|
||||||
const u = member.user;
|
const u = member.user;
|
||||||
return "(" + member.name + ", " + member.powerLevel + ", " + (u ? u.lastActiveAgo : "<null>") + ", " + (u ? u.getLastActiveTs() : "<null>") + ", " + (u ? u.currentlyActive : "<null>") + ", " + (u ? u.presence : "<null>") + ")";
|
return "(" + member.name + ", " + member.powerLevel + ", " + (u ? u.lastActiveAgo : "<null>") + ", " + (u ? u.getLastActiveTs() : "<null>") + ", " + (u ? u.currentlyActive : "<null>") + ", " + (u ? u.presence : "<null>") + ")";
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// returns negative if a comes before b,
|
// returns negative if a comes before b,
|
||||||
// returns 0 if a and b are equivalent in ordering
|
// returns 0 if a and b are equivalent in ordering
|
||||||
// returns positive if a comes after b.
|
// returns positive if a comes after b.
|
||||||
memberSort: function(memberA, memberB) {
|
memberSort = (memberA, memberB) => {
|
||||||
// order by presence, with "active now" first.
|
// order by presence, with "active now" first.
|
||||||
// ...and then by power level
|
// ...and then by power level
|
||||||
// ...and then by last active
|
// ...and then by last active
|
||||||
|
@ -348,24 +343,24 @@ export default createReactClass({
|
||||||
ignorePunctuation: true,
|
ignorePunctuation: true,
|
||||||
sensitivity: "base",
|
sensitivity: "base",
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
onSearchQueryChanged: function(searchQuery) {
|
onSearchQueryChanged = searchQuery => {
|
||||||
this.setState({
|
this.setState({
|
||||||
searchQuery,
|
searchQuery,
|
||||||
filteredJoinedMembers: this._filterMembers(this.state.members, 'join', searchQuery),
|
filteredJoinedMembers: this._filterMembers(this.state.members, 'join', searchQuery),
|
||||||
filteredInvitedMembers: this._filterMembers(this.state.members, 'invite', searchQuery),
|
filteredInvitedMembers: this._filterMembers(this.state.members, 'invite', searchQuery),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_onPending3pidInviteClick: function(inviteEvent) {
|
_onPending3pidInviteClick = inviteEvent => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_3pid_invite',
|
action: 'view_3pid_invite',
|
||||||
event: inviteEvent,
|
event: inviteEvent,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_filterMembers: function(members, membership, query) {
|
_filterMembers(members, membership, query) {
|
||||||
return members.filter((m) => {
|
return members.filter((m) => {
|
||||||
if (query) {
|
if (query) {
|
||||||
query = query.toLowerCase();
|
query = query.toLowerCase();
|
||||||
|
@ -379,9 +374,9 @@ export default createReactClass({
|
||||||
|
|
||||||
return m.membership === membership;
|
return m.membership === membership;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_getPending3PidInvites: function() {
|
_getPending3PidInvites() {
|
||||||
// include 3pid invites (m.room.third_party_invite) state events.
|
// include 3pid invites (m.room.third_party_invite) state events.
|
||||||
// The HS may have already converted these into m.room.member invites so
|
// The HS may have already converted these into m.room.member invites so
|
||||||
// we shouldn't add them if the 3pid invite state key (token) is in the
|
// we shouldn't add them if the 3pid invite state key (token) is in the
|
||||||
|
@ -399,9 +394,9 @@ export default createReactClass({
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_makeMemberTiles: function(members) {
|
_makeMemberTiles(members) {
|
||||||
const MemberTile = sdk.getComponent("rooms.MemberTile");
|
const MemberTile = sdk.getComponent("rooms.MemberTile");
|
||||||
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
const EntityTile = sdk.getComponent("rooms.EntityTile");
|
||||||
|
|
||||||
|
@ -415,30 +410,30 @@ export default createReactClass({
|
||||||
onClick={() => this._onPending3pidInviteClick(m)} />;
|
onClick={() => this._onPending3pidInviteClick(m)} />;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
_getChildrenJoined: function(start, end) {
|
_getChildrenJoined(start, end) {
|
||||||
return this._makeMemberTiles(this.state.filteredJoinedMembers.slice(start, end));
|
return this._makeMemberTiles(this.state.filteredJoinedMembers.slice(start, end));
|
||||||
},
|
}
|
||||||
|
|
||||||
_getChildCountJoined: function() {
|
_getChildCountJoined() {
|
||||||
return this.state.filteredJoinedMembers.length;
|
return this.state.filteredJoinedMembers.length;
|
||||||
},
|
}
|
||||||
|
|
||||||
_getChildrenInvited: function(start, end) {
|
_getChildrenInvited(start, end) {
|
||||||
let targets = this.state.filteredInvitedMembers;
|
let targets = this.state.filteredInvitedMembers;
|
||||||
if (end > this.state.filteredInvitedMembers.length) {
|
if (end > this.state.filteredInvitedMembers.length) {
|
||||||
targets = targets.concat(this._getPending3PidInvites());
|
targets = targets.concat(this._getPending3PidInvites());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._makeMemberTiles(targets.slice(start, end));
|
return this._makeMemberTiles(targets.slice(start, end));
|
||||||
},
|
}
|
||||||
|
|
||||||
_getChildCountInvited: function() {
|
_getChildCountInvited() {
|
||||||
return this.state.filteredInvitedMembers.length + (this._getPending3PidInvites() || []).length;
|
return this.state.filteredInvitedMembers.length + (this._getPending3PidInvites() || []).length;
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
if (this.state.loading) {
|
if (this.state.loading) {
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
return <div className="mx_MemberList"><Spinner /></div>;
|
return <div className="mx_MemberList"><Spinner /></div>;
|
||||||
|
@ -501,9 +496,9 @@ export default createReactClass({
|
||||||
onSearch={ this.onSearchQueryChanged } />
|
onSearch={ this.onSearchQueryChanged } />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
onInviteButtonClick: function() {
|
onInviteButtonClick = () => {
|
||||||
if (MatrixClientPeg.get().isGuest()) {
|
if (MatrixClientPeg.get().isGuest()) {
|
||||||
dis.dispatch({action: 'require_registration'});
|
dis.dispatch({action: 'require_registration'});
|
||||||
return;
|
return;
|
||||||
|
@ -514,5 +509,5 @@ export default createReactClass({
|
||||||
action: 'view_invite',
|
action: 'view_invite',
|
||||||
roomId: this.props.roomId,
|
roomId: this.props.roomId,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,34 +18,31 @@ limitations under the License.
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import {Action} from "../../../dispatcher/actions";
|
import {Action} from "../../../dispatcher/actions";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class MemberTile extends React.Component {
|
||||||
displayName: 'MemberTile',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
member: PropTypes.any.isRequired, // RoomMember
|
member: PropTypes.any.isRequired, // RoomMember
|
||||||
showPresence: PropTypes.bool,
|
showPresence: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
showPresence: true,
|
||||||
showPresence: true,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
return {
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
statusMessage: this.getStatusMessage(),
|
statusMessage: this.getStatusMessage(),
|
||||||
isRoomEncrypted: false,
|
isRoomEncrypted: false,
|
||||||
e2eStatus: null,
|
e2eStatus: null,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
@ -72,7 +69,7 @@ export default createReactClass({
|
||||||
cli.on("RoomState.events", this.onRoomStateEvents);
|
cli.on("RoomState.events", this.onRoomStateEvents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
@ -90,9 +87,9 @@ export default createReactClass({
|
||||||
cli.removeListener("userTrustStatusChanged", this.onUserTrustStatusChanged);
|
cli.removeListener("userTrustStatusChanged", this.onUserTrustStatusChanged);
|
||||||
cli.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
cli.removeListener("deviceVerificationChanged", this.onDeviceVerificationChanged);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onRoomStateEvents: function(ev) {
|
onRoomStateEvents = ev => {
|
||||||
if (ev.getType() !== "m.room.encryption") return;
|
if (ev.getType() !== "m.room.encryption") return;
|
||||||
const { roomId } = this.props.member;
|
const { roomId } = this.props.member;
|
||||||
if (ev.getRoomId() !== roomId) return;
|
if (ev.getRoomId() !== roomId) return;
|
||||||
|
@ -104,19 +101,19 @@ export default createReactClass({
|
||||||
isRoomEncrypted: true,
|
isRoomEncrypted: true,
|
||||||
});
|
});
|
||||||
this.updateE2EStatus();
|
this.updateE2EStatus();
|
||||||
},
|
};
|
||||||
|
|
||||||
onUserTrustStatusChanged: function(userId, trustStatus) {
|
onUserTrustStatusChanged = (userId, trustStatus) => {
|
||||||
if (userId !== this.props.member.userId) return;
|
if (userId !== this.props.member.userId) return;
|
||||||
this.updateE2EStatus();
|
this.updateE2EStatus();
|
||||||
},
|
};
|
||||||
|
|
||||||
onDeviceVerificationChanged: function(userId, deviceId, deviceInfo) {
|
onDeviceVerificationChanged = (userId, deviceId, deviceInfo) => {
|
||||||
if (userId !== this.props.member.userId) return;
|
if (userId !== this.props.member.userId) return;
|
||||||
this.updateE2EStatus();
|
this.updateE2EStatus();
|
||||||
},
|
};
|
||||||
|
|
||||||
updateE2EStatus: async function() {
|
async updateE2EStatus() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
const { userId } = this.props.member;
|
const { userId } = this.props.member;
|
||||||
const isMe = userId === cli.getUserId();
|
const isMe = userId === cli.getUserId();
|
||||||
|
@ -142,7 +139,7 @@ export default createReactClass({
|
||||||
this.setState({
|
this.setState({
|
||||||
e2eStatus: anyDeviceUnverified ? "warning" : "verified",
|
e2eStatus: anyDeviceUnverified ? "warning" : "verified",
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
getStatusMessage() {
|
getStatusMessage() {
|
||||||
const { user } = this.props.member;
|
const { user } = this.props.member;
|
||||||
|
@ -150,16 +147,16 @@ export default createReactClass({
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return user._unstable_statusMessage;
|
return user._unstable_statusMessage;
|
||||||
},
|
}
|
||||||
|
|
||||||
_onStatusMessageCommitted() {
|
_onStatusMessageCommitted = () => {
|
||||||
// The `User` object has observed a status message change.
|
// The `User` object has observed a status message change.
|
||||||
this.setState({
|
this.setState({
|
||||||
statusMessage: this.getStatusMessage(),
|
statusMessage: this.getStatusMessage(),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
shouldComponentUpdate: function(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
if (
|
if (
|
||||||
this.member_last_modified_time === undefined ||
|
this.member_last_modified_time === undefined ||
|
||||||
this.member_last_modified_time < nextProps.member.getLastModifiedTime()
|
this.member_last_modified_time < nextProps.member.getLastModifiedTime()
|
||||||
|
@ -180,27 +177,27 @@ export default createReactClass({
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
}
|
||||||
|
|
||||||
onClick: function(e) {
|
onClick = e => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: Action.ViewUser,
|
action: Action.ViewUser,
|
||||||
member: this.props.member,
|
member: this.props.member,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_getDisplayName: function() {
|
_getDisplayName() {
|
||||||
return this.props.member.name;
|
return this.props.member.name;
|
||||||
},
|
}
|
||||||
|
|
||||||
getPowerLabel: function() {
|
getPowerLabel() {
|
||||||
return _t("%(userName)s (power %(powerLevelNumber)s)", {
|
return _t("%(userName)s (power %(powerLevelNumber)s)", {
|
||||||
userName: this.props.member.userId,
|
userName: this.props.member.userId,
|
||||||
powerLevelNumber: this.props.member.powerLevel,
|
powerLevelNumber: this.props.member.powerLevel,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||||
const EntityTile = sdk.getComponent('rooms.EntityTile');
|
const EntityTile = sdk.getComponent('rooms.EntityTile');
|
||||||
|
|
||||||
|
@ -260,5 +257,5 @@ export default createReactClass({
|
||||||
onClick={this.onClick}
|
onClick={this.onClick}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
@ -25,22 +24,23 @@ import MemberAvatar from "../avatars/MemberAvatar";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {formatFullDate} from '../../../DateUtils';
|
import {formatFullDate} from '../../../DateUtils';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class PinnedEventTile extends React.Component {
|
||||||
displayName: 'PinnedEventTile',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
mxRoom: PropTypes.object.isRequired,
|
mxRoom: PropTypes.object.isRequired,
|
||||||
mxEvent: PropTypes.object.isRequired,
|
mxEvent: PropTypes.object.isRequired,
|
||||||
onUnpinned: PropTypes.func,
|
onUnpinned: PropTypes.func,
|
||||||
},
|
};
|
||||||
onTileClicked: function() {
|
|
||||||
|
onTileClicked = () => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
event_id: this.props.mxEvent.getId(),
|
event_id: this.props.mxEvent.getId(),
|
||||||
highlighted: true,
|
highlighted: true,
|
||||||
room_id: this.props.mxEvent.getRoomId(),
|
room_id: this.props.mxEvent.getRoomId(),
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
onUnpinClicked: function() {
|
|
||||||
|
onUnpinClicked = () => {
|
||||||
const pinnedEvents = this.props.mxRoom.currentState.getStateEvents("m.room.pinned_events", "");
|
const pinnedEvents = this.props.mxRoom.currentState.getStateEvents("m.room.pinned_events", "");
|
||||||
if (!pinnedEvents || !pinnedEvents.getContent().pinned) {
|
if (!pinnedEvents || !pinnedEvents.getContent().pinned) {
|
||||||
// Nothing to do: already unpinned
|
// Nothing to do: already unpinned
|
||||||
|
@ -56,11 +56,13 @@ export default createReactClass({
|
||||||
});
|
});
|
||||||
} else if (this.props.onUnpinned) this.props.onUnpinned();
|
} else if (this.props.onUnpinned) this.props.onUnpinned();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
_canUnpin: function() {
|
|
||||||
|
_canUnpin() {
|
||||||
return this.props.mxRoom.currentState.mayClientSendStateEvent('m.room.pinned_events', MatrixClientPeg.get());
|
return this.props.mxRoom.currentState.mayClientSendStateEvent('m.room.pinned_events', MatrixClientPeg.get());
|
||||||
},
|
}
|
||||||
render: function() {
|
|
||||||
|
render() {
|
||||||
const sender = this.props.mxEvent.getSender();
|
const sender = this.props.mxEvent.getSender();
|
||||||
// Get the latest sender profile rather than historical
|
// Get the latest sender profile rather than historical
|
||||||
const senderProfile = this.props.mxRoom.getMember(sender);
|
const senderProfile = this.props.mxRoom.getMember(sender);
|
||||||
|
@ -100,5 +102,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,46 +17,42 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import PinnedEventTile from "./PinnedEventTile";
|
import PinnedEventTile from "./PinnedEventTile";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import PinningUtils from "../../../utils/PinningUtils";
|
import PinningUtils from "../../../utils/PinningUtils";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class PinnedEventsPanel extends React.Component {
|
||||||
displayName: 'PinnedEventsPanel',
|
static propTypes = {
|
||||||
propTypes: {
|
|
||||||
// The Room from the js-sdk we're going to show pinned events for
|
// The Room from the js-sdk we're going to show pinned events for
|
||||||
room: PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
|
|
||||||
onCancelClick: PropTypes.func,
|
onCancelClick: PropTypes.func,
|
||||||
},
|
};
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
loading: true,
|
||||||
loading: true,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this._updatePinnedMessages();
|
this._updatePinnedMessages();
|
||||||
MatrixClientPeg.get().on("RoomState.events", this._onStateEvent);
|
MatrixClientPeg.get().on("RoomState.events", this._onStateEvent);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
if (MatrixClientPeg.get()) {
|
if (MatrixClientPeg.get()) {
|
||||||
MatrixClientPeg.get().removeListener("RoomState.events", this._onStateEvent);
|
MatrixClientPeg.get().removeListener("RoomState.events", this._onStateEvent);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_onStateEvent: function(ev) {
|
_onStateEvent = ev => {
|
||||||
if (ev.getRoomId() === this.props.room.roomId && ev.getType() === "m.room.pinned_events") {
|
if (ev.getRoomId() === this.props.room.roomId && ev.getType() === "m.room.pinned_events") {
|
||||||
this._updatePinnedMessages();
|
this._updatePinnedMessages();
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_updatePinnedMessages: function() {
|
_updatePinnedMessages = () => {
|
||||||
const pinnedEvents = this.props.room.currentState.getStateEvents("m.room.pinned_events", "");
|
const pinnedEvents = this.props.room.currentState.getStateEvents("m.room.pinned_events", "");
|
||||||
if (!pinnedEvents || !pinnedEvents.getContent().pinned) {
|
if (!pinnedEvents || !pinnedEvents.getContent().pinned) {
|
||||||
this.setState({ loading: false, pinned: [] });
|
this.setState({ loading: false, pinned: [] });
|
||||||
|
@ -85,9 +81,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
this._updateReadState();
|
this._updateReadState();
|
||||||
},
|
};
|
||||||
|
|
||||||
_updateReadState: function() {
|
_updateReadState() {
|
||||||
const pinnedEvents = this.props.room.currentState.getStateEvents("m.room.pinned_events", "");
|
const pinnedEvents = this.props.room.currentState.getStateEvents("m.room.pinned_events", "");
|
||||||
if (!pinnedEvents) return; // nothing to read
|
if (!pinnedEvents) return; // nothing to read
|
||||||
|
|
||||||
|
@ -107,9 +103,9 @@ export default createReactClass({
|
||||||
event_ids: readStateEvents,
|
event_ids: readStateEvents,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_getPinnedTiles: function() {
|
_getPinnedTiles() {
|
||||||
if (this.state.pinned.length === 0) {
|
if (this.state.pinned.length === 0) {
|
||||||
return (<div>{ _t("No pinned messages.") }</div>);
|
return (<div>{ _t("No pinned messages.") }</div>);
|
||||||
}
|
}
|
||||||
|
@ -120,9 +116,9 @@ export default createReactClass({
|
||||||
mxEvent={context.event}
|
mxEvent={context.event}
|
||||||
onUnpinned={this._updatePinnedMessages} />);
|
onUnpinned={this._updatePinnedMessages} />);
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let tiles = <div>{ _t("Loading...") }</div>;
|
let tiles = <div>{ _t("Loading...") }</div>;
|
||||||
if (this.state && !this.state.loading) {
|
if (this.state && !this.state.loading) {
|
||||||
tiles = this._getPinnedTiles();
|
tiles = this._getPinnedTiles();
|
||||||
|
@ -139,5 +135,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,15 +16,12 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
|
|
||||||
export default createReactClass({
|
export default class PresenceLabel extends React.Component {
|
||||||
displayName: 'PresenceLabel',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// number of milliseconds ago this user was last active.
|
// number of milliseconds ago this user was last active.
|
||||||
// zero = unknown
|
// zero = unknown
|
||||||
activeAgo: PropTypes.number,
|
activeAgo: PropTypes.number,
|
||||||
|
@ -35,18 +32,16 @@ export default createReactClass({
|
||||||
|
|
||||||
// offline, online, etc
|
// offline, online, etc
|
||||||
presenceState: PropTypes.string,
|
presenceState: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
activeAgo: -1,
|
||||||
ago: -1,
|
presenceState: null,
|
||||||
presenceState: null,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// Return duration as a string using appropriate time units
|
// Return duration as a string using appropriate time units
|
||||||
// XXX: This would be better handled using a culture-aware library, but we don't use one yet.
|
// XXX: This would be better handled using a culture-aware library, but we don't use one yet.
|
||||||
getDuration: function(time) {
|
getDuration(time) {
|
||||||
if (!time) return;
|
if (!time) return;
|
||||||
const t = parseInt(time / 1000);
|
const t = parseInt(time / 1000);
|
||||||
const s = t % 60;
|
const s = t % 60;
|
||||||
|
@ -66,9 +61,9 @@ export default createReactClass({
|
||||||
return _t("%(duration)sh", {duration: h});
|
return _t("%(duration)sh", {duration: h});
|
||||||
}
|
}
|
||||||
return _t("%(duration)sd", {duration: d});
|
return _t("%(duration)sd", {duration: d});
|
||||||
},
|
}
|
||||||
|
|
||||||
getPrettyPresence: function(presence, activeAgo, currentlyActive) {
|
getPrettyPresence(presence, activeAgo, currentlyActive) {
|
||||||
if (!currentlyActive && activeAgo !== undefined && activeAgo > 0) {
|
if (!currentlyActive && activeAgo !== undefined && activeAgo > 0) {
|
||||||
const duration = this.getDuration(activeAgo);
|
const duration = this.getDuration(activeAgo);
|
||||||
if (presence === "online") return _t("Online for %(duration)s", { duration: duration });
|
if (presence === "online") return _t("Online for %(duration)s", { duration: duration });
|
||||||
|
@ -81,13 +76,13 @@ export default createReactClass({
|
||||||
if (presence === "offline") return _t("Offline");
|
if (presence === "offline") return _t("Offline");
|
||||||
return _t("Unknown");
|
return _t("Unknown");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="mx_PresenceLabel">
|
<div className="mx_PresenceLabel">
|
||||||
{ this.getPrettyPresence(this.props.presenceState, this.props.activeAgo, this.props.currentlyActive) }
|
{ this.getPrettyPresence(this.props.presenceState, this.props.activeAgo, this.props.currentlyActive) }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import '../../../VelocityBounce';
|
import '../../../VelocityBounce';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {formatDate} from '../../../DateUtils';
|
import {formatDate} from '../../../DateUtils';
|
||||||
|
@ -33,10 +32,8 @@ try {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createReactClass({
|
export default class ReadReceiptMarker extends React.Component {
|
||||||
displayName: 'ReadReceiptMarker',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// the RoomMember to show the RR for
|
// the RoomMember to show the RR for
|
||||||
member: PropTypes.object,
|
member: PropTypes.object,
|
||||||
// userId to fallback the avatar to
|
// userId to fallback the avatar to
|
||||||
|
@ -70,30 +67,27 @@ export default createReactClass({
|
||||||
|
|
||||||
// True to show twelve hour format, false otherwise
|
// True to show twelve hour format, false otherwise
|
||||||
showTwelveHour: PropTypes.bool,
|
showTwelveHour: PropTypes.bool,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
leftOffset: 0,
|
||||||
leftOffset: 0,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
constructor(props) {
|
||||||
// if we are going to animate the RR, we don't show it on first render,
|
super(props);
|
||||||
// and instead just add a placeholder to the DOM; once we've been
|
|
||||||
// mounted, we start an animation which moves the RR from its old
|
this._avatar = createRef();
|
||||||
// position.
|
|
||||||
return {
|
this.state = {
|
||||||
|
// if we are going to animate the RR, we don't show it on first render,
|
||||||
|
// and instead just add a placeholder to the DOM; once we've been
|
||||||
|
// mounted, we start an animation which moves the RR from its old
|
||||||
|
// position.
|
||||||
suppressDisplay: !this.props.suppressAnimation,
|
suppressDisplay: !this.props.suppressAnimation,
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
componentWillUnmount() {
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._avatar = createRef();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
// before we remove the rr, store its location in the map, so that if
|
// before we remove the rr, store its location in the map, so that if
|
||||||
// it reappears, it can be animated from the right place.
|
// it reappears, it can be animated from the right place.
|
||||||
const rrInfo = this.props.readReceiptInfo;
|
const rrInfo = this.props.readReceiptInfo;
|
||||||
|
@ -112,9 +106,9 @@ export default createReactClass({
|
||||||
rrInfo.top = avatarNode.offsetTop;
|
rrInfo.top = avatarNode.offsetTop;
|
||||||
rrInfo.left = avatarNode.offsetLeft;
|
rrInfo.left = avatarNode.offsetLeft;
|
||||||
rrInfo.parent = avatarNode.offsetParent;
|
rrInfo.parent = avatarNode.offsetParent;
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
if (!this.state.suppressDisplay) {
|
if (!this.state.suppressDisplay) {
|
||||||
// we've already done our display - nothing more to do.
|
// we've already done our display - nothing more to do.
|
||||||
return;
|
return;
|
||||||
|
@ -172,10 +166,9 @@ export default createReactClass({
|
||||||
startStyles: startStyles,
|
startStyles: startStyles,
|
||||||
enterTransitionOpts: enterTransitionOpts,
|
enterTransitionOpts: enterTransitionOpts,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
render: function() {
|
|
||||||
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
const MemberAvatar = sdk.getComponent('avatars.MemberAvatar');
|
||||||
if (this.state.suppressDisplay) {
|
if (this.state.suppressDisplay) {
|
||||||
return <div ref={this._avatar} />;
|
return <div ref={this._avatar} />;
|
||||||
|
@ -222,5 +215,5 @@ export default createReactClass({
|
||||||
/>
|
/>
|
||||||
</Velociraptor>
|
</Velociraptor>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -19,35 +19,32 @@ import dis from '../../../dispatcher/dispatcher';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import {roomShape} from './RoomDetailRow';
|
import {roomShape} from './RoomDetailRow';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomDetailList extends React.Component {
|
||||||
displayName: 'RoomDetailList',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
rooms: PropTypes.arrayOf(roomShape),
|
rooms: PropTypes.arrayOf(roomShape),
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
getRows: function() {
|
getRows() {
|
||||||
if (!this.props.rooms) return [];
|
if (!this.props.rooms) return [];
|
||||||
|
|
||||||
const RoomDetailRow = sdk.getComponent('rooms.RoomDetailRow');
|
const RoomDetailRow = sdk.getComponent('rooms.RoomDetailRow');
|
||||||
return this.props.rooms.map((room, index) => {
|
return this.props.rooms.map((room, index) => {
|
||||||
return <RoomDetailRow key={index} room={room} onClick={this.onDetailsClick} />;
|
return <RoomDetailRow key={index} room={room} onClick={this.onDetailsClick} />;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
onDetailsClick: function(ev, room) {
|
onDetailsClick = (ev, room) => {
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_room',
|
action: 'view_room',
|
||||||
room_id: room.roomId,
|
room_id: room.roomId,
|
||||||
room_alias: room.canonicalAlias || (room.aliases || [])[0],
|
room_alias: room.canonicalAlias || (room.aliases || [])[0],
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const rows = this.getRows();
|
const rows = this.getRows();
|
||||||
|
@ -64,5 +61,5 @@ export default createReactClass({
|
||||||
return <div className={classNames("mx_RoomDetailList", this.props.className)}>
|
return <div className={classNames("mx_RoomDetailList", this.props.className)}>
|
||||||
{ rooms }
|
{ rooms }
|
||||||
</div>;
|
</div>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import { _t } from '../../../languageHandler';
|
||||||
import { linkifyElement } from '../../../HtmlUtils';
|
import { linkifyElement } from '../../../HtmlUtils';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
|
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
|
||||||
|
|
||||||
export function getDisplayAliasForRoom(room) {
|
export function getDisplayAliasForRoom(room) {
|
||||||
|
@ -40,47 +39,48 @@ export const roomShape = PropTypes.shape({
|
||||||
guestCanJoin: PropTypes.bool,
|
guestCanJoin: PropTypes.bool,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomDetailRow extends React.Component {
|
||||||
propTypes: {
|
static propTypes = {
|
||||||
room: roomShape,
|
room: roomShape,
|
||||||
// passes ev, room as args
|
// passes ev, room as args
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
onMouseDown: PropTypes.func,
|
onMouseDown: PropTypes.func,
|
||||||
},
|
};
|
||||||
|
|
||||||
_linkifyTopic: function() {
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this._topic = createRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._linkifyTopic();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this._linkifyTopic();
|
||||||
|
}
|
||||||
|
|
||||||
|
_linkifyTopic() {
|
||||||
if (this._topic.current) {
|
if (this._topic.current) {
|
||||||
linkifyElement(this._topic.current);
|
linkifyElement(this._topic.current);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
onClick = (ev) => {
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._topic = createRef();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this._linkifyTopic();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidUpdate: function() {
|
|
||||||
this._linkifyTopic();
|
|
||||||
},
|
|
||||||
|
|
||||||
onClick: function(ev) {
|
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
if (this.props.onClick) {
|
if (this.props.onClick) {
|
||||||
this.props.onClick(ev, this.props.room);
|
this.props.onClick(ev, this.props.room);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onTopicClick: function(ev) {
|
onTopicClick = (ev) => {
|
||||||
// When clicking a link in the topic, prevent the event being propagated
|
// When clicking a link in the topic, prevent the event being propagated
|
||||||
// to `onClick`.
|
// to `onClick`.
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
const BaseAvatar = sdk.getComponent('avatars.BaseAvatar');
|
||||||
|
|
||||||
const room = this.props.room;
|
const room = this.props.room;
|
||||||
|
@ -118,5 +118,5 @@ export default createReactClass({
|
||||||
{ room.numJoinedMembers }
|
{ room.numJoinedMembers }
|
||||||
</td>
|
</td>
|
||||||
</tr>;
|
</tr>;
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -35,10 +34,8 @@ import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
||||||
import {DefaultTagID} from "../../../stores/room-list/models";
|
import {DefaultTagID} from "../../../stores/room-list/models";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomHeader extends React.Component {
|
||||||
displayName: 'RoomHeader',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
room: PropTypes.object,
|
room: PropTypes.object,
|
||||||
oobData: PropTypes.object,
|
oobData: PropTypes.object,
|
||||||
inRoom: PropTypes.bool,
|
inRoom: PropTypes.bool,
|
||||||
|
@ -48,22 +45,21 @@ export default createReactClass({
|
||||||
onLeaveClick: PropTypes.func,
|
onLeaveClick: PropTypes.func,
|
||||||
onCancelClick: PropTypes.func,
|
onCancelClick: PropTypes.func,
|
||||||
e2eStatus: PropTypes.string,
|
e2eStatus: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
editing: false,
|
||||||
editing: false,
|
inRoom: false,
|
||||||
inRoom: false,
|
onCancelClick: null,
|
||||||
onCancelClick: null,
|
};
|
||||||
};
|
|
||||||
},
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._topic = createRef();
|
this._topic = createRef();
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
cli.on("RoomState.events", this._onRoomStateEvents);
|
cli.on("RoomState.events", this._onRoomStateEvents);
|
||||||
cli.on("Room.accountData", this._onRoomAccountData);
|
cli.on("Room.accountData", this._onRoomAccountData);
|
||||||
|
@ -74,15 +70,15 @@ export default createReactClass({
|
||||||
if (this.props.room) {
|
if (this.props.room) {
|
||||||
this.props.room.on("Room.name", this._onRoomNameChange);
|
this.props.room.on("Room.name", this._onRoomNameChange);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate() {
|
||||||
if (this._topic.current) {
|
if (this._topic.current) {
|
||||||
linkifyElement(this._topic.current);
|
linkifyElement(this._topic.current);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
if (this.props.room) {
|
if (this.props.room) {
|
||||||
this.props.room.removeListener("Room.name", this._onRoomNameChange);
|
this.props.room.removeListener("Room.name", this._onRoomNameChange);
|
||||||
}
|
}
|
||||||
|
@ -91,41 +87,41 @@ export default createReactClass({
|
||||||
cli.removeListener("RoomState.events", this._onRoomStateEvents);
|
cli.removeListener("RoomState.events", this._onRoomStateEvents);
|
||||||
cli.removeListener("Room.accountData", this._onRoomAccountData);
|
cli.removeListener("Room.accountData", this._onRoomAccountData);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_onRoomStateEvents: function(event, state) {
|
_onRoomStateEvents = (event, state) => {
|
||||||
if (!this.props.room || event.getRoomId() !== this.props.room.roomId) {
|
if (!this.props.room || event.getRoomId() !== this.props.room.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// redisplay the room name, topic, etc.
|
// redisplay the room name, topic, etc.
|
||||||
this._rateLimitedUpdate();
|
this._rateLimitedUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
_onRoomAccountData: function(event, room) {
|
_onRoomAccountData = (event, room) => {
|
||||||
if (!this.props.room || room.roomId !== this.props.room.roomId) return;
|
if (!this.props.room || room.roomId !== this.props.room.roomId) return;
|
||||||
if (event.getType() !== "im.vector.room.read_pins") return;
|
if (event.getType() !== "im.vector.room.read_pins") return;
|
||||||
|
|
||||||
this._rateLimitedUpdate();
|
this._rateLimitedUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
_rateLimitedUpdate: new RateLimitedFunc(function() {
|
_rateLimitedUpdate = new RateLimitedFunc(function() {
|
||||||
/* eslint-disable babel/no-invalid-this */
|
/* eslint-disable babel/no-invalid-this */
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}, 500),
|
}, 500);
|
||||||
|
|
||||||
_onRoomNameChange: function(room) {
|
_onRoomNameChange = (room) => {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
},
|
};
|
||||||
|
|
||||||
onShareRoomClick: function(ev) {
|
onShareRoomClick = (ev) => {
|
||||||
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
const ShareDialog = sdk.getComponent("dialogs.ShareDialog");
|
||||||
Modal.createTrackedDialog('share room dialog', '', ShareDialog, {
|
Modal.createTrackedDialog('share room dialog', '', ShareDialog, {
|
||||||
target: this.props.room,
|
target: this.props.room,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_hasUnreadPins: function() {
|
_hasUnreadPins() {
|
||||||
const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
|
const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
|
||||||
if (!currentPinEvent) return false;
|
if (!currentPinEvent) return false;
|
||||||
if (currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0) {
|
if (currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0) {
|
||||||
|
@ -142,16 +138,16 @@ export default createReactClass({
|
||||||
|
|
||||||
// There's pins, and we haven't read any of them
|
// There's pins, and we haven't read any of them
|
||||||
return true;
|
return true;
|
||||||
},
|
}
|
||||||
|
|
||||||
_hasPins: function() {
|
_hasPins() {
|
||||||
const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
|
const currentPinEvent = this.props.room.currentState.getStateEvents("m.room.pinned_events", '');
|
||||||
if (!currentPinEvent) return false;
|
if (!currentPinEvent) return false;
|
||||||
|
|
||||||
return !(currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0);
|
return !(currentPinEvent.getContent().pinned && currentPinEvent.getContent().pinned.length <= 0);
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let searchStatus = null;
|
let searchStatus = null;
|
||||||
let cancelButton = null;
|
let cancelButton = null;
|
||||||
let settingsButton = null;
|
let settingsButton = null;
|
||||||
|
@ -301,5 +297,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
|
@ -46,10 +45,8 @@ const MessageCase = Object.freeze({
|
||||||
OtherError: "OtherError",
|
OtherError: "OtherError",
|
||||||
});
|
});
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomPreviewBar extends React.Component {
|
||||||
displayName: 'RoomPreviewBar',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
onJoinClick: PropTypes.func,
|
onJoinClick: PropTypes.func,
|
||||||
onRejectClick: PropTypes.func,
|
onRejectClick: PropTypes.func,
|
||||||
onRejectAndIgnoreClick: PropTypes.func,
|
onRejectAndIgnoreClick: PropTypes.func,
|
||||||
|
@ -86,36 +83,32 @@ export default createReactClass({
|
||||||
// If given, this will be how the room is referred to (eg.
|
// If given, this will be how the room is referred to (eg.
|
||||||
// in error messages).
|
// in error messages).
|
||||||
roomAlias: PropTypes.string,
|
roomAlias: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
onJoinClick() {},
|
||||||
onJoinClick: function() {},
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
busy: false,
|
||||||
busy: false,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
this._checkInvitedEmail();
|
this._checkInvitedEmail();
|
||||||
CommunityPrototypeStore.instance.on(UPDATE_EVENT, this._onCommunityUpdate);
|
CommunityPrototypeStore.instance.on(UPDATE_EVENT, this._onCommunityUpdate);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function(prevProps, prevState) {
|
componentDidUpdate(prevProps, prevState) {
|
||||||
if (this.props.invitedEmail !== prevProps.invitedEmail || this.props.inviterName !== prevProps.inviterName) {
|
if (this.props.invitedEmail !== prevProps.invitedEmail || this.props.inviterName !== prevProps.inviterName) {
|
||||||
this._checkInvitedEmail();
|
this._checkInvitedEmail();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
CommunityPrototypeStore.instance.off(UPDATE_EVENT, this._onCommunityUpdate);
|
CommunityPrototypeStore.instance.off(UPDATE_EVENT, this._onCommunityUpdate);
|
||||||
},
|
}
|
||||||
|
|
||||||
_checkInvitedEmail: async function() {
|
async _checkInvitedEmail() {
|
||||||
// If this is an invite and we've been told what email address was
|
// If this is an invite and we've been told what email address was
|
||||||
// invited, fetch the user's account emails and discovery bindings so we
|
// invited, fetch the user's account emails and discovery bindings so we
|
||||||
// can check them against the email that was invited.
|
// can check them against the email that was invited.
|
||||||
|
@ -148,14 +141,14 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
this.setState({busy: false});
|
this.setState({busy: false});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_onCommunityUpdate: function (roomId) {
|
_onCommunityUpdate = (roomId) => {
|
||||||
if (this.props.room && this.props.room.roomId !== roomId) {
|
if (this.props.room && this.props.room.roomId !== roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.forceUpdate(); // we have nothing to update
|
this.forceUpdate(); // we have nothing to update
|
||||||
},
|
};
|
||||||
|
|
||||||
_getMessageCase() {
|
_getMessageCase() {
|
||||||
const isGuest = MatrixClientPeg.get().isGuest();
|
const isGuest = MatrixClientPeg.get().isGuest();
|
||||||
|
@ -207,7 +200,7 @@ export default createReactClass({
|
||||||
} else {
|
} else {
|
||||||
return MessageCase.ViewingRoom;
|
return MessageCase.ViewingRoom;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_getKickOrBanInfo() {
|
_getKickOrBanInfo() {
|
||||||
const myMember = this._getMyMember();
|
const myMember = this._getMyMember();
|
||||||
|
@ -221,9 +214,9 @@ export default createReactClass({
|
||||||
kickerMember.name : myMember.events.member.getSender();
|
kickerMember.name : myMember.events.member.getSender();
|
||||||
const reason = myMember.events.member.getContent().reason;
|
const reason = myMember.events.member.getContent().reason;
|
||||||
return {memberName, reason};
|
return {memberName, reason};
|
||||||
},
|
}
|
||||||
|
|
||||||
_joinRule: function() {
|
_joinRule() {
|
||||||
const room = this.props.room;
|
const room = this.props.room;
|
||||||
if (room) {
|
if (room) {
|
||||||
const joinRules = room.currentState.getStateEvents('m.room.join_rules', '');
|
const joinRules = room.currentState.getStateEvents('m.room.join_rules', '');
|
||||||
|
@ -231,14 +224,14 @@ export default createReactClass({
|
||||||
return joinRules.getContent().join_rule;
|
return joinRules.getContent().join_rule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_communityProfile: function() {
|
_communityProfile() {
|
||||||
if (this.props.room) return CommunityPrototypeStore.instance.getInviteProfile(this.props.room.roomId);
|
if (this.props.room) return CommunityPrototypeStore.instance.getInviteProfile(this.props.room.roomId);
|
||||||
return {displayName: null, avatarMxc: null};
|
return {displayName: null, avatarMxc: null};
|
||||||
},
|
}
|
||||||
|
|
||||||
_roomName: function(atStart = false) {
|
_roomName(atStart = false) {
|
||||||
let name = this.props.room ? this.props.room.name : this.props.roomAlias;
|
let name = this.props.room ? this.props.room.name : this.props.roomAlias;
|
||||||
const profile = this._communityProfile();
|
const profile = this._communityProfile();
|
||||||
if (profile.displayName) name = profile.displayName;
|
if (profile.displayName) name = profile.displayName;
|
||||||
|
@ -249,16 +242,16 @@ export default createReactClass({
|
||||||
} else {
|
} else {
|
||||||
return _t("this room");
|
return _t("this room");
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_getMyMember() {
|
_getMyMember() {
|
||||||
return (
|
return (
|
||||||
this.props.room &&
|
this.props.room &&
|
||||||
this.props.room.getMember(MatrixClientPeg.get().getUserId())
|
this.props.room.getMember(MatrixClientPeg.get().getUserId())
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
|
|
||||||
_getInviteMember: function() {
|
_getInviteMember() {
|
||||||
const {room} = this.props;
|
const {room} = this.props;
|
||||||
if (!room) {
|
if (!room) {
|
||||||
return;
|
return;
|
||||||
|
@ -270,7 +263,7 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
const inviterUserId = inviteEvent.events.member.getSender();
|
const inviterUserId = inviteEvent.events.member.getSender();
|
||||||
return room.currentState.getMember(inviterUserId);
|
return room.currentState.getMember(inviterUserId);
|
||||||
},
|
}
|
||||||
|
|
||||||
_isDMInvite() {
|
_isDMInvite() {
|
||||||
const myMember = this._getMyMember();
|
const myMember = this._getMyMember();
|
||||||
|
@ -280,7 +273,7 @@ export default createReactClass({
|
||||||
const memberEvent = myMember.events.member;
|
const memberEvent = myMember.events.member;
|
||||||
const memberContent = memberEvent.getContent();
|
const memberContent = memberEvent.getContent();
|
||||||
return memberContent.membership === "invite" && memberContent.is_direct;
|
return memberContent.membership === "invite" && memberContent.is_direct;
|
||||||
},
|
}
|
||||||
|
|
||||||
_makeScreenAfterLogin() {
|
_makeScreenAfterLogin() {
|
||||||
return {
|
return {
|
||||||
|
@ -293,17 +286,17 @@ export default createReactClass({
|
||||||
inviter_name: this.props.oobData ? this.props.oobData.inviterName : null,
|
inviter_name: this.props.oobData ? this.props.oobData.inviterName : null,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
onLoginClick: function() {
|
onLoginClick = () => {
|
||||||
dis.dispatch({ action: 'start_login', screenAfterLogin: this._makeScreenAfterLogin() });
|
dis.dispatch({ action: 'start_login', screenAfterLogin: this._makeScreenAfterLogin() });
|
||||||
},
|
};
|
||||||
|
|
||||||
onRegisterClick: function() {
|
onRegisterClick = () => {
|
||||||
dis.dispatch({ action: 'start_registration', screenAfterLogin: this._makeScreenAfterLogin() });
|
dis.dispatch({ action: 'start_registration', screenAfterLogin: this._makeScreenAfterLogin() });
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
const Spinner = sdk.getComponent('elements.Spinner');
|
const Spinner = sdk.getComponent('elements.Spinner');
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
@ -597,5 +590,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,29 +16,26 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class RoomUpgradeWarningBar extends React.Component {
|
||||||
displayName: 'RoomUpgradeWarningBar',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
room: PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
recommendation: PropTypes.object.isRequired,
|
recommendation: PropTypes.object.isRequired,
|
||||||
},
|
};
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
const tombstone = this.props.room.currentState.getStateEvents("m.room.tombstone", "");
|
const tombstone = this.props.room.currentState.getStateEvents("m.room.tombstone", "");
|
||||||
this.setState({upgraded: tombstone && tombstone.getContent().replacement_room});
|
this.setState({upgraded: tombstone && tombstone.getContent().replacement_room});
|
||||||
|
|
||||||
MatrixClientPeg.get().on("RoomState.events", this._onStateEvents);
|
MatrixClientPeg.get().on("RoomState.events", this._onStateEvents);
|
||||||
},
|
}
|
||||||
|
|
||||||
_onStateEvents: function(event, state) {
|
_onStateEvents = (event, state) => {
|
||||||
if (!this.props.room || event.getRoomId() !== this.props.room.roomId) {
|
if (!this.props.room || event.getRoomId() !== this.props.room.roomId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -47,14 +44,14 @@ export default createReactClass({
|
||||||
|
|
||||||
const tombstone = this.props.room.currentState.getStateEvents("m.room.tombstone", "");
|
const tombstone = this.props.room.currentState.getStateEvents("m.room.tombstone", "");
|
||||||
this.setState({upgraded: tombstone && tombstone.getContent().replacement_room});
|
this.setState({upgraded: tombstone && tombstone.getContent().replacement_room});
|
||||||
},
|
};
|
||||||
|
|
||||||
onUpgradeClick: function() {
|
onUpgradeClick = () => {
|
||||||
const RoomUpgradeDialog = sdk.getComponent('dialogs.RoomUpgradeDialog');
|
const RoomUpgradeDialog = sdk.getComponent('dialogs.RoomUpgradeDialog');
|
||||||
Modal.createTrackedDialog('Upgrade Room Version', '', RoomUpgradeDialog, {room: this.props.room});
|
Modal.createTrackedDialog('Upgrade Room Version', '', RoomUpgradeDialog, {room: this.props.room});
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
||||||
let doUpgradeWarnings = (
|
let doUpgradeWarnings = (
|
||||||
|
@ -117,5 +114,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -15,35 +15,31 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React, {createRef} from 'react';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {Key} from "../../../Keyboard";
|
import {Key} from "../../../Keyboard";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class SearchBar extends React.Component {
|
||||||
displayName: 'SearchBar',
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return ({
|
|
||||||
scope: 'Room',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._search_term = createRef();
|
this._search_term = createRef();
|
||||||
},
|
|
||||||
|
|
||||||
onThisRoomClick: function() {
|
this.state = {
|
||||||
|
scope: 'Room',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
onThisRoomClick = () => {
|
||||||
this.setState({ scope: 'Room' }, () => this._searchIfQuery());
|
this.setState({ scope: 'Room' }, () => this._searchIfQuery());
|
||||||
},
|
};
|
||||||
|
|
||||||
onAllRoomsClick: function() {
|
onAllRoomsClick = () => {
|
||||||
this.setState({ scope: 'All' }, () => this._searchIfQuery());
|
this.setState({ scope: 'All' }, () => this._searchIfQuery());
|
||||||
},
|
};
|
||||||
|
|
||||||
onSearchChange: function(e) {
|
onSearchChange = (e) => {
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case Key.ENTER:
|
case Key.ENTER:
|
||||||
this.onSearch();
|
this.onSearch();
|
||||||
|
@ -52,19 +48,19 @@ export default createReactClass({
|
||||||
this.props.onCancelClick();
|
this.props.onCancelClick();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
_searchIfQuery: function() {
|
_searchIfQuery() {
|
||||||
if (this._search_term.current.value) {
|
if (this._search_term.current.value) {
|
||||||
this.onSearch();
|
this.onSearch();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
onSearch: function() {
|
onSearch = () => {
|
||||||
this.props.onSearch(this._search_term.current.value, this.state.scope);
|
this.props.onSearch(this._search_term.current.value, this.state.scope);
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const searchButtonClasses = classNames("mx_SearchBar_searchButton", {
|
const searchButtonClasses = classNames("mx_SearchBar_searchButton", {
|
||||||
mx_SearchBar_searching: this.props.searchInProgress,
|
mx_SearchBar_searching: this.props.searchInProgress,
|
||||||
});
|
});
|
||||||
|
@ -92,5 +88,5 @@ export default createReactClass({
|
||||||
<AccessibleButton className="mx_SearchBar_cancel" onClick={this.props.onCancelClick} />
|
<AccessibleButton className="mx_SearchBar_cancel" onClick={this.props.onCancelClick} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,14 +17,11 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import {haveTileForEvent} from "./EventTile";
|
import {haveTileForEvent} from "./EventTile";
|
||||||
|
|
||||||
export default createReactClass({
|
export default class SearchResultTile extends React.Component {
|
||||||
displayName: 'SearchResult',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// a matrix-js-sdk SearchResult containing the details of this result
|
// a matrix-js-sdk SearchResult containing the details of this result
|
||||||
searchResult: PropTypes.object.isRequired,
|
searchResult: PropTypes.object.isRequired,
|
||||||
|
|
||||||
|
@ -35,9 +32,9 @@ export default createReactClass({
|
||||||
resultLink: PropTypes.string,
|
resultLink: PropTypes.string,
|
||||||
|
|
||||||
onHeightChanged: PropTypes.func,
|
onHeightChanged: PropTypes.func,
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
const DateSeparator = sdk.getComponent('messages.DateSeparator');
|
const DateSeparator = sdk.getComponent('messages.DateSeparator');
|
||||||
const EventTile = sdk.getComponent('rooms.EventTile');
|
const EventTile = sdk.getComponent('rooms.EventTile');
|
||||||
const result = this.props.searchResult;
|
const result = this.props.searchResult;
|
||||||
|
@ -66,5 +63,5 @@ export default createReactClass({
|
||||||
<li data-scroll-tokens={eventId+"+"+j}>
|
<li data-scroll-tokens={eventId+"+"+j}>
|
||||||
{ ret }
|
{ ret }
|
||||||
</li>);
|
</li>);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
|
@ -37,18 +36,16 @@ export function CancelButton(props) {
|
||||||
* A stripped-down room header used for things like the user settings
|
* A stripped-down room header used for things like the user settings
|
||||||
* and room directory.
|
* and room directory.
|
||||||
*/
|
*/
|
||||||
export default createReactClass({
|
export default class SimpleRoomHeader extends React.Component {
|
||||||
displayName: 'SimpleRoomHeader',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
onCancelClick: PropTypes.func,
|
onCancelClick: PropTypes.func,
|
||||||
|
|
||||||
// `src` to a TintableSvg. Optional.
|
// `src` to a TintableSvg. Optional.
|
||||||
icon: PropTypes.string,
|
icon: PropTypes.string,
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let cancelButton;
|
let cancelButton;
|
||||||
let icon;
|
let icon;
|
||||||
if (this.props.onCancelClick) {
|
if (this.props.onCancelClick) {
|
||||||
|
@ -73,5 +70,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -18,19 +18,16 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class TopUnreadMessagesBar extends React.Component {
|
||||||
displayName: 'TopUnreadMessagesBar',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
onScrollUpClick: PropTypes.func,
|
onScrollUpClick: PropTypes.func,
|
||||||
onCloseClick: PropTypes.func,
|
onCloseClick: PropTypes.func,
|
||||||
},
|
};
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="mx_TopUnreadMessagesBar">
|
<div className="mx_TopUnreadMessagesBar">
|
||||||
<AccessibleButton className="mx_TopUnreadMessagesBar_scrollUp"
|
<AccessibleButton className="mx_TopUnreadMessagesBar_scrollUp"
|
||||||
|
@ -43,5 +40,5 @@ export default createReactClass({
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,16 +17,13 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as WhoIsTyping from '../../../WhoIsTyping';
|
import * as WhoIsTyping from '../../../WhoIsTyping';
|
||||||
import Timer from '../../../utils/Timer';
|
import Timer from '../../../utils/Timer';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import MemberAvatar from '../avatars/MemberAvatar';
|
import MemberAvatar from '../avatars/MemberAvatar';
|
||||||
|
|
||||||
export default createReactClass({
|
export default class WhoIsTypingTile extends React.Component {
|
||||||
displayName: 'WhoIsTypingTile',
|
static propTypes = {
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
// the room this statusbar is representing.
|
// the room this statusbar is representing.
|
||||||
room: PropTypes.object.isRequired,
|
room: PropTypes.object.isRequired,
|
||||||
onShown: PropTypes.func,
|
onShown: PropTypes.func,
|
||||||
|
@ -34,32 +31,28 @@ export default createReactClass({
|
||||||
// Number of names to display in typing indication. E.g. set to 3, will
|
// Number of names to display in typing indication. E.g. set to 3, will
|
||||||
// result in "X, Y, Z and 100 others are typing."
|
// result in "X, Y, Z and 100 others are typing."
|
||||||
whoIsTypingLimit: PropTypes.number,
|
whoIsTypingLimit: PropTypes.number,
|
||||||
},
|
};
|
||||||
|
|
||||||
getDefaultProps: function() {
|
static defaultProps = {
|
||||||
return {
|
whoIsTypingLimit: 3,
|
||||||
whoIsTypingLimit: 3,
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
state = {
|
||||||
return {
|
usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room),
|
||||||
usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room),
|
// a map with userid => Timer to delay
|
||||||
// a map with userid => Timer to delay
|
// hiding the "x is typing" message for a
|
||||||
// hiding the "x is typing" message for a
|
// user so hiding it can coincide
|
||||||
// user so hiding it can coincide
|
// with the sent message by the other side
|
||||||
// with the sent message by the other side
|
// resulting in less timeline jumpiness
|
||||||
// resulting in less timeline jumpiness
|
delayedStopTypingTimers: {},
|
||||||
delayedStopTypingTimers: {},
|
};
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
componentDidMount() {
|
||||||
MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
|
MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping);
|
||||||
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline);
|
||||||
},
|
}
|
||||||
|
|
||||||
componentDidUpdate: function(_, prevState) {
|
componentDidUpdate(_, prevState) {
|
||||||
const wasVisible = this._isVisible(prevState);
|
const wasVisible = this._isVisible(prevState);
|
||||||
const isVisible = this._isVisible(this.state);
|
const isVisible = this._isVisible(this.state);
|
||||||
if (this.props.onShown && !wasVisible && isVisible) {
|
if (this.props.onShown && !wasVisible && isVisible) {
|
||||||
|
@ -67,9 +60,9 @@ export default createReactClass({
|
||||||
} else if (this.props.onHidden && wasVisible && !isVisible) {
|
} else if (this.props.onHidden && wasVisible && !isVisible) {
|
||||||
this.props.onHidden();
|
this.props.onHidden();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
componentWillUnmount() {
|
||||||
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
|
// we may have entirely lost our client as we're logging out before clicking login on the guest bar...
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
if (client) {
|
if (client) {
|
||||||
|
@ -77,17 +70,17 @@ export default createReactClass({
|
||||||
client.removeListener("Room.timeline", this.onRoomTimeline);
|
client.removeListener("Room.timeline", this.onRoomTimeline);
|
||||||
}
|
}
|
||||||
Object.values(this.state.delayedStopTypingTimers).forEach((t) => t.abort());
|
Object.values(this.state.delayedStopTypingTimers).forEach((t) => t.abort());
|
||||||
},
|
}
|
||||||
|
|
||||||
_isVisible: function(state) {
|
_isVisible(state) {
|
||||||
return state.usersTyping.length !== 0 || Object.keys(state.delayedStopTypingTimers).length !== 0;
|
return state.usersTyping.length !== 0 || Object.keys(state.delayedStopTypingTimers).length !== 0;
|
||||||
},
|
}
|
||||||
|
|
||||||
isVisible: function() {
|
isVisible = () => {
|
||||||
return this._isVisible(this.state);
|
return this._isVisible(this.state);
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomTimeline: function(event, room) {
|
onRoomTimeline = (event, room) => {
|
||||||
if (room && room.roomId === this.props.room.roomId) {
|
if (room && room.roomId === this.props.room.roomId) {
|
||||||
const userId = event.getSender();
|
const userId = event.getSender();
|
||||||
// remove user from usersTyping
|
// remove user from usersTyping
|
||||||
|
@ -96,15 +89,15 @@ export default createReactClass({
|
||||||
// abort timer if any
|
// abort timer if any
|
||||||
this._abortUserTimer(userId);
|
this._abortUserTimer(userId);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
onRoomMemberTyping: function(ev, member) {
|
onRoomMemberTyping = (ev, member) => {
|
||||||
const usersTyping = WhoIsTyping.usersTypingApartFromMeAndIgnored(this.props.room);
|
const usersTyping = WhoIsTyping.usersTypingApartFromMeAndIgnored(this.props.room);
|
||||||
this.setState({
|
this.setState({
|
||||||
delayedStopTypingTimers: this._updateDelayedStopTypingTimers(usersTyping),
|
delayedStopTypingTimers: this._updateDelayedStopTypingTimers(usersTyping),
|
||||||
usersTyping,
|
usersTyping,
|
||||||
});
|
});
|
||||||
},
|
};
|
||||||
|
|
||||||
_updateDelayedStopTypingTimers(usersTyping) {
|
_updateDelayedStopTypingTimers(usersTyping) {
|
||||||
const usersThatStoppedTyping = this.state.usersTyping.filter((a) => {
|
const usersThatStoppedTyping = this.state.usersTyping.filter((a) => {
|
||||||
|
@ -142,26 +135,26 @@ export default createReactClass({
|
||||||
}, delayedStopTypingTimers);
|
}, delayedStopTypingTimers);
|
||||||
|
|
||||||
return delayedStopTypingTimers;
|
return delayedStopTypingTimers;
|
||||||
},
|
}
|
||||||
|
|
||||||
_abortUserTimer: function(userId) {
|
_abortUserTimer(userId) {
|
||||||
const timer = this.state.delayedStopTypingTimers[userId];
|
const timer = this.state.delayedStopTypingTimers[userId];
|
||||||
if (timer) {
|
if (timer) {
|
||||||
timer.abort();
|
timer.abort();
|
||||||
this._removeUserTimer(userId);
|
this._removeUserTimer(userId);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_removeUserTimer: function(userId) {
|
_removeUserTimer(userId) {
|
||||||
const timer = this.state.delayedStopTypingTimers[userId];
|
const timer = this.state.delayedStopTypingTimers[userId];
|
||||||
if (timer) {
|
if (timer) {
|
||||||
const delayedStopTypingTimers = Object.assign({}, this.state.delayedStopTypingTimers);
|
const delayedStopTypingTimers = Object.assign({}, this.state.delayedStopTypingTimers);
|
||||||
delete delayedStopTypingTimers[userId];
|
delete delayedStopTypingTimers[userId];
|
||||||
this.setState({delayedStopTypingTimers});
|
this.setState({delayedStopTypingTimers});
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_renderTypingIndicatorAvatars: function(users, limit) {
|
_renderTypingIndicatorAvatars(users, limit) {
|
||||||
let othersCount = 0;
|
let othersCount = 0;
|
||||||
if (users.length > limit) {
|
if (users.length > limit) {
|
||||||
othersCount = users.length - limit + 1;
|
othersCount = users.length - limit + 1;
|
||||||
|
@ -190,9 +183,9 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
return avatars;
|
return avatars;
|
||||||
},
|
}
|
||||||
|
|
||||||
render: function() {
|
render() {
|
||||||
let usersTyping = this.state.usersTyping;
|
let usersTyping = this.state.usersTyping;
|
||||||
const stoppedUsersOnTimer = Object.keys(this.state.delayedStopTypingTimers)
|
const stoppedUsersOnTimer = Object.keys(this.state.delayedStopTypingTimers)
|
||||||
.map((userId) => this.props.room.getMember(userId));
|
.map((userId) => this.props.room.getMember(userId));
|
||||||
|
@ -222,5 +215,5 @@ export default createReactClass({
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue