Merge branch 'develop' into travis/stickerpicker/remount

This commit is contained in:
Travis Ralston 2019-04-03 17:00:19 -06:00
commit ad777782b8
18 changed files with 521 additions and 236 deletions

View file

@ -26,6 +26,7 @@ import RoomViewStore from '../../../stores/RoomViewStore';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import Stickerpicker from './Stickerpicker';
import { makeRoomPermalink } from '../../../matrix-to';
import ContentMessages from '../../../ContentMessages';
import classNames from 'classnames';
import E2EIcon from './E2EIcon';
@ -47,8 +48,7 @@ export default class MessageComposer extends React.Component {
this.onCallClick = this.onCallClick.bind(this);
this.onHangupClick = this.onHangupClick.bind(this);
this.onUploadClick = this.onUploadClick.bind(this);
this.onUploadFileSelected = this.onUploadFileSelected.bind(this);
this.uploadFiles = this.uploadFiles.bind(this);
this._onUploadFileInputChange = this._onUploadFileInputChange.bind(this);
this.onVoiceCallClick = this.onVoiceCallClick.bind(this);
this._onAutocompleteConfirm = this._onAutocompleteConfirm.bind(this);
this.onToggleFormattingClicked = this.onToggleFormattingClicked.bind(this);
@ -145,89 +145,25 @@ export default class MessageComposer extends React.Component {
this.refs.uploadInput.click();
}
onUploadFileSelected(files) {
const tfiles = files.target.files;
this.uploadFiles(tfiles);
}
_onUploadFileInputChange(ev) {
if (ev.target.files.length === 0) return;
uploadFiles(files) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const TintableSvg = sdk.getComponent("elements.TintableSvg");
const fileList = [];
const acceptedFiles = [];
const failedFiles = [];
for (let i=0; i<files.length; i++) {
const fileAcceptedOrError = this.props.uploadAllowed(files[i]);
if (fileAcceptedOrError === true) {
acceptedFiles.push(<li key={i}>
<TintableSvg key={i} src={require("../../../../res/img/files.svg")} width="16" height="16" /> { files[i].name || _t('Attachment') }
</li>);
fileList.push(files[i]);
} else {
failedFiles.push(<li key={i}>
<TintableSvg key={i} src={require("../../../../res/img/files.svg")} width="16" height="16" /> { files[i].name || _t('Attachment') } <p>{ _t('Reason') + ": " + fileAcceptedOrError}</p>
</li>);
}
// take a copy so we can safely reset the value of the form control
// (Note it is a FileList: we can't use slice or sesnible iteration).
const tfiles = [];
for (let i = 0; i < ev.target.files.length; ++i) {
tfiles.push(ev.target.files[i]);
}
const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
let replyToWarning = null;
if (isQuoting) {
replyToWarning = <p>{
_t('At this time it is not possible to reply with a file so this will be sent without being a reply.')
}</p>;
}
const acceptedFilesPart = acceptedFiles.length === 0 ? null : (
<div>
<p>{ _t('Are you sure you want to upload the following files?') }</p>
<ul style={{listStyle: 'none', textAlign: 'left'}}>
{ acceptedFiles }
</ul>
</div>
ContentMessages.sharedInstance().sendContentListToRoom(
tfiles, this.props.room.roomId, MatrixClientPeg.get(),
);
const failedFilesPart = failedFiles.length === 0 ? null : (
<div>
<p>{ _t('The following files cannot be uploaded:') }</p>
<ul style={{listStyle: 'none', textAlign: 'left'}}>
{ failedFiles }
</ul>
</div>
);
let buttonText;
if (acceptedFiles.length > 0 && failedFiles.length > 0) {
buttonText = "Upload selected"
} else if (failedFiles.length > 0) {
buttonText = "Close"
}
Modal.createTrackedDialog('Upload Files confirmation', '', QuestionDialog, {
title: _t('Upload Files'),
description: (
<div>
{ acceptedFilesPart }
{ failedFilesPart }
{ replyToWarning }
</div>
),
hasCancelButton: acceptedFiles.length > 0,
button: buttonText,
onFinished: (shouldUpload) => {
if (shouldUpload) {
// MessageComposer shouldn't have to rely on its parent passing in a callback to upload a file
if (fileList) {
for (let i=0; i<fileList.length; i++) {
this.props.uploadFile(fileList[i]);
}
}
}
this.refs.uploadInput.value = null;
},
});
// This is the onChange handler for a file form control, but we're
// not keeping any state, so reset the value of the form control
// to empty.
// NB. we need to set 'value': the 'files' property is immutable.
ev.target.value = '';
}
onHangupClick() {
@ -376,7 +312,7 @@ export default class MessageComposer extends React.Component {
<input ref="uploadInput" type="file"
style={uploadInputStyle}
multiple
onChange={this.onUploadFileSelected} />
onChange={this._onUploadFileInputChange} />
</AccessibleButton>
);
@ -414,7 +350,6 @@ export default class MessageComposer extends React.Component {
key="controls_input"
room={this.props.room}
placeholder={placeholderText}
onFilesPasted={this.uploadFiles}
onInputStateChanged={this.onInputStateChanged}
permalinkCreator={this.props.permalinkCreator} />,
formattingButton,
@ -510,12 +445,6 @@ MessageComposer.propTypes = {
// string representing the current voip call state
callState: PropTypes.string,
// callback when a file to upload is chosen
uploadFile: PropTypes.func.isRequired,
// function to test whether a file should be allowed to be uploaded.
uploadAllowed: PropTypes.func.isRequired,
// string representing the current room app drawer state
showApps: PropTypes.bool
};

View file

@ -47,6 +47,7 @@ import {Completion} from "../../../autocomplete/Autocompleter";
import Markdown from '../../../Markdown';
import ComposerHistoryManager from '../../../ComposerHistoryManager';
import MessageComposerStore from '../../../stores/MessageComposerStore';
import ContentMessage from '../../../ContentMessages';
import {MATRIXTO_URL_PATTERN} from '../../../linkify-matrix';
@ -1009,7 +1010,13 @@ export default class MessageComposerInput extends React.Component {
switch (transfer.type) {
case 'files':
return this.props.onFilesPasted(transfer.files);
// This actually not so much for 'files' as such (at time of writing
// neither chrome nor firefox let you paste a plain file copied
// from Finder) but more images copied from a different website
// / word processor etc.
return ContentMessage.sharedInstance().sendContentListToRoom(
transfer.files, this.props.room.roomId, this.client,
);
case 'html': {
if (this.state.isRichTextEnabled) {
// FIXME: https://github.com/ianstormtaylor/slate/issues/1497 means

View file

@ -52,10 +52,15 @@ export default class RoomBreadcrumbs extends React.Component {
console.error("Failed to parse breadcrumbs:", e);
}
}
MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership);
}
componentWillUnmount() {
dis.unregister(this._dispatcherRef);
const client = MatrixClientPeg.get();
if (client) client.removeListener("Room.myMembership", this.onMyMembership);
}
componentDidUpdate() {
@ -81,6 +86,17 @@ export default class RoomBreadcrumbs extends React.Component {
}
}
onMyMembership = (room, membership) => {
if (membership === "leave" || membership === "ban") {
const rooms = this.state.rooms.slice();
const roomState = rooms.find((r) => r.room.roomId === room.roomId);
if (roomState) {
roomState.left = true;
this.setState({rooms});
}
}
};
_appendRoomId(roomId) {
const room = MatrixClientPeg.get().getRoom(roomId);
if (!room) {
@ -130,23 +146,24 @@ export default class RoomBreadcrumbs extends React.Component {
return null;
}
const rooms = this.state.rooms;
const avatars = rooms.map(({room, animated, hover}, i) => {
const avatars = rooms.map((r, i) => {
const isFirst = i === 0;
const classes = classNames({
"mx_RoomBreadcrumbs_crumb": true,
"mx_RoomBreadcrumbs_preAnimate": isFirst && !animated,
"mx_RoomBreadcrumbs_preAnimate": isFirst && !r.animated,
"mx_RoomBreadcrumbs_animate": isFirst,
"mx_RoomBreadcrumbs_left": r.left,
});
let tooltip = null;
if (hover) {
tooltip = <Tooltip label={room.name} />;
if (r.hover) {
tooltip = <Tooltip label={r.room.name} />;
}
return (
<AccessibleButton className={classes} key={room.roomId} onClick={() => this._viewRoom(room)}
onMouseEnter={() => this._onMouseEnter(room)} onMouseLeave={() => this._onMouseLeave(room)}>
<RoomAvatar room={room} width={32} height={32} />
<AccessibleButton className={classes} key={r.room.roomId} onClick={() => this._viewRoom(r.room)}
onMouseEnter={() => this._onMouseEnter(r.room)} onMouseLeave={() => this._onMouseLeave(r.room)}>
<RoomAvatar room={r.room} width={32} height={32} />
{tooltip}
</AccessibleButton>
);

View file

@ -29,9 +29,9 @@ import PersistedElement from "../elements/PersistedElement";
const widgetType = 'm.stickerpicker';
// We sit in a context menu, so the persisted element container needs to float
// above it, so it needs a greater z-index than the ContextMenu
const STICKERPICKER_Z_INDEX = 5000;
// This should be below the dialog level (4000), but above the rest of the UI (1000-2000).
// We sit in a context menu, so this should be given to the context menu.
const STICKERPICKER_Z_INDEX = 3500;
// Key to store the widget's AppTile under in PersistedElement
const PERSISTED_ELEMENT_KEY = "stickerPicker";
@ -373,6 +373,7 @@ export default class Stickerpicker extends React.Component {
menuPaddingTop={0}
menuPaddingLeft={0}
menuPaddingRight={0}
zIndex={STICKERPICKER_Z_INDEX}
/>;
if (this.state.showStickers) {