mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2025-12-24 12:19:41 +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.alpha',
|
||||||
'desktop.pollReceive.beta',
|
'desktop.pollReceive.beta',
|
||||||
'desktop.pollReceive.prod',
|
'desktop.pollReceive.prod',
|
||||||
|
'desktop.pollSend.alpha',
|
||||||
|
'desktop.pollSend.beta',
|
||||||
|
'desktop.pollSend.prod',
|
||||||
'global.attachments.maxBytes',
|
'global.attachments.maxBytes',
|
||||||
'global.attachments.maxReceiveBytes',
|
'global.attachments.maxReceiveBytes',
|
||||||
'global.backups.mediaTierFallbackCdnNumber',
|
'global.backups.mediaTierFallbackCdnNumber',
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ import { copyCdnFields } from '../../util/attachments.preload.js';
|
|||||||
import type { RawBodyRange } from '../../types/BodyRange.std.js';
|
import type { RawBodyRange } from '../../types/BodyRange.std.js';
|
||||||
import type { EmbeddedContactWithUploadedAvatar } from '../../types/EmbeddedContact.std.js';
|
import type { EmbeddedContactWithUploadedAvatar } from '../../types/EmbeddedContact.std.js';
|
||||||
import type { StoryContextType } from '../../types/Util.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 type { LoggerType } from '../../types/Logging.std.js';
|
||||||
import { GROUP } from '../../types/Message2.preload.js';
|
import { GROUP } from '../../types/Message2.preload.js';
|
||||||
import type {
|
import type {
|
||||||
@@ -231,6 +232,7 @@ export async function sendNormalMessage(
|
|||||||
sticker,
|
sticker,
|
||||||
storyMessage,
|
storyMessage,
|
||||||
storyContext,
|
storyContext,
|
||||||
|
poll,
|
||||||
} = await getMessageSendData({
|
} = await getMessageSendData({
|
||||||
log,
|
log,
|
||||||
message,
|
message,
|
||||||
@@ -317,6 +319,7 @@ export async function sendNormalMessage(
|
|||||||
: undefined,
|
: undefined,
|
||||||
timestamp: targetTimestamp,
|
timestamp: targetTimestamp,
|
||||||
reaction,
|
reaction,
|
||||||
|
pollCreate: poll,
|
||||||
});
|
});
|
||||||
messageSendPromise = sendSyncMessageOnly(message, {
|
messageSendPromise = sendSyncMessageOnly(message, {
|
||||||
dataMessage,
|
dataMessage,
|
||||||
@@ -362,6 +365,7 @@ export async function sendNormalMessage(
|
|||||||
sticker,
|
sticker,
|
||||||
storyContext,
|
storyContext,
|
||||||
reaction,
|
reaction,
|
||||||
|
pollCreate: poll,
|
||||||
targetTimestampForEdit: editedMessageTimestamp
|
targetTimestampForEdit: editedMessageTimestamp
|
||||||
? targetOfThisEditTimestamp
|
? targetOfThisEditTimestamp
|
||||||
: undefined,
|
: undefined,
|
||||||
@@ -619,6 +623,7 @@ async function getMessageSendData({
|
|||||||
reaction: ReactionType | undefined;
|
reaction: ReactionType | undefined;
|
||||||
storyMessage?: MessageModel;
|
storyMessage?: MessageModel;
|
||||||
storyContext?: StoryContextType;
|
storyContext?: StoryContextType;
|
||||||
|
poll?: PollCreateType;
|
||||||
}> {
|
}> {
|
||||||
const storyId = message.get('storyId');
|
const storyId = message.get('storyId');
|
||||||
|
|
||||||
@@ -761,6 +766,7 @@ async function getMessageSendData({
|
|||||||
timestamp: storyMessage.get('sent_at'),
|
timestamp: storyMessage.get('sent_at'),
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
|
poll: message.get('poll'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,7 @@ import type {
|
|||||||
LinkPreviewType,
|
LinkPreviewType,
|
||||||
LinkPreviewWithHydratedData,
|
LinkPreviewWithHydratedData,
|
||||||
} from '../types/message/LinkPreviews.std.js';
|
} from '../types/message/LinkPreviews.std.js';
|
||||||
|
import type { PollCreateType } from '../types/Polls.dom.js';
|
||||||
import {
|
import {
|
||||||
MINUTE,
|
MINUTE,
|
||||||
SECOND,
|
SECOND,
|
||||||
@@ -4022,6 +4023,7 @@ export class ConversationModel {
|
|||||||
preview,
|
preview,
|
||||||
quote,
|
quote,
|
||||||
sticker,
|
sticker,
|
||||||
|
poll,
|
||||||
}: {
|
}: {
|
||||||
attachments: Array<AttachmentType>;
|
attachments: Array<AttachmentType>;
|
||||||
body: string | undefined;
|
body: string | undefined;
|
||||||
@@ -4030,6 +4032,7 @@ export class ConversationModel {
|
|||||||
preview?: Array<LinkPreviewWithHydratedData>;
|
preview?: Array<LinkPreviewWithHydratedData>;
|
||||||
quote?: QuotedMessageType;
|
quote?: QuotedMessageType;
|
||||||
sticker?: StickerWithHydratedData;
|
sticker?: StickerWithHydratedData;
|
||||||
|
poll?: PollCreateType;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
dontClearDraft = false,
|
dontClearDraft = false,
|
||||||
@@ -4154,6 +4157,7 @@ export class ConversationModel {
|
|||||||
})
|
})
|
||||||
),
|
),
|
||||||
storyId,
|
storyId,
|
||||||
|
poll,
|
||||||
});
|
});
|
||||||
|
|
||||||
const model = window.MessageCache.register(new MessageModel(attributes));
|
const model = window.MessageCache.register(new MessageModel(attributes));
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ import {
|
|||||||
import { MAX_MESSAGE_COUNT } from '../util/deleteForMe.types.std.js';
|
import { MAX_MESSAGE_COUNT } from '../util/deleteForMe.types.std.js';
|
||||||
import { isProtoBinaryEncodingEnabled } from '../util/isProtoBinaryEncodingEnabled.std.js';
|
import { isProtoBinaryEncodingEnabled } from '../util/isProtoBinaryEncodingEnabled.std.js';
|
||||||
import type { GroupSendToken } from '../types/GroupSendEndorsements.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 { itemStorage } from './Storage.preload.js';
|
||||||
import { accountManager } from './AccountManager.preload.js';
|
import { accountManager } from './AccountManager.preload.js';
|
||||||
|
|
||||||
@@ -214,6 +215,7 @@ export type MessageOptionsType = {
|
|||||||
recipients: ReadonlyArray<ServiceIdString>;
|
recipients: ReadonlyArray<ServiceIdString>;
|
||||||
sticker?: OutgoingStickerType;
|
sticker?: OutgoingStickerType;
|
||||||
reaction?: ReactionType;
|
reaction?: ReactionType;
|
||||||
|
pollCreate?: PollCreateType;
|
||||||
deletedForEveryoneTimestamp?: number;
|
deletedForEveryoneTimestamp?: number;
|
||||||
targetTimestampForEdit?: number;
|
targetTimestampForEdit?: number;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
@@ -238,6 +240,7 @@ export type GroupSendOptionsType = {
|
|||||||
sticker?: OutgoingStickerType;
|
sticker?: OutgoingStickerType;
|
||||||
storyContext?: StoryContextType;
|
storyContext?: StoryContextType;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
pollCreate?: PollCreateType;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Message {
|
class Message {
|
||||||
@@ -276,6 +279,8 @@ class Message {
|
|||||||
|
|
||||||
reaction?: ReactionType;
|
reaction?: ReactionType;
|
||||||
|
|
||||||
|
pollCreate?: PollCreateType;
|
||||||
|
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
|
||||||
dataMessage?: Proto.DataMessage;
|
dataMessage?: Proto.DataMessage;
|
||||||
@@ -303,6 +308,7 @@ class Message {
|
|||||||
this.recipients = options.recipients;
|
this.recipients = options.recipients;
|
||||||
this.sticker = options.sticker;
|
this.sticker = options.sticker;
|
||||||
this.reaction = options.reaction;
|
this.reaction = options.reaction;
|
||||||
|
this.pollCreate = options.pollCreate;
|
||||||
this.timestamp = options.timestamp;
|
this.timestamp = options.timestamp;
|
||||||
this.deletedForEveryoneTimestamp = options.deletedForEveryoneTimestamp;
|
this.deletedForEveryoneTimestamp = options.deletedForEveryoneTimestamp;
|
||||||
this.groupCallUpdate = options.groupCallUpdate;
|
this.groupCallUpdate = options.groupCallUpdate;
|
||||||
@@ -627,6 +633,14 @@ class Message {
|
|||||||
proto.storyContext = storyContext;
|
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;
|
this.dataMessage = proto;
|
||||||
return proto;
|
return proto;
|
||||||
}
|
}
|
||||||
@@ -938,6 +952,7 @@ export class MessageSender {
|
|||||||
storyContext,
|
storyContext,
|
||||||
targetTimestampForEdit,
|
targetTimestampForEdit,
|
||||||
timestamp,
|
timestamp,
|
||||||
|
pollCreate,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
if (!groupV2) {
|
if (!groupV2) {
|
||||||
@@ -981,6 +996,7 @@ export class MessageSender {
|
|||||||
storyContext,
|
storyContext,
|
||||||
targetTimestampForEdit,
|
targetTimestampForEdit,
|
||||||
timestamp,
|
timestamp,
|
||||||
|
pollCreate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,12 +80,18 @@ export type PollMessageAttribute = {
|
|||||||
terminatedAt?: number;
|
terminatedAt?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PollCreateType = Pick<
|
||||||
|
PollMessageAttribute,
|
||||||
|
'question' | 'options' | 'allowMultiple'
|
||||||
|
>;
|
||||||
|
|
||||||
export function isPollReceiveEnabled(): boolean {
|
export function isPollReceiveEnabled(): boolean {
|
||||||
const env = getEnvironment();
|
const env = getEnvironment();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
env === Environment.Development ||
|
env === Environment.Development ||
|
||||||
env === Environment.Test ||
|
env === Environment.Test ||
|
||||||
|
env === Environment.Staging ||
|
||||||
isMockEnvironment()
|
isMockEnvironment()
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
@@ -107,3 +113,32 @@ export function isPollReceiveEnabled(): boolean {
|
|||||||
|
|
||||||
return false;
|
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 { isProduction } from '../../util/version.std.js';
|
||||||
import { benchmarkConversationOpen } from '../../CI/benchmarkConversationOpen.preload.js';
|
import { benchmarkConversationOpen } from '../../CI/benchmarkConversationOpen.preload.js';
|
||||||
import { itemStorage } from '../../textsecure/Storage.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;
|
const { has } = lodash;
|
||||||
|
|
||||||
@@ -117,6 +122,18 @@ if (
|
|||||||
},
|
},
|
||||||
setRtcStatsInterval: (intervalMillis: number) =>
|
setRtcStatsInterval: (intervalMillis: number) =>
|
||||||
calling.setAllRtcStatsInterval(intervalMillis),
|
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'
|
...(window.SignalContext.config.ciMode === 'benchmark'
|
||||||
? {
|
? {
|
||||||
benchmarkConversationOpen,
|
benchmarkConversationOpen,
|
||||||
|
|||||||
Reference in New Issue
Block a user