Always allow reporting chats as spam

This commit is contained in:
ayumi-signal
2026-04-09 09:30:14 -07:00
committed by GitHub
parent 60a1e12545
commit 2bdfc4979e
21 changed files with 264 additions and 58 deletions
+24
View File
@@ -6508,6 +6508,10 @@
"messageformat": "Unblock group",
"description": "This is a button to unblock a group"
},
"icu:ConversationDetailsActions--report-spam": {
"messageformat": "Report spam",
"description": "This is a button to report spam"
},
"icu:ConversationDetailsActions--archive": {
"messageformat": "Archive chat",
"description": "This is a button to archive a chat"
@@ -6576,6 +6580,26 @@
"messageformat": "Unblock",
"description": "This is the modal button to confirm unblock of a group"
},
"icu:ConversationDetailsActions--report-spam-modal-title": {
"messageformat": "Report spam?",
"description": "This is the modal title for confirming reporting spam"
},
"icu:ConversationDetailsActions--report-spam-modal-content-direct": {
"messageformat": "Signal will be notified that this person may be sending spam. Signal cant see the content of any chats.",
"description": "This is the modal content for confirming reporting a 1-1 chat as spam"
},
"icu:ConversationDetailsActions--report-spam-modal-content-group": {
"messageformat": "Signal will be notified that the person who invited you to this group may be sending spam. Signal can't see the content of any chats.",
"description": "This is the modal content for confirming reporting a group as spam"
},
"icu:ConversationDetailsActions--report-spam-modal-report-spam": {
"messageformat": "Report spam",
"description": "This is the modal button to confirm reporting spam"
},
"icu:ConversationDetailsActions--report-spam-modal-report-and-block": {
"messageformat": "Report and block",
"description": "This is the modal button to confirm reporting spam and block"
},
"icu:ConversationDetailsHeader--members": {
"messageformat": "{number, plural, one {# member} other {# members}}",
"description": "This is the number of members in a group"
@@ -89,6 +89,7 @@
}
&__block-group,
&__report-spam,
&__terminate-group,
&__delete {
color: variables.$color-accent-red;
@@ -358,6 +359,16 @@
}
}
&--spam {
&::after {
@include details-icon(
'../images/icons/v3/spam/spam.svg',
variables.$color-accent-red,
variables.$color-accent-red
);
}
}
&--terminate {
&::after {
@include details-icon(
@@ -127,6 +127,7 @@ const createProps = (
setMuteExpiration: action('setMuteExpiration'),
showToast: action('showToast'),
userAvatarData: [],
reportSpam: action('reportSpam'),
terminateGroup: action('terminateGroup'),
toggleSafetyNumberModal: action('toggleSafetyNumberModal'),
toggleAboutContactModal: action('toggleAboutContactModal'),
@@ -143,6 +143,7 @@ type ActionProps = {
onOutgoingVideoCallInConversation: (conversationId: string) => unknown;
pushPanelForConversation: PushPanelForConversationActionType;
replaceAvatar: ReplaceAvatarActionType;
reportSpam: (id: string) => void;
saveAvatarToDisk: SaveAvatarToDiskActionType;
searchInConversation: (id: string) => unknown;
setDisappearingMessages: (id: string, seconds: DurationInSeconds) => void;
@@ -215,6 +216,7 @@ export function ConversationDetails({
renderChooseGroupMembersModal,
renderConfirmAdditionsModal,
replaceAvatar,
reportSpam,
saveAvatarToDisk,
searchInConversation,
selectedNavTab,
@@ -882,11 +884,17 @@ export function ConversationDetails({
isBlocked={Boolean(conversation.isBlocked)}
isGroup={isGroup}
isGroupTerminated={isGroupTerminated}
isSignalConversation={isSignalConversation}
left={Boolean(conversation.left)}
onArchive={onConversationArchive}
onDelete={onConversationDeleteMessages}
onUnarchive={onConversationUnarchive}
onLeave={() => leaveGroup(conversation.id)}
onReportSpam={() => reportSpam(conversation.id)}
onReportSpamAndBlock={() => {
reportSpam(conversation.id);
blockConversation(conversation.id);
}}
onTerminateGroup={() => terminateGroup(conversation.id)}
/>
)}
@@ -39,10 +39,13 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
isGroupTerminated: isBoolean(overrideProps.isGroupTerminated)
? overrideProps.isGroupTerminated
: false,
isSignalConversation: false,
left: isBoolean(overrideProps.left) ? overrideProps.left : false,
onArchive: action('onArchive'),
onDelete: action('onDelet'),
onLeave: action('onLeave'),
onReportSpamAndBlock: action('onBlockAndReportSpam'),
onReportSpam: action('onReportSpam'),
onTerminateGroup: action('onTerminateGroup'),
onUnarchive: action('onUnarchive'),
});
@@ -29,11 +29,14 @@ export type Props = {
isBlocked: boolean;
isGroup: boolean;
isGroupTerminated: boolean;
isSignalConversation: boolean;
left: boolean;
onArchive: () => void;
onDelete: () => void;
onUnarchive: () => void;
onLeave: () => void;
onReportSpam: () => void;
onReportSpamAndBlock: () => void;
onTerminateGroup: () => void;
};
@@ -49,11 +52,14 @@ export function ConversationDetailsActions({
isBlocked,
isGroup,
isGroupTerminated,
isSignalConversation,
left,
onArchive,
onDelete,
onUnarchive,
onLeave,
onReportSpamAndBlock,
onReportSpam,
onTerminateGroup,
}: Props): React.JSX.Element {
const [confirmLeave, gLeave] = useState<boolean>(false);
@@ -61,6 +67,7 @@ export function ConversationDetailsActions({
const [confirmGroupUnblock, gGroupUnblock] = useState<boolean>(false);
const [confirmDirectBlock, gDirectBlock] = useState<boolean>(false);
const [confirmDirectUnblock, gDirectUnblock] = useState<boolean>(false);
const [confirmReportSpam, gConfirmReportSpam] = useState<boolean>(false);
const [promptTerminateGroup, gPromptTerminateGroup] =
useState<boolean>(false);
const [confirmTerminateGroup, gConfirmTerminateGroup] =
@@ -184,6 +191,26 @@ export function ConversationDetailsActions({
);
}
let reportSpamNode: ReactNode;
if (!isSignalConversation) {
reportSpamNode = (
<PanelRow
onClick={() => gConfirmReportSpam(true)}
icon={
<ConversationDetailsIcon
ariaLabel={i18n('icu:ConversationDetailsActions--report-spam')}
icon={IconType.spam}
/>
}
label={
<div className="ConversationDetails__report-spam">
{i18n('icu:ConversationDetailsActions--report-spam')}
</div>
}
/>
);
}
let terminateGroupNode: ReactNode;
if (canTerminateGroup) {
terminateGroupNode = (
@@ -267,6 +294,7 @@ export function ConversationDetailsActions({
{blockNode}
{archiveNode}
{deleteNode}
{reportSpamNode}
</PanelSection>
{terminateGroupNode && <PanelSection>{terminateGroupNode}</PanelSection>}
{confirmLeave && (
@@ -379,6 +407,43 @@ export function ConversationDetailsActions({
</ConfirmationDialog>
)}
{confirmReportSpam && (
<ConfirmationDialog
dialogName="ConversationDetailsAction.confirmSpam"
actions={[
{
text: i18n(
'icu:ConversationDetailsActions--report-spam-modal-report-spam'
),
action: onReportSpam,
style: 'negative',
},
...(isBlocked
? []
: ([
{
text: i18n(
'icu:ConversationDetailsActions--report-spam-modal-report-and-block'
),
action: onReportSpamAndBlock,
style: 'negative',
},
] as const)),
]}
i18n={i18n}
onClose={() => gConfirmReportSpam(false)}
title={i18n('icu:MessageRequests--ReportAndMaybeBlockModal-title')}
>
{isGroup
? i18n(
'icu:ConversationDetailsActions--report-spam-modal-content-group'
)
: i18n(
'icu:ConversationDetailsActions--report-spam-modal-content-direct'
)}
</ConfirmationDialog>
)}
{promptTerminateGroup && (
<ConfirmationDialog
dialogName="ConversationDetailsAction.promptTerminateGroup"
@@ -32,6 +32,7 @@ export enum IconType {
'official' = 'official',
'reset' = 'reset',
'share' = 'share',
'spam' = 'spam',
'spinner' = 'spinner',
'tag' = 'tag',
'terminate' = 'terminate',
+6 -1
View File
@@ -3042,6 +3042,7 @@ type MaybeUpdatePropsType = Readonly<{
newRevision?: number;
receivedAt?: number;
sentAt?: number;
serverGuid?: string;
dropInitialJoinMessage?: boolean;
force?: boolean;
groupChange?: WrappedGroupChangeType;
@@ -3102,6 +3103,7 @@ export async function maybeUpdateGroup(
newRevision,
receivedAt,
sentAt,
serverGuid,
}: MaybeUpdatePropsType,
{ viaFirstStorageSync = false } = {}
): Promise<void> {
@@ -3120,7 +3122,7 @@ export async function maybeUpdateGroup(
});
await updateGroup(
{ conversation, receivedAt, sentAt, updates },
{ conversation, receivedAt, sentAt, serverGuid, updates },
{ viaFirstStorageSync }
);
} catch (error) {
@@ -3147,11 +3149,13 @@ async function updateGroup(
conversation,
receivedAt,
sentAt,
serverGuid,
updates,
}: {
conversation: ConversationModel;
receivedAt?: number;
sentAt?: number;
serverGuid?: string;
updates: UpdatesResultType;
},
{ viaFirstStorageSync = false } = {}
@@ -3211,6 +3215,7 @@ async function updateGroup(
conversationId: conversation.id,
received_at_ms: syntheticSentAt,
sent_at: syntheticSentAt,
serverGuid,
timestamp,
};
});
+20 -7
View File
@@ -10,25 +10,32 @@ import type { ConversationType } from '../../state/ducks/conversations.preload.t
const log = createLogger('addReportSpamJob');
// The Aci being reported is always the directConversation's Aci.
// When groupConversationId is missing, then we report messages in the directConversation.
// When groupConversationId is specified, then we report messages or group updates
// from the Aci within that group.
export async function addReportSpamJob({
conversation,
directConversation,
getMessageServerGuidsForSpam,
groupConversationId,
jobQueue,
}: Readonly<{
conversation: Readonly<
directConversation: Readonly<
Pick<ConversationType, 'id' | 'type' | 'serviceId' | 'reportingToken'>
>;
getMessageServerGuidsForSpam: (
conversationId: string
conversationId: string,
sourceServiceId?: string
) => Promise<Array<string>>;
groupConversationId?: string;
jobQueue: Pick<typeof reportSpamJobQueue, 'add'>;
}>): Promise<void> {
assertDev(
isDirectConversation(conversation),
isDirectConversation(directConversation),
'addReportSpamJob: cannot report spam for non-direct conversations'
);
const { serviceId: aci } = conversation;
const { serviceId: aci, reportingToken: token } = directConversation;
if (!aci || !isAciString(aci)) {
log.info(
'got a conversation with no aci, which the server does not support. Doing nothing'
@@ -36,7 +43,13 @@ export async function addReportSpamJob({
return;
}
const serverGuids = await getMessageServerGuidsForSpam(conversation.id);
let serverGuids: Array<string> = [];
if (groupConversationId) {
serverGuids = await getMessageServerGuidsForSpam(groupConversationId, aci);
} else {
serverGuids = await getMessageServerGuidsForSpam(directConversation.id);
}
if (!serverGuids.length) {
// This can happen under normal conditions. We haven't always stored server GUIDs, so
// a user might try to report spam for a conversation that doesn't have them. (It
@@ -45,5 +58,5 @@ export async function addReportSpamJob({
return;
}
await jobQueue.add({ aci, serverGuids, token: conversation.reportingToken });
await jobQueue.add({ aci, serverGuids, token });
}
+1
View File
@@ -284,6 +284,7 @@ export async function handleDataMessage(
newRevision: revision,
receivedAt: message.get('received_at'),
sentAt: message.get('sent_at'),
serverGuid: message.get('serverGuid'),
});
} catch (error) {
const errorText = Errors.toLogFormat(error);
+4 -1
View File
@@ -1086,7 +1086,10 @@ type ReadableInterface = {
limit: number,
options: { maxVersion: number }
) => Array<MessageType>;
getMessageServerGuidsForSpam: (conversationId: string) => Array<string>;
getMessageServerGuidsForSpam: (
conversationId: string,
sourceServiceId?: string
) => Array<string>;
getJobsInQueue(queueType: string): Array<StoredJob>;
+36 -12
View File
@@ -8728,26 +8728,50 @@ export function incrementMessagesMigrationAttempts(
function getMessageServerGuidsForSpam(
db: ReadableDB,
conversationId: string
conversationId: string,
sourceServiceId?: string
): Array<string> {
// The server's maximum is 3, which is why you see `LIMIT 3` in this query. Note that we
// use `pluck` here to only get the first column!
// The server's maximum is 3.
const limit = 3;
// Group reports -- sourceServiceId should be Aci matching addedBy of user who
// added you to a group
if (sourceServiceId != null) {
return db
.prepare(
`
SELECT DISTINCT serverGuid
FROM messages
WHERE conversationId = $conversationId
AND sourceServiceId = $sourceServiceId
AND type IS NOT 'outgoing'
AND serverGuid IS NOT NULL
ORDER BY received_at DESC, sent_at DESC
LIMIT $limit;
`,
{
pluck: true,
}
)
.all({ conversationId, sourceServiceId, limit });
}
return db
.prepare(
`
SELECT serverGuid
FROM messages
WHERE conversationId = $conversationId
AND type = 'incoming'
AND serverGuid IS NOT NULL
ORDER BY received_at DESC, sent_at DESC
LIMIT 3;
`,
SELECT DISTINCT serverGuid
FROM messages
WHERE conversationId = $conversationId
AND type IS NOT 'outgoing'
AND serverGuid IS NOT NULL
ORDER BY received_at DESC, sent_at DESC
LIMIT $limit;
`,
{
pluck: true,
}
)
.all({ conversationId });
.all({ conversationId, limit });
}
function getExternalFilesForConversation(
+37 -18
View File
@@ -196,7 +196,7 @@ import {
SettingsPage,
} from '../../types/Nav.std.ts';
import { sortByMessageOrder } from '../../types/ForwardDraft.std.ts';
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation.preload.ts';
import { getAddedByForGroup } from '../../util/getAddedByForGroup.preload.ts';
import {
getConversationIdForLogging,
getMessageIdForLogging,
@@ -3783,17 +3783,27 @@ async function syncMessageRequestResponse(
}
}
function getConversationForReportSpam(
function getDirectConversationForReportSpam(
conversation: ConversationType
): ConversationType | null {
const ourAci = itemStorage.user.getAci();
if (conversation.type === 'group') {
const addedBy = getAddedByForOurPendingInvitation(conversation);
const addedBy = getAddedByForGroup(conversation);
if (addedBy == null) {
log.error(
`getConversationForReportSpam: No addedBy found for ${conversation.id}`
`getDirectConversationForReportSpam: No addedBy found for ${conversation.id}`
);
return null;
}
if (addedBy.serviceId === ourAci) {
log.warn(
"getDirectConversationForReportSpam: We added ourself to this group, but can't report ourself for spam."
);
return null;
}
return addedBy;
}
@@ -3813,19 +3823,23 @@ function reportSpam(
return;
}
const conversation = getConversationForReportSpam(conversationOrGroup);
const conversationForSpam =
getDirectConversationForReportSpam(conversationOrGroup);
const conversationModel = window.ConversationController.get(
conversation?.id
conversationOrGroup?.id
);
if (!conversation || !conversationModel) {
if (!conversationForSpam || !conversationModel) {
log.error(
`reportSpam: Conversation for report spam not found ${conversation?.id}. Doing nothing.`
`reportSpam: Conversation for report spam not found ${conversationForSpam?.id}. Doing nothing.`
);
return;
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
const idForLogging = getConversationIdForLogging(conversation);
const idForLogging = getConversationIdForLogging(conversationForSpam);
const groupConversationId = isGroup(conversationOrGroup)
? conversationOrGroup.id
: undefined;
drop(
longRunningTaskWrapper({
@@ -3838,9 +3852,10 @@ function reportSpam(
messageRequestEnum.SPAM
),
addReportSpamJob({
conversation,
directConversation: conversationForSpam,
getMessageServerGuidsForSpam:
DataReader.getMessageServerGuidsForSpam,
groupConversationId,
jobQueue: reportSpamJobQueue,
}),
]);
@@ -3871,7 +3886,7 @@ function blockAndReportSpam(
}
const conversationForSpam =
getConversationForReportSpam(conversationOrGroup);
getDirectConversationForReportSpam(conversationOrGroup);
const conversationModel = window.ConversationController.get(
conversationForSpam?.id
);
@@ -3881,8 +3896,12 @@ function blockAndReportSpam(
);
return;
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
const idForLogging = getConversationIdForLogging(conversationOrGroup);
const groupConversationId = isGroup(conversationOrGroup)
? conversationOrGroup.id
: undefined;
if (conversationModel.getAci()) {
drop(
@@ -3895,13 +3914,13 @@ function blockAndReportSpam(
conversationModel,
messageRequestEnum.BLOCK_AND_SPAM
),
conversationForSpam != null &&
addReportSpamJob({
conversation: conversationForSpam,
getMessageServerGuidsForSpam:
DataReader.getMessageServerGuidsForSpam,
jobQueue: reportSpamJobQueue,
}),
addReportSpamJob({
directConversation: conversationForSpam,
getMessageServerGuidsForSpam:
DataReader.getMessageServerGuidsForSpam,
groupConversationId,
jobQueue: reportSpamJobQueue,
}),
]);
dispatch({
+2 -2
View File
@@ -17,7 +17,7 @@ import type { ConversationType } from '../ducks/conversations.preload.ts';
import { useConversationsActions } from '../ducks/conversations.preload.ts';
import { useGlobalModalActions } from '../ducks/globalModals.preload.ts';
import { strictAssert } from '../../util/assert.std.ts';
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation.preload.ts';
import { getAddedByForGroup } from '../../util/getAddedByForGroup.preload.ts';
import { getItems } from '../selectors/items.dom.ts';
import { isFeaturedEnabledSelector } from '../../util/isFeatureEnabled.dom.ts';
import { getCanAddLabel } from '../../types/GroupMemberLabels.std.ts';
@@ -38,7 +38,7 @@ function isFromOrAddedByTrustedContact(
return Boolean(conversation.name) || Boolean(conversation.profileSharing);
}
const addedByConv = getAddedByForOurPendingInvitation(conversation);
const addedByConv = getAddedByForGroup(conversation);
if (!addedByConv) {
return false;
}
+2 -2
View File
@@ -11,7 +11,7 @@ import type {
} from '../../types/BodyRange.std.ts';
import { hydrateRanges } from '../../util/BodyRange.node.ts';
import { strictAssert } from '../../util/assert.std.ts';
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation.preload.ts';
import { getAddedByForGroup } from '../../util/getAddedByForGroup.preload.ts';
import { AutoSubstituteAsciiEmojis } from '../../quill/auto-substitute-ascii-emojis/index.dom.tsx';
import { imageToBlurHash } from '../../util/imageToBlurHash.dom.ts';
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly.std.ts';
@@ -151,7 +151,7 @@ export const SmartCompositionArea = memo(function SmartCompositionArea({
const addedBy = useMemo(() => {
if (conversation.type === 'group') {
return getAddedByForOurPendingInvitation(conversation);
return getAddedByForGroup(conversation);
}
return null;
}, [conversation]);
@@ -133,6 +133,7 @@ export const SmartConversationDetails = memo(function SmartConversationDetails({
onArchive,
onMoveToInbox,
replaceAvatar,
reportSpam,
saveAvatarToDisk,
setDisappearingMessages,
setMuteExpiration,
@@ -295,6 +296,7 @@ export const SmartConversationDetails = memo(function SmartConversationDetails({
renderChooseGroupMembersModal={renderChooseGroupMembersModal}
renderConfirmAdditionsModal={renderConfirmAdditionsModal}
replaceAvatar={replaceAvatar}
reportSpam={reportSpam}
saveAvatarToDisk={saveAvatarToDisk}
searchInConversation={searchInConversation}
selectedNavTab={selectedNavTab}
@@ -14,7 +14,7 @@ import { CallMode } from '../../types/CallDisposition.std.ts';
import { PanelType } from '../../types/Panels.std.ts';
import { StoryViewModeType } from '../../types/Stories.std.ts';
import { strictAssert } from '../../util/assert.std.ts';
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation.preload.ts';
import { getAddedByForGroup } from '../../util/getAddedByForGroup.preload.ts';
import { getGroupMemberships } from '../../util/getGroupMemberships.dom.ts';
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly.std.ts';
import { isGroupOrAdhocCallState } from '../../util/isGroupOrAdhocCall.std.ts';
@@ -175,7 +175,7 @@ export const SmartConversationHeader = memo(function SmartConversationHeader({
const addedBy = useMemo(() => {
if (conversation.type === 'group') {
return getAddedByForOurPendingInvitation(conversation);
return getAddedByForGroup(conversation);
}
return null;
}, [conversation]);
+2 -2
View File
@@ -20,7 +20,7 @@ import {
} from '../ducks/conversations.preload.ts';
import { useGlobalModalActions } from '../ducks/globalModals.preload.ts';
import { useStoriesActions } from '../ducks/stories.preload.ts';
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation.preload.ts';
import { getAddedByForGroup } from '../../util/getAddedByForGroup.preload.ts';
import { getGroupMemberships } from '../../util/getGroupMemberships.dom.ts';
import { useNavActions } from '../ducks/nav.std.ts';
@@ -35,7 +35,7 @@ function isFromOrAddedByTrustedContact(
return Boolean(conversation.name) || Boolean(conversation.profileSharing);
}
const addedByConv = getAddedByForOurPendingInvitation(conversation);
const addedByConv = getAddedByForGroup(conversation);
if (!addedByConv) {
return false;
}
@@ -12,7 +12,7 @@ import {
MessageRequestState,
} from '../../components/conversation/MessageRequestActionsConfirmation.dom.tsx';
import { useContactNameData } from '../../components/conversation/ContactName.dom.tsx';
import { getAddedByForOurPendingInvitation } from '../../util/getAddedByForOurPendingInvitation.preload.ts';
import { getAddedByForGroup } from '../../util/getAddedByForGroup.preload.ts';
import { strictAssert } from '../../util/assert.std.ts';
import { useGlobalModalActions } from '../ducks/globalModals.preload.ts';
@@ -31,7 +31,7 @@ export const SmartMessageRequestActionsConfirmation = memo(
const conversation = getConversation(conversationId);
const addedBy = useMemo(() => {
if (conversation.type === 'group') {
return getAddedByForOurPendingInvitation(conversation);
return getAddedByForGroup(conversation);
}
return null;
}, [conversation]);
@@ -32,7 +32,7 @@ describe('addReportSpamJob', () => {
it('does nothing if the conversation lacks a serviceId', async () => {
await addReportSpamJob({
conversation: {
directConversation: {
...conversation,
serviceId: undefined,
},
@@ -48,7 +48,7 @@ describe('addReportSpamJob', () => {
getMessageServerGuidsForSpam.resolves([]);
await addReportSpamJob({
conversation,
directConversation: conversation,
getMessageServerGuidsForSpam,
jobQueue,
});
@@ -58,7 +58,7 @@ describe('addReportSpamJob', () => {
it('enqueues a job without a token', async () => {
await addReportSpamJob({
conversation,
directConversation: conversation,
getMessageServerGuidsForSpam,
jobQueue,
});
@@ -76,7 +76,7 @@ describe('addReportSpamJob', () => {
it('enqueues a job with a token', async () => {
await addReportSpamJob({
conversation: {
directConversation: {
...conversation,
reportingToken: 'uvw',
},
@@ -94,4 +94,23 @@ describe('addReportSpamJob', () => {
token: 'uvw',
});
});
it('calls getMessageServerGuidsForSpam with groupConversationId', async () => {
await addReportSpamJob({
directConversation: {
...conversation,
reportingToken: 'uvw',
},
getMessageServerGuidsForSpam,
groupConversationId: 'group',
jobQueue,
});
sinon.assert.calledOnce(getMessageServerGuidsForSpam);
sinon.assert.calledWith(
getMessageServerGuidsForSpam,
'group',
conversation.serviceId
);
});
});
@@ -3,16 +3,23 @@
import type { ConversationType } from '../state/ducks/conversations.preload.ts';
import { itemStorage } from '../textsecure/Storage.preload.ts';
export function getAddedByForOurPendingInvitation(
export function getAddedByForGroup(
conversation: ConversationType
): ConversationType | null {
const ourAci = itemStorage.user.getCheckedAci();
const ourPni = itemStorage.user.getPni();
const addedBy = conversation.pendingMemberships?.find(
let addedByAci;
addedByAci = conversation.pendingMemberships?.find(
item => item.serviceId === ourAci || item.serviceId === ourPni
)?.addedByUserId;
if (addedBy == null) {
return null;
if (addedByAci == null) {
const conversationModel = window.ConversationController.get(
conversation.id
);
addedByAci = conversationModel?.attributes.addedBy;
}
return window.ConversationController.get(addedBy)?.format() ?? null;
return window.ConversationController.get(addedByAci)?.format() ?? null;
}