Module API for adding new file upload mechanisms (#33355)
* Initial reword of upload to MVVM. * Update tests * More incremental improvements * Refactor tests to use helper method for composer uploads. * Add drag and drop tests * lint * Add commentary * fixup test * More precise selector * Retarget uploads * lint * fixup * one more type * update snap * Fixup composerUploadFiles * fix import * lint * Copy and paste fixes too * Add tests for pasting * Add tests for pasting files. * Remove redundant fn * rm comment * tidy up * Test cleanup * More clean up * another fix * Begin fleshing out * Park changes * More stuff * Use condensed version * Cleanup tests * more cleaning * last bity * Add a test for the composer * Park up changes * Rewrite Measured to be a functional component * Add tests to cover narrow viewports * lint * breakpoint is optional * Cleanup * Support narrow mode * fixup * begone * Provide default value * add label * fixup test * update copyright * cleanup * Be a bit more lazy with FileDropTarget * remove a debug statement * Fixup * fix two snaps * Update screenshot * and the other one * Update snaps * unfake CIDER * update screens again * remove extra test * Undo accidental snapshots * Bit of tidyup * fixup * even more tidyup * may drag and drop file * tidy up again * snap snap snap * Use load to make sonarQube happy * Bunch of refactors * More cleanup * cleanup debug code * tweaks * remove a test we no longer need * make it happy * fix import * fixup * Update snaps * typo * one off * Add tests * lint * remove only * Reduce screenshot scope * fix snapshot usage * cleanup
This commit is contained in:
@@ -29,10 +29,10 @@ const FileDropTarget: React.FC<IProps> = ({ parent }) => {
|
||||
counter: 0,
|
||||
});
|
||||
const vm = useRoomUploadViewModel();
|
||||
const { mayUpload } = useViewModel(vm);
|
||||
const { mayDragAndDropFile } = useViewModel(vm);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mayUpload || !parent || parent.ondrop) return;
|
||||
if (!mayDragAndDropFile || !parent || parent.ondrop) return;
|
||||
|
||||
const onDragEnter = (ev: DragEvent): void => {
|
||||
ev.stopPropagation();
|
||||
@@ -106,9 +106,9 @@ const FileDropTarget: React.FC<IProps> = ({ parent }) => {
|
||||
parent?.removeEventListener("dragenter", onDragEnter);
|
||||
parent?.removeEventListener("dragleave", onDragLeave);
|
||||
};
|
||||
}, [parent, mayUpload, vm]);
|
||||
}, [parent, mayDragAndDropFile, vm]);
|
||||
|
||||
if (mayUpload && state.dragging) {
|
||||
if (mayDragAndDropFile && state.dragging) {
|
||||
return (
|
||||
<div className="mx_FileDropTarget">
|
||||
<img src={UploadBigSvg} className="mx_FileDropTarget_image" alt="" />
|
||||
|
||||
@@ -1299,7 +1299,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
const composerInsertPayload = payload as ComposerInsertPayload;
|
||||
if (composerInsertPayload.composerType) break;
|
||||
|
||||
let timelineRenderingType: TimelineRenderingType | undefined;
|
||||
let timelineRenderingType = composerInsertPayload.timelineRenderingType;
|
||||
// ThreadView handles Action.ComposerInsert itself due to it having its own editState
|
||||
if (composerInsertPayload.timelineRenderingType === TimelineRenderingType.Thread) break;
|
||||
if (
|
||||
@@ -1311,12 +1311,6 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
||||
timelineRenderingType = TimelineRenderingType.Room;
|
||||
}
|
||||
|
||||
// If the dispatchee didn't request a timeline rendering type, use the current one.
|
||||
timelineRenderingType =
|
||||
timelineRenderingType ??
|
||||
composerInsertPayload.timelineRenderingType ??
|
||||
this.state.timelineRenderingType;
|
||||
|
||||
// re-dispatch to the correct composer
|
||||
defaultDispatcher.dispatch<ComposerInsertPayload>({
|
||||
...composerInsertPayload,
|
||||
|
||||
@@ -17,13 +17,13 @@ import {
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
import React, { type JSX, createContext, type ReactElement, type ReactNode, useContext } from "react";
|
||||
import {
|
||||
AttachmentIcon,
|
||||
MicOnIcon,
|
||||
OverflowHorizontalIcon,
|
||||
PollsIcon,
|
||||
StickerIcon,
|
||||
TextFormattingIcon,
|
||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
import { UploadButton, useViewModel } from "@element-hq/web-shared-components";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { CollapsibleButton } from "./CollapsibleButton";
|
||||
@@ -34,7 +34,10 @@ import Modal from "../../../Modal";
|
||||
import PollCreateDialog from "../elements/PollCreateDialog";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import IconizedContextMenu, { IconizedContextMenuOptionList } from "../context_menus/IconizedContextMenu";
|
||||
import IconizedContextMenu, {
|
||||
IconizedContextMenuOption,
|
||||
IconizedContextMenuOptionList,
|
||||
} from "../context_menus/IconizedContextMenu";
|
||||
import { EmojiButton } from "./EmojiButton";
|
||||
import { filterBoolean } from "../../../utils/arrays";
|
||||
import { useSettingValue } from "../../../hooks/useSettings";
|
||||
@@ -64,6 +67,8 @@ export const OverflowMenuContext = createContext<OverflowMenuCloser | null>(null
|
||||
|
||||
const MessageComposerButtons: React.FC<IProps> = (props: IProps) => {
|
||||
const matrixClient = useContext(MatrixClientContext);
|
||||
const roomUploadVM = useRoomUploadViewModel();
|
||||
const roomUploadSnapshot = useViewModel(roomUploadVM);
|
||||
const { room, narrow } = useScopedRoomContext("room", "narrow");
|
||||
|
||||
const isWysiwygLabEnabled = useSettingValue("feature_wysiwyg_composer");
|
||||
@@ -87,7 +92,15 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => {
|
||||
),
|
||||
];
|
||||
moreButtons = [
|
||||
uploadButton(), // props passed via UploadButtonContext
|
||||
// This a textual list of buttons, so we can't use the UploadButton here.
|
||||
roomUploadSnapshot.options.map(({ type, icon: Icon, label }) => (
|
||||
<IconizedContextMenuOption
|
||||
onClick={() => roomUploadVM.onUploadOptionSelected(type)}
|
||||
icon={Icon && <Icon />}
|
||||
label={label}
|
||||
key={type}
|
||||
/>
|
||||
)),
|
||||
showStickersButton(props),
|
||||
voiceRecordingButton(props, narrow),
|
||||
props.showPollsButton ? pollButton(room, props.relation) : null,
|
||||
@@ -104,7 +117,7 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => {
|
||||
) : (
|
||||
emojiButton(props)
|
||||
),
|
||||
uploadButton(), // props passed via UploadButtonContext
|
||||
<UploadButton key="upload" vm={roomUploadVM} />,
|
||||
];
|
||||
moreButtons = [
|
||||
showStickersButton(props),
|
||||
@@ -162,27 +175,6 @@ function emojiButton(props: IProps): ReactElement {
|
||||
);
|
||||
}
|
||||
|
||||
function uploadButton(): ReactElement {
|
||||
return <UploadButton key="controls_upload" />;
|
||||
}
|
||||
|
||||
// Must be rendered within an UploadButtonContextProvider
|
||||
const UploadButton: React.FC = () => {
|
||||
const overflowMenuCloser = useContext(OverflowMenuContext);
|
||||
const vm = useRoomUploadViewModel();
|
||||
|
||||
const onClick = (): void => {
|
||||
vm.openUploadDialog();
|
||||
overflowMenuCloser?.(); // close overflow menu
|
||||
};
|
||||
|
||||
return (
|
||||
<CollapsibleButton className="mx_MessageComposer_button" onClick={onClick} title={_t("common|attachment")}>
|
||||
<AttachmentIcon />
|
||||
</CollapsibleButton>
|
||||
);
|
||||
};
|
||||
|
||||
function showStickersButton(props: IProps): ReactElement | null {
|
||||
return props.showStickersButton ? (
|
||||
<CollapsibleButton
|
||||
@@ -296,5 +288,4 @@ function ComposerModeButton({ isRichTextEnabled, onClick }: WysiwygToggleButtonP
|
||||
</CollapsibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
export default MessageComposerButtons;
|
||||
|
||||
Reference in New Issue
Block a user