import { useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import useKey from 'react-use/lib/useKey'; import styled from 'styled-components'; import { isEmpty } from 'lodash'; import { SessionButton } from '../../basic/SessionButton'; import { SessionSpinner } from '../../loading'; import { useSet } from '../../../hooks/useSet'; import { VALIDATION } from '../../../session/constants'; import { createClosedGroup } from '../../../session/conversations/createClosedGroup'; import { ToastUtils } from '../../../session/utils'; import LIBSESSION_CONSTANTS from '../../../session/utils/libsession/libsession_constants'; import { clearSearch } from '../../../state/ducks/search'; import { resetLeftOverlayMode } from '../../../state/ducks/section'; import { getPrivateContactsPubkeys } from '../../../state/selectors/conversations'; import { getSearchResultsContactOnly, getSearchTerm, useIsSearching, } from '../../../state/selectors/search'; import { MemberListItem } from '../../MemberListItem'; import { SessionSearchInput } from '../../SessionSearchInput'; import { Flex } from '../../basic/Flex'; import { SpacerLG, SpacerMD } from '../../basic/Text'; import { SessionInput } from '../../inputs'; import { StyledLeftPaneOverlay } from './OverlayMessage'; const StyledMemberListNoContacts = styled.div` text-align: center; padding: 20px; `; const StyledNoResults = styled.div` width: 100%; padding: var(--margins-xl) var(--margins-sm); text-align: center; `; const StyledGroupMemberListContainer = styled.div` display: flex; flex-direction: column; flex: 1; width: 100%; overflow-x: hidden; overflow-y: auto; &::-webkit-scrollbar-track { background-color: var(--background-secondary-color); } `; const NoContacts = () => { return ( {window.i18n('conversationsNone')} ); }; /** * Makes some validity check and return true if the group was indeed created */ async function createClosedGroupWithErrorHandling( groupName: string, groupMemberIds: Array, onError: (error: string) => void ): Promise { // Validate groupName and groupMembers length if (groupName.length === 0) { ToastUtils.pushToastError('invalidGroupName', window.i18n('groupNameEnterPlease')); onError(window.i18n('groupNameEnterPlease')); return false; } if (groupName.length > LIBSESSION_CONSTANTS.BASE_GROUP_MAX_NAME_LENGTH) { onError(window.i18n('groupNameEnterShorter')); return false; } // >= because we add ourself as a member AFTER this. so a 10 group is already invalid as it will be 11 when we are included // the same is valid with groups count < 1 if (groupMemberIds.length < 1) { onError(window.i18n('groupCreateErrorNoMembers')); return false; } if (groupMemberIds.length >= VALIDATION.CLOSED_GROUP_SIZE_LIMIT) { onError(window.i18n('groupAddMemberMaximum')); return false; } await createClosedGroup(groupName, groupMemberIds, window.sessionFeatureFlags.useClosedGroupV3); return true; } export const OverlayClosedGroup = () => { const dispatch = useDispatch(); const privateContactsPubkeys = useSelector(getPrivateContactsPubkeys); const [groupName, setGroupName] = useState(''); const [groupNameError, setGroupNameError] = useState(undefined); const [loading, setLoading] = useState(false); const { uniqueValues: selectedMemberIds, addTo: addToSelected, removeFrom: removeFromSelected, } = useSet([]); const isSearch = useIsSearching(); const searchTerm = useSelector(getSearchTerm); const searchResultContactsOnly = useSelector(getSearchResultsContactOnly); function closeOverlay() { dispatch(clearSearch()); dispatch(resetLeftOverlayMode()); } async function onEnterPressed() { setGroupNameError(undefined); if (loading) { window?.log?.warn('Closed group creation already in progress'); return; } setLoading(true); const groupCreated = await createClosedGroupWithErrorHandling( groupName, selectedMemberIds, setGroupNameError ); if (groupCreated) { closeOverlay(); return; } setLoading(false); } useKey('Escape', closeOverlay); const contactsToRender = isSearch ? searchResultContactsOnly : privateContactsPubkeys; const noContactsForClosedGroup = isEmpty(searchTerm) && contactsToRender.length === 0; const disableCreateButton = loading || (!selectedMemberIds.length && !groupName.length); return ( {noContactsForClosedGroup ? ( ) : searchTerm && !contactsToRender.length ? ( {window.i18n('searchMatchesNoneSpecific', { query: searchTerm })} ) : ( contactsToRender.map((pubkey: string) => ( )) )} ); };