Make sure that storySendMode is in group types

This commit is contained in:
Fedor Indutny 2022-11-19 00:31:18 -08:00 committed by GitHub
parent fa2fd5eef3
commit a2f1b469ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 101 additions and 110 deletions

View file

@ -24,6 +24,7 @@ import { setupI18n } from '../util/setupI18n';
import type { SafetyNumberProps } from './SafetyNumberChangeDialog'; import type { SafetyNumberProps } from './SafetyNumberChangeDialog';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
import { ThemeType } from '../types/Util'; import { ThemeType } from '../types/Util';
import { StorySendMode } from '../types/Stories';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -194,6 +195,8 @@ export function RingingGroupCall(): JSX.Element {
...getConversation(), ...getConversation(),
type: 'group', type: 'group',
title: 'Tahoe Trip', title: 'Tahoe Trip',
acknowledgedGroupNameCollisions: {},
storySendMode: StorySendMode.IfActive,
}, },
otherMembersRung: [ otherMembersRung: [
{ firstName: 'Morty', title: 'Morty Smith' }, { firstName: 'Morty', title: 'Morty Smith' },

View file

@ -7,6 +7,7 @@ import { times } from 'lodash';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { UUID } from '../../../types/UUID'; import { UUID } from '../../../types/UUID';
import { StorySendMode } from '../../../types/Stories';
import { setupI18n } from '../../../util/setupI18n'; import { setupI18n } from '../../../util/setupI18n';
import enMessages from '../../../../_locales/en/messages.json'; import enMessages from '../../../../_locales/en/messages.json';
import type { PropsType } from './PendingInvites'; import type { PropsType } from './PendingInvites';
@ -40,6 +41,8 @@ const conversation: ConversationType = {
title: 'Some Conversation', title: 'Some Conversation',
type: 'group', type: 'group',
sharedGroupNames: [], sharedGroupNames: [],
acknowledgedGroupNameCollisions: {},
storySendMode: StorySendMode.IfActive,
}; };
const OUR_UUID = UUID.generate().toString(); const OUR_UUID = UUID.generate().toString();

View file

@ -1884,7 +1884,6 @@ export class ConversationModel extends window.Backbone
groupVersion, groupVersion,
groupId: this.get('groupId'), groupId: this.get('groupId'),
groupLink: this.getGroupLink(), groupLink: this.getGroupLink(),
storySendMode: this.getStorySendMode(),
hideStory: Boolean(this.get('hideStory')), hideStory: Boolean(this.get('hideStory')),
inboxPosition, inboxPosition,
isArchived: this.get('isArchived'), isArchived: this.get('isArchived'),
@ -1945,6 +1944,7 @@ export class ConversationModel extends window.Backbone
acknowledgedGroupNameCollisions: acknowledgedGroupNameCollisions:
this.get('acknowledgedGroupNameCollisions') || {}, this.get('acknowledgedGroupNameCollisions') || {},
sharedGroupNames: [], sharedGroupNames: [],
storySendMode: this.getGroupStorySendMode(),
}), }),
voiceNotePlaybackRate: this.get('voiceNotePlaybackRate'), voiceNotePlaybackRate: this.get('voiceNotePlaybackRate'),
}; };
@ -5548,10 +5548,21 @@ export class ConversationModel extends window.Backbone
/** @return only undefined if not a group */ /** @return only undefined if not a group */
getStorySendMode(): StorySendMode | undefined { getStorySendMode(): StorySendMode | undefined {
if (!isGroup(this.attributes)) { // isDirectConversation is used instead of isGroup because this is what
// used in `format()` when sending conversation "type" to redux.
if (isDirectConversation(this.attributes)) {
return undefined; return undefined;
} }
return this.getGroupStorySendMode();
}
private getGroupStorySendMode(): StorySendMode {
strictAssert(
!isDirectConversation(this.attributes),
'Must be a group to have send story mode'
);
return this.get('storySendMode') ?? StorySendMode.IfActive; return this.get('storySendMode') ?? StorySendMode.IfActive;
} }
} }

View file

@ -192,7 +192,6 @@ export type ConversationType = {
bannedMemberships?: Array<UUIDStringType>; bannedMemberships?: Array<UUIDStringType>;
muteExpiresAt?: number; muteExpiresAt?: number;
dontNotifyForMentionsIfMuted?: boolean; dontNotifyForMentionsIfMuted?: boolean;
type: ConversationTypeType;
isMe: boolean; isMe: boolean;
lastUpdated?: number; lastUpdated?: number;
// This is used by the CompositionInput for @mentions // This is used by the CompositionInput for @mentions
@ -217,12 +216,10 @@ export type ConversationType = {
groupVersion?: 1 | 2; groupVersion?: 1 | 2;
groupId?: string; groupId?: string;
groupLink?: string; groupLink?: string;
storySendMode?: StorySendMode;
messageRequestsEnabled?: boolean; messageRequestsEnabled?: boolean;
acceptedMessageRequest: boolean; acceptedMessageRequest: boolean;
secretParams?: string; secretParams?: string;
publicParams?: string; publicParams?: string;
acknowledgedGroupNameCollisions?: GroupNameCollisionsWithIdsByTitle;
profileKey?: string; profileKey?: string;
voiceNotePlaybackRate?: number; voiceNotePlaybackRate?: number;
@ -236,7 +233,18 @@ export type ConversationType = {
isVisible: boolean; isVisible: boolean;
} }
>; >;
}; } & (
| {
type: 'direct';
storySendMode?: undefined;
acknowledgedGroupNameCollisions?: undefined;
}
| {
type: 'group';
storySendMode: StorySendMode;
acknowledgedGroupNameCollisions: GroupNameCollisionsWithIdsByTitle;
}
);
export type ProfileDataType = { export type ProfileDataType = {
firstName: string; firstName: string;
} & Pick<ConversationType, 'aboutEmoji' | 'aboutText' | 'familyName'>; } & Pick<ConversationType, 'aboutEmoji' | 'aboutText' | 'familyName'>;

View file

