From 356f123092914ff96488481b536d0e2f80e246a7 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Tue, 22 Mar 2022 13:45:34 -0700 Subject: [PATCH] Use parallel-prettier for lint --- package.json | 7 +- ts/components/AddGroupMemberErrorDialog.tsx | 56 +- ts/components/CompositionUpload.tsx | 27 +- ...NewlyCreatedGroupInvitedContactsDialog.tsx | 129 ++-- .../ContactSpoofingReviewDialog.tsx | 551 +++++++++--------- .../ContactSpoofingReviewDialogPerson.tsx | 95 +-- .../RemoveGroupMemberConfirmationDialog.tsx | 61 +- .../ConversationNotificationsSettings.tsx | 171 +++--- .../SearchResultsLoadingFakeHeader.tsx | 5 +- ts/util/lint/exceptions.json | 77 +++ yarn.lock | 200 ++++++- 11 files changed, 817 insertions(+), 562 deletions(-) diff --git a/package.json b/package.json index 241919f53..cb2e96173 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,8 @@ "lint": "run-s --print-label lint-prettier check:types eslint", "lint-deps": "node ts/util/lint/linter.js", "lint-license-comments": "ts-node ts/util/lint/license_comments.ts", - "lint-prettier": "prettier --check .", - "format": "prettier --write .", + "lint-prettier": "pprettier --check '**/*.{ts,tsx,d.ts,js,json,html,scss,md,yml,yaml}'", + "format": "pprettier --write '**/*.{ts,tsx,d.ts,js,json,html,scss,md,yml,yaml}'", "transpile": "run-p check:types build:esbuild", "check:types": "tsc --noEmit", "clean-transpile-once": "rimraf app/**/*.js app/*.js ts/**/*.js ts/*.js tsconfig.tsbuildinfo", @@ -187,6 +187,7 @@ "@babel/preset-typescript": "7.16.0", "@chanzuckerberg/axe-storybook-testing": "3.0.2", "@electron/fuses": "1.5.0", + "@mixer/parallel-prettier": "2.0.1", "@signalapp/mock-server": "1.2.0", "@storybook/addon-actions": "5.1.11", "@storybook/addon-knobs": "5.1.11", @@ -282,7 +283,7 @@ "nyc": "11.4.1", "patch-package": "6.4.7", "playwright": "1.17.1", - "prettier": "2.4.1", + "prettier": "2.6.0", "sass": "1.49.7", "sass-loader": "10.2.0", "sinon": "11.1.1", diff --git a/ts/components/AddGroupMemberErrorDialog.tsx b/ts/components/AddGroupMemberErrorDialog.tsx index 3de4625d4..52f51c61f 100644 --- a/ts/components/AddGroupMemberErrorDialog.tsx +++ b/ts/components/AddGroupMemberErrorDialog.tsx @@ -28,35 +28,33 @@ type PropsType = { onClose: () => void; } & PropsDataType; -export const AddGroupMemberErrorDialog: FunctionComponent = - props => { - const { i18n, onClose } = props; +export const AddGroupMemberErrorDialog: FunctionComponent< + PropsType +> = props => { + const { i18n, onClose } = props; - let title: string; - let body: ReactNode; - switch (props.mode) { - case AddGroupMemberErrorDialogMode.MaximumGroupSize: { - const { maximumNumberOfContacts } = props; - title = i18n('chooseGroupMembers__maximum-group-size__title'); - body = i18n('chooseGroupMembers__maximum-group-size__body', [ - maximumNumberOfContacts.toString(), - ]); - break; - } - case AddGroupMemberErrorDialogMode.RecommendedMaximumGroupSize: { - const { recommendedMaximumNumberOfContacts } = props; - title = i18n( - 'chooseGroupMembers__maximum-recommended-group-size__title' - ); - body = i18n( - 'chooseGroupMembers__maximum-recommended-group-size__body', - [recommendedMaximumNumberOfContacts.toString()] - ); - break; - } - default: - throw missingCaseError(props); + let title: string; + let body: ReactNode; + switch (props.mode) { + case AddGroupMemberErrorDialogMode.MaximumGroupSize: { + const { maximumNumberOfContacts } = props; + title = i18n('chooseGroupMembers__maximum-group-size__title'); + body = i18n('chooseGroupMembers__maximum-group-size__body', [ + maximumNumberOfContacts.toString(), + ]); + break; } + case AddGroupMemberErrorDialogMode.RecommendedMaximumGroupSize: { + const { recommendedMaximumNumberOfContacts } = props; + title = i18n('chooseGroupMembers__maximum-recommended-group-size__title'); + body = i18n('chooseGroupMembers__maximum-recommended-group-size__body', [ + recommendedMaximumNumberOfContacts.toString(), + ]); + break; + } + default: + throw missingCaseError(props); + } - return ; - }; + return ; +}; diff --git a/ts/components/CompositionUpload.tsx b/ts/components/CompositionUpload.tsx index b08c21180..1be58d7d4 100644 --- a/ts/components/CompositionUpload.tsx +++ b/ts/components/CompositionUpload.tsx @@ -52,20 +52,21 @@ export const CompositionUpload = forwardRef( AttachmentToastType | undefined >(); - const onFileInputChange: ChangeEventHandler = - async event => { - const files = event.target.files || []; + const onFileInputChange: ChangeEventHandler< + HTMLInputElement + > = async event => { + const files = event.target.files || []; - await processAttachments({ - addAttachment, - addPendingAttachment, - conversationId, - files: Array.from(files), - draftAttachments, - onShowToast: setToastType, - removeAttachment, - }); - }; + await processAttachments({ + addAttachment, + addPendingAttachment, + conversationId, + files: Array.from(files), + draftAttachments, + onShowToast: setToastType, + removeAttachment, + }); + }; function closeToast() { setToastType(undefined); diff --git a/ts/components/NewlyCreatedGroupInvitedContactsDialog.tsx b/ts/components/NewlyCreatedGroupInvitedContactsDialog.tsx index eccdd4c2a..9ee255947 100644 --- a/ts/components/NewlyCreatedGroupInvitedContactsDialog.tsx +++ b/ts/components/NewlyCreatedGroupInvitedContactsDialog.tsx @@ -20,73 +20,70 @@ type PropsType = { theme: ThemeType; }; -export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent = - ({ contacts, getPreferredBadge, i18n, onClose, theme }) => { - let title: string; - let body: ReactNode; - if (contacts.length === 1) { - const contact = contacts[0]; +export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent< + PropsType +> = ({ contacts, getPreferredBadge, i18n, onClose, theme }) => { + let title: string; + let body: ReactNode; + if (contacts.length === 1) { + const contact = contacts[0]; - title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--one'); - body = ( - <> - - ]} - /> - - - {i18n( - 'NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph' - )} - - - ); - } else { - title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--many', [ - contacts.length.toString(), - ]); - body = ( - <> - - {i18n( - 'NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--many' - )} - - - {i18n( - 'NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph' - )} - - + + ]} /> - - ); - } - - return ( - { - openLinkInWebBrowser( - 'https://support.signal.org/hc/articles/360007319331-Group-chats' - ); - }} - onClose={onClose} - title={title} - > - {body} - + + + {i18n('NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph')} + + ); - }; + } else { + title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--many', [ + contacts.length.toString(), + ]); + body = ( + <> + + {i18n( + 'NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--many' + )} + + + {i18n('NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph')} + + + + ); + } + + return ( + { + openLinkInWebBrowser( + 'https://support.signal.org/hc/articles/360007319331-Group-chats' + ); + }} + onClose={onClose} + title={title} + > + {body} + + ); +}; diff --git a/ts/components/conversation/ContactSpoofingReviewDialog.tsx b/ts/components/conversation/ContactSpoofingReviewDialog.tsx index 221b2b940..a7555ba6d 100644 --- a/ts/components/conversation/ContactSpoofingReviewDialog.tsx +++ b/ts/components/conversation/ContactSpoofingReviewDialog.tsx @@ -60,305 +60,302 @@ enum ConfirmationStateType { ConfirmingGroupRemoval, } -export const ContactSpoofingReviewDialog: FunctionComponent = - props => { - const { - getPreferredBadge, - i18n, - onBlock, - onBlockAndReportSpam, - onClose, - onDelete, - onShowContactModal, - onUnblock, - removeMember, - theme, - } = props; +export const ContactSpoofingReviewDialog: FunctionComponent< + PropsType +> = props => { + const { + getPreferredBadge, + i18n, + onBlock, + onBlockAndReportSpam, + onClose, + onDelete, + onShowContactModal, + onUnblock, + removeMember, + theme, + } = props; - const [confirmationState, setConfirmationState] = useState< - | undefined - | { - type: ConfirmationStateType.ConfirmingGroupRemoval; - affectedConversation: ConversationType; - group: ConversationType; - } - | { - type: - | ConfirmationStateType.ConfirmingDelete - | ConfirmationStateType.ConfirmingBlock; - affectedConversation: ConversationType; - } - >(); - - if (confirmationState) { - const { type, affectedConversation } = confirmationState; - switch (type) { - case ConfirmationStateType.ConfirmingDelete: - case ConfirmationStateType.ConfirmingBlock: - return ( - { - onBlock(affectedConversation.id); - }} - onBlockAndReportSpam={() => { - onBlockAndReportSpam(affectedConversation.id); - }} - onUnblock={() => { - onUnblock(affectedConversation.id); - }} - onDelete={() => { - onDelete(affectedConversation.id); - }} - title={affectedConversation.title} - conversationType="direct" - state={ - type === ConfirmationStateType.ConfirmingDelete - ? MessageRequestState.deleting - : MessageRequestState.blocking - } - onChangeState={messageRequestState => { - switch (messageRequestState) { - case MessageRequestState.blocking: - setConfirmationState({ - type: ConfirmationStateType.ConfirmingBlock, - affectedConversation, - }); - break; - case MessageRequestState.deleting: - setConfirmationState({ - type: ConfirmationStateType.ConfirmingDelete, - affectedConversation, - }); - break; - case MessageRequestState.unblocking: - assert( - false, - 'Got unexpected MessageRequestState.unblocking state. Clearing confiration state' - ); - setConfirmationState(undefined); - break; - case MessageRequestState.default: - setConfirmationState(undefined); - break; - default: - throw missingCaseError(messageRequestState); - } - }} - /> - ); - case ConfirmationStateType.ConfirmingGroupRemoval: { - const { group } = confirmationState; - return ( - { - setConfirmationState(undefined); - }} - onRemove={() => { - removeMember(affectedConversation.id); - }} - /> - ); - } - default: - throw missingCaseError(type); + const [confirmationState, setConfirmationState] = useState< + | undefined + | { + type: ConfirmationStateType.ConfirmingGroupRemoval; + affectedConversation: ConversationType; + group: ConversationType; } + | { + type: + | ConfirmationStateType.ConfirmingDelete + | ConfirmationStateType.ConfirmingBlock; + affectedConversation: ConversationType; + } + >(); + + if (confirmationState) { + const { type, affectedConversation } = confirmationState; + switch (type) { + case ConfirmationStateType.ConfirmingDelete: + case ConfirmationStateType.ConfirmingBlock: + return ( + { + onBlock(affectedConversation.id); + }} + onBlockAndReportSpam={() => { + onBlockAndReportSpam(affectedConversation.id); + }} + onUnblock={() => { + onUnblock(affectedConversation.id); + }} + onDelete={() => { + onDelete(affectedConversation.id); + }} + title={affectedConversation.title} + conversationType="direct" + state={ + type === ConfirmationStateType.ConfirmingDelete + ? MessageRequestState.deleting + : MessageRequestState.blocking + } + onChangeState={messageRequestState => { + switch (messageRequestState) { + case MessageRequestState.blocking: + setConfirmationState({ + type: ConfirmationStateType.ConfirmingBlock, + affectedConversation, + }); + break; + case MessageRequestState.deleting: + setConfirmationState({ + type: ConfirmationStateType.ConfirmingDelete, + affectedConversation, + }); + break; + case MessageRequestState.unblocking: + assert( + false, + 'Got unexpected MessageRequestState.unblocking state. Clearing confiration state' + ); + setConfirmationState(undefined); + break; + case MessageRequestState.default: + setConfirmationState(undefined); + break; + default: + throw missingCaseError(messageRequestState); + } + }} + /> + ); + case ConfirmationStateType.ConfirmingGroupRemoval: { + const { group } = confirmationState; + return ( + { + setConfirmationState(undefined); + }} + onRemove={() => { + removeMember(affectedConversation.id); + }} + /> + ); + } + default: + throw missingCaseError(type); } + } - let title: string; - let contents: ReactChild; + let title: string; + let contents: ReactChild; - switch (props.type) { - case ContactSpoofingType.DirectConversationWithSameTitle: { - const { possiblyUnsafeConversation, safeConversation } = props; - assert( - possiblyUnsafeConversation.type === 'direct', - ' expected a direct conversation for the "possibly unsafe" conversation' - ); - assert( - safeConversation.type === 'direct', - ' expected a direct conversation for the "safe" conversation' - ); + switch (props.type) { + case ContactSpoofingType.DirectConversationWithSameTitle: { + const { possiblyUnsafeConversation, safeConversation } = props; + assert( + possiblyUnsafeConversation.type === 'direct', + ' expected a direct conversation for the "possibly unsafe" conversation' + ); + assert( + safeConversation.type === 'direct', + ' expected a direct conversation for the "safe" conversation' + ); - title = i18n('ContactSpoofingReviewDialog__title'); - contents = ( - <> -

{i18n('ContactSpoofingReviewDialog__description')}

-

- {i18n('ContactSpoofingReviewDialog__possibly-unsafe-title')} -

- -
+ title = i18n('ContactSpoofingReviewDialog__title'); + contents = ( + <> +

{i18n('ContactSpoofingReviewDialog__description')}

+

{i18n('ContactSpoofingReviewDialog__possibly-unsafe-title')}

+ +
+ + +
+
+
+

{i18n('ContactSpoofingReviewDialog__safe-title')}

+ { + onShowContactModal(safeConversation.id); + }} + theme={theme} + /> + + ); + break; + } + case ContactSpoofingType.MultipleGroupMembersWithSameTitle: { + const { group, collisionInfoByTitle } = props; + + const unsortedConversationInfos = concat( + // This empty array exists to appease Lodash's type definitions. + [], + ...Object.values(collisionInfoByTitle) + ); + const conversationInfos = orderBy(unsortedConversationInfos, [ + // We normally use an `Intl.Collator` to sort by title. We do this instead, as + // we only really care about stability (not perfect ordering). + 'title', + 'id', + ]); + + title = i18n('ContactSpoofingReviewDialog__group__title'); + contents = ( + <> +

+ {i18n('ContactSpoofingReviewDialog__group__description', [ + conversationInfos.length.toString(), + ])} +

+

{i18n('ContactSpoofingReviewDialog__group__members-header')}

+ {conversationInfos.map((conversationInfo, index) => { + let button: ReactNode; + if (group.areWeAdmin) { + button = ( + ); + } else if (conversationInfo.conversation.isBlocked) { + button = ( + + ); + } else if (!isInSystemContacts(conversationInfo.conversation)) { + button = ( -
-
-
-

{i18n('ContactSpoofingReviewDialog__safe-title')}

- { - onShowContactModal(safeConversation.id); - }} - theme={theme} - /> - - ); - break; - } - case ContactSpoofingType.MultipleGroupMembersWithSameTitle: { - const { group, collisionInfoByTitle } = props; - - const unsortedConversationInfos = concat( - // This empty array exists to appease Lodash's type definitions. - [], - ...Object.values(collisionInfoByTitle) - ); - const conversationInfos = orderBy(unsortedConversationInfos, [ - // We normally use an `Intl.Collator` to sort by title. We do this instead, as - // we only really care about stability (not perfect ordering). - 'title', - 'id', - ]); - - title = i18n('ContactSpoofingReviewDialog__group__title'); - contents = ( - <> -

- {i18n('ContactSpoofingReviewDialog__group__description', [ - conversationInfos.length.toString(), - ])} -

-

- {i18n('ContactSpoofingReviewDialog__group__members-header')} -

- {conversationInfos.map((conversationInfo, index) => { - let button: ReactNode; - if (group.areWeAdmin) { - button = ( - - ); - } else if (conversationInfo.conversation.isBlocked) { - button = ( - - ); - } else if (!isInSystemContacts(conversationInfo.conversation)) { - button = ( - - ); - } - - const { oldName } = conversationInfo; - const newName = - conversationInfo.conversation.profileName || - conversationInfo.conversation.title; - - return ( - <> - {index !== 0 &&
} - - {Boolean(oldName) && oldName !== newName && ( -
- , - newName: , - }} - /> -
- )} - {button && ( -
- {button} -
- )} -
- ); - })} - - ); - break; - } - default: - throw missingCaseError(props); - } + } - return ( - - {contents} - - ); - }; + const { oldName } = conversationInfo; + const newName = + conversationInfo.conversation.profileName || + conversationInfo.conversation.title; + + return ( + <> + {index !== 0 &&
} + + {Boolean(oldName) && oldName !== newName && ( +
+ , + newName: , + }} + /> +
+ )} + {button && ( +
+ {button} +
+ )} +
+ + ); + })} + + ); + break; + } + default: + throw missingCaseError(props); + } + + return ( + + {contents} + + ); +}; diff --git a/ts/components/conversation/ContactSpoofingReviewDialogPerson.tsx b/ts/components/conversation/ContactSpoofingReviewDialogPerson.tsx index 7ae5e786e..435ab6989 100644 --- a/ts/components/conversation/ContactSpoofingReviewDialogPerson.tsx +++ b/ts/components/conversation/ContactSpoofingReviewDialogPerson.tsx @@ -22,58 +22,59 @@ type PropsType = { theme: ThemeType; }; -export const ContactSpoofingReviewDialogPerson: FunctionComponent = - ({ children, conversation, getPreferredBadge, i18n, onClick, theme }) => { - assert( - conversation.type === 'direct', - ' expected a direct conversation' - ); +export const ContactSpoofingReviewDialogPerson: FunctionComponent< + PropsType +> = ({ children, conversation, getPreferredBadge, i18n, onClick, theme }) => { + assert( + conversation.type === 'direct', + ' expected a direct conversation' + ); - const contents = ( - <> - + +
+ -
- - {conversation.phoneNumber ? ( -
- {conversation.phoneNumber} -
- ) : null} + {conversation.phoneNumber ? (
- + {conversation.phoneNumber}
- {children} + ) : null} +
+
- - ); - - if (onClick) { - return ( - - ); - } + {children} +
+ + ); + if (onClick) { return ( -
{contents}
+ ); - }; + } + + return ( +
{contents}
+ ); +}; diff --git a/ts/components/conversation/RemoveGroupMemberConfirmationDialog.tsx b/ts/components/conversation/RemoveGroupMemberConfirmationDialog.tsx index 699d6572e..0f30a8af7 100644 --- a/ts/components/conversation/RemoveGroupMemberConfirmationDialog.tsx +++ b/ts/components/conversation/RemoveGroupMemberConfirmationDialog.tsx @@ -20,34 +20,35 @@ type PropsType = { onRemove: () => void; }; -export const RemoveGroupMemberConfirmationDialog: FunctionComponent = - ({ conversation, group, i18n, onClose, onRemove }) => { - const descriptionKey = isAccessControlEnabled( - group.accessControlAddFromInviteLink - ) - ? 'RemoveGroupMemberConfirmation__description__with-link' - : 'RemoveGroupMemberConfirmation__description'; +export const RemoveGroupMemberConfirmationDialog: FunctionComponent< + PropsType +> = ({ conversation, group, i18n, onClose, onRemove }) => { + const descriptionKey = isAccessControlEnabled( + group.accessControlAddFromInviteLink + ) + ? 'RemoveGroupMemberConfirmation__description__with-link' + : 'RemoveGroupMemberConfirmation__description'; - return ( - , - }} - /> - } - /> - ); - }; + return ( + , + }} + /> + } + /> + ); +}; diff --git a/ts/components/conversation/conversation-details/ConversationNotificationsSettings.tsx b/ts/components/conversation/conversation-details/ConversationNotificationsSettings.tsx index b22d5ff75..1477bfc4f 100644 --- a/ts/components/conversation/conversation-details/ConversationNotificationsSettings.tsx +++ b/ts/components/conversation/conversation-details/ConversationNotificationsSettings.tsx @@ -25,103 +25,100 @@ type PropsType = { setMuteExpiration: (muteExpiresAt: undefined | number) => unknown; }; -export const ConversationNotificationsSettings: FunctionComponent = - ({ - conversationType, - dontNotifyForMentionsIfMuted, - i18n, - muteExpiresAt, - setMuteExpiration, - setDontNotifyForMentionsIfMuted, - }) => { - const muteOptions = useMemo( - () => [ - ...(isMuted(muteExpiresAt) - ? [] - : [ - { - disabled: true, - text: i18n('notMuted'), - value: -1, - }, - ]), - ...getMuteOptions(muteExpiresAt, i18n).map( - ({ disabled, name, value }) => ({ - disabled, - text: name, - value, - }) - ), - ], - [i18n, muteExpiresAt] +export const ConversationNotificationsSettings: FunctionComponent< + PropsType +> = ({ + conversationType, + dontNotifyForMentionsIfMuted, + i18n, + muteExpiresAt, + setMuteExpiration, + setDontNotifyForMentionsIfMuted, +}) => { + const muteOptions = useMemo( + () => [ + ...(isMuted(muteExpiresAt) + ? [] + : [ + { + disabled: true, + text: i18n('notMuted'), + value: -1, + }, + ]), + ...getMuteOptions(muteExpiresAt, i18n).map( + ({ disabled, name, value }) => ({ + disabled, + text: name, + value, + }) + ), + ], + [i18n, muteExpiresAt] + ); + + const onMuteChange = (rawValue: string) => { + const ms = parseIntOrThrow( + rawValue, + 'NotificationSettings: mute ms was not an integer' ); + setMuteExpiration(ms); + }; - const onMuteChange = (rawValue: string) => { - const ms = parseIntOrThrow( - rawValue, - 'NotificationSettings: mute ms was not an integer' - ); - setMuteExpiration(ms); - }; + const onChangeDontNotifyForMentionsIfMuted = (rawValue: string) => { + setDontNotifyForMentionsIfMuted(rawValue === 'yes'); + }; - const onChangeDontNotifyForMentionsIfMuted = (rawValue: string) => { - setDontNotifyForMentionsIfMuted(rawValue === 'yes'); - }; - - return ( -
- + return ( +
+ + + } + label={i18n('muteNotificationsTitle')} + right={ + } /> - {conversationType === 'group' && ( - - } - label={i18n('ConversationNotificationsSettings__mentions__label')} - info={i18n('ConversationNotificationsSettings__mentions__info')} - right={ -