Add to group by username
This commit is contained in:
parent
8dd321d0b6
commit
973b2264fe
13 changed files with 332 additions and 45 deletions
|
@ -25,6 +25,7 @@ import { ContactListItem } from './conversationList/ContactListItem';
|
|||
import type { ContactCheckboxDisabledReason } from './conversationList/ContactCheckbox';
|
||||
import { ContactCheckbox as ContactCheckboxComponent } from './conversationList/ContactCheckbox';
|
||||
import { PhoneNumberCheckbox as PhoneNumberCheckboxComponent } from './conversationList/PhoneNumberCheckbox';
|
||||
import { UsernameCheckbox as UsernameCheckboxComponent } from './conversationList/UsernameCheckbox';
|
||||
import { CreateNewGroupButton } from './conversationList/CreateNewGroupButton';
|
||||
import { StartNewConversation as StartNewConversationComponent } from './conversationList/StartNewConversation';
|
||||
import { SearchResultsLoadingFakeHeader as SearchResultsLoadingFakeHeaderComponent } from './conversationList/SearchResultsLoadingFakeHeader';
|
||||
|
@ -32,19 +33,20 @@ import { SearchResultsLoadingFakeRow as SearchResultsLoadingFakeRowComponent } f
|
|||
import { UsernameSearchResultListItem } from './conversationList/UsernameSearchResultListItem';
|
||||
|
||||
export enum RowType {
|
||||
ArchiveButton,
|
||||
Blank,
|
||||
Contact,
|
||||
ContactCheckbox,
|
||||
PhoneNumberCheckbox,
|
||||
Conversation,
|
||||
CreateNewGroup,
|
||||
Header,
|
||||
MessageSearchResult,
|
||||
SearchResultsLoadingFakeHeader,
|
||||
SearchResultsLoadingFakeRow,
|
||||
StartNewConversation,
|
||||
UsernameSearchResult,
|
||||
ArchiveButton = 'ArchiveButton',
|
||||
Blank = 'Blank',
|
||||
Contact = 'Contact',
|
||||
ContactCheckbox = 'ContactCheckbox',
|
||||
PhoneNumberCheckbox = 'PhoneNumberCheckbox',
|
||||
UsernameCheckbox = 'UsernameCheckbox',
|
||||
Conversation = 'Conversation',
|
||||
CreateNewGroup = 'CreateNewGroup',
|
||||
Header = 'Header',
|
||||
MessageSearchResult = 'MessageSearchResult',
|
||||
SearchResultsLoadingFakeHeader = 'SearchResultsLoadingFakeHeader',
|
||||
SearchResultsLoadingFakeRow = 'SearchResultsLoadingFakeRow',
|
||||
StartNewConversation = 'StartNewConversation',
|
||||
UsernameSearchResult = 'UsernameSearchResult',
|
||||
}
|
||||
|
||||
type ArchiveButtonRowType = {
|
||||
|
@ -74,6 +76,13 @@ type PhoneNumberCheckboxRowType = {
|
|||
isFetching: boolean;
|
||||
};
|
||||
|
||||
type UsernameCheckboxRowType = {
|
||||
type: RowType.UsernameCheckbox;
|
||||
username: string;
|
||||
isChecked: boolean;
|
||||
isFetching: boolean;
|
||||
};
|
||||
|
||||
type ConversationRowType = {
|
||||
type: RowType.Conversation;
|
||||
conversation: ConversationListItemPropsType;
|
||||
|
@ -119,6 +128,7 @@ export type Row =
|
|||
| ContactRowType
|
||||
| ContactCheckboxRowType
|
||||
| PhoneNumberCheckboxRowType
|
||||
| UsernameCheckboxRowType
|
||||
| ConversationRowType
|
||||
| CreateNewGroupRowType
|
||||
| MessageRowType
|
||||
|
@ -283,6 +293,23 @@ export const ConversationList: React.FC<PropsType> = ({
|
|||
/>
|
||||
);
|
||||
break;
|
||||
case RowType.UsernameCheckbox:
|
||||
result = (
|
||||
<UsernameCheckboxComponent
|
||||
username={row.username}
|
||||
lookupConversationWithoutUuid={lookupConversationWithoutUuid}
|
||||
showUserNotFoundModal={showUserNotFoundModal}
|
||||
setIsFetchingUUID={setIsFetchingUUID}
|
||||
toggleConversationInChooseMembers={conversationId =>
|
||||
onClickContactCheckbox(conversationId, undefined)
|
||||
}
|
||||
isChecked={row.isChecked}
|
||||
isFetching={row.isFetching}
|
||||
i18n={i18n}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
case RowType.Conversation: {
|
||||
const itemProps = pick(row.conversation, [
|
||||
'acceptedMessageRequest',
|
||||
|
|
|
@ -883,6 +883,7 @@ export const ChooseGroupMembersPartialPhoneNumber = (): JSX.Element => (
|
|||
candidateContacts: [],
|
||||
isShowingRecommendedGroupSizeModal: false,
|
||||
isShowingMaximumGroupSizeModal: false,
|
||||
isUsernamesEnabled: true,
|
||||
searchTerm: '+1(212) 555',
|
||||
regionCode: 'US',
|
||||
selectedContacts: [],
|
||||
|
@ -904,6 +905,7 @@ export const ChooseGroupMembersValidPhoneNumber = (): JSX.Element => (
|
|||
candidateContacts: [],
|
||||
isShowingRecommendedGroupSizeModal: false,
|
||||
isShowingMaximumGroupSizeModal: false,
|
||||
isUsernamesEnabled: true,
|
||||
searchTerm: '+1(212) 555 5454',
|
||||
regionCode: 'US',
|
||||
selectedContacts: [],
|
||||
|
@ -916,6 +918,28 @@ ChooseGroupMembersValidPhoneNumber.story = {
|
|||
name: 'Choose Group Members: Valid phone number',
|
||||
};
|
||||
|
||||
export const ChooseGroupMembersUsername = (): JSX.Element => (
|
||||
<LeftPane
|
||||
{...useProps({
|
||||
modeSpecificProps: {
|
||||
mode: LeftPaneMode.ChooseGroupMembers,
|
||||
uuidFetchState: {},
|
||||
candidateContacts: [],
|
||||
isShowingRecommendedGroupSizeModal: false,
|
||||
isShowingMaximumGroupSizeModal: false,
|
||||
isUsernamesEnabled: true,
|
||||
searchTerm: '@signal',
|
||||
regionCode: 'US',
|
||||
selectedContacts: [],
|
||||
},
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
ChooseGroupMembersUsername.story = {
|
||||
name: 'Choose Group Members: username',
|
||||
};
|
||||
|
||||
export const GroupMetadataNoTimer = (): JSX.Element => (
|
||||
<LeftPane
|
||||
{...useProps({
|
||||
|
|
|
@ -68,6 +68,7 @@ const createProps = (
|
|||
i18n={i18n}
|
||||
lookupConversationWithoutUuid={lookupConversationWithoutUuid}
|
||||
showUserNotFoundModal={action('showUserNotFoundModal')}
|
||||
isUsernamesEnabled
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -14,6 +14,7 @@ import type { MeasuredComponentProps } from 'react-measure';
|
|||
import Measure from 'react-measure';
|
||||
|
||||
import type { LocalizerType, ThemeType } from '../../../../types/Util';
|
||||
import { getUsernameFromSearch } from '../../../../types/Username';
|
||||
import { assert } from '../../../../util/assert';
|
||||
import { refMerger } from '../../../../util/refMerger';
|
||||
import { useRestoreFocus } from '../../../../hooks/useRestoreFocus';
|
||||
|
@ -27,7 +28,10 @@ import type {
|
|||
UUIDFetchStateKeyType,
|
||||
UUIDFetchStateType,
|
||||
} from '../../../../util/uuidFetchState';
|
||||
import { isFetchingByE164 } from '../../../../util/uuidFetchState';
|
||||
import {
|
||||
isFetchingByE164,
|
||||
isFetchingByUsername,
|
||||
} from '../../../../util/uuidFetchState';
|
||||
import { ModalHost } from '../../../ModalHost';
|
||||
import { ContactPills } from '../../../ContactPills';
|
||||
import { ContactPill } from '../../../ContactPill';
|
||||
|
@ -53,6 +57,7 @@ export type StatePropsType = {
|
|||
removeSelectedContact: (_: string) => void;
|
||||
setSearchTerm: (_: string) => void;
|
||||
toggleSelectedContact: (conversationId: string) => void;
|
||||
isUsernamesEnabled: boolean;
|
||||
} & Pick<
|
||||
LookupConversationWithoutUuidActionsType,
|
||||
'lookupConversationWithoutUuid'
|
||||
|
@ -83,6 +88,7 @@ export const ChooseGroupMembersModal: FunctionComponent<PropsType> = ({
|
|||
toggleSelectedContact,
|
||||
lookupConversationWithoutUuid,
|
||||
showUserNotFoundModal,
|
||||
isUsernamesEnabled,
|
||||
}) => {
|
||||
const [focusRef] = useRestoreFocus();
|
||||
|
||||
|
@ -99,6 +105,21 @@ export const ChooseGroupMembersModal: FunctionComponent<PropsType> = ({
|
|||
phoneNumber &&
|
||||
candidateContacts.every(contact => contact.e164 !== phoneNumber.e164);
|
||||
|
||||
let username: string | undefined;
|
||||
let isUsernameChecked = false;
|
||||
let isUsernameVisible = false;
|
||||
if (!phoneNumber && isUsernamesEnabled) {
|
||||
username = getUsernameFromSearch(searchTerm);
|
||||
|
||||
isUsernameChecked = selectedContacts.some(
|
||||
contact => contact.username === username
|
||||
);
|
||||
|
||||
isUsernameVisible = candidateContacts.every(
|
||||
contact => contact.username !== username
|
||||
);
|
||||
}
|
||||
|
||||
const inputRef = useRef<null | HTMLInputElement>(null);
|
||||
|
||||
const numberOfContactsAlreadyInGroup = conversationIdsAlreadyInGroup.size;
|
||||
|
@ -157,19 +178,24 @@ export const ChooseGroupMembersModal: FunctionComponent<PropsType> = ({
|
|||
if (filteredContacts.length) {
|
||||
rowCount += filteredContacts.length;
|
||||
}
|
||||
if (isPhoneNumberVisible) {
|
||||
if (isPhoneNumberVisible || isUsernameVisible) {
|
||||
// "Contacts" header
|
||||
if (filteredContacts.length) {
|
||||
rowCount += 1;
|
||||
}
|
||||
|
||||
// "Find by phone number" + phone number
|
||||
// or
|
||||
// "Find by username" + username
|
||||
rowCount += 2;
|
||||
}
|
||||
const getRow = (index: number): undefined | Row => {
|
||||
let virtualIndex = index;
|
||||
|
||||
if (isPhoneNumberVisible && filteredContacts.length) {
|
||||
if (
|
||||
(isPhoneNumberVisible || isUsernameVisible) &&
|
||||
filteredContacts.length
|
||||
) {
|
||||
if (virtualIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
|
@ -221,6 +247,24 @@ export const ChooseGroupMembersModal: FunctionComponent<PropsType> = ({
|
|||
virtualIndex -= 2;
|
||||
}
|
||||
|
||||
if (username) {
|
||||
if (virtualIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
};
|
||||
}
|
||||
if (virtualIndex === 1) {
|
||||
return {
|
||||
type: RowType.UsernameCheckbox,
|
||||
isChecked: isUsernameChecked,
|
||||
isFetching: isFetchingByUsername(uuidFetchState, username),
|
||||
username,
|
||||
};
|
||||
}
|
||||
virtualIndex -= 2;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
|
|
|
@ -110,6 +110,7 @@ const createProps = (hasGroupLink = false, expireTimer?: number): Props => ({
|
|||
i18n={i18n}
|
||||
lookupConversationWithoutUuid={makeFakeLookupConversationWithoutUuid()}
|
||||
showUserNotFoundModal={action('showUserNotFoundModal')}
|
||||
isUsernamesEnabled
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
|
|
@ -30,6 +30,7 @@ export type ContactListItemConversationType = Pick<
|
|||
| 'title'
|
||||
| 'type'
|
||||
| 'unblurredAvatarPath'
|
||||
| 'username'
|
||||
| 'e164'
|
||||
>;
|
||||
|
||||
|
|
84
ts/components/conversationList/UsernameCheckbox.tsx
Normal file
84
ts/components/conversationList/UsernameCheckbox.tsx
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { FunctionComponent } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { BaseConversationListItem } from './BaseConversationListItem';
|
||||
import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||
import { AvatarColors } from '../../types/Colors';
|
||||
import type { LookupConversationWithoutUuidActionsType } from '../../util/lookupConversationWithoutUuid';
|
||||
|
||||
export type PropsDataType = {
|
||||
username: string;
|
||||
isChecked: boolean;
|
||||
isFetching: boolean;
|
||||
};
|
||||
|
||||
type PropsHousekeepingType = {
|
||||
i18n: LocalizerType;
|
||||
theme: ThemeType;
|
||||
toggleConversationInChooseMembers: (conversationId: string) => void;
|
||||
} & LookupConversationWithoutUuidActionsType;
|
||||
|
||||
type PropsType = PropsDataType & PropsHousekeepingType;
|
||||
|
||||
export const UsernameCheckbox: FunctionComponent<PropsType> = React.memo(
|
||||
function UsernameCheckbox({
|
||||
username,
|
||||
isChecked,
|
||||
isFetching,
|
||||
theme,
|
||||
i18n,
|
||||
lookupConversationWithoutUuid,
|
||||
showUserNotFoundModal,
|
||||
setIsFetchingUUID,
|
||||
toggleConversationInChooseMembers,
|
||||
}) {
|
||||
const onClickItem = React.useCallback(async () => {
|
||||
if (isFetching) {
|
||||
return;
|
||||
}
|
||||
|
||||
const conversationId = await lookupConversationWithoutUuid({
|
||||
showUserNotFoundModal,
|
||||
setIsFetchingUUID,
|
||||
|
||||
type: 'username',
|
||||
username,
|
||||
});
|
||||
|
||||
if (conversationId !== undefined) {
|
||||
toggleConversationInChooseMembers(conversationId);
|
||||
}
|
||||
}, [
|
||||
isFetching,
|
||||
toggleConversationInChooseMembers,
|
||||
lookupConversationWithoutUuid,
|
||||
showUserNotFoundModal,
|
||||
setIsFetchingUUID,
|
||||
username,
|
||||
]);
|
||||
|
||||
const title = i18n('at-username', { username });
|
||||
|
||||
return (
|
||||
<BaseConversationListItem
|
||||
acceptedMessageRequest={false}
|
||||
checked={isChecked}
|
||||
color={AvatarColors[0]}
|
||||
conversationType="direct"
|
||||
headerName={title}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
isSelected={false}
|
||||
isUsernameSearchResult
|
||||
onClick={onClickItem}
|
||||
shouldShowSpinner={isFetching}
|
||||
theme={theme}
|
||||
sharedGroupNames={[]}
|
||||
title={title}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -18,10 +18,14 @@ import {
|
|||
} from '../AddGroupMemberErrorDialog';
|
||||
import { Button } from '../Button';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { getUsernameFromSearch } from '../../types/Username';
|
||||
import type { ParsedE164Type } from '../../util/libphonenumberInstance';
|
||||
import { parseAndFormatPhoneNumber } from '../../util/libphonenumberInstance';
|
||||
import type { UUIDFetchStateType } from '../../util/uuidFetchState';
|
||||
import { isFetchingByE164 } from '../../util/uuidFetchState';
|
||||
import {
|
||||
isFetchingByUsername,
|
||||
isFetchingByE164,
|
||||
} from '../../util/uuidFetchState';
|
||||
import {
|
||||
getGroupSizeRecommendedLimit,
|
||||
getGroupSizeHardLimit,
|
||||
|
@ -32,6 +36,7 @@ export type LeftPaneChooseGroupMembersPropsType = {
|
|||
candidateContacts: ReadonlyArray<ConversationType>;
|
||||
isShowingRecommendedGroupSizeModal: boolean;
|
||||
isShowingMaximumGroupSizeModal: boolean;
|
||||
isUsernamesEnabled: boolean;
|
||||
searchTerm: string;
|
||||
regionCode: string | undefined;
|
||||
selectedContacts: Array<ConversationType>;
|
||||
|
@ -42,6 +47,8 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
|
||||
private readonly isPhoneNumberChecked: boolean;
|
||||
|
||||
private readonly isUsernameChecked: boolean;
|
||||
|
||||
private readonly isShowingMaximumGroupSizeModal: boolean;
|
||||
|
||||
private readonly isShowingRecommendedGroupSizeModal: boolean;
|
||||
|
@ -50,6 +57,8 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
|
||||
private readonly phoneNumber: ParsedE164Type | undefined;
|
||||
|
||||
private readonly username: string | undefined;
|
||||
|
||||
private readonly selectedContacts: Array<ConversationType>;
|
||||
|
||||
private readonly selectedConversationIdsSet: Set<string>;
|
||||
|
@ -60,6 +69,7 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
candidateContacts,
|
||||
isShowingMaximumGroupSizeModal,
|
||||
isShowingRecommendedGroupSizeModal,
|
||||
isUsernamesEnabled,
|
||||
searchTerm,
|
||||
regionCode,
|
||||
selectedContacts,
|
||||
|
@ -90,6 +100,22 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
} else {
|
||||
this.isPhoneNumberChecked = false;
|
||||
}
|
||||
if (!this.phoneNumber && isUsernamesEnabled) {
|
||||
const username = getUsernameFromSearch(searchTerm);
|
||||
const isVisible = this.candidateContacts.every(
|
||||
contact => contact.username !== username
|
||||
);
|
||||
|
||||
if (isVisible) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
this.isUsernameChecked = selectedContacts.some(
|
||||
contact => contact.username === this.username
|
||||
);
|
||||
} else {
|
||||
this.isUsernameChecked = false;
|
||||
}
|
||||
this.selectedContacts = selectedContacts;
|
||||
|
||||
this.selectedConversationIdsSet = new Set(
|
||||
|
@ -246,6 +272,11 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
rowCount += 2;
|
||||
}
|
||||
|
||||
// Header + Username
|
||||
if (this.username) {
|
||||
rowCount += 2;
|
||||
}
|
||||
|
||||
// Header + Contacts
|
||||
if (this.candidateContacts.length) {
|
||||
rowCount += 1 + this.candidateContacts.length;
|
||||
|
@ -260,7 +291,7 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
}
|
||||
|
||||
getRow(actualRowIndex: number): undefined | Row {
|
||||
if (!this.candidateContacts.length && !this.phoneNumber) {
|
||||
if (!this.candidateContacts.length && !this.phoneNumber && !this.username) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
@ -322,6 +353,24 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
virtualRowIndex -= 2;
|
||||
}
|
||||
|
||||
if (this.username) {
|
||||
if (virtualRowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
};
|
||||
}
|
||||
if (virtualRowIndex === 1) {
|
||||
return {
|
||||
type: RowType.UsernameCheckbox,
|
||||
isChecked: this.isUsernameChecked,
|
||||
isFetching: isFetchingByUsername(this.uuidFetchState, this.username),
|
||||
username: this.username,
|
||||
};
|
||||
}
|
||||
virtualRowIndex -= 2;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,14 +43,16 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
|
||||
private readonly uuidFetchState: UUIDFetchStateType;
|
||||
|
||||
private readonly isUsernamesEnabled: boolean;
|
||||
|
||||
private readonly searchTerm: string;
|
||||
|
||||
private readonly phoneNumber: ParsedE164Type | undefined;
|
||||
|
||||
private readonly isPhoneNumberVisible: boolean;
|
||||
|
||||
private readonly username: string | undefined;
|
||||
|
||||
private readonly isUsernameVisible: boolean;
|
||||
|
||||
constructor({
|
||||
composeContacts,
|
||||
composeGroups,
|
||||
|
@ -74,7 +76,18 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
this.isPhoneNumberVisible = false;
|
||||
}
|
||||
this.uuidFetchState = uuidFetchState;
|
||||
this.isUsernamesEnabled = isUsernamesEnabled;
|
||||
|
||||
if (isUsernamesEnabled && !this.phoneNumber) {
|
||||
this.username = getUsernameFromSearch(this.searchTerm);
|
||||
this.isUsernameVisible =
|
||||
isUsernamesEnabled &&
|
||||
Boolean(this.username) &&
|
||||
this.composeContacts.every(
|
||||
contact => contact.username !== this.username
|
||||
);
|
||||
} else {
|
||||
this.isUsernameVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
override getHeaderContents({
|
||||
|
@ -148,7 +161,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
if (this.hasGroupsHeader()) {
|
||||
result += 1;
|
||||
}
|
||||
if (this.getUsernameFromSearch()) {
|
||||
if (this.isUsernameVisible) {
|
||||
result += 2;
|
||||
}
|
||||
if (this.isPhoneNumberVisible) {
|
||||
|
@ -218,8 +231,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
virtualRowIndex -= this.composeGroups.length;
|
||||
}
|
||||
|
||||
const username = this.getUsernameFromSearch();
|
||||
if (username) {
|
||||
if (this.username && this.isUsernameVisible) {
|
||||
if (virtualRowIndex === 0) {
|
||||
return {
|
||||
type: RowType.Header,
|
||||
|
@ -232,10 +244,10 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
if (virtualRowIndex === 0) {
|
||||
return {
|
||||
type: RowType.UsernameSearchResult,
|
||||
username,
|
||||
username: this.username,
|
||||
isFetchingUsername: isFetchingByUsername(
|
||||
this.uuidFetchState,
|
||||
username
|
||||
this.username
|
||||
),
|
||||
};
|
||||
|
||||
|
@ -295,7 +307,8 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
currHeaderIndices.top !== prevHeaderIndices.top ||
|
||||
currHeaderIndices.contact !== prevHeaderIndices.contact ||
|
||||
currHeaderIndices.group !== prevHeaderIndices.group ||
|
||||
currHeaderIndices.username !== prevHeaderIndices.username
|
||||
currHeaderIndices.username !== prevHeaderIndices.username ||
|
||||
currHeaderIndices.phoneNumber !== prevHeaderIndices.phoneNumber
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -318,31 +331,17 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
return Boolean(this.composeGroups.length);
|
||||
}
|
||||
|
||||
private getUsernameFromSearch(): string | undefined {
|
||||
if (!this.isUsernamesEnabled) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this.phoneNumber) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this.searchTerm) {
|
||||
return getUsernameFromSearch(this.searchTerm);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private getHeaderIndices(): {
|
||||
top?: number;
|
||||
contact?: number;
|
||||
group?: number;
|
||||
phoneNumber?: number;
|
||||
username?: number;
|
||||
} {
|
||||
let top: number | undefined;
|
||||
let contact: number | undefined;
|
||||
let group: number | undefined;
|
||||
let phoneNumber: number | undefined;
|
||||
let username: number | undefined;
|
||||
|
||||
let rowCount = 0;
|
||||
|
@ -359,7 +358,10 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
group = rowCount;
|
||||
rowCount += this.composeContacts.length;
|
||||
}
|
||||
if (this.getUsernameFromSearch()) {
|
||||
if (this.phoneNumber) {
|
||||
phoneNumber = rowCount;
|
||||
}
|
||||
if (this.username) {
|
||||
username = rowCount;
|
||||
}
|
||||
|
||||
|
@ -367,6 +369,7 @@ export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsTy
|
|||
top,
|
||||
contact,
|
||||
group,
|
||||
phoneNumber,
|
||||
username,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import type { StatePropsType } from '../../components/conversation/conversation-
|
|||
import { ChooseGroupMembersModal } from '../../components/conversation/conversation-details/AddGroupMembersModal/ChooseGroupMembersModal';
|
||||
|
||||
import { getIntl, getTheme, getRegionCode } from '../selectors/user';
|
||||
import { getUsernamesEnabled } from '../selectors/items';
|
||||
import {
|
||||
getCandidateContactsForNewGroup,
|
||||
getConversationByIdSelector,
|
||||
|
@ -55,6 +56,7 @@ const mapStateToProps = (
|
|||
theme: getTheme(state),
|
||||
selectedContacts,
|
||||
lookupConversationWithoutUuid,
|
||||
isUsernamesEnabled: getUsernamesEnabled(state),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -156,6 +156,7 @@ const getModeSpecificProps = (
|
|||
regionCode: getRegionCode(state),
|
||||
searchTerm: getComposerConversationSearchTerm(state),
|
||||
selectedContacts: getComposeSelectedContacts(state),
|
||||
isUsernamesEnabled: getUsernamesEnabled(state),
|
||||
uuidFetchState: getComposerUUIDFetchState(state),
|
||||
};
|
||||
case ComposerStep.SetGroupMetadata:
|
||||
|
|
|
@ -17,6 +17,7 @@ describe('LeftPaneChooseGroupMembersHelper', () => {
|
|||
candidateContacts: [],
|
||||
isShowingRecommendedGroupSizeModal: false,
|
||||
isShowingMaximumGroupSizeModal: false,
|
||||
isUsernamesEnabled: true,
|
||||
searchTerm: '',
|
||||
regionCode: 'US',
|
||||
selectedContacts: [],
|
||||
|
@ -62,6 +63,7 @@ describe('LeftPaneChooseGroupMembersHelper', () => {
|
|||
candidateContacts: [],
|
||||
searchTerm: 'foo bar',
|
||||
selectedContacts: [getDefaultConversation()],
|
||||
isUsernamesEnabled: false,
|
||||
}).getRowCount(),
|
||||
0
|
||||
);
|
||||
|
@ -107,6 +109,7 @@ describe('LeftPaneChooseGroupMembersHelper', () => {
|
|||
candidateContacts: [],
|
||||
searchTerm: 'foo bar',
|
||||
selectedContacts: [getDefaultConversation()],
|
||||
isUsernamesEnabled: false,
|
||||
}).getRow(0)
|
||||
);
|
||||
});
|
||||
|
@ -120,6 +123,7 @@ describe('LeftPaneChooseGroupMembersHelper', () => {
|
|||
...defaults,
|
||||
candidateContacts,
|
||||
searchTerm: 'foo bar',
|
||||
isUsernamesEnabled: false,
|
||||
selectedContacts: [candidateContacts[1]],
|
||||
});
|
||||
|
||||
|
@ -164,5 +168,51 @@ describe('LeftPaneChooseGroupMembersHelper', () => {
|
|||
disabledReason: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a header, then the phone number, then a blank space if there are contacts', () => {
|
||||
const helper = new LeftPaneChooseGroupMembersHelper({
|
||||
...defaults,
|
||||
candidateContacts: [],
|
||||
searchTerm: '212 555',
|
||||
selectedContacts: [],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByPhoneNumberHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.PhoneNumberCheckbox,
|
||||
phoneNumber: {
|
||||
isValid: false,
|
||||
userInput: '212 555',
|
||||
e164: '+1212555',
|
||||
},
|
||||
isChecked: false,
|
||||
isFetching: false,
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), { type: RowType.Blank });
|
||||
});
|
||||
|
||||
it('returns a header, then the username, then a blank space if there are contacts', () => {
|
||||
const helper = new LeftPaneChooseGroupMembersHelper({
|
||||
...defaults,
|
||||
candidateContacts: [],
|
||||
searchTerm: 'signal',
|
||||
selectedContacts: [],
|
||||
});
|
||||
|
||||
assert.deepEqual(helper.getRow(0), {
|
||||
type: RowType.Header,
|
||||
i18nKey: 'findByUsernameHeader',
|
||||
});
|
||||
assert.deepEqual(helper.getRow(1), {
|
||||
type: RowType.UsernameCheckbox,
|
||||
username: 'signal',
|
||||
isChecked: false,
|
||||
isFetching: false,
|
||||
});
|
||||
assert.deepEqual(helper.getRow(2), { type: RowType.Blank });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1626,7 +1626,7 @@ export function initialize({
|
|||
return (await _ajax({
|
||||
call: 'profile',
|
||||
httpType: 'GET',
|
||||
urlParameters: `username/${usernameToFetch}`,
|
||||
urlParameters: `/username/${usernameToFetch}`,
|
||||
responseType: 'json',
|
||||
redactUrl: _createRedactor(usernameToFetch),
|
||||
})) as ProfileType;
|
||||
|
|
Loading…
Add table
Reference in a new issue