Wire up drag-drop file uploads for the thread view (#7860)

This commit is contained in:
Michael Telatynski 2022-02-22 11:14:56 +00:00 committed by GitHub
parent 42e9ea4540
commit 8fccef86d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 616 additions and 482 deletions

View file

@ -78,6 +78,7 @@ import { copyPlaintext } from '../../../utils/strings';
import { DecryptionFailureTracker } from '../../../DecryptionFailureTracker';
import RedactedBody from '../messages/RedactedBody';
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { shouldDisplayReply } from '../../../utils/Reply';
export type GetRelationsForEvent = (eventId: string, relationType: string, eventType: string) => Relations;
@ -1390,8 +1391,7 @@ export default class EventTile extends React.Component<IProps, IState> {
? RelationType.Thread
: undefined;
const replyChain = haveTileForEvent(this.props.mxEvent)
&& ReplyChain.shouldDisplayReply(this.props.mxEvent, renderTarget)
const replyChain = haveTileForEvent(this.props.mxEvent) && shouldDisplayReply(this.props.mxEvent, renderTarget)
? <ReplyChain
parentEv={this.props.mxEvent}
onHeightChanged={this.props.onHeightChanged}

View file

@ -17,7 +17,7 @@ limitations under the License.
import classNames from 'classnames';
import { IEventRelation } from "matrix-js-sdk/src/models/event";
import { M_POLL_START } from "matrix-events-sdk";
import React, { createContext, ReactElement, useContext } from 'react';
import React, { createContext, ReactElement, useContext, useRef } from 'react';
import { Room } from 'matrix-js-sdk/src/models/room';
import { MatrixClient } from 'matrix-js-sdk/src/client';
import { RelationType } from 'matrix-js-sdk/src/@types/event';
@ -33,10 +33,10 @@ import LocationButton from '../location/LocationButton';
import Modal from "../../../Modal";
import PollCreateDialog from "../elements/PollCreateDialog";
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import { ActionPayload } from '../../../dispatcher/payloads';
import ContentMessages from '../../../ContentMessages';
import MatrixClientContext from '../../../contexts/MatrixClientContext';
import RoomContext from '../../../contexts/RoomContext';
import { useDispatcher } from "../../../hooks/useDispatcher";
interface IProps {
addEmoji: (emoji: string) => boolean;
@ -189,50 +189,37 @@ interface IUploadButtonProps {
relation?: IEventRelation | null;
}
class UploadButton extends React.Component<IUploadButtonProps> {
private uploadInput = React.createRef<HTMLInputElement>();
private dispatcherRef: string;
const UploadButton = ({ roomId, relation }: IUploadButtonProps) => {
const cli = useContext(MatrixClientContext);
const roomContext = useContext(RoomContext);
const overflowMenuCloser = useContext(OverflowMenuContext);
const uploadInput = useRef<HTMLInputElement>();
constructor(props: IUploadButtonProps) {
super(props);
this.dispatcherRef = dis.register(this.onAction);
}
componentWillUnmount() {
dis.unregister(this.dispatcherRef);
}
private onAction = (payload: ActionPayload) => {
if (payload.action === "upload_file") {
this.onUploadClick();
}
};
private onUploadClick = () => {
if (MatrixClientPeg.get().isGuest()) {
const onUploadClick = () => {
if (cli.isGuest()) {
dis.dispatch({ action: 'require_registration' });
return;
}
this.uploadInput.current?.click();
uploadInput.current?.click();
overflowMenuCloser?.(); // close overflow menu
};
private onUploadFileInputChange = (ev: React.ChangeEvent<HTMLInputElement>) => {
useDispatcher(dis, payload => {
if (roomContext.timelineRenderingType === payload.context && payload.action === "upload_file") {
onUploadClick();
}
});
const onUploadFileInputChange = (ev: React.ChangeEvent<HTMLInputElement>) => {
if (ev.target.files.length === 0) return;
// 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 sensible iteration).
const tfiles = [];
for (let i = 0; i < ev.target.files.length; ++i) {
tfiles.push(ev.target.files[i]);
}
// Take a copy, so we can safely reset the value of the form control
ContentMessages.sharedInstance().sendContentListToRoom(
tfiles,
this.props.roomId,
this.props.relation,
MatrixClientPeg.get(),
this.context.timelineRenderingType,
Array.from(ev.target.files),
roomId,
relation,
cli,
roomContext.timelineRenderingType,
);
// This is the onChange handler for a file form control, but we're
@ -242,24 +229,22 @@ class UploadButton extends React.Component<IUploadButtonProps> {
ev.target.value = '';
};
render() {
const uploadInputStyle = { display: 'none' };
return <>
<CollapsibleButton
className="mx_MessageComposer_button mx_MessageComposer_upload"
onClick={this.onUploadClick}
title={_t('Attachment')}
/>
<input
ref={this.uploadInput}
type="file"
style={uploadInputStyle}
multiple
onChange={this.onUploadFileInputChange}
/>
</>;
}
}
const uploadInputStyle = { display: 'none' };
return <>
<CollapsibleButton
className="mx_MessageComposer_button mx_MessageComposer_upload"
onClick={onUploadClick}
title={_t('Attachment')}
/>
<input
ref={uploadInput}
type="file"
style={uploadInputStyle}
multiple
onChange={onUploadFileInputChange}
/>
</>;
};
function showStickersButton(props: IProps): ReactElement {
return (

View file

@ -36,7 +36,6 @@ import {
} from '../../../editor/serialize';
import BasicMessageComposer, { REGEX_EMOTICON } from "./BasicMessageComposer";
import { CommandPartCreator, Part, PartCreator, SerializedPart } from '../../../editor/parts';
import ReplyChain from "../elements/ReplyChain";
import { findEditableEvent } from '../../../utils/EventUtils';
import SendHistoryManager from "../../../SendHistoryManager";
import { CommandCategories } from '../../../SlashCommands';
@ -58,6 +57,7 @@ import { ComposerType } from "../../../dispatcher/payloads/ComposerInsertPayload
import { getSlashCommand, isSlashCommand, runSlashCommand, shouldSendAnyway } from "../../../editor/commands";
import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts";
import { PosthogAnalytics } from "../../../PosthogAnalytics";
import { getNestedReplyText, getRenderInMixin, makeReplyMixIn } from '../../../utils/Reply';
interface IAddReplyOpts {
permalinkCreator?: RoomPermalinkCreator;
@ -72,13 +72,13 @@ function addReplyToMessageContent(
includeLegacyFallback: true,
},
): void {
const replyContent = ReplyChain.makeReplyMixIn(replyToEvent, opts.renderIn);
const replyContent = makeReplyMixIn(replyToEvent, opts.renderIn);
Object.assign(content, replyContent);
if (opts.includeLegacyFallback) {
// Part of Replies fallback support - prepend the text we're sending
// with the text we're replying to
const nestedReply = ReplyChain.getNestedReplyText(replyToEvent, opts.permalinkCreator);
const nestedReply = getNestedReplyText(replyToEvent, opts.permalinkCreator);
if (nestedReply) {
if (content.formatted_body) {
content.formatted_body = nestedReply.html + content.formatted_body;
@ -132,7 +132,7 @@ export function createMessageContent(
addReplyToMessageContent(content, replyToEvent, {
permalinkCreator,
includeLegacyFallback: true,
renderIn: ReplyChain.getRenderInMixin(relation),
renderIn: getRenderInMixin(relation),
});
}
@ -384,7 +384,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
addReplyToMessageContent(content, replyToEvent, {
permalinkCreator: this.props.permalinkCreator,
includeLegacyFallback: true,
renderIn: ReplyChain.getRenderInMixin(this.props.relation),
renderIn: getRenderInMixin(this.props.relation),
});
}
} else {