Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3386609
D4434.id14131.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
D4434.id14131.diff
View Options
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(
+ <div className={containerStyle} ref={containerRef}>
+ <ThreadTopBar threadInfo={threadInfo} />
+ <ChatMessageList threadInfo={threadInfo} />
+ <ChatInputBar threadInfo={threadInfo} inputState={inputState} />
+ </div>,
+ );
+}
+
+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<ChatMessageItem>,
+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 <div className={css.container} />;
}
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(
- <div className={containerStyle} ref={this.containerRef}>
- <ThreadTopBar threadInfo={threadInfo} />
- <div className={css.outerMessageContainer}>
- {relationshipPrompt}
- <div className={messageContainerStyle} ref={this.messageContainerRef}>
- {messages}
- </div>
+ return (
+ <div className={css.outerMessageContainer}>
+ {relationshipPrompt}
+ <div className={messageContainerStyle} ref={this.messageContainerRef}>
+ {messages}
</div>
- <ChatInputBar threadInfo={threadInfo} inputState={inputState} />
- </div>,
+ </div>
);
}
@@ -379,8 +352,9 @@
registerFetchKey(fetchMessagesBeforeCursorActionTypes);
registerFetchKey(fetchMostRecentMessagesActionTypes);
-const ConnectedChatMessageList: React.ComponentType<{}> = React.memo<{}>(
- function ConnectedChatMessageList(): React.Node {
+const ConnectedChatMessageList: React.ComponentType<BaseProps> = React.memo<BaseProps>(
+ 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 (
<MessageListContext.Provider value={messageListContext}>
<ChatMessageList
- activeChatThreadID={activeChatThreadID}
+ activeChatThreadID={threadInfo?.id}
threadInfo={threadInfo}
messageListData={messageListData}
startReached={startReached}
@@ -481,8 +419,6 @@
dispatchActionPromise={dispatchActionPromise}
fetchMessagesBeforeCursor={callFetchMessagesBeforeCursor}
fetchMostRecentMessages={callFetchMostRecentMessages}
- {...dndProps}
- connectDropTarget={connectDropTarget}
/>
</MessageListContext.Provider>
);
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 @@
<ThreadListProvider>
<ChatTabs />
</ThreadListProvider>
- <ChatMessageList />
+ <ChatMessageListContainer />
</>
);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 30, 5:19 AM (19 h, 29 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2599746
Default Alt Text
D4434.id14131.diff (11 KB)
Attached To
Mode
D4434: [web] Separate `ChatMessageListContainer` from `ChatMessageList`
Attached
Detach File
Event Timeline
Log In to Comment