diff --git a/web/chat/chat-message-list-container.css b/web/chat/chat-message-list-container.css
new file mode 100644
--- /dev/null
+++ b/web/chat/chat-message-list-container.css
@@ -0,0 +1,12 @@
+div.container {
+ margin-left: 400px;
+ height: 100%;
+ background-color: var(--bg);
+ display: flex;
+ flex-direction: column;
+ box-sizing: border-box;
+}
+div.activeContainer {
+ border: 2px solid #5989d6;
+ margin-left: 402px;
+}
diff --git a/web/chat/chat-message-list-container.react.js b/web/chat/chat-message-list-container.react.js
new file mode 100644
--- /dev/null
+++ b/web/chat/chat-message-list-container.react.js
@@ -0,0 +1,108 @@
+// @flow
+
+import classNames from 'classnames';
+import invariant from 'invariant';
+import * as React from 'react';
+import { useDrop } from 'react-dnd';
+import { NativeTypes } from 'react-dnd-html5-backend';
+
+import { threadInfoSelector } from 'lib/selectors/thread-selectors';
+import {
+ useWatchThread,
+ useExistingThreadInfoFinder,
+} from 'lib/shared/thread-utils';
+
+import { InputStateContext } from '../input/input-state';
+import { useSelector } from '../redux/redux-utils';
+import ChatInputBar from './chat-input-bar.react';
+import css from './chat-message-list-container.css';
+import ChatMessageList from './chat-message-list.react';
+import ThreadTopBar from './thread-top-bar.react';
+
+function ChatMessageListContainer(): React.Node {
+ const activeChatThreadID = useSelector(
+ state => state.navInfo.activeChatThreadID,
+ );
+ const baseThreadInfo = useSelector(state => {
+ const activeID = activeChatThreadID;
+ if (!activeID) {
+ return null;
+ }
+ return threadInfoSelector(state)[activeID] ?? state.navInfo.pendingThread;
+ });
+ const existingThreadInfoFinder = useExistingThreadInfoFinder(baseThreadInfo);
+ const threadInfo = React.useMemo(
+ () =>
+ existingThreadInfoFinder({
+ searching: false,
+ userInfoInputArray: [],
+ }),
+ [existingThreadInfoFinder],
+ );
+
+ const inputState = React.useContext(InputStateContext);
+ const [{ isActive }, connectDropTarget] = useDrop({
+ accept: NativeTypes.FILE,
+ drop: item => {
+ const { files } = item;
+ if (inputState && files.length > 0) {
+ inputState.appendFiles(files);
+ }
+ },
+ collect: monitor => ({
+ isActive: monitor.isOver() && monitor.canDrop(),
+ }),
+ });
+
+ useWatchThread(threadInfo);
+
+ invariant(threadInfo, 'ThreadInfo should be set if messageListData is');
+ invariant(inputState, 'InputState should be set');
+ const containerStyle = classNames({
+ [css.container]: true,
+ [css.activeContainer]: isActive,
+ });
+
+ const containerRef = React.useRef();
+
+ const onPaste = React.useCallback(
+ (e: ClipboardEvent) => {
+ if (!inputState) {
+ return;
+ }
+ const { clipboardData } = e;
+ if (!clipboardData) {
+ return;
+ }
+ const { files } = clipboardData;
+ if (files.length === 0) {
+ return;
+ }
+ e.preventDefault();
+ console.log('Append files');
+ inputState.appendFiles([...files]);
+ },
+ [inputState],
+ );
+
+ React.useEffect(() => {
+ const currentContainerRef = containerRef.current;
+ if (!currentContainerRef) {
+ return;
+ }
+ currentContainerRef.addEventListener('paste', onPaste);
+ return () => {
+ currentContainerRef.removeEventListener('paste', onPaste);
+ };
+ }, [onPaste]);
+
+ return connectDropTarget(
+
+
+
+
+
,
+ );
+}
+
+export default ChatMessageListContainer;
diff --git a/web/chat/chat-message-list.css b/web/chat/chat-message-list.css
--- a/web/chat/chat-message-list.css
+++ b/web/chat/chat-message-list.css
@@ -1,15 +1,3 @@
-div.container {
- margin-left: 400px;
- height: 100%;
- background-color: var(--bg);
- display: flex;
- flex-direction: column;
- box-sizing: border-box;
-}
-div.activeContainer {
- border: 2px solid #5989d6;
- margin-left: 402px;
-}
div.outerMessageContainer {
position: relative;
height: calc(100vh - 128px);
diff --git a/web/chat/chat-message-list.react.js b/web/chat/chat-message-list.react.js
--- a/web/chat/chat-message-list.react.js
+++ b/web/chat/chat-message-list.react.js
@@ -4,8 +4,6 @@
import { detect as detectBrowser } from 'detect-browser';
import invariant from 'invariant';
import * as React from 'react';
-import { useDrop } from 'react-dnd';
-import { NativeTypes } from 'react-dnd-html5-backend';
import {
fetchMessagesBeforeCursorActionTypes,
@@ -18,13 +16,8 @@
type ChatMessageItem,
useMessageListData,
} from 'lib/selectors/chat-selectors';
-import { threadInfoSelector } from 'lib/selectors/thread-selectors';
import { messageKey } from 'lib/shared/message-utils';
-import {
- useWatchThread,
- useExistingThreadInfoFinder,
- threadIsPending,
-} from 'lib/shared/thread-utils';
+import { threadIsPending } from 'lib/shared/thread-utils';
import type { FetchMessageInfosPayload } from 'lib/types/message-types';
import { type ThreadInfo } from 'lib/types/thread-types';
import {
@@ -37,7 +30,6 @@
import LoadingIndicator from '../loading-indicator.react';
import { useTextMessageRulesFunc } from '../markdown/rules.react';
import { useSelector } from '../redux/redux-utils';
-import ChatInputBar from './chat-input-bar.react';
import css from './chat-message-list.css';
import { MessageListContext } from './message-list-types';
import Message from './message.react';
@@ -46,12 +38,15 @@
MessagePositionInfo,
} from './position-types';
import RelationshipPrompt from './relationship-prompt/relationship-prompt';
-import ThreadTopBar from './thread-top-bar.react';
-type PassedProps = {
+type BaseProps = {
+ +threadInfo: ThreadInfo,
+};
+
+type Props = {
+ ...BaseProps,
// Redux state
+activeChatThreadID: ?string,
- +threadInfo: ?ThreadInfo,
+messageListData: ?$ReadOnlyArray,
+startReached: boolean,
+timeZone: ?string,
@@ -69,14 +64,6 @@
// withInputState
+inputState: ?InputState,
};
-type ReactDnDProps = {
- +isActive: boolean,
- +connectDropTarget: (node: React.Node) => React.Node,
-};
-type Props = {
- ...PassedProps,
- ...ReactDnDProps,
-};
type State = {
+mouseOverMessagePosition: ?OnMessagePositionWithContainerInfo,
};
@@ -243,23 +230,13 @@
};
render() {
- const {
- messageListData,
- threadInfo,
- inputState,
- connectDropTarget,
- isActive,
- } = this.props;
+ const { messageListData, threadInfo, inputState } = this.props;
if (!messageListData) {
return ;
}
invariant(threadInfo, 'ThreadInfo should be set if messageListData is');
invariant(inputState, 'InputState should be set');
const messages = messageListData.map(this.renderItem);
- const containerStyle = classNames({
- [css.container]: true,
- [css.activeContainer]: isActive,
- });
let relationshipPrompt;
if (this.props.threadInfo) {
@@ -272,17 +249,13 @@
[css.messageContainer]: true,
[css.mirroredMessageContainer]: !this.props.supportsReverseFlex,
});
- return connectDropTarget(
-
-
-
- {relationshipPrompt}
-
- {messages}
-
+ return (
+
+ {relationshipPrompt}
+
+ {messages}
-
-
,
+
);
}
@@ -379,8 +352,9 @@
registerFetchKey(fetchMessagesBeforeCursorActionTypes);
registerFetchKey(fetchMostRecentMessagesActionTypes);
-const ConnectedChatMessageList: React.ComponentType<{}> = React.memo<{}>(
- function ConnectedChatMessageList(): React.Node {
+const ConnectedChatMessageList: React.ComponentType
= React.memo(
+ function ConnectedChatMessageList(props: BaseProps): React.Node {
+ const { threadInfo } = props;
const userAgent = useSelector(state => state.userAgent);
const supportsReverseFlex = React.useMemo(() => {
const browser = detectBrowser(userAgent);
@@ -393,41 +367,19 @@
const timeZone = useSelector(state => state.timeZone);
- const activeChatThreadID = useSelector(
- state => state.navInfo.activeChatThreadID,
- );
- const baseThreadInfo = useSelector(state => {
- const activeID = state.navInfo.activeChatThreadID;
- if (!activeID) {
- return null;
- }
- return threadInfoSelector(state)[activeID] ?? state.navInfo.pendingThread;
- });
- const existingThreadInfoFinder = useExistingThreadInfoFinder(
- baseThreadInfo,
- );
- const threadInfo = React.useMemo(
- () =>
- existingThreadInfoFinder({
- searching: false,
- userInfoInputArray: [],
- }),
- [existingThreadInfoFinder],
- );
-
const messageListData = useMessageListData({
threadInfo,
searching: false,
userInfoInputArray: [],
});
- const startReached = useSelector(state => {
- const activeID = state.navInfo.activeChatThreadID;
+ const startReached = !!useSelector(state => {
+ const activeID = threadInfo?.id;
if (!activeID) {
return null;
}
- if (threadIsPending(threadInfo?.id)) {
+ if (threadIsPending(activeID)) {
return true;
}
@@ -445,18 +397,6 @@
const callFetchMostRecentMessages = useServerCall(fetchMostRecentMessages);
const inputState = React.useContext(InputStateContext);
- const [dndProps, connectDropTarget] = useDrop({
- accept: NativeTypes.FILE,
- drop: item => {
- const { files } = item;
- if (inputState && files.length > 0) {
- inputState.appendFiles(files);
- }
- },
- collect: monitor => ({
- isActive: monitor.isOver() && monitor.canDrop(),
- }),
- });
const getTextMessageMarkdownRules = useTextMessageRulesFunc(threadInfo?.id);
const messageListContext = React.useMemo(() => {
@@ -466,12 +406,10 @@
return { getTextMessageMarkdownRules };
}, [getTextMessageMarkdownRules]);
- useWatchThread(threadInfo);
-
return (
);
diff --git a/web/chat/chat.react.js b/web/chat/chat.react.js
--- a/web/chat/chat.react.js
+++ b/web/chat/chat.react.js
@@ -2,7 +2,7 @@
import * as React from 'react';
-import ChatMessageList from './chat-message-list.react';
+import ChatMessageListContainer from './chat-message-list-container.react';
import ChatTabs from './chat-tabs.react';
import { ThreadListProvider } from './thread-list-provider';
@@ -12,7 +12,7 @@
-
+
>
);
}