Add ability to send poll create messages

This commit is contained in:
yash-signal
2025-10-21 16:40:10 -05:00
committed by GitHub
parent 9d34375bb7
commit 1ddb81e053
7 changed files with 113 additions and 0 deletions

View File

@@ -41,6 +41,9 @@ const KnownConfigKeys = [
'desktop.pollReceive.alpha',
'desktop.pollReceive.beta',
'desktop.pollReceive.prod',
'desktop.pollSend.alpha',
'desktop.pollSend.beta',
'desktop.pollSend.prod',
'global.attachments.maxBytes',
'global.attachments.maxReceiveBytes',
'global.backups.mediaTierFallbackCdnNumber',

View File

@@ -46,6 +46,7 @@ import { copyCdnFields } from '../../util/attachments.preload.js';
import type { RawBodyRange } from '../../types/BodyRange.std.js';
import type { EmbeddedContactWithUploadedAvatar } from '../../types/EmbeddedContact.std.js';
import type { StoryContextType } from '../../types/Util.std.js';
import type { PollCreateType } from '../../types/Polls.dom.js';
import type { LoggerType } from '../../types/Logging.std.js';
import { GROUP } from '../../types/Message2.preload.js';
import type {
@@ -231,6 +232,7 @@ export async function sendNormalMessage(
sticker,
storyMessage,
storyContext,
poll,
} = await getMessageSendData({
log,
message,
@@ -317,6 +319,7 @@ export async function sendNormalMessage(
: undefined,
timestamp: targetTimestamp,
reaction,
pollCreate: poll,
});
messageSendPromise = sendSyncMessageOnly(message, {
dataMessage,
@@ -362,6 +365,7 @@ export async function sendNormalMessage(
sticker,
storyContext,
reaction,
pollCreate: poll,
targetTimestampForEdit: editedMessageTimestamp
? targetOfThisEditTimestamp
: undefined,
@@ -619,6 +623,7 @@ async function getMessageSendData({
reaction: ReactionType | undefined;
storyMessage?: MessageModel;
storyContext?: StoryContextType;
poll?: PollCreateType;
}> {
const storyId = message.get('storyId');
@@ -761,6 +766,7 @@ async function getMessageSendData({
timestamp: storyMessage.get('sent_at'),
}
: undefined,
poll: message.get('poll'),
};
}

View File

@@ -140,6 +140,7 @@ import type {
LinkPreviewType,
LinkPreviewWithHydratedData,
} from '../types/message/LinkPreviews.std.js';
import type { PollCreateType } from '../types/Polls.dom.js';
import {
MINUTE,
SECOND,
@@ -4022,6 +4023,7 @@ export class ConversationModel {
preview,
quote,
sticker,
poll,
}: {
attachments: Array<AttachmentType>;
body: string | undefined;
@@ -4030,6 +4032,7 @@ export class ConversationModel {
preview?: Array<LinkPreviewWithHydratedData>;
quote?: QuotedMessageType;
sticker?: StickerWithHydratedData;
poll?: PollCreateType;
},
{
dontClearDraft = false,
@@ -4154,6 +4157,7 @@ export class ConversationModel {
})
),
storyId,
poll,
});
const model = window.MessageCache.register(new MessageModel(attributes));

View File

@@ -101,6 +101,7 @@ import {
import { MAX_MESSAGE_COUNT } from '../util/deleteForMe.types.std.js';
import { isProtoBinaryEncodingEnabled } from '../util/isProtoBinaryEncodingEnabled.std.js';
import type { GroupSendToken } from '../types/GroupSendEndorsements.std.js';
import type { PollCreateType } from '../types/Polls.dom.js';
import { itemStorage } from './Storage.preload.js';
import { accountManager } from './AccountManager.preload.js';
@@ -214,6 +215,7 @@ export type MessageOptionsType = {
recipients: ReadonlyArray<ServiceIdString>;
sticker?: OutgoingStickerType;
reaction?: ReactionType;
pollCreate?: PollCreateType;
deletedForEveryoneTimestamp?: number;
targetTimestampForEdit?: number;
timestamp: number;
@@ -238,6 +240,7 @@ export type GroupSendOptionsType = {
sticker?: OutgoingStickerType;
storyContext?: StoryContextType;
timestamp: number;
pollCreate?: PollCreateType;
};
class Message {
@@ -276,6 +279,8 @@ class Message {
reaction?: ReactionType;
pollCreate?: PollCreateType;
timestamp: number;
dataMessage?: Proto.DataMessage;
@@ -303,6 +308,7 @@ class Message {
this.recipients = options.recipients;
this.sticker = options.sticker;
this.reaction = options.reaction;
this.pollCreate = options.pollCreate;
this.timestamp = options.timestamp;
this.deletedForEveryoneTimestamp = options.deletedForEveryoneTimestamp;
this.groupCallUpdate = options.groupCallUpdate;
@@ -627,6 +633,14 @@ class Message {
proto.storyContext = storyContext;
}
if (this.pollCreate) {
const create = new Proto.DataMessage.PollCreate();
create.question = this.pollCreate.question;
create.allowMultiple = Boolean(this.pollCreate.allowMultiple);
create.options = this.pollCreate.options.slice();
proto.pollCreate = create;
}
this.dataMessage = proto;
return proto;
}
@@ -938,6 +952,7 @@ export class MessageSender {
storyContext,
targetTimestampForEdit,
timestamp,
pollCreate,
} = options;
if (!groupV2) {
@@ -981,6 +996,7 @@ export class MessageSender {
storyContext,
targetTimestampForEdit,
timestamp,
pollCreate,
};
}

View File

@@ -80,12 +80,18 @@ export type PollMessageAttribute = {
terminatedAt?: number;
};
export type PollCreateType = Pick<
PollMessageAttribute,
'question' | 'options' | 'allowMultiple'
>;
export function isPollReceiveEnabled(): boolean {
const env = getEnvironment();
if (
env === Environment.Development ||
env === Environment.Test ||
env === Environment.Staging ||
isMockEnvironment()
) {
return true;
@@ -107,3 +113,32 @@ export function isPollReceiveEnabled(): boolean {
return false;
}
export function isPollSendEnabled(): boolean {
const env = getEnvironment();
if (
env === Environment.Development ||
env === Environment.Test ||
env === Environment.Staging ||
isMockEnvironment()
) {
return true;
}
const version = window.getVersion?.();
if (version != null) {
if (isProduction(version)) {
return RemoteConfig.isEnabled('desktop.pollSend.prod');
}
if (isBeta(version)) {
return RemoteConfig.isEnabled('desktop.pollSend.beta');
}
if (isAlpha(version)) {
return RemoteConfig.isEnabled('desktop.pollSend.alpha');
}
}
return false;
}

View File

@@ -0,0 +1,32 @@
// Copyright 2025 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import type { ConversationModel } from '../models/conversations.preload.js';
import { isGroupV2 } from './whatTypeOfConversation.dom.js';
import { isPollSendEnabled, type PollCreateType } from '../types/Polls.dom.js';
export async function enqueuePollCreateForSend(
conversation: ConversationModel,
poll: PollCreateType
): Promise<void> {
if (!isPollSendEnabled()) {
throw new Error('enqueuePollCreateForSend: poll sending is not enabled');
}
if (!isGroupV2(conversation.attributes)) {
throw new Error(
'enqueuePollCreateForSend: polls are group-only. Conversation is not GroupV2.'
);
}
await conversation.enqueueMessageForSend(
{
attachments: [],
body: undefined,
poll,
},
{
timestamp: Date.now(),
}
);
}

View File

@@ -31,6 +31,11 @@ import { Environment, getEnvironment } from '../../environment.std.js';
import { isProduction } from '../../util/version.std.js';
import { benchmarkConversationOpen } from '../../CI/benchmarkConversationOpen.preload.js';
import { itemStorage } from '../../textsecure/Storage.preload.js';
import { enqueuePollCreateForSend } from '../../util/enqueuePollCreateForSend.dom.js';
import {
isPollSendEnabled,
type PollCreateType,
} from '../../types/Polls.dom.js';
const { has } = lodash;
@@ -117,6 +122,18 @@ if (
},
setRtcStatsInterval: (intervalMillis: number) =>
calling.setAllRtcStatsInterval(intervalMillis),
sendPollInSelectedConversation: async (poll: PollCreateType) => {
if (!isPollSendEnabled()) {
throw new Error('Poll sending is not enabled');
}
const conversationId =
window.reduxStore.getState().conversations.selectedConversationId;
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error('No conversation selected');
}
await enqueuePollCreateForSend(conversation, poll);
},
...(window.SignalContext.config.ciMode === 'benchmark'
? {
benchmarkConversationOpen,