@ -214,7 +214,7 @@ const getWarning = (
return { return {
type: ContactSpoofingType.MultipleGroupMembersWithSameTitle, type: ContactSpoofingType.MultipleGroupMembersWithSameTitle,
acknowledgedGroupNameCollisions: acknowledgedGroupNameCollisions:
conversation.acknowledgedGroupNameCollisions || {}, conversation.acknowledgedGroupNameCollisions,
groupNameCollisions: groupNameCollisions:
dehydrateCollisionsWithConversations(groupNameCollisions), dehydrateCollisionsWithConversations(groupNameCollisions),
}; };
@ -223,7 +223,7 @@ const getWarning = (
return undefined; return undefined;
} }
default: default:
throw missingCaseError(conversation.type); throw missingCaseError(conversation);
} }
}; };
@ -251,6 +251,10 @@ const getContactSpoofingReview = (
), ),
}; };
case ContactSpoofingType.MultipleGroupMembersWithSameTitle: { case ContactSpoofingType.MultipleGroupMembersWithSameTitle: {
assertDev(
currentConversation.type === 'group',
'MultipleGroupMembersWithSameTitle: expects group conversation'
);
const { memberships } = getGroupMemberships( const { memberships } = getGroupMemberships(
currentConversation, currentConversation,
getConversationByUuid getConversationByUuid
@ -258,7 +262,7 @@ const getContactSpoofingReview = (
const groupNameCollisions = getCollisionsFromMemberships(memberships); const groupNameCollisions = getCollisionsFromMemberships(memberships);
const previouslyAcknowledgedTitlesById = invertIdsByTitle( const previouslyAcknowledgedTitlesById = invertIdsByTitle(
currentConversation.acknowledgedGroupNameCollisions || {} currentConversation.acknowledgedGroupNameCollisions
); );
const collisionInfoByTitle = mapValues( const collisionInfoByTitle = mapValues(

View file

@ -8,6 +8,7 @@ import { UUID } from '../../types/UUID';
import type { UUIDStringType } from '../../types/UUID'; import type { UUIDStringType } from '../../types/UUID';
import { getRandomColor } from './getRandomColor'; import { getRandomColor } from './getRandomColor';
import { ConversationColors } from '../../types/Colors'; import { ConversationColors } from '../../types/Colors';
import { StorySendMode } from '../../types/Stories';
export const getAvatarPath = (): string => export const getAvatarPath = (): string =>
sample([ sample([
@ -37,9 +38,11 @@ export function getDefaultConversation(
sharedGroupNames: [], sharedGroupNames: [],
title: `${firstName} ${lastName}`, title: `${firstName} ${lastName}`,
titleNoDefault: `${firstName} ${lastName}`, titleNoDefault: `${firstName} ${lastName}`,
type: 'direct' as const,
uuid: UUID.generate().toString(), uuid: UUID.generate().toString(),
...overrideProps, ...overrideProps,
type: 'direct' as const,
acknowledgedGroupNameCollisions: undefined,
storySendMode: undefined,
}; };
} }
@ -70,9 +73,11 @@ export function getDefaultGroup(
memberships, memberships,
sharedGroupNames: [], sharedGroupNames: [],
title: casual.title, title: casual.title,
type: 'group' as const,
uuid: UUID.generate().toString(), uuid: UUID.generate().toString(),
acknowledgedGroupNameCollisions: {},
storySendMode: StorySendMode.IfActive,
...overrideProps, ...overrideProps,
type: 'group' as const,
}; };
} }

View file

@ -51,6 +51,7 @@ import type { UUIDStringType } from '../../../types/UUID';
import enMessages from '../../../../_locales/en/messages.json'; import enMessages from '../../../../_locales/en/messages.json';
import { import {
getDefaultConversation, getDefaultConversation,
getDefaultGroup,
getDefaultConversationWithUuid, getDefaultConversationWithUuid,
} from '../../helpers/getDefaultConversation'; } from '../../helpers/getDefaultConversation';
import { import {
@ -77,6 +78,16 @@ describe('both/state/selectors/conversations-extra', () => {
}); });
} }
function makeGroup(id: string): ConversationType {
const title = `${id} title`;
return getDefaultGroup({
id,
searchableTitle: title,
title,
titleNoDefault: title,
});
}
function makeConversationWithUuid( function makeConversationWithUuid(
id: string id: string
): ConversationType & { uuid: UUIDStringType } { ): ConversationType & { uuid: UUIDStringType } {
@ -535,15 +546,13 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'A', title: 'A',
}, },
'convo-2': { 'convo-2': {
...makeConversation('convo-2'), ...makeGroup('convo-2'),
type: 'group',
isGroupV1AndDisabled: true, isGroupV1AndDisabled: true,
name: '2', name: '2',
title: 'Should Be Dropped (GV1)', title: 'Should Be Dropped (GV1)',
}, },
'convo-3': { 'convo-3': {
...makeConversation('convo-3'), ...makeGroup('convo-3'),
type: 'group',
name: 'B', name: 'B',
title: 'B', title: 'B',
}, },
@ -624,10 +633,8 @@ describe('both/state/selectors/conversations-extra', () => {
profileSharing: false, profileSharing: false,
}, },
'convo-1': { 'convo-1': {
...makeConversation('convo-1'), ...makeGroup('convo-1'),
type: 'group' as const,
name: 'Friends!', name: 'Friends!',
sharedGroupNames: [],
}, },
'convo-2': { 'convo-2': {
...makeConversation('convo-2'), ...makeConversation('convo-2'),
@ -707,10 +714,8 @@ describe('both/state/selectors/conversations-extra', () => {
name: 'Me!', name: 'Me!',
}, },
'convo-1': { 'convo-1': {
...makeConversation('convo-1'), ...makeGroup('convo-1'),
type: 'group' as const,
name: 'Friends!', name: 'Friends!',
sharedGroupNames: [],
}, },
'convo-2': { 'convo-2': {
...makeConversation('convo-2'), ...makeConversation('convo-2'),
@ -790,15 +795,11 @@ describe('both/state/selectors/conversations-extra', () => {
name: 'Me!', name: 'Me!',
}, },
'convo-1': { 'convo-1': {
...makeConversation('convo-1'), ...makeGroup('convo-1'),
type: 'group' as const,
name: 'Friends!', name: 'Friends!',
sharedGroupNames: [],
}, },
'convo-2': { 'convo-2': {
...makeConversation('convo-2'), ...makeGroup('convo-2'),
type: 'group' as const,
sharedGroupNames: [],
}, },
}, },
}, },
@ -816,22 +817,16 @@ describe('both/state/selectors/conversations-extra', () => {
...getEmptyState(), ...getEmptyState(),
conversationLookup: { conversationLookup: {
'convo-0': { 'convo-0': {
...makeConversation('convo-0'), ...makeGroup('convo-0'),
type: 'group' as const,
name: 'Family!', name: 'Family!',
isBlocked: true, isBlocked: true,
sharedGroupNames: [],
}, },
'convo-1': { 'convo-1': {
...makeConversation('convo-1'), ...makeGroup('convo-1'),
type: 'group' as const,
name: 'Friends!', name: 'Friends!',
sharedGroupNames: [],
}, },
'convo-2': { 'convo-2': {
...makeConversation('convo-2'), ...makeGroup('convo-2'),
type: 'group' as const,
sharedGroupNames: [],
}, },
}, },
}, },
@ -886,8 +881,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Should Be Dropped (no name, no profile sharing)', title: 'Should Be Dropped (no name, no profile sharing)',
}, },
'convo-3': { 'convo-3': {
...makeConversation('convo-3'), ...makeGroup('convo-3'),
type: 'group',
title: 'Should Be Dropped (group)', title: 'Should Be Dropped (group)',
}, },
'convo-4': { 'convo-4': {
@ -983,39 +977,29 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Should be dropped (contact)', title: 'Should be dropped (contact)',
}, },
'convo-3': { 'convo-3': {
...makeConversation('convo-3'), ...makeGroup('convo-3'),
type: 'group',
name: 'Hello World', name: 'Hello World',
title: 'Hello World', title: 'Hello World',
sharedGroupNames: [],
}, },
'convo-4': { 'convo-4': {
...makeConversation('convo-4'), ...makeGroup('convo-4'),
type: 'group',
isBlocked: true, isBlocked: true,
title: 'Should be dropped (blocked)', title: 'Should be dropped (blocked)',
sharedGroupNames: [],
}, },
'convo-5': { 'convo-5': {
...makeConversation('convo-5'), ...makeGroup('convo-5'),
type: 'group',
title: 'Unknown Group', title: 'Unknown Group',
sharedGroupNames: [],
}, },
'convo-6': { 'convo-6': {
...makeConversation('convo-6'), ...makeGroup('convo-6'),
type: 'group',
name: 'Signal', name: 'Signal',
title: 'Signal', title: 'Signal',
sharedGroupNames: [],
}, },
'convo-7': { 'convo-7': {
...makeConversation('convo-7'), ...makeGroup('convo-7'),
profileSharing: false, profileSharing: false,
type: 'group',
name: 'Signal Fake', name: 'Signal Fake',
title: 'Signal Fake', title: 'Signal Fake',
sharedGroupNames: [],
}, },
}, },
composer: { composer: {
@ -1070,10 +1054,8 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Should be dropped (has no name)', title: 'Should be dropped (has no name)',
}, },
'convo-3': { 'convo-3': {
...makeConversation('convo-3'), ...makeGroup('convo-3'),
type: 'group',
title: 'Should Be Dropped (group)', title: 'Should Be Dropped (group)',
sharedGroupNames: [],
}, },
'convo-4': { 'convo-4': {
...makeConversation('convo-4'), ...makeConversation('convo-4'),
@ -1654,17 +1636,18 @@ describe('both/state/selectors/conversations-extra', () => {
describe('#getContactNameColorSelector', () => { describe('#getContactNameColorSelector', () => {
it('returns the right color order sorted by UUID ASC', () => { it('returns the right color order sorted by UUID ASC', () => {
const group = makeConversation('group'); const group: ConversationType = {
group.type = 'group'; ...makeGroup('group'),
group.sortedGroupMembers = [ sortedGroupMembers: [
makeConversationWithUuid('fff'), makeConversationWithUuid('fff'),
makeConversationWithUuid('f00'), makeConversationWithUuid('f00'),
makeConversationWithUuid('e00'), makeConversationWithUuid('e00'),
makeConversationWithUuid('d00'), makeConversationWithUuid('d00'),
makeConversationWithUuid('c00'), makeConversationWithUuid('c00'),
makeConversationWithUuid('b00'), makeConversationWithUuid('b00'),
makeConversationWithUuid('a00'), makeConversationWithUuid('a00'),
]; ],
};
const state = { const state = {
...getEmptyRootState(), ...getEmptyRootState(),
conversations: { conversations: {

View file

@ -38,6 +38,7 @@ import { UUID } from '../../../types/UUID';
import { import {
getDefaultConversation, getDefaultConversation,
getDefaultConversationWithUuid, getDefaultConversationWithUuid,
getDefaultGroup,
} from '../../../test-both/helpers/getDefaultConversation'; } from '../../../test-both/helpers/getDefaultConversation';
import { getDefaultAvatars } from '../../../types/Avatar'; import { getDefaultAvatars } from '../../../types/Avatar';
import { import {
@ -111,6 +112,7 @@ describe('both/state/ducks/conversations', () => {
describe('helpers', () => { describe('helpers', () => {
describe('getConversationCallMode', () => { describe('getConversationCallMode', () => {
const fakeConversation: ConversationType = getDefaultConversation(); const fakeConversation: ConversationType = getDefaultConversation();
const fakeGroup: ConversationType = getDefaultGroup();
it("returns CallMode.None if you've left the conversation", () => { it("returns CallMode.None if you've left the conversation", () => {
assert.strictEqual( assert.strictEqual(
@ -155,19 +157,16 @@ describe('both/state/ducks/conversations', () => {
it('returns CallMode.None for v1 groups', () => { it('returns CallMode.None for v1 groups', () => {
assert.strictEqual( assert.strictEqual(
getConversationCallMode({ getConversationCallMode({
...fakeConversation, ...fakeGroup,
type: 'group',
groupVersion: 1, groupVersion: 1,
sharedGroupNames: [],
}), }),
CallMode.None CallMode.None
); );
assert.strictEqual( assert.strictEqual(
getConversationCallMode({ getConversationCallMode({
...fakeConversation, ...fakeGroup,
type: 'group', groupVersion: undefined,
sharedGroupNames: [],
}), }),
CallMode.None CallMode.None
); );
@ -183,10 +182,8 @@ describe('both/state/ducks/conversations', () => {
it('returns CallMode.Group if the conversation is a v2 group', () => { it('returns CallMode.Group if the conversation is a v2 group', () => {
assert.strictEqual( assert.strictEqual(
getConversationCallMode({ getConversationCallMode({
...fakeConversation, ...fakeGroup,
type: 'group',
groupVersion: 2, groupVersion: 2,
sharedGroupNames: [],
}), }),
CallMode.Group CallMode.Group
); );

View file

@ -10,6 +10,10 @@ import type {
ShallowChallengeError, ShallowChallengeError,
} from '../../../model-types.d'; } from '../../../model-types.d';
import type { ConversationType } from '../../../state/ducks/conversations'; import type { ConversationType } from '../../../state/ducks/conversations';
import {
getDefaultConversation,
getDefaultGroup,
} from '../../../test-both/helpers/getDefaultConversation';
import { import {
canDeleteForEveryone, canDeleteForEveryone,
@ -156,15 +160,7 @@ describe('state/selectors/messages', () => {
}); });
describe('canReact', () => { describe('canReact', () => {
const defaultConversation: ConversationType = { const defaultConversation = getDefaultConversation();
id: uuid(),
type: 'direct',
title: 'Test conversation',
isMe: false,
sharedGroupNames: [],
acceptedMessageRequest: true,
badges: [],
};
it('returns false for disabled v1 groups', () => { it('returns false for disabled v1 groups', () => {
const message = { const message = {
@ -172,8 +168,7 @@ describe('state/selectors/messages', () => {
type: 'incoming' as const, type: 'incoming' as const,
}; };
const getConversationById = () => ({ const getConversationById = () => ({
...defaultConversation, ...getDefaultGroup(),
type: 'group' as const,
isGroupV1AndDisabled: true, isGroupV1AndDisabled: true,
}); });
@ -249,8 +244,7 @@ describe('state/selectors/messages', () => {
}, },
}; };
const getConversationById = () => ({ const getConversationById = () => ({
...defaultConversation, ...getDefaultGroup(),
type: 'group' as const,
}); });
assert.isTrue(canReact(message, ourConversationId, getConversationById)); assert.isTrue(canReact(message, ourConversationId, getConversationById));
@ -284,8 +278,7 @@ describe('state/selectors/messages', () => {
type: 'incoming' as const, type: 'incoming' as const,
}; };
const getConversationById = () => ({ const getConversationById = () => ({
...defaultConversation, ...getDefaultGroup(),
type: 'group' as const,
isGroupV1AndDisabled: true, isGroupV1AndDisabled: true,
}); });
@ -360,10 +353,7 @@ describe('state/selectors/messages', () => {
}, },
}, },
}; };
const getConversationById = () => ({ const getConversationById = () => getDefaultGroup();
...defaultConversation,
type: 'group' as const,
});
assert.isTrue(canReply(message, ourConversationId, getConversationById)); assert.isTrue(canReply(message, ourConversationId, getConversationById));
}); });

View file

@ -4,20 +4,7 @@
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import { format, isValidNumber } from '../types/PhoneNumber'; import { format, isValidNumber } from '../types/PhoneNumber';
type FormattedContact = Partial<ConversationType> & const PLACEHOLDER_CONTACT: ConversationType = {
Pick<
ConversationType,
| 'acceptedMessageRequest'
| 'badges'
| 'id'
| 'isMe'
| 'sharedGroupNames'
| 'title'
| 'type'
| 'unblurredAvatarPath'
>;
const PLACEHOLDER_CONTACT: FormattedContact = {
acceptedMessageRequest: false, acceptedMessageRequest: false,
badges: [], badges: [],
id: 'placeholder-contact', id: 'placeholder-contact',
@ -27,7 +14,7 @@ const PLACEHOLDER_CONTACT: FormattedContact = {
type: 'direct', type: 'direct',
}; };
export function findAndFormatContact(identifier?: string): FormattedContact { export function findAndFormatContact(identifier?: string): ConversationType {
if (!identifier) { if (!identifier) {
return PLACEHOLDER_CONTACT; return PLACEHOLDER_CONTACT;
} }