Upgrade Prettier to 2.4.1
This commit is contained in:
parent
f204784afe
commit
5619eeca83
176 changed files with 1961 additions and 2465 deletions
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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}`;
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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',
|
||||
})}
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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} />;
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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} />,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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(),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue