Moves blockAndReportSpam to redux

This commit is contained in:
Josh Perez 2022-12-06 14:03:09 -05:00 committed by GitHub
parent 92a512a16d
commit 105162dc66
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 282 additions and 324 deletions

View file

@ -96,11 +96,10 @@ const useProps = (overrideProps: Partial<Props> = {}): Props => ({
clearShowPickerHint: action('clearShowPickerHint'),
// Message Requests
conversationType: 'direct',
onAccept: action('onAccept'),
onBlock: action('onBlock'),
onBlockAndReportSpam: action('onBlockAndReportSpam'),
onDelete: action('onDelete'),
onUnblock: action('onUnblock'),
acceptConversation: action('acceptConversation'),
blockConversation: action('blockConversation'),
blockAndReportSpam: action('blockAndReportSpam'),
deleteConversation: action('deleteConversation'),
messageRequestsEnabled: boolean(
'messageRequestsEnabled',
overrideProps.messageRequestsEnabled || false

View file

@ -240,11 +240,10 @@ export function CompositionArea({
isMissingMandatoryProfileSharing,
left,
messageRequestsEnabled,
onAccept,
onBlock,
onBlockAndReportSpam,
onDelete,
onUnblock,
acceptConversation,
blockConversation,
blockAndReportSpam,
deleteConversation,
title,
// GroupV1 Disabled Actions
isGroupV1AndDisabled,
@ -497,14 +496,14 @@ export function CompositionArea({
) {
return (
<MessageRequestActions
i18n={i18n}
acceptConversation={acceptConversation}
blockAndReportSpam={blockAndReportSpam}
blockConversation={blockConversation}
conversationId={conversationId}
conversationType={conversationType}
deleteConversation={deleteConversation}
i18n={i18n}
isBlocked={isBlocked}
onBlock={onBlock}
onBlockAndReportSpam={onBlockAndReportSpam}
onUnblock={onUnblock}
onDelete={onDelete}
onAccept={onAccept}
title={title}
/>
);
@ -549,12 +548,13 @@ export function CompositionArea({
) {
return (
<MandatoryProfileSharingActions
i18n={i18n}
acceptConversation={acceptConversation}
blockAndReportSpam={blockAndReportSpam}
blockConversation={blockConversation}
conversationId={conversationId}
conversationType={conversationType}
onBlock={onBlock}
onBlockAndReportSpam={onBlockAndReportSpam}
onDelete={onDelete}
onAccept={onAccept}
deleteConversation={deleteConversation}
i18n={i18n}
title={title}
/>
);

View file

@ -80,3 +80,10 @@ StoryVideoError.args = {
toastType: ToastType.StoryVideoError,
},
};
export const ReportedSpamAndBlocked = Template.bind({});
ReportedSpamAndBlocked.args = {
toast: {
toastType: ToastType.ReportedSpamAndBlocked,
},
};

View file

@ -97,6 +97,14 @@ export function ToastManager({
return <ToastMessageBodyTooLong i18n={i18n} onClose={hideToast} />;
}
if (toastType === ToastType.ReportedSpamAndBlocked) {
return (
<Toast onClose={hideToast}>
{i18n('MessageRequests--block-and-report-spam-success-toast')}
</Toast>
);
}
if (toastType === ToastType.StoryMuted) {
return (
<Toast onClose={hideToast} timeout={SHORT_TIMEOUT}>

View file

@ -1,28 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import { action } from '@storybook/addon-actions';
import { ToastReportedSpamAndBlocked } from './ToastReportedSpamAndBlocked';
import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const defaultProps = {
i18n,
onClose: action('onClose'),
};
export default {
title: 'Components/ToastReportedSpamAndBlocked',
};
export const _ToastReportedSpamAndBlocked = (): JSX.Element => (
<ToastReportedSpamAndBlocked {...defaultProps} />
);
_ToastReportedSpamAndBlocked.story = {
name: 'ToastReportedSpamAndBlocked',
};

View file

@ -1,22 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React from 'react';
import type { LocalizerType } from '../types/Util';
import { Toast } from './Toast';
type PropsType = {
i18n: LocalizerType;
onClose: () => unknown;
};
export function ToastReportedSpamAndBlocked({
i18n,
onClose,
}: PropsType): JSX.Element {
return (
<Toast onClose={onClose}>
{i18n('MessageRequests--block-and-report-spam-success-toast')}
</Toast>
);
}

View file

@ -19,15 +19,15 @@ export default {
};
const getCommonProps = () => ({
acceptConversation: action('acceptConversation'),
blockAndReportSpam: action('blockAndReportSpam'),
blockConversation: action('blockConversation'),
deleteConversation: action('deleteConversation'),
getPreferredBadge: () => undefined,
i18n,
groupConversationId: 'convo-id',
onBlock: action('onBlock'),
onBlockAndReportSpam: action('onBlockAndReportSpam'),
i18n,
onClose: action('onClose'),
onDelete: action('onDelete'),
onShowContactModal: action('onShowContactModal'),
onUnblock: action('onUnblock'),
removeMember: action('removeMember'),
theme: ThemeType.light,
});

View file

@ -25,14 +25,14 @@ import { missingCaseError } from '../../util/missingCaseError';
import { isInSystemContacts } from '../../util/isInSystemContacts';
export type PropsType = {
acceptConversation: (conversationId: string) => unknown;
blockAndReportSpam: (conversationId: string) => unknown;
blockConversation: (conversationId: string) => unknown;
deleteConversation: (conversationId: string) => unknown;
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType;
onBlock: (conversationId: string) => unknown;
onBlockAndReportSpam: (conversationId: string) => unknown;
onClose: () => void;
onDelete: (conversationId: string) => unknown;
onShowContactModal: (contactId: string, conversationId?: string) => unknown;
onUnblock: (conversationId: string) => unknown;
removeMember: (conversationId: string) => unknown;
theme: ThemeType;
} & (
@ -62,14 +62,14 @@ enum ConfirmationStateType {
export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
const {
acceptConversation,
blockAndReportSpam,
blockConversation,
deleteConversation,
getPreferredBadge,
i18n,
onBlock,
onBlockAndReportSpam,
onClose,
onDelete,
onShowContactModal,
onUnblock,
removeMember,
theme,
} = props;
@ -96,21 +96,14 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
case ConfirmationStateType.ConfirmingBlock:
return (
<MessageRequestActionsConfirmation
i18n={i18n}
onBlock={() => {
onBlock(affectedConversation.id);
}}
onBlockAndReportSpam={() => {
onBlockAndReportSpam(affectedConversation.id);
}}
onUnblock={() => {
onUnblock(affectedConversation.id);
}}
onDelete={() => {
onDelete(affectedConversation.id);
}}
title={affectedConversation.title}
acceptConversation={acceptConversation}
blockAndReportSpam={blockAndReportSpam}
blockConversation={blockConversation}
conversationId={affectedConversation.id}
conversationType="direct"
deleteConversation={deleteConversation}
i18n={i18n}
title={affectedConversation.title}
state={
type === ConfirmationStateType.ConfirmingDelete
? MessageRequestState.deleting
@ -279,7 +272,7 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
<Button
variant={ButtonVariant.SecondaryAffirmative}
onClick={() => {
onUnblock(conversationInfo.conversation.id);
acceptConversation(conversationInfo.conversation.id);
}}
>
{i18n('MessageRequests--unblock')}

View file

@ -15,16 +15,17 @@ const i18n = setupI18n('en', enMessages);
const getBaseProps = (
isGroup = false
): MandatoryProfileSharingActionsProps => ({
conversationId: '123',
i18n,
conversationType: isGroup ? 'group' : 'direct',
firstName: text('firstName', 'Cayce'),
title: isGroup
? text('title', 'NYC Rock Climbers')
: text('title', 'Cayce Bollard'),
onBlock: action('block'),
onBlockAndReportSpam: action('onBlockAndReportSpam'),
onDelete: action('delete'),
onAccept: action('accept'),
acceptConversation: action('acceptConversation'),
blockAndReportSpam: action('blockAndReportSpam'),
blockConversation: action('blockConversation'),
deleteConversation: action('deleteConversation'),
});
export default {

View file

@ -16,21 +16,26 @@ import type { LocalizerType } from '../../types/Util';
export type Props = {
i18n: LocalizerType;
firstName?: string;
onAccept(): unknown;
} & Omit<ContactNameProps, 'module'> &
Pick<
MessageRequestActionsConfirmationProps,
'conversationType' | 'onBlock' | 'onBlockAndReportSpam' | 'onDelete'
| 'acceptConversation'
| 'blockAndReportSpam'
| 'blockConversation'
| 'conversationId'
| 'conversationType'
| 'deleteConversation'
>;
export function MandatoryProfileSharingActions({
acceptConversation,
blockAndReportSpam,
blockConversation,
conversationId,
conversationType,
deleteConversation,
firstName,
i18n,
onAccept,
onBlock,
onBlockAndReportSpam,
onDelete,
title,
}: Props): JSX.Element {
const [mrState, setMrState] = React.useState(MessageRequestState.default);
@ -39,15 +44,16 @@ export function MandatoryProfileSharingActions({
<>
{mrState !== MessageRequestState.default ? (
<MessageRequestActionsConfirmation
i18n={i18n}
onBlock={onBlock}
onBlockAndReportSpam={onBlockAndReportSpam}
onUnblock={() => {
acceptConversation={() => {
throw new Error(
'Should not be able to unblock from MandatoryProfileSharingActions'
);
}}
onDelete={onDelete}
blockConversation={blockConversation}
conversationId={conversationId}
deleteConversation={deleteConversation}
i18n={i18n}
blockAndReportSpam={blockAndReportSpam}
title={title}
conversationType={conversationType}
state={mrState}
@ -103,7 +109,7 @@ export function MandatoryProfileSharingActions({
{i18n('MessageRequests--delete')}
</Button>
<Button
onClick={onAccept}
onClick={() => acceptConversation(conversationId)}
variant={ButtonVariant.SecondaryAffirmative}
>
{i18n('MessageRequests--continue')}

View file

@ -13,17 +13,17 @@ import enMessages from '../../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
const getBaseProps = (isGroup = false): MessageRequestActionsProps => ({
conversationId: '123',
i18n,
conversationType: isGroup ? 'group' : 'direct',
firstName: text('firstName', 'Cayce'),
title: isGroup
? text('title', 'NYC Rock Climbers')
: text('title', 'Cayce Bollard'),
onBlock: action('block'),
onDelete: action('delete'),
onBlockAndReportSpam: action('blockAndReportSpam'),
onUnblock: action('unblock'),
onAccept: action('accept'),
acceptConversation: action('acceptConversation'),
blockAndReportSpam: action('blockAndReportSpam'),
blockConversation: action('blockConversation'),
deleteConversation: action('deleteConversation'),
});
export default {

View file

@ -15,7 +15,6 @@ import type { LocalizerType } from '../../types/Util';
export type Props = {
i18n: LocalizerType;
onAccept(): unknown;
} & Omit<ContactNameProps, 'module'> &
Omit<
MessageRequestActionsConfirmationProps,
@ -23,15 +22,15 @@ export type Props = {
>;
export function MessageRequestActions({
acceptConversation,
blockAndReportSpam,
blockConversation,
conversationId,
conversationType,
deleteConversation,
firstName,
i18n,
isBlocked,
onAccept,
onBlock,
onBlockAndReportSpam,
onDelete,
onUnblock,
title,
}: Props): JSX.Element {
const [mrState, setMrState] = React.useState(MessageRequestState.default);
@ -40,15 +39,16 @@ export function MessageRequestActions({
<>
{mrState !== MessageRequestState.default ? (
<MessageRequestActionsConfirmation
i18n={i18n}
onBlock={onBlock}
onBlockAndReportSpam={onBlockAndReportSpam}
onUnblock={onUnblock}
onDelete={onDelete}
title={title}
acceptConversation={acceptConversation}
blockAndReportSpam={blockAndReportSpam}
blockConversation={blockConversation}
conversationId={conversationId}
conversationType={conversationType}
state={mrState}
deleteConversation={deleteConversation}
i18n={i18n}
onChangeState={setMrState}
state={mrState}
title={title}
/>
) : null}
<div className="module-message-request-actions">
@ -102,7 +102,7 @@ export function MessageRequestActions({
)}
{!isBlocked ? (
<Button
onClick={onAccept}
onClick={() => acceptConversation(conversationId)}
variant={ButtonVariant.SecondaryAffirmative}
>
{i18n('MessageRequests--accept')}

View file

@ -16,25 +16,27 @@ export enum MessageRequestState {
}
export type Props = {
i18n: LocalizerType;
acceptConversation(conversationId: string): unknown;
blockAndReportSpam(conversationId: string): unknown;
blockConversation(conversationId: string): unknown;
conversationId: string;
conversationType: 'group' | 'direct';
deleteConversation(conversationId: string): unknown;
i18n: LocalizerType;
isBlocked?: boolean;
onBlock(): unknown;
onBlockAndReportSpam(): unknown;
onUnblock(): unknown;
onDelete(): unknown;
state: MessageRequestState;
onChangeState(state: MessageRequestState): unknown;
state: MessageRequestState;
} & Omit<ContactNameProps, 'module'>;
export function MessageRequestActionsConfirmation({
acceptConversation,
blockAndReportSpam,
blockConversation,
conversationId,
conversationType,
deleteConversation,
i18n,
onBlock,
onBlockAndReportSpam,
onChangeState,
onDelete,
onUnblock,
state,
title,
}: Props): JSX.Element | null {
@ -58,14 +60,14 @@ export function MessageRequestActionsConfirmation({
? [
{
text: i18n('MessageRequests--block-and-report-spam'),
action: onBlockAndReportSpam,
action: () => blockAndReportSpam(conversationId),
style: 'negative' as const,
},
]
: []),
{
text: i18n('MessageRequests--block'),
action: onBlock,
action: () => blockConversation(conversationId),
style: 'negative',
},
]}
@ -95,7 +97,7 @@ export function MessageRequestActionsConfirmation({
actions={[
{
text: i18n('MessageRequests--unblock'),
action: onUnblock,
action: () => acceptConversation(conversationId),
style: 'affirmative',
},
]}
@ -123,7 +125,7 @@ export function MessageRequestActionsConfirmation({
actions={[
{
text: i18n(`MessageRequests--delete-${conversationType}`),
action: onDelete,
action: () => deleteConversation(conversationId),
style: 'negative',
},
]}

View file

@ -321,10 +321,10 @@ const actions = () => ({
'reviewMessageRequestNameCollision'
),
onBlock: action('onBlock'),
onBlockAndReportSpam: action('onBlockAndReportSpam'),
onDelete: action('onDelete'),
onUnblock: action('onUnblock'),
acceptConversation: action('acceptConversation'),
blockAndReportSpam: action('blockAndReportSpam'),
blockConversation: action('blockConversation'),
deleteConversation: action('deleteConversation'),
removeMember: action('removeMember'),
unblurAvatar: action('unblurAvatar'),

View file

@ -165,10 +165,10 @@ export type PropsActionsType = {
loadNewerMessages: (messageId: string) => unknown;
loadNewestMessages: (messageId: string, setFocus?: boolean) => unknown;
markMessageRead: (messageId: string) => unknown;
onBlock: (conversationId: string) => unknown;
onBlockAndReportSpam: (conversationId: string) => unknown;
onDelete: (conversationId: string) => unknown;
onUnblock: (conversationId: string) => unknown;
blockConversation: (conversationId: string) => unknown;
blockAndReportSpam: (conversationId: string) => unknown;
deleteConversation: (conversationId: string) => unknown;
acceptConversation: (conversationId: string) => unknown;
peekGroupCallForTheFirstTime: (conversationId: string) => unknown;
peekGroupCallIfItHasMembers: (conversationId: string) => unknown;
removeMember: (conversationId: string) => unknown;
@ -224,10 +224,10 @@ const getActions = createSelector(
'loadNewestMessages',
'markMessageRead',
'markViewed',
'onBlock',
'onBlockAndReportSpam',
'onDelete',
'onUnblock',
'acceptConversation',
'blockAndReportSpam',
'blockConversation',
'deleteConversation',
'peekGroupCallForTheFirstTime',
'peekGroupCallIfItHasMembers',
'removeMember',
@ -827,10 +827,14 @@ export class Timeline extends React.Component<
public override render(): JSX.Element | null {
const {
acceptConversation,
acknowledgeGroupMemberNameCollisions,
blockAndReportSpam,
blockConversation,
clearInvitedUuidsForNewlyCreatedGroup,
closeContactSpoofingReview,
contactSpoofingReview,
deleteConversation,
getPreferredBadge,
getTimestampForMessage,
haveNewest,
@ -844,15 +848,11 @@ export class Timeline extends React.Component<
items,
messageLoadingState,
oldestUnseenIndex,
onBlock,
onBlockAndReportSpam,
onDelete,
onUnblock,
removeMember,
renderContactSpoofingReviewDialog,
renderHeroRow,
renderItem,
renderTypingBubble,
renderContactSpoofingReviewDialog,
reviewGroupMemberNameCollision,
reviewMessageRequestNameCollision,
showContactModal,
@ -1082,14 +1082,14 @@ export class Timeline extends React.Component<
let contactSpoofingReviewDialog: ReactNode;
if (contactSpoofingReview) {
const commonProps = {
acceptConversation,
blockAndReportSpam,
blockConversation,
deleteConversation,
getPreferredBadge,
i18n,
onBlock,
onBlockAndReportSpam,
onClose: closeContactSpoofingReview,
onDelete,
onShowContactModal: showContactModal,
onUnblock,
removeMember,
theme,
};

View file

@ -40,10 +40,12 @@ const createProps = (
hasGroupLink = false,
expireTimer?: DurationInSeconds
): Props => ({
acceptConversation: action('acceptConversation'),
addMembers: async () => {
action('addMembers');
},
areWeASubscriber: false,
blockConversation: action('blockConversation'),
canEditGroupInfo: false,
canAddNewMembers: false,
conversation: expireTimer
@ -90,9 +92,7 @@ const createProps = (
updateGroupAttributes: async () => {
action('updateGroupAttributes')();
},
onBlock: action('onBlock'),
onLeave: action('onLeave'),
onUnblock: action('onUnblock'),
deleteAvatarFromDisk: action('deleteAvatarFromDisk'),
replaceAvatar: action('replaceAvatar'),
saveAvatarToDisk: action('saveAvatarToDisk'),

View file

@ -96,9 +96,7 @@ export type StateProps = {
title?: string;
}>
) => Promise<void>;
onBlock: () => void;
onLeave: () => void;
onUnblock: () => void;
theme: ThemeType;
userAvatarData: Array<AvatarDataType>;
renderChooseGroupMembersModal: (
@ -110,6 +108,8 @@ export type StateProps = {
};
type ActionProps = {
acceptConversation: (id: string) => void;
blockConversation: (id: string) => void;
deleteAvatarFromDisk: DeleteAvatarFromDiskActionType;
loadRecentMediaItems: (id: string, limit: number) => void;
onOutgoingAudioCallInConversation: (conversationId: string) => unknown;
@ -128,9 +128,11 @@ type ActionProps = {
export type Props = StateProps & ActionProps;
export function ConversationDetails({
acceptConversation,
addMembers,
areWeASubscriber,
badges,
blockConversation,
canEditGroupInfo,
canAddNewMembers,
conversation,
@ -146,11 +148,9 @@ export function ConversationDetails({
memberships,
maxGroupSize,
maxRecommendedGroupSize,
onBlock,
onLeave,
onOutgoingAudioCallInConversation,
onOutgoingVideoCallInConversation,
onUnblock,
pendingApprovalMemberships,
pendingMemberships,
renderChooseGroupMembersModal,
@ -551,15 +551,16 @@ export function ConversationDetails({
{!conversation.isMe && (
<ConversationDetailsActions
acceptConversation={acceptConversation}
blockConversation={blockConversation}
cannotLeaveBecauseYouAreLastAdmin={cannotLeaveBecauseYouAreLastAdmin}
conversationId={conversation.id}
conversationTitle={conversation.title}
i18n={i18n}
isBlocked={Boolean(conversation.isBlocked)}
isGroup={isGroup}
left={Boolean(conversation.left)}
onBlock={onBlock}
onLeave={onLeave}
onUnblock={onUnblock}
/>
)}

View file

@ -19,19 +19,20 @@ export default {
};
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
acceptConversation: action('acceptConversation'),
blockConversation: action('blockConversation'),
cannotLeaveBecauseYouAreLastAdmin: isBoolean(
overrideProps.cannotLeaveBecauseYouAreLastAdmin
)
? overrideProps.cannotLeaveBecauseYouAreLastAdmin
: false,
conversationId: '123',
conversationTitle: overrideProps.conversationTitle || '',
left: isBoolean(overrideProps.left) ? overrideProps.left : false,
onBlock: action('onBlock'),
onLeave: action('onLeave'),
onUnblock: action('onUnblock'),
i18n,
isBlocked: isBoolean(overrideProps.isBlocked),
isGroup: true,
left: isBoolean(overrideProps.left) ? overrideProps.left : false,
onLeave: action('onLeave'),
});
export function Basic(): JSX.Element {

View file

@ -14,27 +14,29 @@ import { PanelSection } from './PanelSection';
import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon';
export type Props = {
acceptConversation: (id: string) => void;
blockConversation: (id: string) => void;
cannotLeaveBecauseYouAreLastAdmin: boolean;
conversationId: string;
conversationTitle: string;
i18n: LocalizerType;
isBlocked: boolean;
isGroup: boolean;
left: boolean;
onBlock: () => void;
onLeave: () => void;
onUnblock: () => void;
};
export function ConversationDetailsActions({
acceptConversation,
blockConversation,
cannotLeaveBecauseYouAreLastAdmin,
conversationId,
conversationTitle,
i18n,
isBlocked,
isGroup,
left,
onBlock,
onLeave,
onUnblock,
}: Props): JSX.Element {
const [confirmLeave, gLeave] = useState<boolean>(false);
const [confirmGroupBlock, gGroupBlock] = useState<boolean>(false);
@ -193,7 +195,7 @@ export function ConversationDetailsActions({
text: i18n(
'ConversationDetailsActions--block-group-modal-confirm'
),
action: onBlock,
action: () => blockConversation(conversationId),
style: 'affirmative',
},
]}
@ -214,7 +216,7 @@ export function ConversationDetailsActions({
text: i18n(
'ConversationDetailsActions--unblock-group-modal-confirm'
),
action: onUnblock,
action: () => acceptConversation(conversationId),
style: 'affirmative',
},
]}
@ -234,7 +236,7 @@ export function ConversationDetailsActions({
actions={[
{
text: i18n('MessageRequests--block'),
action: onBlock,
action: () => blockConversation(conversationId),
style: 'affirmative',
},
]}
@ -253,7 +255,7 @@ export function ConversationDetailsActions({
actions={[
{
text: i18n('MessageRequests--unblock'),
action: onUnblock,
action: () => acceptConversation(conversationId),
style: 'affirmative',
},
]}

View file

@ -104,6 +104,9 @@ import { SHOW_TOAST, ToastType } from './toast';
import { isMemberRequestingToJoin } from '../../util/isMemberRequestingToJoin';
import { removePendingMember } from '../../util/removePendingMember';
import { denyPendingApprovalRequest } from '../../util/denyPendingApprovalRequest';
import { SignalService as Proto } from '../../protobuf';
import { addReportSpamJob } from '../../jobs/helpers/addReportSpamJob';
import { reportSpamJobQueue } from '../../jobs/reportSpamJobQueue';
// State
@ -840,8 +843,11 @@ export type ConversationActionType =
// Action Creators
export const actions = {
acceptConversation,
addMemberToGroup,
approvePendingMembershipFromGroupV2,
blockAndReportSpam,
blockConversation,
cancelConversationVerification,
changeHasGroupLink,
clearCancelledConversationVerification,
@ -863,6 +869,7 @@ export const actions = {
conversationUnloaded,
createGroup,
deleteAvatarFromDisk,
deleteConversation,
deleteMessageForEveryone,
destroyMessages,
discardMessages,
@ -2205,6 +2212,121 @@ function revokePendingMembershipsFromGroupV2(
};
}
function blockAndReportSpam(
conversationId: string
): ThunkAction<void, RootStateType, unknown, ShowToastActionType> {
return async dispatch => {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
log.error(
`blockAndReportSpam: Expected a conversation to be found for ${conversationId}. Doing nothing.`
);
return;
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
const idForLogging = conversation.idForLogging();
longRunningTaskWrapper({
name: 'blockAndReportSpam',
idForLogging,
task: async () => {
await Promise.all([
conversation.syncMessageRequestResponse(messageRequestEnum.BLOCK),
addReportSpamJob({
conversation: conversation.format(),
getMessageServerGuidsForSpam:
window.Signal.Data.getMessageServerGuidsForSpam,
jobQueue: reportSpamJobQueue,
}),
]);
dispatch({
type: SHOW_TOAST,
payload: {
toastType: ToastType.ReportedSpamAndBlocked,
},
});
},
});
};
}
function acceptConversation(conversationId: string): NoopActionType {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error(
'acceptConversation: Expected a conversation to be found. Doing nothing'
);
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
longRunningTaskWrapper({
name: 'acceptConversation',
idForLogging: conversation.idForLogging(),
task: conversation.syncMessageRequestResponse.bind(
conversation,
messageRequestEnum.ACCEPT
),
});
return {
type: 'NOOP',
payload: null,
};
}
function blockConversation(conversationId: string): NoopActionType {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error(
'blockConversation: Expected a conversation to be found. Doing nothing'
);
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
longRunningTaskWrapper({
name: 'blockConversation',
idForLogging: conversation.idForLogging(),
task: conversation.syncMessageRequestResponse.bind(
conversation,
messageRequestEnum.BLOCK
),
});
return {
type: 'NOOP',
payload: null,
};
}
function deleteConversation(conversationId: string): NoopActionType {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error(
'deleteConversation: Expected a conversation to be found. Doing nothing'
);
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
longRunningTaskWrapper({
name: 'deleteConversation',
idForLogging: conversation.idForLogging(),
task: conversation.syncMessageRequestResponse.bind(
conversation,
messageRequestEnum.DELETE
),
});
return {
type: 'NOOP',
payload: null,
};
}
function loadRecentMediaItems(
conversationId: string,
limit: number

View file

@ -13,6 +13,7 @@ export enum ToastType {
Error = 'Error',
FailedToDeleteUsername = 'FailedToDeleteUsername',
MessageBodyTooLong = 'MessageBodyTooLong',
ReportedSpamAndBlocked = 'ReportedSpamAndBlocked',
StoryMuted = 'StoryMuted',
StoryReact = 'StoryReact',
StoryReply = 'StoryReply',

View file

@ -54,9 +54,7 @@ export type SmartConversationDetailsProps = {
title?: string;
}>
) => Promise<void>;
onBlock: () => void;
onLeave: () => void;
onUnblock: () => void;
};
const ACCESS_ENUM = Proto.AccessControl.AccessRequired;

View file

@ -21,21 +21,16 @@ export type PropsType = {
| 'getQuotedMessage'
| 'handleClickQuotedMessage'
| 'id'
| 'onAccept'
| 'onBlock'
| 'onBlockAndReportSpam'
| 'onCancelJoinRequest'
| 'onClearAttachments'
| 'onClickAddPack'
| 'onCloseLinkPreview'
| 'onDelete'
| 'onEditorStateChange'
| 'onPickSticker'
| 'onSelectMediaQuality'
| 'onSendMessage'
| 'onStartGroupMigration'
| 'onTextTooLong'
| 'onUnblock'
| 'openConversation'
>;
conversationHeaderProps: ConversationHeaderPropsType;

View file

@ -76,10 +76,6 @@ export type TimelinePropsType = ExternalProps &
| 'loadOlderMessages'
| 'markAttachmentAsCorrupted'
| 'markMessageRead'
| 'onBlock'
| 'onBlockAndReportSpam'
| 'onDelete'
| 'onUnblock'
| 'openConversation'
| 'openGiftBadge'
| 'openLink'

View file

@ -46,7 +46,6 @@ import type { ToastMessageBodyTooLong } from '../components/ToastMessageBodyTooL
import type { ToastOriginalMessageNotFound } from '../components/ToastOriginalMessageNotFound';
import type { ToastPinnedConversationsFull } from '../components/ToastPinnedConversationsFull';
import type { ToastReactionFailed } from '../components/ToastReactionFailed';
import type { ToastReportedSpamAndBlocked } from '../components/ToastReportedSpamAndBlocked';
import type { ToastStickerPackInstallFailed } from '../components/ToastStickerPackInstallFailed';
import type { ToastTapToViewExpiredIncoming } from '../components/ToastTapToViewExpiredIncoming';
import type { ToastTapToViewExpiredOutgoing } from '../components/ToastTapToViewExpiredOutgoing';
@ -96,7 +95,6 @@ export function showToast(Toast: typeof ToastUnsupportedMultiAttachment): void;
export function showToast(Toast: typeof ToastOriginalMessageNotFound): void;
export function showToast(Toast: typeof ToastPinnedConversationsFull): void;
export function showToast(Toast: typeof ToastReactionFailed): void;
export function showToast(Toast: typeof ToastReportedSpamAndBlocked): void;
export function showToast(Toast: typeof ToastStickerPackInstallFailed): void;
export function showToast(Toast: typeof ToastTapToViewExpiredIncoming): void;
export function showToast(Toast: typeof ToastTapToViewExpiredOutgoing): void;

View file

@ -27,8 +27,6 @@ import { getMessageById } from '../messages/getMessageById';
import { getContactId } from '../messages/helpers';
import { strictAssert } from '../util/assert';
import { enqueueReactionForSend } from '../reactions/enqueueReactionForSend';
import { addReportSpamJob } from '../jobs/helpers/addReportSpamJob';
import { reportSpamJobQueue } from '../jobs/reportSpamJobQueue';
import type { GroupNameCollisionsWithIdsByTitle } from '../util/groupMemberNameCollisions';
import {
isDirectConversation,
@ -57,7 +55,6 @@ import type { EmbeddedContactType } from '../types/EmbeddedContact';
import { createConversationView } from '../state/roots/createConversationView';
import { AttachmentToastType } from '../types/AttachmentToastType';
import type { CompositionAPIType } from '../components/CompositionArea';
import { SignalService as Proto } from '../protobuf';
import { ToastBlocked } from '../components/ToastBlocked';
import { ToastBlockedGroup } from '../components/ToastBlockedGroup';
import { ToastCannotMixMultiAndNonMultiAttachments } from '../components/ToastCannotMixMultiAndNonMultiAttachments';
@ -75,7 +72,6 @@ import { ToastUnsupportedMultiAttachment } from '../components/ToastUnsupportedM
import { ToastOriginalMessageNotFound } from '../components/ToastOriginalMessageNotFound';
import { ToastPinnedConversationsFull } from '../components/ToastPinnedConversationsFull';
import { ToastReactionFailed } from '../components/ToastReactionFailed';
import { ToastReportedSpamAndBlocked } from '../components/ToastReportedSpamAndBlocked';
import { ToastTapToViewExpiredIncoming } from '../components/ToastTapToViewExpiredIncoming';
import { ToastTapToViewExpiredOutgoing } from '../components/ToastTapToViewExpiredOutgoing';
import { ToastUnableToLoadAttachment } from '../components/ToastUnableToLoadAttachment';
@ -368,7 +364,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
window.reduxActions.conversations.setSelectedConversationHeaderTitle();
// setupTimeline
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
const contactSupport = () => {
const baseUrl =
@ -433,19 +428,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
});
};
const createMessageRequestResponseHandler =
(name: string, enumValue: number): ((conversationId: string) => void) =>
conversationId => {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
log.error(
`createMessageRequestResponseHandler: Expected a conversation to be found in ${name}. Doing nothing`
);
return;
}
this.syncMessageRequestResponse(name, conversation, enumValue);
};
const timelineProps = {
id: this.model.id,
@ -465,28 +447,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
loadNewestMessages: this.model.loadNewestMessages.bind(this.model),
loadOlderMessages: this.model.loadOlderMessages.bind(this.model),
markMessageRead,
onBlock: createMessageRequestResponseHandler(
'onBlock',
messageRequestEnum.BLOCK
),
onBlockAndReportSpam: (conversationId: string) => {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
log.error(
`onBlockAndReportSpam: Expected a conversation to be found for ${conversationId}. Doing nothing.`
);
return;
}
this.blockAndReportSpam(conversation);
},
onDelete: createMessageRequestResponseHandler(
'onDelete',
messageRequestEnum.DELETE
),
onUnblock: createMessageRequestResponseHandler(
'onUnblock',
messageRequestEnum.ACCEPT
),
removeMember: (conversationId: string) => {
longRunningTaskWrapper({
idForLogging: this.model.idForLogging(),
@ -518,37 +478,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
onTextTooLong: () => showToast(ToastMessageBodyTooLong),
getQuotedMessage: () => this.model.get('quotedMessageId'),
clearQuotedMessage: () => this.setQuoteMessage(null),
onAccept: () => {
this.syncMessageRequestResponse(
'onAccept',
this.model,
messageRequestEnum.ACCEPT
);
},
onBlock: () => {
this.syncMessageRequestResponse(
'onBlock',
this.model,
messageRequestEnum.BLOCK
);
},
onUnblock: () => {
this.syncMessageRequestResponse(
'onUnblock',
this.model,
messageRequestEnum.ACCEPT
);
},
onDelete: () => {
this.syncMessageRequestResponse(
'onDelete',
this.model,
messageRequestEnum.DELETE
);
},
onBlockAndReportSpam: () => {
this.blockAndReportSpam(this.model);
},
onStartGroupMigration: () => this.startMigrationToGV2(),
onCancelJoinRequest: async () => {
await window.showConfirmationDialog({
@ -1008,39 +937,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
this.processAttachments(files);
}
syncMessageRequestResponse(
name: string,
model: ConversationModel,
messageRequestType: number
): Promise<void> {
return longRunningTaskWrapper({
idForLogging: this.model.idForLogging(),
name,
task: model.syncMessageRequestResponse.bind(model, messageRequestType),
});
}
blockAndReportSpam(model: ConversationModel): Promise<void> {
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
return longRunningTaskWrapper({
idForLogging: this.model.idForLogging(),
name: 'blockAndReportSpam',
task: async () => {
await Promise.all([
model.syncMessageRequestResponse(messageRequestEnum.BLOCK),
addReportSpamJob({
conversation: model.format(),
getMessageServerGuidsForSpam:
window.Signal.Data.getMessageServerGuidsForSpam,
jobQueue: reportSpamJobQueue,
}),
]);
showToast(ToastReportedSpamAndBlocked);
},
});
}
async saveModel(): Promise<void> {
window.Signal.Data.updateConversation(this.model.attributes);
}
@ -1803,8 +1699,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
this.model.throttledGetProfiles();
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
// these methods are used in more than one place and should probably be
// dried up and hoisted to methods on ConversationView
@ -1816,14 +1710,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
});
};
const onBlock = () => {
this.syncMessageRequestResponse(
'onBlock',
this.model,
messageRequestEnum.BLOCK
);
};
const props = {
addMembers: this.model.addMembersV2.bind(this.model),
conversationId: this.model.get('id'),
@ -1840,14 +1726,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
this.model
),
onLeave,
onBlock,
onUnblock: () => {
this.syncMessageRequestResponse(
'onUnblock',
this.model,
messageRequestEnum.ACCEPT
);
},
};
const view = new ReactWrapperView({