mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-19 17:58:48 +00:00
Add ability to send poll create messages
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
32
ts/util/enqueuePollCreateForSend.dom.ts
Normal file
32
ts/util/enqueuePollCreateForSend.dom.ts
Normal 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(),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user