mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-09-08 14:20:01 +01:00
Prevent poll voting in ended groups
This commit is contained in:
@@ -55,6 +55,7 @@ const { noop, orderBy } = lodash;
|
||||
const MESSAGE_DEFAULT_PROPS = {
|
||||
canDeleteForEveryone: false,
|
||||
canRetryDeleteForEveryone: false,
|
||||
canSendPollVote: false,
|
||||
retryDeleteForEveryone: shouldNeverBeCalled,
|
||||
checkForAccount: shouldNeverBeCalled,
|
||||
clearTargetedMessage: shouldNeverBeCalled,
|
||||
|
||||
@@ -270,6 +270,7 @@ export type PropsData = {
|
||||
isSpoilerExpanded?: Record<number, boolean>;
|
||||
isVoiceMessagePlayed: boolean;
|
||||
canEndPoll?: boolean;
|
||||
canSendPollVote: boolean;
|
||||
direction: DirectionType;
|
||||
timestamp: number;
|
||||
receivedAtMS?: number;
|
||||
@@ -2082,7 +2083,8 @@ export class Message extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
public renderPoll(): React.JSX.Element | null {
|
||||
const { poll, direction, i18n, id, endPoll, canEndPoll } = this.props;
|
||||
const { poll, direction, i18n, id, endPoll, canEndPoll, canSendPollVote } =
|
||||
this.props;
|
||||
if (!poll) {
|
||||
return null;
|
||||
}
|
||||
@@ -2095,6 +2097,7 @@ export class Message extends React.PureComponent<Props, State> {
|
||||
sendPollVote={this.props.sendPollVote}
|
||||
endPoll={endPoll}
|
||||
canEndPoll={canEndPoll}
|
||||
canSendPollVote={canSendPollVote}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ const defaultMessage: MessageDataPropsType = {
|
||||
}),
|
||||
canDeleteForEveryone: true,
|
||||
canRetryDeleteForEveryone: false,
|
||||
canSendPollVote: true,
|
||||
conversationColor: 'crimson',
|
||||
conversationId: 'my-convo',
|
||||
conversationTitle: 'Conversation Title',
|
||||
|
||||
@@ -82,6 +82,7 @@ const defaultMessageProps: TimelineMessagesProps = {
|
||||
canReply: true,
|
||||
canRetry: true,
|
||||
canRetryDeleteForEveryone: true,
|
||||
canSendPollVote: true,
|
||||
canDeleteForEveryone: true,
|
||||
canDownload: true,
|
||||
checkForAccount: action('checkForAccount'),
|
||||
|
||||
@@ -48,6 +48,7 @@ function mockMessageTimelineItem(
|
||||
canReact: true,
|
||||
canReply: true,
|
||||
canRetry: true,
|
||||
canSendPollVote: true,
|
||||
conversationId: 'conversation-id',
|
||||
conversationTitle: 'Conversation Title',
|
||||
conversationType: 'group',
|
||||
|
||||
@@ -242,6 +242,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||
canPinMessage: overrideProps.canPinMessage ?? true,
|
||||
canReact: true,
|
||||
canReply: true,
|
||||
canSendPollVote: true,
|
||||
canDownload: true,
|
||||
canDeleteForEveryone: overrideProps.canDeleteForEveryone || false,
|
||||
canForward: true,
|
||||
|
||||
@@ -250,6 +250,7 @@ export function GroupMemberLabelEditor({
|
||||
isPinned={false}
|
||||
canDeleteForEveryone={false}
|
||||
canRetryDeleteForEveryone={false}
|
||||
canSendPollVote={false}
|
||||
retryDeleteForEveryone={noop}
|
||||
isBlocked={false}
|
||||
isMessageRequestAccepted={false}
|
||||
|
||||
@@ -49,7 +49,7 @@ type PollCheckboxProps = {
|
||||
};
|
||||
|
||||
const PollCheckbox = memo((props: PollCheckboxProps) => {
|
||||
const { isIncoming, isPending, checked } = props;
|
||||
const { checked, isIncoming, isPending, onCheckedChange } = props;
|
||||
|
||||
let bgColor: TailwindStyles;
|
||||
let borderColor: TailwindStyles;
|
||||
@@ -107,8 +107,8 @@ const PollCheckbox = memo((props: PollCheckboxProps) => {
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<Checkbox.Root
|
||||
checked={props.checked}
|
||||
onCheckedChange={props.onCheckedChange}
|
||||
checked={checked}
|
||||
onCheckedChange={onCheckedChange}
|
||||
className={tw(
|
||||
'flex size-6 items-center justify-center rounded-full',
|
||||
isPending ? '' : 'border-[1.5px]',
|
||||
@@ -146,12 +146,13 @@ export type PollMessageContentsProps = {
|
||||
direction: DirectionType;
|
||||
i18n: LocalizerType;
|
||||
messageId: string;
|
||||
canEndPoll?: boolean;
|
||||
canSendPollVote: boolean;
|
||||
sendPollVote: (params: {
|
||||
messageId: string;
|
||||
optionIndexes: ReadonlyArray<number>;
|
||||
}) => void;
|
||||
endPoll: (messageId: string) => void;
|
||||
canEndPoll?: boolean;
|
||||
};
|
||||
|
||||
const DELAY_BEFORE_SHOWING_PENDING_ANIMATION = 500;
|
||||
@@ -160,9 +161,10 @@ export function PollMessageContents({
|
||||
direction,
|
||||
i18n,
|
||||
messageId,
|
||||
canEndPoll,
|
||||
canSendPollVote,
|
||||
sendPollVote,
|
||||
endPoll,
|
||||
canEndPoll,
|
||||
}: PollMessageContentsProps): React.JSX.Element {
|
||||
const [showVotesModal, setShowVotesModal] = useState(false);
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
@@ -281,7 +283,7 @@ export function PollMessageContents({
|
||||
return (
|
||||
// oxlint-disable-next-line react/no-array-index-key
|
||||
<div key={`option-${index}`} className={tw('flex gap-3')}>
|
||||
{poll.terminatedAt == null && (
|
||||
{canSendPollVote && poll.terminatedAt == null && (
|
||||
// 3px offset: type-body-large has 14px font-size and 20px line-height,
|
||||
// creating 3px space above text. This aligns checkbox with text baseline.
|
||||
<div className={tw('mt-[3px] self-start')}>
|
||||
|
||||
@@ -32,7 +32,10 @@ import { strictAssert } from '../../util/assert.std.js';
|
||||
import { DataWriter } from '../../sql/Client.preload.js';
|
||||
import { cleanupMessages } from '../../util/cleanup.preload.js';
|
||||
import { addPniSignatureMessageToProto } from '../../textsecure/SendMessage.preload.js';
|
||||
import { shouldSendToDirectConversation } from './shouldSendToConversation.preload.js';
|
||||
import {
|
||||
shouldSendToConversation,
|
||||
shouldSendToDirectConversation,
|
||||
} from './shouldSendToConversation.preload.js';
|
||||
import { handleMessageSend } from '../../util/handleMessageSend.preload.js';
|
||||
|
||||
const { isNumber } = lodash;
|
||||
@@ -187,6 +190,11 @@ export async function sendPollTerminate(
|
||||
`${logId}: expected GroupV2 conversation when not direct`
|
||||
);
|
||||
|
||||
const shouldSend = shouldSendToConversation(conversation, jobLog);
|
||||
if (!shouldSend) {
|
||||
return;
|
||||
}
|
||||
|
||||
await conversation.queueJob(
|
||||
'conversationQueue/sendPollTerminate',
|
||||
async abortSignal => {
|
||||
|
||||
@@ -30,7 +30,10 @@ import { getSendRecipientLists } from './getSendRecipientLists.dom.js';
|
||||
import { isDirectConversation } from '../../util/whatTypeOfConversation.dom.js';
|
||||
import type { CallbackResultType } from '../../textsecure/Types.d.ts';
|
||||
import { addPniSignatureMessageToProto } from '../../textsecure/SendMessage.preload.js';
|
||||
import { shouldSendToDirectConversation } from './shouldSendToConversation.preload.js';
|
||||
import {
|
||||
shouldSendToConversation,
|
||||
shouldSendToDirectConversation,
|
||||
} from './shouldSendToConversation.preload.js';
|
||||
|
||||
export async function sendPollVote(
|
||||
conversation: ConversationModel,
|
||||
@@ -98,19 +101,7 @@ export async function sendPollVote(
|
||||
|
||||
if (!shouldContinue) {
|
||||
jobLog.info('sendPollVote: ran out of time; giving up');
|
||||
const pollField = pollMessage.get('poll');
|
||||
if (pollField?.votes) {
|
||||
const updatedVotes = pollVoteUtil.markOutgoingPollVoteFailed(
|
||||
pollField.votes,
|
||||
currentPendingVote
|
||||
);
|
||||
pollMessage.set({
|
||||
poll: {
|
||||
...pollField,
|
||||
votes: updatedVotes,
|
||||
},
|
||||
});
|
||||
}
|
||||
setMessagePollVoteFailed(pollMessage, currentPendingVote);
|
||||
await window.MessageCache.saveMessage(pollMessage.attributes);
|
||||
return;
|
||||
}
|
||||
@@ -251,6 +242,13 @@ export async function sendPollVote(
|
||||
urgent: true,
|
||||
});
|
||||
} else {
|
||||
const shouldSend = shouldSendToConversation(conversation, jobLog);
|
||||
if (!shouldSend) {
|
||||
setMessagePollVoteFailed(pollMessage, currentPendingVote);
|
||||
await window.MessageCache.saveMessage(pollMessage.attributes);
|
||||
return;
|
||||
}
|
||||
|
||||
jobLog.info('sending group poll vote message');
|
||||
promise = conversation.queueJob(
|
||||
'conversationQueue/sendPollVote',
|
||||
@@ -363,19 +361,7 @@ export async function sendPollVote(
|
||||
log: jobLog,
|
||||
markFailed: () => {
|
||||
jobLog.info('poll vote send failed');
|
||||
const updatedPoll = pollMessage.get('poll');
|
||||
if (updatedPoll?.votes && pendingVote) {
|
||||
const updatedVotes = pollVoteUtil.markOutgoingPollVoteFailed(
|
||||
updatedPoll.votes,
|
||||
pendingVote
|
||||
);
|
||||
pollMessage.set({
|
||||
poll: {
|
||||
...updatedPoll,
|
||||
votes: updatedVotes,
|
||||
},
|
||||
});
|
||||
}
|
||||
setMessagePollVoteFailed(pollMessage, pendingVote);
|
||||
},
|
||||
timeRemaining,
|
||||
toThrow: originalError || thrownError,
|
||||
@@ -384,3 +370,24 @@ export async function sendPollVote(
|
||||
await window.MessageCache.saveMessage(pollMessage.attributes);
|
||||
}
|
||||
}
|
||||
|
||||
function setMessagePollVoteFailed(
|
||||
message: MessageModel,
|
||||
pendingVote: MessagePollVoteType | undefined
|
||||
): void {
|
||||
const poll = message.get('poll');
|
||||
if (!poll?.votes || pendingVote == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedVotes = pollVoteUtil.markOutgoingPollVoteFailed(
|
||||
poll.votes,
|
||||
pendingVote
|
||||
);
|
||||
message.set({
|
||||
poll: {
|
||||
...poll,
|
||||
votes: updatedVotes,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,6 +42,13 @@ export function shouldSendToConversation(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (conversation.get('terminated')) {
|
||||
log.info(
|
||||
`conversation ${conversation.idForLogging()} is terminated; refusing to send`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isSignalConversation(conversation.attributes)) {
|
||||
log.info(
|
||||
`conversation ${conversation.idForLogging()} is Signal conversation; refusing to send`
|
||||
|
||||
@@ -973,6 +973,7 @@ export const getPropsForMessage = (
|
||||
conversation,
|
||||
ourAci,
|
||||
}),
|
||||
canSendPollVote: !isGroupTerminated,
|
||||
contact: getPropsForEmbeddedContact(message, regionCode, accountSelector),
|
||||
contactLabel,
|
||||
contactNameColor,
|
||||
|
||||
Reference in New Issue
Block a user