Changeset View
Changeset View
Standalone View
Standalone View
web/chat/chat-message-list-container.react.js
Show All 12 Lines | |||||
import { userInfoSelectorForPotentialMembers } from 'lib/selectors/user-selectors.js'; | import { userInfoSelectorForPotentialMembers } from 'lib/selectors/user-selectors.js'; | ||||
import { | import { | ||||
useWatchThread, | useWatchThread, | ||||
useExistingThreadInfoFinder, | useExistingThreadInfoFinder, | ||||
createPendingThread, | createPendingThread, | ||||
threadIsPending, | threadIsPending, | ||||
} from 'lib/shared/thread-utils.js'; | } from 'lib/shared/thread-utils.js'; | ||||
import { threadTypes } from 'lib/types/thread-types.js'; | import { threadTypes } from 'lib/types/thread-types.js'; | ||||
import type { AccountUserInfo } from 'lib/types/user-types.js'; | |||||
import ChatInputBar from './chat-input-bar.react.js'; | import ChatInputBar from './chat-input-bar.react.js'; | ||||
import css from './chat-message-list-container.css'; | import css from './chat-message-list-container.css'; | ||||
import ChatMessageList from './chat-message-list.react.js'; | import ChatMessageList from './chat-message-list.react.js'; | ||||
import ChatThreadComposer from './chat-thread-composer.react.js'; | import ChatThreadComposer from './chat-thread-composer.react.js'; | ||||
import ThreadTopBar from './thread-top-bar.react.js'; | import ThreadTopBar from './thread-top-bar.react.js'; | ||||
import { InputStateContext } from '../input/input-state.js'; | import { InputStateContext } from '../input/input-state.js'; | ||||
import { updateNavInfoActionType } from '../redux/action-types.js'; | import { updateNavInfoActionType } from '../redux/action-types.js'; | ||||
import { useSelector } from '../redux/redux-utils.js'; | import { useSelector } from '../redux/redux-utils.js'; | ||||
type Props = { | type Props = { | ||||
+activeChatThreadID: string, | +activeChatThreadID: string, | ||||
}; | }; | ||||
function ChatMessageListContainer(props: Props): React.Node { | function ChatMessageListContainer(props: Props): React.Node { | ||||
const { activeChatThreadID } = props; | const { activeChatThreadID } = props; | ||||
const isChatCreation = | const isChatCreation = | ||||
useSelector(state => state.navInfo.chatMode) === 'create'; | useSelector(state => state.navInfo.chatMode) === 'create'; | ||||
const selectedUserIDs = useSelector(state => state.navInfo.selectedUserList); | const selectedUserIDs = useSelector( | ||||
state => state.navInfo.selectedUserList ?? [], | |||||
); | |||||
const otherUserInfos = useSelector(userInfoSelectorForPotentialMembers); | const otherUserInfos = useSelector(userInfoSelectorForPotentialMembers); | ||||
const userInfoInputArray: $ReadOnlyArray<AccountUserInfo> = React.useMemo( | const [userInfoInputArray, setUserInfoInputArray] = React.useState(() => | ||||
() => selectedUserIDs?.map(id => otherUserInfos[id]).filter(Boolean) ?? [], | selectedUserIDs.map(id => otherUserInfos[id]).filter(Boolean), | ||||
[otherUserInfos, selectedUserIDs], | |||||
); | ); | ||||
React.useEffect(() => { | |||||
if (!isChatCreation) { | |||||
setUserInfoInputArray([]); | |||||
} | |||||
}, [isChatCreation]); | |||||
const loggedInUserInfo = useLoggedInUserInfo(); | const loggedInUserInfo = useLoggedInUserInfo(); | ||||
invariant(loggedInUserInfo, 'loggedInUserInfo should be set'); | invariant(loggedInUserInfo, 'loggedInUserInfo should be set'); | ||||
const pendingPrivateThread = React.useRef( | const pendingPrivateThread = React.useRef( | ||||
createPendingThread({ | createPendingThread({ | ||||
viewerID: loggedInUserInfo.id, | viewerID: loggedInUserInfo.id, | ||||
threadType: threadTypes.PRIVATE, | threadType: threadTypes.PRIVATE, | ||||
members: [loggedInUserInfo], | members: [loggedInUserInfo], | ||||
▲ Show 20 Lines • Show All 49 Lines • ▼ Show 20 Lines | const threadInfo = React.useMemo(() => { | ||||
existingThreadInfoFinderForCreatingThread, | existingThreadInfoFinderForCreatingThread, | ||||
isChatCreation, | isChatCreation, | ||||
userInfoInputArray, | userInfoInputArray, | ||||
pendingNewThread, | pendingNewThread, | ||||
]); | ]); | ||||
invariant(threadInfo, 'ThreadInfo should be set'); | invariant(threadInfo, 'ThreadInfo should be set'); | ||||
const dispatch = useDispatch(); | const dispatch = useDispatch(); | ||||
// The effect removes members from list in navInfo | |||||
// if some of the user IDs don't exist in redux store | |||||
React.useEffect(() => { | React.useEffect(() => { | ||||
if (!isChatCreation) { | if (!isChatCreation) { | ||||
return; | return; | ||||
} | } | ||||
const existingSelectedUsersSet = new Set( | const newSelectedUserIDs = userInfoInputArray.map(user => user.id); | ||||
userInfoInputArray.map(userInfo => userInfo.id), | if (_isEqual(new Set(selectedUserIDs), new Set(newSelectedUserIDs))) { | ||||
); | return; | ||||
if ( | } | ||||
selectedUserIDs?.length !== existingSelectedUsersSet.size || | const payload = { | ||||
!_isEqual(new Set(selectedUserIDs), existingSelectedUsersSet) | selectedUserList: newSelectedUserIDs, | ||||
) { | }; | ||||
dispatch({ | dispatch({ | ||||
type: updateNavInfoActionType, | type: updateNavInfoActionType, | ||||
payload: { | payload, | ||||
selectedUserList: Array.from(existingSelectedUsersSet), | |||||
}, | |||||
}); | }); | ||||
} | }, [dispatch, isChatCreation, selectedUserIDs, userInfoInputArray]); | ||||
}, [ | |||||
dispatch, | |||||
isChatCreation, | |||||
otherUserInfos, | |||||
selectedUserIDs, | |||||
userInfoInputArray, | |||||
]); | |||||
React.useEffect(() => { | React.useEffect(() => { | ||||
if (isChatCreation && activeChatThreadID !== threadInfo?.id) { | if (!isChatCreation) { | ||||
return; | |||||
} | |||||
if (activeChatThreadID === threadInfo?.id) { | |||||
return; | |||||
} | |||||
let payload = { | let payload = { | ||||
activeChatThreadID: threadInfo?.id, | activeChatThreadID: threadInfo?.id, | ||||
}; | }; | ||||
if (threadIsPending(threadInfo?.id)) { | if (threadIsPending(threadInfo?.id)) { | ||||
payload = { | payload = { | ||||
...payload, | ...payload, | ||||
pendingThread: threadInfo, | pendingThread: threadInfo, | ||||
}; | }; | ||||
} | } | ||||
dispatch({ | dispatch({ | ||||
type: updateNavInfoActionType, | type: updateNavInfoActionType, | ||||
payload, | payload, | ||||
}); | }); | ||||
} | |||||
}, [activeChatThreadID, dispatch, isChatCreation, threadInfo]); | }, [activeChatThreadID, dispatch, isChatCreation, threadInfo]); | ||||
const inputState = React.useContext(InputStateContext); | const inputState = React.useContext(InputStateContext); | ||||
invariant(inputState, 'InputState should be set'); | invariant(inputState, 'InputState should be set'); | ||||
const [{ isActive }, connectDropTarget] = useDrop({ | const [{ isActive }, connectDropTarget] = useDrop({ | ||||
accept: NativeTypes.FILE, | accept: NativeTypes.FILE, | ||||
drop: item => { | drop: item => { | ||||
const { files } = item; | const { files } = item; | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | if (!isChatCreation) { | ||||
{topBar} | {topBar} | ||||
{messageListAndInput} | {messageListAndInput} | ||||
</> | </> | ||||
); | ); | ||||
} | } | ||||
const chatUserSelection = ( | const chatUserSelection = ( | ||||
<ChatThreadComposer | <ChatThreadComposer | ||||
userInfoInputArray={userInfoInputArray} | userInfoInputArray={userInfoInputArray} | ||||
setUserInfoInputArray={setUserInfoInputArray} | |||||
otherUserInfos={otherUserInfos} | otherUserInfos={otherUserInfos} | ||||
threadID={threadInfo.id} | threadID={threadInfo.id} | ||||
inputState={inputState} | inputState={inputState} | ||||
/> | /> | ||||
); | ); | ||||
if (!userInfoInputArray.length) { | if (!userInfoInputArray.length) { | ||||
return chatUserSelection; | return chatUserSelection; | ||||
Show All 24 Lines |