Upgrade Prettier to 2.4.1

This commit is contained in:
Evan Hahn 2021-11-11 16:43:05 -06:00 committed by GitHub
parent f204784afe
commit 5619eeca83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
176 changed files with 1961 additions and 2465 deletions

View file

@ -57,281 +57,286 @@ enum ConfirmationStateType {
ConfirmingGroupRemoval,
}
export const ContactSpoofingReviewDialog: FunctionComponent<PropsType> = props => {
const {
i18n,
onBlock,
onBlockAndReportSpam,
onClose,
onDelete,
onShowContactModal,
onUnblock,
removeMember,
} = props;
export const ContactSpoofingReviewDialog: FunctionComponent<PropsType> =
props => {
const {
i18n,
onBlock,
onBlockAndReportSpam,
onClose,
onDelete,
onShowContactModal,
onUnblock,
removeMember,
} = props;
const [confirmationState, setConfirmationState] = useState<
| undefined
| {
type: ConfirmationStateType;
affectedConversation: ConversationType;
}
>();
const [confirmationState, setConfirmationState] = useState<
| undefined
| {
type: ConfirmationStateType;
affectedConversation: ConversationType;
}
>();
if (confirmationState) {
const { affectedConversation, type } = confirmationState;
switch (type) {
case ConfirmationStateType.ConfirmingDelete:
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}
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);
if (confirmationState) {
const { affectedConversation, type } = confirmationState;
switch (type) {
case ConfirmationStateType.ConfirmingDelete:
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}
conversationType="direct"
state={
type === ConfirmationStateType.ConfirmingDelete
? MessageRequestState.deleting
: MessageRequestState.blocking
}
}}
/>
);
case ConfirmationStateType.ConfirmingGroupRemoval:
return (
<RemoveGroupMemberConfirmationDialog
conversation={affectedConversation}
i18n={i18n}
onClose={() => {
setConfirmationState(undefined);
}}
onRemove={() => {
removeMember(affectedConversation.id);
}}
/>
);
default:
throw missingCaseError(type);
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:
return (
<RemoveGroupMemberConfirmationDialog
conversation={affectedConversation}
i18n={i18n}
onClose={() => {
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',
'<ContactSpoofingReviewDialog> expected a direct conversation for the "possibly unsafe" conversation'
);
assert(
safeConversation.type === 'direct',
'<ContactSpoofingReviewDialog> expected a direct conversation for the "safe" conversation'
);
switch (props.type) {
case ContactSpoofingType.DirectConversationWithSameTitle: {
const { possiblyUnsafeConversation, safeConversation } = props;
assert(
possiblyUnsafeConversation.type === 'direct',
'<ContactSpoofingReviewDialog> expected a direct conversation for the "possibly unsafe" conversation'
);
assert(
safeConversation.type === 'direct',
'<ContactSpoofingReviewDialog> expected a direct conversation for the "safe" conversation'
);
title = i18n('ContactSpoofingReviewDialog__title');
contents = (
<>
<p>{i18n('ContactSpoofingReviewDialog__description')}</p>
<h2>{i18n('ContactSpoofingReviewDialog__possibly-unsafe-title')}</h2>
<ContactSpoofingReviewDialogPerson
conversation={possiblyUnsafeConversation}
i18n={i18n}
>
<div className="module-ContactSpoofingReviewDialog__buttons">
<Button
variant={ButtonVariant.SecondaryDestructive}
onClick={() => {
setConfirmationState({
type: ConfirmationStateType.ConfirmingDelete,
affectedConversation: possiblyUnsafeConversation,
});
}}
>
{i18n('MessageRequests--delete')}
</Button>
<Button
variant={ButtonVariant.SecondaryDestructive}
onClick={() => {
setConfirmationState({
type: ConfirmationStateType.ConfirmingBlock,
affectedConversation: possiblyUnsafeConversation,
});
}}
>
{i18n('MessageRequests--block')}
</Button>
</div>
</ContactSpoofingReviewDialogPerson>
<hr />
<h2>{i18n('ContactSpoofingReviewDialog__safe-title')}</h2>
<ContactSpoofingReviewDialogPerson
conversation={safeConversation}
i18n={i18n}
onClick={() => {
onShowContactModal(safeConversation.id);
}}
/>
</>
);
break;
}
case ContactSpoofingType.MultipleGroupMembersWithSameTitle: {
const { areWeAdmin, 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 = (
<>
<p>
{i18n('ContactSpoofingReviewDialog__group__description', [
conversationInfos.length.toString(),
])}
</p>
<h2>{i18n('ContactSpoofingReviewDialog__group__members-header')}</h2>
{conversationInfos.map((conversationInfo, index) => {
let button: ReactNode;
if (areWeAdmin) {
button = (
title = i18n('ContactSpoofingReviewDialog__title');
contents = (
<>
<p>{i18n('ContactSpoofingReviewDialog__description')}</p>
<h2>
{i18n('ContactSpoofingReviewDialog__possibly-unsafe-title')}
</h2>
<ContactSpoofingReviewDialogPerson
conversation={possiblyUnsafeConversation}
i18n={i18n}
>
<div className="module-ContactSpoofingReviewDialog__buttons">
<Button
variant={ButtonVariant.SecondaryAffirmative}
variant={ButtonVariant.SecondaryDestructive}
onClick={() => {
setConfirmationState({
type: ConfirmationStateType.ConfirmingGroupRemoval,
affectedConversation: conversationInfo.conversation,
type: ConfirmationStateType.ConfirmingDelete,
affectedConversation: possiblyUnsafeConversation,
});
}}
>
{i18n('RemoveGroupMemberConfirmation__remove-button')}
{i18n('MessageRequests--delete')}
</Button>
);
} else if (conversationInfo.conversation.isBlocked) {
button = (
<Button
variant={ButtonVariant.SecondaryAffirmative}
onClick={() => {
onUnblock(conversationInfo.conversation.id);
}}
>
{i18n('MessageRequests--unblock')}
</Button>
);
} else if (!isInSystemContacts(conversationInfo.conversation)) {
button = (
<Button
variant={ButtonVariant.SecondaryDestructive}
onClick={() => {
setConfirmationState({
type: ConfirmationStateType.ConfirmingBlock,
affectedConversation: conversationInfo.conversation,
affectedConversation: possiblyUnsafeConversation,
});
}}
>
{i18n('MessageRequests--block')}
</Button>
</div>
</ContactSpoofingReviewDialogPerson>
<hr />
<h2>{i18n('ContactSpoofingReviewDialog__safe-title')}</h2>
<ContactSpoofingReviewDialogPerson
conversation={safeConversation}
i18n={i18n}
onClick={() => {
onShowContactModal(safeConversation.id);
}}
/>
</>
);
break;
}
case ContactSpoofingType.MultipleGroupMembersWithSameTitle: {
const { areWeAdmin, 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 = (
<>
<p>
{i18n('ContactSpoofingReviewDialog__group__description', [
conversationInfos.length.toString(),
])}
</p>
<h2>
{i18n('ContactSpoofingReviewDialog__group__members-header')}
</h2>
{conversationInfos.map((conversationInfo, index) => {
let button: ReactNode;
if (areWeAdmin) {
button = (
<Button
variant={ButtonVariant.SecondaryAffirmative}
onClick={() => {
setConfirmationState({
type: ConfirmationStateType.ConfirmingGroupRemoval,
affectedConversation: conversationInfo.conversation,
});
}}
>
{i18n('RemoveGroupMemberConfirmation__remove-button')}
</Button>
);
} else if (conversationInfo.conversation.isBlocked) {
button = (
<Button
variant={ButtonVariant.SecondaryAffirmative}
onClick={() => {
onUnblock(conversationInfo.conversation.id);
}}
>
{i18n('MessageRequests--unblock')}
</Button>
);
} else if (!isInSystemContacts(conversationInfo.conversation)) {
button = (
<Button
variant={ButtonVariant.SecondaryDestructive}
onClick={() => {
setConfirmationState({
type: ConfirmationStateType.ConfirmingBlock,
affectedConversation: conversationInfo.conversation,
});
}}
>
{i18n('MessageRequests--block')}
</Button>
);
}
const { oldName } = conversationInfo;
const newName =
conversationInfo.conversation.profileName ||
conversationInfo.conversation.title;
return (
<>
{index !== 0 && <hr />}
<ContactSpoofingReviewDialogPerson
key={conversationInfo.conversation.id}
conversation={conversationInfo.conversation}
i18n={i18n}
>
{Boolean(oldName) && oldName !== newName && (
<div className="module-ContactSpoofingReviewDialogPerson__info__property module-ContactSpoofingReviewDialogPerson__info__property--callout">
<Intl
i18n={i18n}
id="ContactSpoofingReviewDialog__group__name-change-info"
components={{
oldName: <Emojify text={oldName} />,
newName: <Emojify text={newName} />,
}}
/>
</div>
)}
{button && (
<div className="module-ContactSpoofingReviewDialog__buttons">
{button}
</div>
)}
</ContactSpoofingReviewDialogPerson>
</>
);
}
const { oldName } = conversationInfo;
const newName =
conversationInfo.conversation.profileName ||
conversationInfo.conversation.title;
return (
<>
{index !== 0 && <hr />}
<ContactSpoofingReviewDialogPerson
key={conversationInfo.conversation.id}
conversation={conversationInfo.conversation}
i18n={i18n}
>
{Boolean(oldName) && oldName !== newName && (
<div className="module-ContactSpoofingReviewDialogPerson__info__property module-ContactSpoofingReviewDialogPerson__info__property--callout">
<Intl
i18n={i18n}
id="ContactSpoofingReviewDialog__group__name-change-info"
components={{
oldName: <Emojify text={oldName} />,
newName: <Emojify text={newName} />,
}}
/>
</div>
)}
{button && (
<div className="module-ContactSpoofingReviewDialog__buttons">
{button}
</div>
)}
</ContactSpoofingReviewDialogPerson>
</>
);
})}
</>
);
break;
})}
</>
);
break;
}
default:
throw missingCaseError(props);
}
default:
throw missingCaseError(props);
}
return (
<Modal
hasXButton
i18n={i18n}
moduleClassName="module-ContactSpoofingReviewDialog"
onClose={onClose}
title={title}
>
{contents}
</Modal>
);
};
return (
<Modal
hasXButton
i18n={i18n}
moduleClassName="module-ContactSpoofingReviewDialog"
onClose={onClose}
title={title}
>
{contents}
</Modal>
);
};

View file

@ -19,60 +19,56 @@ type PropsType = {
onClick?: () => void;
};
export const ContactSpoofingReviewDialogPerson: FunctionComponent<PropsType> = ({
children,
conversation,
i18n,
onClick,
}) => {
assert(
conversation.type === 'direct',
'<ContactSpoofingReviewDialogPerson> expected a direct conversation'
);
const contents = (
<>
<Avatar
{...conversation}
conversationType={conversation.type}
size={AvatarSize.FIFTY_TWO}
className="module-ContactSpoofingReviewDialogPerson__avatar"
i18n={i18n}
/>
<div className="module-ContactSpoofingReviewDialogPerson__info">
<ContactName
module="module-ContactSpoofingReviewDialogPerson__info__contact-name"
title={conversation.title}
/>
{conversation.phoneNumber ? (
<div className="module-ContactSpoofingReviewDialogPerson__info__property">
{conversation.phoneNumber}
</div>
) : null}
<div className="module-ContactSpoofingReviewDialogPerson__info__property">
<SharedGroupNames
i18n={i18n}
sharedGroupNames={conversation.sharedGroupNames || []}
/>
</div>
{children}
</div>
</>
);
if (onClick) {
return (
<button
type="button"
className="module-ContactSpoofingReviewDialogPerson"
onClick={onClick}
>
{contents}
</button>
export const ContactSpoofingReviewDialogPerson: FunctionComponent<PropsType> =
({ children, conversation, i18n, onClick }) => {
assert(
conversation.type === 'direct',
'<ContactSpoofingReviewDialogPerson> expected a direct conversation'
);
}
return (
<div className="module-ContactSpoofingReviewDialogPerson">{contents}</div>
);
};
const contents = (
<>
<Avatar
{...conversation}
conversationType={conversation.type}
size={AvatarSize.FIFTY_TWO}
className="module-ContactSpoofingReviewDialogPerson__avatar"
i18n={i18n}
/>
<div className="module-ContactSpoofingReviewDialogPerson__info">
<ContactName
module="module-ContactSpoofingReviewDialogPerson__info__contact-name"
title={conversation.title}
/>
{conversation.phoneNumber ? (
<div className="module-ContactSpoofingReviewDialogPerson__info__property">
{conversation.phoneNumber}
</div>
) : null}
<div className="module-ContactSpoofingReviewDialogPerson__info__property">
<SharedGroupNames
i18n={i18n}
sharedGroupNames={conversation.sharedGroupNames || []}
/>
</div>
{children}
</div>
</>
);
if (onClick) {
return (
<button
type="button"
className="module-ContactSpoofingReviewDialogPerson"
onClick={onClick}
>
{contents}
</button>
);
}
return (
<div className="module-ContactSpoofingReviewDialogPerson">{contents}</div>
);
};

View file

@ -523,12 +523,8 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
}
private renderHeader(): ReactNode {
const {
conversationTitle,
groupVersion,
onShowConversationDetails,
type,
} = this.props;
const { conversationTitle, groupVersion, onShowConversationDetails, type } =
this.props;
if (conversationTitle !== undefined) {
return (
@ -592,13 +588,8 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
}
public render(): ReactNode {
const {
id,
isSMSOnly,
i18n,
onSetDisappearingMessages,
expireTimer,
} = this.props;
const { id, isSMSOnly, i18n, onSetDisappearingMessages, expireTimer } =
this.props;
const { isNarrow, modalState } = this.state;
const triggerId = `conversation-${id}`;

View file

@ -118,10 +118,8 @@ export const ConversationHero = ({
}: Props): JSX.Element => {
const firstRenderRef = useRef(true);
const [
isShowingMessageRequestWarning,
setIsShowingMessageRequestWarning,
] = useState(false);
const [isShowingMessageRequestWarning, setIsShowingMessageRequestWarning] =
useState(false);
const closeMessageRequestWarning = () => {
setIsShowingMessageRequestWarning(false);
};

View file

@ -26,8 +26,7 @@ story.add('Default', () => <GroupDescription {...createProps()} />);
story.add('Long', () => (
<GroupDescription
{...createProps({
text:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed vehicula urna. Ut rhoncus, justo a vestibulum elementum, libero ligula molestie massa, et volutpat nibh ipsum sit amet enim. Vestibulum ac mi enim. Nulla fringilla justo justo, volutpat semper ex convallis quis. Proin posuere, mi at auctor tincidunt, magna turpis mattis nibh, ullamcorper vehicula lectus mauris in mauris. Nullam blandit sapien tortor, quis vehicula quam molestie nec. Nam sagittis dolor in eros dapibus scelerisque. Proin vitae ex sed magna lobortis tincidunt. Aenean dictum laoreet dolor, at suscipit ligula fermentum ac. Nam condimentum turpis quis sollicitudin rhoncus.',
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed vehicula urna. Ut rhoncus, justo a vestibulum elementum, libero ligula molestie massa, et volutpat nibh ipsum sit amet enim. Vestibulum ac mi enim. Nulla fringilla justo justo, volutpat semper ex convallis quis. Proin posuere, mi at auctor tincidunt, magna turpis mattis nibh, ullamcorper vehicula lectus mauris in mauris. Nullam blandit sapien tortor, quis vehicula quam molestie nec. Nam sagittis dolor in eros dapibus scelerisque. Proin vitae ex sed magna lobortis tincidunt. Aenean dictum laoreet dolor, at suscipit ligula fermentum ac. Nam condimentum turpis quis sollicitudin rhoncus.',
})}
/>
));
@ -51,8 +50,7 @@ story.add('With emoji', () => (
story.add('With link', () => (
<GroupDescription
{...createProps({
text:
'I love https://example.com and http://example.com and example.com, but not https://user:bar@example.com',
text: 'I love https://example.com and http://example.com and example.com, but not https://user:bar@example.com',
})}
/>
));
@ -60,8 +58,7 @@ story.add('With link', () => (
story.add('Kitchen sink', () => (
<GroupDescription
{...createProps({
text:
'🍒 https://example.com this is a long thing\nhttps://example.com on another line\nhttps://example.com',
text: '🍒 https://example.com this is a long thing\nhttps://example.com on another line\nhttps://example.com',
})}
/>
));

View file

@ -26,8 +26,7 @@ story.add('Only Link', () => {
story.add('Links with Text', () => {
const props = createProps({
text:
'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
text: 'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
});
return <Linkify {...props} />;
@ -43,8 +42,7 @@ story.add('Links with Emoji without space', () => {
story.add('Links with Emoji and Text', () => {
const props = createProps({
text:
'https://example.com ⚠️ 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ https://example.com',
text: 'https://example.com ⚠️ 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ https://example.com',
});
return <Linkify {...props} />;
@ -60,8 +58,7 @@ story.add('No Link', () => {
story.add('Blocked Protocols', () => {
const props = createProps({
text:
'smailto:someone@somewhere.com - ftp://something.com - //local/share - \\localshare',
text: 'smailto:someone@somewhere.com - ftp://something.com - //local/share - \\localshare',
});
return <Linkify {...props} />;
@ -69,8 +66,7 @@ story.add('Blocked Protocols', () => {
story.add('Missing protocols', () => {
const props = createProps({
text:
'I love example.com. I also love кц.рф. I also love مثال.تونس. But I do not love test.example.',
text: 'I love example.com. I also love кц.рф. I also love مثال.تونس. But I do not love test.example.',
});
return <Linkify {...props} />;
@ -78,8 +74,7 @@ story.add('Missing protocols', () => {
story.add('Custom Text Render', () => {
const props = createProps({
text:
'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
text: 'you should see this: https://www.signal.org - it is good. Also: https://placekitten.com!',
renderNonLink: ({ text: theText, key }) => (
<div key={key} style={{ backgroundColor: 'aquamarine' }}>
{theText}

View file

@ -188,8 +188,7 @@ const renderBothDirections = (props: Props) => (
story.add('Plain Message', () => {
const props = createProps({
text:
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
});
return renderBothDirections(props);
@ -296,8 +295,7 @@ story.add('Delivered', () => {
const props = createProps({
direction: 'outgoing',
status: 'delivered',
text:
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
});
return <Message {...props} />;
@ -307,8 +305,7 @@ story.add('Read', () => {
const props = createProps({
direction: 'outgoing',
status: 'read',
text:
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
});
return <Message {...props} />;
@ -318,8 +315,7 @@ story.add('Sending', () => {
const props = createProps({
direction: 'outgoing',
status: 'sending',
text:
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
});
return <Message {...props} />;
@ -329,8 +325,7 @@ story.add('Expiring', () => {
const props = createProps({
expirationLength: 30 * 1000,
expirationTimestamp: Date.now() + 30 * 1000,
text:
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
});
return renderBothDirections(props);
@ -338,8 +333,7 @@ story.add('Expiring', () => {
story.add('Pending', () => {
const props = createProps({
text:
'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
text: 'Hello there from a pal! I am sending a long message so that it will wrap a bit, since I like that look.',
textPending: true,
});

View file

@ -296,7 +296,8 @@ export class Message extends React.PureComponent<Props, State> {
public audioButtonRef: React.RefObject<HTMLButtonElement> = React.createRef();
public reactionsContainerRef: React.RefObject<HTMLDivElement> = React.createRef();
public reactionsContainerRef: React.RefObject<HTMLDivElement> =
React.createRef();
public reactionsContainerRefMerger = createRefMerger();
@ -1194,7 +1195,8 @@ export class Message extends React.PureComponent<Props, State> {
return (
<div
className={classNames('module-message__author-avatar-container', {
'module-message__author-avatar-container--with-reactions': this.hasReactions(),
'module-message__author-avatar-container--with-reactions':
this.hasReactions(),
})}
>
<Avatar
@ -2405,14 +2407,8 @@ export class Message extends React.PureComponent<Props, State> {
}
public render(): JSX.Element | null {
const {
author,
attachments,
direction,
id,
isSticker,
timestamp,
} = this.props;
const { author, attachments, direction, id, isSticker, timestamp } =
this.props;
const { expired, expiring, imageBroken, isSelected } = this.state;
// This id is what connects our triple-dot click with our associated pop-up menu.

View file

@ -107,8 +107,7 @@ story.add('@Mention', () => {
replacementText: 'Bender B Rodriguez 🤖',
},
],
text:
'Like \uFFFC once said: My story is a lot like yours, only more interesting because it involves robots',
text: 'Like \uFFFC once said: My story is a lot like yours, only more interesting because it involves robots',
});
return <MessageBody {...props} />;
@ -167,8 +166,7 @@ story.add('Complex MessageBody', () => {
},
],
direction: 'outgoing',
text:
'Hey \uFFFC\nCheck out https://www.signal.org I think you will really like it 😍\n\ncc \uFFFC \uFFFC',
text: 'Hey \uFFFC\nCheck out https://www.signal.org I think you will really like it 😍\n\ncc \uFFFC \uFFFC',
});
return <MessageBody {...props} />;

View file

@ -85,7 +85,8 @@ export const MessageMetadata: FunctionComponent<PropsType> = props => {
'module-message__metadata__date': true,
'module-message__metadata__date--with-sticker': isSticker,
[`module-message__metadata__date--${direction}`]: !isSticker,
'module-message__metadata__date--with-image-no-caption': withImageNoCaption,
'module-message__metadata__date--with-image-no-caption':
withImageNoCaption,
})}
>
{statusInfo}

View file

@ -127,10 +127,8 @@ export class Quote extends React.Component<Props, State> {
}
componentDidMount(): void {
const {
doubleCheckMissingQuoteReference,
referencedMessageNotFound,
} = this.props;
const { doubleCheckMissingQuoteReference, referencedMessageNotFound } =
this.props;
if (referencedMessageNotFound) {
doubleCheckMissingQuoteReference?.();
@ -275,14 +273,8 @@ export class Quote extends React.Component<Props, State> {
}
public renderText(): JSX.Element | null {
const {
bodyRanges,
i18n,
text,
rawAttachment,
isIncoming,
isViewOnce,
} = this.props;
const { bodyRanges, i18n, text, rawAttachment, isIncoming, isViewOnce } =
this.props;
if (text) {
const quoteText = bodyRanges

View file

@ -123,10 +123,8 @@ export const ReactionViewer = React.forwardRef<HTMLDivElement, Props>(
[reactionsWithEmojiData, groupedAndSortedReactions]
);
const [
selectedReactionCategory,
setSelectedReactionCategory,
] = React.useState(pickedReaction || 'all');
const [selectedReactionCategory, setSelectedReactionCategory] =
React.useState(pickedReaction || 'all');
// Handle escape key
useEscapeHandling(onClose);

View file

@ -18,30 +18,26 @@ type PropsType = {
onRemove: () => void;
};
export const RemoveGroupMemberConfirmationDialog: FunctionComponent<PropsType> = ({
conversation,
i18n,
onClose,
onRemove,
}) => (
<ConfirmationDialog
actions={[
{
action: onRemove,
text: i18n('RemoveGroupMemberConfirmation__remove-button'),
style: 'negative',
},
]}
i18n={i18n}
onClose={onClose}
title={
<Intl
i18n={i18n}
id="RemoveGroupMemberConfirmation__description"
components={{
name: <ContactName title={conversation.title} />,
}}
/>
}
/>
);
export const RemoveGroupMemberConfirmationDialog: FunctionComponent<PropsType> =
({ conversation, i18n, onClose, onRemove }) => (
<ConfirmationDialog
actions={[
{
action: onRemove,
text: i18n('RemoveGroupMemberConfirmation__remove-button'),
style: 'negative',
},
]}
i18n={i18n}
onClose={onClose}
title={
<Intl
i18n={i18n}
id="RemoveGroupMemberConfirmation__description"
components={{
name: <ContactName title={conversation.title} />,
}}
/>
}
/>
);

View file

@ -263,8 +263,7 @@ const items: Record<string, TimelineItemType> = {
previews: [],
readStatus: ReadStatus.Read,
status: 'sent',
text:
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
text: 'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
timestamp: Date.now(),
},
},
@ -285,8 +284,7 @@ const items: Record<string, TimelineItemType> = {
previews: [],
readStatus: ReadStatus.Read,
status: 'read',
text:
'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
text: 'Hello there from the new world! And this is multiple lines of text. Lines and lines and lines.',
timestamp: Date.now(),
},
},

View file

@ -710,12 +710,8 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
);
public loadOlderMessages = (): void => {
const {
haveOldest,
isLoadingMessages,
items,
loadOlderMessages,
} = this.props;
const { haveOldest, isLoadingMessages, items, loadOlderMessages } =
this.props;
if (this.loadCountdownTimeout) {
clearTimeout(this.loadCountdownTimeout);

View file

@ -185,9 +185,10 @@ export const AddGroupMembersModal: FunctionComponent<PropsType> = ({
stage: Stage.ChoosingContacts,
});
const contactLookup = useMemo(() => makeLookup(candidateContacts, 'id'), [
candidateContacts,
]);
const contactLookup = useMemo(
() => makeLookup(candidateContacts, 'id'),
[candidateContacts]
);
const selectedContacts = deconstructLookup(
contactLookup,

View file

@ -148,17 +148,12 @@ export const ConversationDetails: React.ComponentType<Props> = ({
const [modalState, setModalState] = useState<ModalState>(
ModalState.NothingOpen
);
const [
editGroupAttributesRequestState,
setEditGroupAttributesRequestState,
] = useState<RequestState>(RequestState.Inactive);
const [
addGroupMembersRequestState,
setAddGroupMembersRequestState,
] = useState<RequestState>(RequestState.Inactive);
const [membersMissingCapability, setMembersMissingCapability] = useState(
false
);
const [editGroupAttributesRequestState, setEditGroupAttributesRequestState] =
useState<RequestState>(RequestState.Inactive);
const [addGroupMembersRequestState, setAddGroupMembersRequestState] =
useState<RequestState>(RequestState.Inactive);
const [membersMissingCapability, setMembersMissingCapability] =
useState(false);
if (conversation === undefined) {
throw new Error('ConversationDetails rendered without a conversation');

View file

@ -25,98 +25,103 @@ type PropsType = {
setMuteExpiration: (muteExpiresAt: undefined | number) => unknown;
};
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'
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]
);
setMuteExpiration(ms);
};
const onChangeDontNotifyForMentionsIfMuted = (rawValue: string) => {
setDontNotifyForMentionsIfMuted(rawValue === 'yes');
};
const onMuteChange = (rawValue: string) => {
const ms = parseIntOrThrow(
rawValue,
'NotificationSettings: mute ms was not an integer'
);
setMuteExpiration(ms);
};
return (
<div className="conversation-details-panel">
<PanelSection>
<PanelRow
icon={
<ConversationDetailsIcon
ariaLabel={i18n('muteNotificationsTitle')}
icon={IconType.mute}
/>
}
label={i18n('muteNotificationsTitle')}
right={
<Select options={muteOptions} onChange={onMuteChange} value={-1} />
}
/>
{conversationType === 'group' && (
const onChangeDontNotifyForMentionsIfMuted = (rawValue: string) => {
setDontNotifyForMentionsIfMuted(rawValue === 'yes');
};
return (
<div className="conversation-details-panel">
<PanelSection>
<PanelRow
icon={
<ConversationDetailsIcon
ariaLabel={i18n(
'ConversationNotificationsSettings__mentions__label'
)}
icon={IconType.mention}
ariaLabel={i18n('muteNotificationsTitle')}
icon={IconType.mute}
/>
}
label={i18n('ConversationNotificationsSettings__mentions__label')}
info={i18n('ConversationNotificationsSettings__mentions__info')}
label={i18n('muteNotificationsTitle')}
right={
<Select
options={[
{
text: i18n(
'ConversationNotificationsSettings__mentions__select__always-notify'
),
value: 'no',
},
{
text: i18n(
'ConversationNotificationsSettings__mentions__select__dont-notify-for-mentions-if-muted'
),
value: 'yes',
},
]}
onChange={onChangeDontNotifyForMentionsIfMuted}
value={dontNotifyForMentionsIfMuted ? 'yes' : 'no'}
options={muteOptions}
onChange={onMuteChange}
value={-1}
/>
}
/>
)}
</PanelSection>
</div>
);
};
{conversationType === 'group' && (
<PanelRow
icon={
<ConversationDetailsIcon
ariaLabel={i18n(
'ConversationNotificationsSettings__mentions__label'
)}
icon={IconType.mention}
/>
}
label={i18n('ConversationNotificationsSettings__mentions__label')}
info={i18n('ConversationNotificationsSettings__mentions__info')}
right={
<Select
options={[
{
text: i18n(
'ConversationNotificationsSettings__mentions__select__always-notify'
),
value: 'no',
},
{
text: i18n(
'ConversationNotificationsSettings__mentions__select__dont-notify-for-mentions-if-muted'
),
value: 'yes',
},
]}
onChange={onChangeDontNotifyForMentionsIfMuted}
value={dontNotifyForMentionsIfMuted ? 'yes' : 'no'}
/>
}
/>
)}
</PanelSection>
</div>
);
};

View file

@ -67,10 +67,8 @@ export const PendingInvites: React.ComponentType<PropsType> = ({
}
const [selectedTab, setSelectedTab] = React.useState(Tab.Requests);
const [
stagedMemberships,
setStagedMemberships,
] = React.useState<Array<StagedMembershipType> | null>(null);
const [stagedMemberships, setStagedMemberships] =
React.useState<Array<StagedMembershipType> | null>(null);
return (
<div className="conversation-details-panel">
@ -379,10 +377,8 @@ function MembersPendingProfileKey({
membership => membership.metadata.addedByUserId
);
const {
[ourUuid]: ourPendingMemberships,
...otherPendingMembershipGroups
} = groupedPendingMemberships;
const { [ourUuid]: ourPendingMemberships, ...otherPendingMembershipGroups } =
groupedPendingMemberships;
const otherPendingMemberships = Object.keys(otherPendingMembershipGroups)
.map(id => members.find(member => member.id === id))

View file

@ -9,28 +9,27 @@ export enum RequestState {
Active,
}
export const bemGenerator = (block: string) => (
element: string,
modifier?: string | Record<string, boolean>
): string => {
const base = `${block}__${element}`;
const classes = [base];
export const bemGenerator =
(block: string) =>
(element: string, modifier?: string | Record<string, boolean>): string => {
const base = `${block}__${element}`;
const classes = [base];
let conditionals: Record<string, boolean> = {};
let conditionals: Record<string, boolean> = {};
if (modifier) {
if (typeof modifier === 'string') {
classes.push(`${base}--${modifier}`);
} else {
conditionals = Object.keys(modifier).reduce(
(acc, key) => ({
...acc,
[`${base}--${key}`]: modifier[key],
}),
{} as Record<string, boolean>
);
if (modifier) {
if (typeof modifier === 'string') {
classes.push(`${base}--${modifier}`);
} else {
conditionals = Object.keys(modifier).reduce(
(acc, key) => ({
...acc,
[`${base}--${key}`]: modifier[key],
}),
{} as Record<string, boolean>
);
}
}
}
return classNames(classes, conditionals);
};
return classNames(classes, conditionals);
};

View file

@ -31,7 +31,7 @@ const DAY_MS = 24 * 60 * 60 * 1000;
export const days = (n: number) => n * DAY_MS;
const tokens = ['foo', 'bar', 'baz', 'qux', 'quux'];
const contentTypes = ({
const contentTypes = {
gif: 'image/gif',
jpg: 'image/jpeg',
png: 'image/png',
@ -39,7 +39,7 @@ const contentTypes = ({
docx: 'application/text',
pdf: 'application/pdf',
txt: 'application/text',
} as unknown) as Record<string, MIMEType>;
} as unknown as Record<string, MIMEType>;
const createRandomFile = (
startTime: number,

View file

@ -94,62 +94,66 @@ type GenericMediaItemWithSection<T> = {
type: T;
mediaItem: MediaItemType;
};
type MediaItemWithStaticSection = GenericMediaItemWithSection<StaticSectionType>;
type MediaItemWithYearMonthSection = GenericMediaItemWithSection<YearMonthSectionType> & {
year: number;
month: number;
};
type MediaItemWithStaticSection =
GenericMediaItemWithSection<StaticSectionType>;
type MediaItemWithYearMonthSection =
GenericMediaItemWithSection<YearMonthSectionType> & {
year: number;
month: number;
};
type MediaItemWithSection =
| MediaItemWithStaticSection
| MediaItemWithYearMonthSection;
const withSection = (referenceDateTime: moment.Moment) => (
mediaItem: MediaItemType
): MediaItemWithSection => {
const today = moment(referenceDateTime).startOf('day');
const yesterday = moment(referenceDateTime).subtract(1, 'day').startOf('day');
const thisWeek = moment(referenceDateTime).startOf('isoWeek');
const thisMonth = moment(referenceDateTime).startOf('month');
const withSection =
(referenceDateTime: moment.Moment) =>
(mediaItem: MediaItemType): MediaItemWithSection => {
const today = moment(referenceDateTime).startOf('day');
const yesterday = moment(referenceDateTime)
.subtract(1, 'day')
.startOf('day');
const thisWeek = moment(referenceDateTime).startOf('isoWeek');
const thisMonth = moment(referenceDateTime).startOf('month');
const { message } = mediaItem;
const mediaItemReceivedDate = moment.utc(getMessageTimestamp(message));
if (mediaItemReceivedDate.isAfter(today)) {
return {
order: 0,
type: 'today',
mediaItem,
};
}
if (mediaItemReceivedDate.isAfter(yesterday)) {
return {
order: 1,
type: 'yesterday',
mediaItem,
};
}
if (mediaItemReceivedDate.isAfter(thisWeek)) {
return {
order: 2,
type: 'thisWeek',
mediaItem,
};
}
if (mediaItemReceivedDate.isAfter(thisMonth)) {
return {
order: 3,
type: 'thisMonth',
mediaItem,
};
}
const { message } = mediaItem;
const mediaItemReceivedDate = moment.utc(getMessageTimestamp(message));
if (mediaItemReceivedDate.isAfter(today)) {
return {
order: 0,
type: 'today',
mediaItem,
};
}
if (mediaItemReceivedDate.isAfter(yesterday)) {
return {
order: 1,
type: 'yesterday',
mediaItem,
};
}
if (mediaItemReceivedDate.isAfter(thisWeek)) {
return {
order: 2,
type: 'thisWeek',
mediaItem,
};
}
if (mediaItemReceivedDate.isAfter(thisMonth)) {
return {
order: 3,
type: 'thisMonth',
mediaItem,
};
}
const month: number = mediaItemReceivedDate.month();
const year: number = mediaItemReceivedDate.year();
const month: number = mediaItemReceivedDate.month();
const year: number = mediaItemReceivedDate.year();
return {
order: year * 100 + month,
type: 'yearMonth',
month,
year,
mediaItem,
return {
order: year * 100 + month,
type: 'yearMonth',
month,
year,
mediaItem,
};
};
};