Skip to content

Commit

Permalink
Store all story reactions as messages
Browse files Browse the repository at this point in the history
  • Loading branch information
indutny-signal committed Nov 2, 2022
1 parent f136117 commit 54aa0d3
Show file tree
Hide file tree
Showing 12 changed files with 236 additions and 222 deletions.
2 changes: 1 addition & 1 deletion ts/components/StoryViewsNRepliesModal.tsx
Expand Up @@ -111,7 +111,7 @@ export type PropsType = {
preferredReactionEmoji: Array<string>;
recentEmojis?: Array<string>;
renderEmojiPicker: (props: RenderEmojiPickerProps) => JSX.Element;
replies: Array<ReplyType>;
replies: ReadonlyArray<ReplyType>;
skinTone?: number;
sortedGroupMembers?: Array<ConversationType>;
storyPreviewAttachment?: AttachmentType;
Expand Down
48 changes: 46 additions & 2 deletions ts/jobs/helpers/sendNormalMessage.ts
Expand Up @@ -4,19 +4,22 @@
import { isNumber } from 'lodash';

import * as Errors from '../../types/errors';
import { strictAssert } from '../../util/assert';
import type { MessageModel } from '../../models/messages';
import { getMessageById } from '../../messages/getMessageById';
import type { ConversationModel } from '../../models/conversations';
import { isGroup, isGroupV2, isMe } from '../../util/whatTypeOfConversation';
import { getSendOptions } from '../../util/getSendOptions';
import { SignalService as Proto } from '../../protobuf';
import { handleMessageSend } from '../../util/handleMessageSend';
import { findAndFormatContact } from '../../util/findAndFormatContact';
import type { CallbackResultType } from '../../textsecure/Types.d';
import { isSent } from '../../messages/MessageSendState';
import { isOutgoing } from '../../state/selectors/message';
import { isOutgoing, canReact } from '../../state/selectors/message';
import type {
AttachmentType,
ContactWithHydratedAvatar,
ReactionType,
} from '../../textsecure/SendMessage';
import type { LinkPreviewType } from '../../types/message/LinkPreviews';
import type { BodyRangesType, StoryContextType } from '../../types/Util';
Expand Down Expand Up @@ -149,9 +152,37 @@ export async function sendNormalMessage(
preview,
quote,
sticker,
storyMessage,
storyContext,
reaction,
} = await getMessageSendData({ log, message });

if (reaction) {
strictAssert(
storyMessage,
'Only story reactions can be sent as normal messages'
);

const ourConversationId =
window.ConversationController.getOurConversationIdOrThrow();

if (
!canReact(
storyMessage.attributes,
ourConversationId,
findAndFormatContact
)
) {
log.info(
`could not react to ${messageId}. Removing this pending reaction`
);
await markMessageFailed(message, [
new Error('Could not react to story'),
]);
return;
}
}

let messageSendPromise: Promise<CallbackResultType | void>;

if (recipientIdentifiersWithoutMe.length === 0) {
Expand Down Expand Up @@ -185,6 +216,7 @@ export async function sendNormalMessage(
sticker,
// No storyContext; you can't reply to your own stories
timestamp: messageTimestamp,
reaction,
});
messageSendPromise = message.sendSyncMessageOnly(dataMessage, saveErrors);
} else {
Expand Down Expand Up @@ -228,6 +260,7 @@ export async function sendNormalMessage(
quote,
sticker,
storyContext,
reaction,
timestamp: messageTimestamp,
mentions,
},
Expand Down Expand Up @@ -280,9 +313,9 @@ export async function sendNormalMessage(
preview,
profileKey,
quote,
reaction: undefined,
sticker,
storyContext,
reaction,
timestamp: messageTimestamp,
// Note: 1:1 story replies should not set story=true - they aren't group sends
urgent: true,
Expand Down Expand Up @@ -436,6 +469,8 @@ async function getMessageSendData({
preview: Array<LinkPreviewType>;
quote: QuotedMessageType | null;
sticker: StickerWithHydratedData | undefined;
reaction: ReactionType | undefined;
storyMessage?: MessageModel;
storyContext?: StoryContextType;
}> {
const {
Expand Down Expand Up @@ -488,6 +523,8 @@ async function getMessageSendData({
}
);

const storyReaction = message.get('storyReaction');

return {
attachments,
body,
Expand All @@ -499,12 +536,19 @@ async function getMessageSendData({
preview,
quote,
sticker,
storyMessage,
storyContext: storyMessage
? {
authorUuid: storyMessage.get('sourceUuid'),
timestamp: storyMessage.get('sent_at'),
}
: undefined,
reaction: storyReaction
? {
...storyReaction,
remove: false,
}
: undefined,
};
}

Expand Down
38 changes: 11 additions & 27 deletions ts/jobs/helpers/sendReaction.ts
Expand Up @@ -4,6 +4,7 @@
import { isNumber } from 'lodash';

import * as Errors from '../../types/errors';
import { strictAssert } from '../../util/assert';
import { repeat, zipObject } from '../../util/iterables';
import type { CallbackResultType } from '../../textsecure/Types.d';
import type { MessageModel } from '../../models/messages';
Expand Down Expand Up @@ -63,11 +64,16 @@ export async function sendReaction(
return;
}

strictAssert(
!isStory(message.attributes),
'Story reactions should be handled by sendStoryReaction'
);
const { pendingReaction, emojiToRemove } =
reactionUtil.getNewestPendingOutgoingReaction(
getReactions(message),
ourConversationId
);

if (!pendingReaction) {
log.info(`no pending reaction for ${messageId}. Doing nothing`);
return;
Expand Down Expand Up @@ -153,17 +159,7 @@ export async function sendReaction(
),
});

if (
isStory(message.attributes) &&
isDirectConversation(conversation.attributes)
) {
ephemeralMessageForReactionSend.set({
storyId: message.id,
storyReactionEmoji: reactionForSend.emoji,
});
} else {
ephemeralMessageForReactionSend.doNotSave = true;
}
ephemeralMessageForReactionSend.doNotSave = true;

let didFullySend: boolean;
const successfulConversationIds = new Set<string>();
Expand Down Expand Up @@ -233,12 +229,6 @@ export async function sendReaction(
groupId: undefined,
profileKey,
options: sendOptions,
storyContext: isStory(message.attributes)
? {
authorUuid: message.get('sourceUuid'),
timestamp: message.get('sent_at'),
}
: undefined,
urgent: true,
includePniSignatureMessage: true,
});
Expand Down Expand Up @@ -271,12 +261,6 @@ export async function sendReaction(
timestamp: pendingReaction.timestamp,
expireTimer,
profileKey,
storyContext: isStory(message.attributes)
? {
authorUuid: message.get('sourceUuid'),
timestamp: message.get('sent_at'),
}
: undefined,
},
messageId,
sendOptions,
Expand Down Expand Up @@ -346,8 +330,7 @@ export async function sendReaction(
const newReactions = reactionUtil.markOutgoingReactionSent(
getReactions(message),
pendingReaction,
successfulConversationIds,
message.attributes
successfulConversationIds
);
setReactions(message, newReactions);

Expand All @@ -372,8 +355,9 @@ export async function sendReaction(
}
}

const getReactions = (message: MessageModel): Array<MessageReactionType> =>
message.get('reactions') || [];
const getReactions = (
message: MessageModel
): ReadonlyArray<MessageReactionType> => message.get('reactions') || [];

const setReactions = (
message: MessageModel,
Expand Down
13 changes: 7 additions & 6 deletions ts/messageModifiers/Reactions.ts
Expand Up @@ -169,14 +169,15 @@ export class Reactions extends Collection<ReactionModel> {
);

// Use the generated message in ts/background.ts to create a message
// if the reaction is targetted at a story on a 1:1 conversation.
if (
isStory(targetMessage) &&
isDirectConversation(targetConversation.attributes)
) {
// if the reaction is targetted at a story.
if (isStory(targetMessage)) {
generatedMessage.set({
storyId: targetMessage.id,
storyReactionEmoji: reaction.get('emoji'),
storyReaction: {
emoji: reaction.get('emoji'),
targetAuthorUuid: reaction.get('targetAuthorUuid'),
targetTimestamp: reaction.get('targetTimestamp'),
},
});

// Note: generatedMessage comes with an id, so we have to force this save
Expand Down
8 changes: 6 additions & 2 deletions ts/model-types.d.ts
Expand Up @@ -146,7 +146,7 @@ export type MessageAttributesType = {
messageTimer?: unknown;
profileChange?: ProfileNameChangeType;
quote?: QuotedMessageType;
reactions?: Array<MessageReactionType>;
reactions?: ReadonlyArray<MessageReactionType>;
requiredProtocolVersion?: number;
retryOptions?: RetryOptions;
sourceDevice?: number;
Expand Down Expand Up @@ -184,7 +184,11 @@ export type MessageAttributesType = {
unidentifiedDeliveries?: Array<string>;
contact?: Array<EmbeddedContactType>;
conversationId: string;
storyReactionEmoji?: string;
storyReaction?: {
emoji: string;
targetAuthorUuid: string;
targetTimestamp: number;
};
giftBadge?: {
expiration: number;
level: number;
Expand Down

0 comments on commit 54aa0d3

Please sign in to comment.