Sort contacts and conversations alphabetically when composing message
This commit is contained in:
parent
65b6d9c2bc
commit
83c1acedd8
4 changed files with 87 additions and 12 deletions
|
@ -30,7 +30,10 @@ import { deconstructLookup } from '../../util/deconstructLookup';
|
||||||
import type { PropsDataType as TimelinePropsType } from '../../components/conversation/Timeline';
|
import type { PropsDataType as TimelinePropsType } from '../../components/conversation/Timeline';
|
||||||
import { assertDev } from '../../util/assert';
|
import { assertDev } from '../../util/assert';
|
||||||
import { isConversationUnregistered } from '../../util/isConversationUnregistered';
|
import { isConversationUnregistered } from '../../util/isConversationUnregistered';
|
||||||
import { filterAndSortConversationsByRecent } from '../../util/filterAndSortConversations';
|
import {
|
||||||
|
filterAndSortConversationsAlphabetically,
|
||||||
|
filterAndSortConversationsByRecent,
|
||||||
|
} from '../../util/filterAndSortConversations';
|
||||||
import type { ContactNameColorType } from '../../types/Colors';
|
import type { ContactNameColorType } from '../../types/Colors';
|
||||||
import { ContactNameColors } from '../../types/Colors';
|
import { ContactNameColors } from '../../types/Colors';
|
||||||
import type { AvatarDataType } from '../../types/Avatar';
|
import type { AvatarDataType } from '../../types/Avatar';
|
||||||
|
@ -648,7 +651,11 @@ export const getFilteredComposeContacts = createSelector(
|
||||||
contacts: ReadonlyArray<ConversationType>,
|
contacts: ReadonlyArray<ConversationType>,
|
||||||
regionCode: string | undefined
|
regionCode: string | undefined
|
||||||
): Array<ConversationType> => {
|
): Array<ConversationType> => {
|
||||||
return filterAndSortConversationsByRecent(contacts, searchTerm, regionCode);
|
return filterAndSortConversationsAlphabetically(
|
||||||
|
contacts,
|
||||||
|
searchTerm,
|
||||||
|
regionCode
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -667,7 +674,7 @@ export const getFilteredComposeGroups = createSelector(
|
||||||
memberships: ReadonlyArray<unknown>;
|
memberships: ReadonlyArray<unknown>;
|
||||||
}
|
}
|
||||||
> => {
|
> => {
|
||||||
return filterAndSortConversationsByRecent(
|
return filterAndSortConversationsAlphabetically(
|
||||||
groups,
|
groups,
|
||||||
searchTerm,
|
searchTerm,
|
||||||
regionCode
|
regionCode
|
||||||
|
|
|
@ -923,10 +923,10 @@ describe('both/state/selectors/conversations-extra', () => {
|
||||||
|
|
||||||
const ids = result.map(contact => contact.id);
|
const ids = result.map(contact => contact.id);
|
||||||
assert.deepEqual(ids, [
|
assert.deepEqual(ids, [
|
||||||
'our-conversation-id',
|
|
||||||
'convo-1',
|
'convo-1',
|
||||||
'convo-5',
|
'convo-5',
|
||||||
'convo-6',
|
'convo-6',
|
||||||
|
'our-conversation-id',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,19 @@
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { getDefaultConversation } from '../helpers/getDefaultConversation';
|
import { getDefaultConversation } from '../helpers/getDefaultConversation';
|
||||||
|
|
||||||
import { filterAndSortConversationsByRecent } from '../../util/filterAndSortConversations';
|
import {
|
||||||
|
filterAndSortConversationsAlphabetically,
|
||||||
|
filterAndSortConversationsByRecent,
|
||||||
|
} from '../../util/filterAndSortConversations';
|
||||||
|
|
||||||
describe('filterAndSortConversationsByRecent', () => {
|
describe('filterAndSortConversations', () => {
|
||||||
const conversations = [
|
const conversations = [
|
||||||
getDefaultConversation({
|
getDefaultConversation({
|
||||||
title: '+16505551234',
|
title: '+16505551234',
|
||||||
activeAt: 1,
|
activeAt: 1,
|
||||||
}),
|
}),
|
||||||
getDefaultConversation({
|
getDefaultConversation({
|
||||||
title: 'Abraham Lincoln',
|
title: 'The Abraham Lincoln Club',
|
||||||
activeAt: 4,
|
activeAt: 4,
|
||||||
}),
|
}),
|
||||||
getDefaultConversation({
|
getDefaultConversation({
|
||||||
|
@ -33,22 +36,50 @@ describe('filterAndSortConversationsByRecent', () => {
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
it('sorts by recency when no search term is provided', () => {
|
it('filterAndSortConversationsByRecent sorts by recency when no search term is provided', () => {
|
||||||
const titles = filterAndSortConversationsByRecent(
|
const titles = filterAndSortConversationsByRecent(
|
||||||
conversations,
|
conversations,
|
||||||
'',
|
'',
|
||||||
'US'
|
'US'
|
||||||
).map(contact => contact.title);
|
).map(contact => contact.title);
|
||||||
assert.sameMembers(titles, [
|
assert.sameOrderedMembers(titles, [
|
||||||
'+16505551234',
|
'The Abraham Lincoln Club',
|
||||||
'George Washington',
|
|
||||||
'Boxing Club',
|
'Boxing Club',
|
||||||
'Abraham Lincoln',
|
'George Washington',
|
||||||
|
'+16505551234',
|
||||||
'Not recent',
|
'Not recent',
|
||||||
'A long long long title ending with burrito',
|
'A long long long title ending with burrito',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('filterAndSortConversationsAlphabetically sorts by title when no search term is provided', () => {
|
||||||
|
const titles = filterAndSortConversationsAlphabetically(
|
||||||
|
conversations,
|
||||||
|
'',
|
||||||
|
'US'
|
||||||
|
).map(contact => contact.title);
|
||||||
|
assert.sameOrderedMembers(titles, [
|
||||||
|
'A long long long title ending with burrito',
|
||||||
|
'Boxing Club',
|
||||||
|
'George Washington',
|
||||||
|
'Not recent',
|
||||||
|
'The Abraham Lincoln Club',
|
||||||
|
'+16505551234',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filterAndSortConversationsAlphabetically sorts by title when a search term is provided', () => {
|
||||||
|
const titles = filterAndSortConversationsAlphabetically(
|
||||||
|
conversations,
|
||||||
|
'club',
|
||||||
|
'US'
|
||||||
|
).map(contact => contact.title);
|
||||||
|
assert.sameOrderedMembers(titles, [
|
||||||
|
'Boxing Club',
|
||||||
|
'The Abraham Lincoln Club',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('finds a conversation when the search term is at the end of a long title', () => {
|
it('finds a conversation when the search term is at the end of a long title', () => {
|
||||||
const titles = filterAndSortConversationsByRecent(
|
const titles = filterAndSortConversationsByRecent(
|
||||||
conversations,
|
conversations,
|
||||||
|
|
|
@ -163,3 +163,40 @@ export function filterAndSortConversationsByRecent(
|
||||||
return a.activeAt && !b.activeAt ? -1 : 1;
|
return a.activeAt && !b.activeAt ? -1 : 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function startsWithLetter(title: string) {
|
||||||
|
// Uses \p, the unicode character class escape, to check if a the first character is a
|
||||||
|
// letter
|
||||||
|
return /^\p{Letter}/u.test(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortAlphabetically(a: ConversationType, b: ConversationType) {
|
||||||
|
// Sort alphabetically with conversations starting with a letter first (and phone
|
||||||
|
// numbers last)
|
||||||
|
const aStartsWithLetter = startsWithLetter(a.title);
|
||||||
|
const bStartsWithLetter = startsWithLetter(b.title);
|
||||||
|
if (aStartsWithLetter && !bStartsWithLetter) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (!aStartsWithLetter && bStartsWithLetter) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return a.title.localeCompare(b.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterAndSortConversationsAlphabetically(
|
||||||
|
conversations: ReadonlyArray<ConversationType>,
|
||||||
|
searchTerm: string,
|
||||||
|
regionCode: string | undefined
|
||||||
|
): Array<ConversationType> {
|
||||||
|
if (searchTerm.length) {
|
||||||
|
const withoutUnknown = conversations.filter(item => item.titleNoDefault);
|
||||||
|
|
||||||
|
return searchConversations(withoutUnknown, searchTerm, regionCode)
|
||||||
|
.slice()
|
||||||
|
.map(result => result.item)
|
||||||
|
.sort(sortAlphabetically);
|
||||||
|
}
|
||||||
|
|
||||||
|
return conversations.concat().sort(sortAlphabetically);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue