Upgrade Prettier to 2.4.1
This commit is contained in:
parent
f204784afe
commit
5619eeca83
176 changed files with 1961 additions and 2465 deletions
|
@ -35,43 +35,47 @@ type PropsType = {
|
|||
onClose: () => void;
|
||||
} & PropsDataType;
|
||||
|
||||
export const AddGroupMemberErrorDialog: FunctionComponent<PropsType> = 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.CantAddContact: {
|
||||
const { contact } = props;
|
||||
title = i18n('chooseGroupMembers__cant-add-member__title');
|
||||
body = (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="chooseGroupMembers__cant-add-member__body"
|
||||
components={[<ContactName key="name" title={contact.title} />]}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
let title: string;
|
||||
let body: ReactNode;
|
||||
switch (props.mode) {
|
||||
case AddGroupMemberErrorDialogMode.CantAddContact: {
|
||||
const { contact } = props;
|
||||
title = i18n('chooseGroupMembers__cant-add-member__title');
|
||||
body = (
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="chooseGroupMembers__cant-add-member__body"
|
||||
components={[<ContactName key="name" title={contact.title} />]}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
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 <Alert body={body} i18n={i18n} onClose={onClose} title={title} />;
|
||||
};
|
||||
return <Alert body={body} i18n={i18n} onClose={onClose} title={title} />;
|
||||
};
|
||||
|
|
|
@ -23,9 +23,8 @@ export const AvatarIconEditor = ({
|
|||
onClose,
|
||||
}: PropsType): JSX.Element => {
|
||||
const [avatarBuffer, setAvatarBuffer] = useState<Uint8Array | undefined>();
|
||||
const [avatarData, setAvatarData] = useState<AvatarDataType>(
|
||||
initialAvatarData
|
||||
);
|
||||
const [avatarData, setAvatarData] =
|
||||
useState<AvatarDataType>(initialAvatarData);
|
||||
|
||||
const onColorSelected = useCallback(
|
||||
(color: AvatarColorType) => {
|
||||
|
|
|
@ -47,9 +47,10 @@ export const AvatarTextEditor = ({
|
|||
onDone,
|
||||
}: PropsType): JSX.Element => {
|
||||
const initialText = useMemo(() => avatarData?.text || '', [avatarData]);
|
||||
const initialColor = useMemo(() => avatarData?.color || AvatarColors[0], [
|
||||
avatarData,
|
||||
]);
|
||||
const initialColor = useMemo(
|
||||
() => avatarData?.color || AvatarColors[0],
|
||||
[avatarData]
|
||||
);
|
||||
|
||||
const [inputText, setInputText] = useState(initialText);
|
||||
const [fontSize, setFontSize] = useState(getFontSizes(BUBBLE_SIZE).text);
|
||||
|
|
|
@ -514,7 +514,8 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
</div>
|
||||
<div
|
||||
className={classNames('module-ongoing-call__footer__local-preview', {
|
||||
'module-ongoing-call__footer__local-preview--audio-muted': !hasLocalAudio,
|
||||
'module-ongoing-call__footer__local-preview--audio-muted':
|
||||
!hasLocalAudio,
|
||||
})}
|
||||
>
|
||||
{localPreviewNode}
|
||||
|
|
|
@ -58,7 +58,8 @@ export const CallingHeader = ({
|
|||
className={classNames(
|
||||
'module-calling-button__participants--container',
|
||||
{
|
||||
'module-calling-button__participants--shown': showParticipantsList,
|
||||
'module-calling-button__participants--shown':
|
||||
showParticipantsList,
|
||||
}
|
||||
)}
|
||||
onClick={toggleParticipants}
|
||||
|
|
|
@ -77,10 +77,8 @@ export const CallingLobbyJoinButton: FunctionComponent<{
|
|||
if (!button) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
width: variantWidth,
|
||||
height: variantHeight,
|
||||
} = button.getBoundingClientRect();
|
||||
const { width: variantWidth, height: variantHeight } =
|
||||
button.getBoundingClientRect();
|
||||
|
||||
// We could set the padding in CSS, but we don't do that in case some other
|
||||
// styling causes a re-render of the button but not of the component. This
|
||||
|
|
|
@ -90,17 +90,16 @@ export const CallingPipRemoteVideo = ({
|
|||
|
||||
const isPageVisible = usePageVisibility();
|
||||
|
||||
const activeGroupCallSpeaker:
|
||||
| undefined
|
||||
| GroupCallRemoteParticipantType = useMemo(() => {
|
||||
if (activeCall.callMode !== CallMode.Group) {
|
||||
return undefined;
|
||||
}
|
||||
const activeGroupCallSpeaker: undefined | GroupCallRemoteParticipantType =
|
||||
useMemo(() => {
|
||||
if (activeCall.callMode !== CallMode.Group) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return maxBy(activeCall.remoteParticipants, participant =>
|
||||
participant.presenting ? Infinity : participant.speakerTime || -Infinity
|
||||
);
|
||||
}, [activeCall.callMode, activeCall.remoteParticipants]);
|
||||
return maxBy(activeCall.remoteParticipants, participant =>
|
||||
participant.presenting ? Infinity : participant.speakerTime || -Infinity
|
||||
);
|
||||
}, [activeCall.callMode, activeCall.remoteParticipants]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeCall.callMode !== CallMode.Group) {
|
||||
|
|
|
@ -110,18 +110,12 @@ export function CompositionInput(props: Props): React.ReactElement {
|
|||
sortedGroupMembers,
|
||||
} = props;
|
||||
|
||||
const [
|
||||
emojiCompletionElement,
|
||||
setEmojiCompletionElement,
|
||||
] = React.useState<JSX.Element>();
|
||||
const [
|
||||
lastSelectionRange,
|
||||
setLastSelectionRange,
|
||||
] = React.useState<RangeStatic | null>(null);
|
||||
const [
|
||||
mentionCompletionElement,
|
||||
setMentionCompletionElement,
|
||||
] = React.useState<JSX.Element>();
|
||||
const [emojiCompletionElement, setEmojiCompletionElement] =
|
||||
React.useState<JSX.Element>();
|
||||
const [lastSelectionRange, setLastSelectionRange] =
|
||||
React.useState<RangeStatic | null>(null);
|
||||
const [mentionCompletionElement, setMentionCompletionElement] =
|
||||
React.useState<JSX.Element>();
|
||||
|
||||
const emojiCompletionRef = React.useRef<EmojiCompletion>();
|
||||
const mentionCompletionRef = React.useRef<MentionCompletion>();
|
||||
|
@ -607,9 +601,8 @@ export function CompositionInput(props: Props): React.ReactElement {
|
|||
);
|
||||
quillRef.current = quill;
|
||||
emojiCompletionRef.current = quill.getModule('emojiCompletion');
|
||||
mentionCompletionRef.current = quill.getModule(
|
||||
'mentionCompletion'
|
||||
);
|
||||
mentionCompletionRef.current =
|
||||
quill.getModule('mentionCompletion');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -49,19 +49,20 @@ export const CompositionUpload = forwardRef<HTMLInputElement, PropsType>(
|
|||
AttachmentToastType | undefined
|
||||
>();
|
||||
|
||||
const onFileInputChange: ChangeEventHandler<HTMLInputElement> = 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);
|
||||
|
|
|
@ -70,16 +70,8 @@ export class ContactListItem extends React.Component<Props> {
|
|||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
const {
|
||||
about,
|
||||
i18n,
|
||||
isAdmin,
|
||||
isMe,
|
||||
name,
|
||||
onClick,
|
||||
title,
|
||||
type,
|
||||
} = this.props;
|
||||
const { about, i18n, isAdmin, isMe, name, onClick, title, type } =
|
||||
this.props;
|
||||
|
||||
const displayName = isMe ? i18n('you') : title;
|
||||
|
||||
|
|
|
@ -35,8 +35,7 @@ const defaultConversations: Array<ConversationListItemPropsType> = [
|
|||
title: 'Marc Barraca',
|
||||
lastMessage: {
|
||||
deletedForEveryone: false,
|
||||
text:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.',
|
||||
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.',
|
||||
},
|
||||
}),
|
||||
getDefaultConversation({
|
||||
|
|
|
@ -17,27 +17,26 @@ const story = storiesOf(
|
|||
module
|
||||
);
|
||||
|
||||
const defaultProps: ComponentProps<
|
||||
typeof CustomizingPreferredReactionsModal
|
||||
> = {
|
||||
cancelCustomizePreferredReactionsModal: action(
|
||||
'cancelCustomizePreferredReactionsModal'
|
||||
),
|
||||
deselectDraftEmoji: action('deselectDraftEmoji'),
|
||||
draftPreferredReactions: ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'],
|
||||
hadSaveError: false,
|
||||
i18n,
|
||||
isSaving: false,
|
||||
onSetSkinTone: action('onSetSkinTone'),
|
||||
originalPreferredReactions: ['❤️', '👍', '👎', '😂', '😮', '😢'],
|
||||
recentEmojis: ['cake'],
|
||||
replaceSelectedDraftEmoji: action('replaceSelectedDraftEmoji'),
|
||||
resetDraftEmoji: action('resetDraftEmoji'),
|
||||
savePreferredReactions: action('savePreferredReactions'),
|
||||
selectDraftEmojiToBeReplaced: action('selectDraftEmojiToBeReplaced'),
|
||||
selectedDraftEmojiIndex: undefined,
|
||||
skinTone: 4,
|
||||
};
|
||||
const defaultProps: ComponentProps<typeof CustomizingPreferredReactionsModal> =
|
||||
{
|
||||
cancelCustomizePreferredReactionsModal: action(
|
||||
'cancelCustomizePreferredReactionsModal'
|
||||
),
|
||||
deselectDraftEmoji: action('deselectDraftEmoji'),
|
||||
draftPreferredReactions: ['✨', '❇️', '🎇', '🦈', '💖', '🅿️'],
|
||||
hadSaveError: false,
|
||||
i18n,
|
||||
isSaving: false,
|
||||
onSetSkinTone: action('onSetSkinTone'),
|
||||
originalPreferredReactions: ['❤️', '👍', '👎', '😂', '😮', '😢'],
|
||||
recentEmojis: ['cake'],
|
||||
replaceSelectedDraftEmoji: action('replaceSelectedDraftEmoji'),
|
||||
resetDraftEmoji: action('resetDraftEmoji'),
|
||||
savePreferredReactions: action('savePreferredReactions'),
|
||||
selectDraftEmojiToBeReplaced: action('selectDraftEmojiToBeReplaced'),
|
||||
selectedDraftEmojiIndex: undefined,
|
||||
skinTone: 4,
|
||||
};
|
||||
|
||||
story.add('Default', () => (
|
||||
<CustomizingPreferredReactionsModal {...defaultProps} />
|
||||
|
|
|
@ -54,10 +54,8 @@ export function CustomizingPreferredReactionsModal({
|
|||
selectedDraftEmojiIndex,
|
||||
skinTone,
|
||||
}: Readonly<PropsType>): JSX.Element {
|
||||
const [
|
||||
referenceElement,
|
||||
setReferenceElement,
|
||||
] = useState<null | HTMLDivElement>(null);
|
||||
const [referenceElement, setReferenceElement] =
|
||||
useState<null | HTMLDivElement>(null);
|
||||
const [popperElement, setPopperElement] = useState<null | HTMLDivElement>(
|
||||
null
|
||||
);
|
||||
|
|
|
@ -60,10 +60,12 @@ story.add('Knobs Playground', () => {
|
|||
);
|
||||
});
|
||||
|
||||
([
|
||||
['wide', WidthBreakpoint.Wide],
|
||||
['narrow', WidthBreakpoint.Narrow],
|
||||
] as const).forEach(([name, containerWidthBreakpoint]) => {
|
||||
(
|
||||
[
|
||||
['wide', WidthBreakpoint.Wide],
|
||||
['narrow', WidthBreakpoint.Narrow],
|
||||
] as const
|
||||
).forEach(([name, containerWidthBreakpoint]) => {
|
||||
const defaultPropsForBreakpoint = {
|
||||
...defaultProps,
|
||||
containerWidthBreakpoint,
|
||||
|
|
|
@ -54,10 +54,12 @@ story.add('Knobs Playground', () => {
|
|||
);
|
||||
});
|
||||
|
||||
([
|
||||
['wide', WidthBreakpoint.Wide],
|
||||
['narrow', WidthBreakpoint.Narrow],
|
||||
] as const).forEach(([name, containerWidthBreakpoint]) => {
|
||||
(
|
||||
[
|
||||
['wide', WidthBreakpoint.Wide],
|
||||
['narrow', WidthBreakpoint.Narrow],
|
||||
] as const
|
||||
).forEach(([name, containerWidthBreakpoint]) => {
|
||||
const defaultPropsForBreakpoint = {
|
||||
...defaultProps,
|
||||
containerWidthBreakpoint,
|
||||
|
|
|
@ -38,9 +38,8 @@ export const DisappearingTimerSelect: React.FC<Props> = (props: Props) => {
|
|||
};
|
||||
});
|
||||
|
||||
const isCustomTimeSelected = !expirationTimer.DEFAULT_DURATIONS_SET.has(
|
||||
value
|
||||
);
|
||||
const isCustomTimeSelected =
|
||||
!expirationTimer.DEFAULT_DURATIONS_SET.has(value);
|
||||
|
||||
const onSelectChange = (newValue: string) => {
|
||||
const intValue = parseInt(newValue, 10);
|
||||
|
|
|
@ -69,9 +69,8 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
videoAspectRatio,
|
||||
} = props.remoteParticipant;
|
||||
|
||||
const [hasReceivedVideoRecently, setHasReceivedVideoRecently] = useState(
|
||||
false
|
||||
);
|
||||
const [hasReceivedVideoRecently, setHasReceivedVideoRecently] =
|
||||
useState(false);
|
||||
const [isWide, setIsWide] = useState<boolean>(
|
||||
videoAspectRatio ? videoAspectRatio >= 1 : true
|
||||
);
|
||||
|
@ -90,10 +89,8 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
const remoteVideoRef = useRef<HTMLCanvasElement | null>(null);
|
||||
const canvasContextRef = useRef<CanvasRenderingContext2D | null>(null);
|
||||
|
||||
const [
|
||||
intersectionRef,
|
||||
intersectionObserverEntry,
|
||||
] = useIntersectionObserver();
|
||||
const [intersectionRef, intersectionObserverEntry] =
|
||||
useIntersectionObserver();
|
||||
const isVisible = intersectionObserverEntry
|
||||
? intersectionObserverEntry.isIntersecting
|
||||
: true;
|
||||
|
@ -268,7 +265,8 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
className={classNames(
|
||||
'module-ongoing-call__group-call-remote-participant--title',
|
||||
{
|
||||
'module-ongoing-call__group-call-remote-participant--audio-muted': !hasRemoteAudio,
|
||||
'module-ongoing-call__group-call-remote-participant--audio-muted':
|
||||
!hasRemoteAudio,
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
|
@ -135,33 +135,34 @@ export const GroupCallRemoteParticipants: React.FC<PropsType> = ({
|
|||
),
|
||||
[remoteParticipants]
|
||||
);
|
||||
const gridParticipants: Array<GroupCallRemoteParticipantType> = useMemo(() => {
|
||||
if (!sortedParticipants.length) {
|
||||
return [];
|
||||
}
|
||||
const gridParticipants: Array<GroupCallRemoteParticipantType> =
|
||||
useMemo(() => {
|
||||
if (!sortedParticipants.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const candidateParticipants = isInSpeakerView
|
||||
? [sortedParticipants[0]]
|
||||
: sortedParticipants;
|
||||
const candidateParticipants = isInSpeakerView
|
||||
? [sortedParticipants[0]]
|
||||
: sortedParticipants;
|
||||
|
||||
// Imagine that we laid out all of the rows end-to-end. That's the maximum total
|
||||
// width. So if there were 5 rows and the container was 100px wide, then we can't
|
||||
// possibly fit more than 500px of participants.
|
||||
const maxTotalWidth = maxRowCount * containerDimensions.width;
|
||||
// Imagine that we laid out all of the rows end-to-end. That's the maximum total
|
||||
// width. So if there were 5 rows and the container was 100px wide, then we can't
|
||||
// possibly fit more than 500px of participants.
|
||||
const maxTotalWidth = maxRowCount * containerDimensions.width;
|
||||
|
||||
// We do the same thing for participants, "laying them out end-to-end" until they
|
||||
// exceed the maximum total width.
|
||||
let totalWidth = 0;
|
||||
return takeWhile(candidateParticipants, remoteParticipant => {
|
||||
totalWidth += remoteParticipant.videoAspectRatio * MIN_RENDERED_HEIGHT;
|
||||
return totalWidth < maxTotalWidth;
|
||||
}).sort(stableParticipantComparator);
|
||||
}, [
|
||||
containerDimensions.width,
|
||||
isInSpeakerView,
|
||||
maxRowCount,
|
||||
sortedParticipants,
|
||||
]);
|
||||
// We do the same thing for participants, "laying them out end-to-end" until they
|
||||
// exceed the maximum total width.
|
||||
let totalWidth = 0;
|
||||
return takeWhile(candidateParticipants, remoteParticipant => {
|
||||
totalWidth += remoteParticipant.videoAspectRatio * MIN_RENDERED_HEIGHT;
|
||||
return totalWidth < maxTotalWidth;
|
||||
}).sort(stableParticipantComparator);
|
||||
}, [
|
||||
containerDimensions.width,
|
||||
isInSpeakerView,
|
||||
maxRowCount,
|
||||
sortedParticipants,
|
||||
]);
|
||||
const overflowedParticipants: Array<GroupCallRemoteParticipantType> = useMemo(
|
||||
() =>
|
||||
sortedParticipants
|
||||
|
|
|
@ -24,8 +24,8 @@ export type HousekeepingPropsType = {
|
|||
|
||||
export type PropsType = DataPropsType & HousekeepingPropsType;
|
||||
|
||||
export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> = React.memo(
|
||||
(props: PropsType) => {
|
||||
export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> =
|
||||
React.memo((props: PropsType) => {
|
||||
const {
|
||||
areWeInvited,
|
||||
droppedMembers,
|
||||
|
@ -94,8 +94,7 @@ export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> = React.
|
|||
)}
|
||||
</GroupDialog>
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function renderMembers(
|
||||
members: Array<ConversationType>,
|
||||
|
|
|
@ -330,9 +330,8 @@ export const LeftPane: React.FC<PropsType> = ({
|
|||
|
||||
const numericIndex = keyboardKeyToNumericIndex(event.key);
|
||||
if (commandOrCtrl && isNumber(numericIndex)) {
|
||||
conversationToOpen = helper.getConversationAndMessageAtIndex(
|
||||
numericIndex
|
||||
);
|
||||
conversationToOpen =
|
||||
helper.getConversationAndMessageAtIndex(numericIndex);
|
||||
} else {
|
||||
let toFind: undefined | ToFindType;
|
||||
if (
|
||||
|
|
|
@ -63,9 +63,8 @@ export function Lightbox({
|
|||
selectedIndex: initialSelectedIndex = 0,
|
||||
}: PropsType): JSX.Element | null {
|
||||
const [root, setRoot] = React.useState<HTMLElement | undefined>();
|
||||
const [selectedIndex, setSelectedIndex] = useState<number>(
|
||||
initialSelectedIndex
|
||||
);
|
||||
const [selectedIndex, setSelectedIndex] =
|
||||
useState<number>(initialSelectedIndex);
|
||||
|
||||
const [videoElement, setVideoElement] = useState<HTMLVideoElement | null>(
|
||||
null
|
||||
|
@ -224,8 +223,13 @@ export function Lightbox({
|
|||
};
|
||||
}, [onKeyDown]);
|
||||
|
||||
const { attachment, contentType, loop = false, objectURL, message } =
|
||||
media[selectedIndex] || {};
|
||||
const {
|
||||
attachment,
|
||||
contentType,
|
||||
loop = false,
|
||||
objectURL,
|
||||
message,
|
||||
} = media[selectedIndex] || {};
|
||||
|
||||
const isAttachmentGIF = isGIF(attachment ? [attachment] : undefined);
|
||||
|
||||
|
@ -251,23 +255,23 @@ export function Lightbox({
|
|||
() => INITIAL_IMAGE_TRANSFORM
|
||||
);
|
||||
|
||||
const maxBoundsLimiter = useCallback((x: number, y: number): [
|
||||
number,
|
||||
number
|
||||
] => {
|
||||
const zoomCache = zoomCacheRef.current;
|
||||
const maxBoundsLimiter = useCallback(
|
||||
(x: number, y: number): [number, number] => {
|
||||
const zoomCache = zoomCacheRef.current;
|
||||
|
||||
if (!zoomCache) {
|
||||
return [0, 0];
|
||||
}
|
||||
if (!zoomCache) {
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
const { maxX, maxY } = zoomCache;
|
||||
const { maxX, maxY } = zoomCache;
|
||||
|
||||
const posX = Math.min(maxX, Math.max(-maxX, x));
|
||||
const posY = Math.min(maxY, Math.max(-maxY, y));
|
||||
const posX = Math.min(maxX, Math.max(-maxX, x));
|
||||
const posY = Math.min(maxY, Math.max(-maxY, y));
|
||||
|
||||
return [posX, posY];
|
||||
}, []);
|
||||
return [posX, posY];
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const positionImage = useCallback(
|
||||
(ev: MouseEvent) => {
|
||||
|
@ -352,12 +356,8 @@ export function Lightbox({
|
|||
screenWidth: window.innerWidth,
|
||||
};
|
||||
|
||||
const {
|
||||
height,
|
||||
left,
|
||||
top,
|
||||
width,
|
||||
} = animateNode.getBoundingClientRect();
|
||||
const { height, left, top, width } =
|
||||
animateNode.getBoundingClientRect();
|
||||
|
||||
const offsetX = ev.clientX - left - width / 2;
|
||||
const offsetY = ev.clientY - top - height / 2;
|
||||
|
|
|
@ -133,7 +133,8 @@ export const MediaQualitySelector = ({
|
|||
<div
|
||||
className={classNames({
|
||||
'MediaQualitySelector__option--checkmark': true,
|
||||
'MediaQualitySelector__option--selected': !isHighQuality,
|
||||
'MediaQualitySelector__option--selected':
|
||||
!isHighQuality,
|
||||
})}
|
||||
/>
|
||||
<div>
|
||||
|
|
|
@ -17,67 +17,68 @@ type PropsType = {
|
|||
onClose: () => void;
|
||||
};
|
||||
|
||||
export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent<PropsType> = ({
|
||||
contacts,
|
||||
i18n,
|
||||
onClose,
|
||||
}) => {
|
||||
let title: string;
|
||||
let body: ReactNode;
|
||||
if (contacts.length === 1) {
|
||||
const contact = contacts[0];
|
||||
export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent<PropsType> =
|
||||
({ contacts, i18n, onClose }) => {
|
||||
let title: string;
|
||||
let body: ReactNode;
|
||||
if (contacts.length === 1) {
|
||||
const contact = contacts[0];
|
||||
|
||||
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--one');
|
||||
body = (
|
||||
<>
|
||||
<GroupDialog.Paragraph>
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--one"
|
||||
components={[<ContactName title={contact.title} />]}
|
||||
/>
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n('NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph')}
|
||||
</GroupDialog.Paragraph>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--many', [
|
||||
contacts.length.toString(),
|
||||
]);
|
||||
body = (
|
||||
<>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--many'
|
||||
)}
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n('NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph')}
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Contacts contacts={contacts} i18n={i18n} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--one');
|
||||
body = (
|
||||
<>
|
||||
<GroupDialog.Paragraph>
|
||||
<Intl
|
||||
i18n={i18n}
|
||||
id="NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--one"
|
||||
components={[<ContactName title={contact.title} />]}
|
||||
/>
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph'
|
||||
)}
|
||||
</GroupDialog.Paragraph>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
title = i18n('NewlyCreatedGroupInvitedContactsDialog--title--many', [
|
||||
contacts.length.toString(),
|
||||
]);
|
||||
body = (
|
||||
<>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--user-paragraph--many'
|
||||
)}
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Paragraph>
|
||||
{i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph'
|
||||
)}
|
||||
</GroupDialog.Paragraph>
|
||||
<GroupDialog.Contacts contacts={contacts} i18n={i18n} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<GroupDialog
|
||||
i18n={i18n}
|
||||
onClickPrimaryButton={onClose}
|
||||
primaryButtonText={i18n('Confirmation--confirm')}
|
||||
secondaryButtonText={i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--learn-more'
|
||||
)}
|
||||
onClickSecondaryButton={() => {
|
||||
openLinkInWebBrowser(
|
||||
'https://support.signal.org/hc/articles/360007319331-Group-chats'
|
||||
);
|
||||
}}
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
>
|
||||
{body}
|
||||
</GroupDialog>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<GroupDialog
|
||||
i18n={i18n}
|
||||
onClickPrimaryButton={onClose}
|
||||
primaryButtonText={i18n('Confirmation--confirm')}
|
||||
secondaryButtonText={i18n(
|
||||
'NewlyCreatedGroupInvitedContactsDialog--body--learn-more'
|
||||
)}
|
||||
onClickSecondaryButton={() => {
|
||||
openLinkInWebBrowser(
|
||||
'https://support.signal.org/hc/articles/360007319331-Group-chats'
|
||||
);
|
||||
}}
|
||||
onClose={onClose}
|
||||
title={title}
|
||||
>
|
||||
{body}
|
||||
</GroupDialog>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -267,10 +267,8 @@ export const Preferences = ({
|
|||
const [page, setPage] = useState<Page>(Page.General);
|
||||
const [showSyncFailed, setShowSyncFailed] = useState(false);
|
||||
const [nowSyncing, setNowSyncing] = useState(false);
|
||||
const [
|
||||
showDisappearingTimerDialog,
|
||||
setShowDisappearingTimerDialog,
|
||||
] = useState(false);
|
||||
const [showDisappearingTimerDialog, setShowDisappearingTimerDialog] =
|
||||
useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
doneRendering();
|
||||
|
@ -281,7 +279,7 @@ export const Preferences = ({
|
|||
const onZoomSelectChange = useCallback(
|
||||
(value: string) => {
|
||||
const number = parseFloat(value);
|
||||
onZoomFactorChange((number as unknown) as ZoomFactorType);
|
||||
onZoomFactorChange(number as unknown as ZoomFactorType);
|
||||
},
|
||||
[onZoomFactorChange]
|
||||
);
|
||||
|
@ -764,9 +762,8 @@ export const Preferences = ({
|
|||
</>
|
||||
);
|
||||
} else if (page === Page.Privacy) {
|
||||
const isCustomDisappearingMessageValue = !DEFAULT_DURATIONS_SET.has(
|
||||
universalExpireTimer
|
||||
);
|
||||
const isCustomDisappearingMessageValue =
|
||||
!DEFAULT_DURATIONS_SET.has(universalExpireTimer);
|
||||
|
||||
settings = (
|
||||
<>
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -61,8 +61,8 @@ type PropsType = {
|
|||
| 'unblurredAvatarPath'
|
||||
>;
|
||||
|
||||
export const BaseConversationListItem: FunctionComponent<PropsType> = React.memo(
|
||||
function BaseConversationListItem({
|
||||
export const BaseConversationListItem: FunctionComponent<PropsType> =
|
||||
React.memo(function BaseConversationListItem({
|
||||
acceptedMessageRequest,
|
||||
avatarPath,
|
||||
badge,
|
||||
|
@ -238,8 +238,7 @@ export const BaseConversationListItem: FunctionComponent<PropsType> = React.memo
|
|||
{contents}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function UnreadIndicator({ count = 0 }: Readonly<{ count?: number }>) {
|
||||
let classModifier: undefined | string;
|
||||
|
|
|
@ -42,8 +42,7 @@ story.add('No Replacement', () => {
|
|||
|
||||
story.add('Two Replacements', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'Begin <<left>>Inside #1<<right>> This is between the two <<left>>Inside #2<<right>> End.',
|
||||
text: 'Begin <<left>>Inside #1<<right>> This is between the two <<left>>Inside #2<<right>> End.',
|
||||
});
|
||||
|
||||
return <MessageBodyHighlight {...props} />;
|
||||
|
@ -59,8 +58,7 @@ story.add('Two Replacements with an @mention', () => {
|
|||
start: 33,
|
||||
},
|
||||
],
|
||||
text:
|
||||
'Begin <<left>>Inside #1<<right>> \uFFFC This is between the two <<left>>Inside #2<<right>> End.',
|
||||
text: 'Begin <<left>>Inside #1<<right>> \uFFFC This is between the two <<left>>Inside #2<<right>> End.',
|
||||
});
|
||||
|
||||
return <MessageBodyHighlight {...props} />;
|
||||
|
@ -68,8 +66,7 @@ story.add('Two Replacements with an @mention', () => {
|
|||
|
||||
story.add('Emoji + Newlines + URLs', () => {
|
||||
const props = createProps({
|
||||
text:
|
||||
'\nhttp://somewhere.com\n\n🔥 Before -- <<left>>A 🔥 inside<<right>> -- After 🔥',
|
||||
text: '\nhttp://somewhere.com\n\n🔥 Before -- <<left>>A 🔥 inside<<right>> -- After 🔥',
|
||||
});
|
||||
|
||||
return <MessageBodyHighlight {...props} />;
|
||||
|
|
|
@ -148,8 +148,7 @@ story.add('Empty (should be invalid)', () => {
|
|||
|
||||
story.add('@mention', () => {
|
||||
const props = createProps({
|
||||
body:
|
||||
'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC',
|
||||
body: 'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC',
|
||||
bodyRanges: [
|
||||
{
|
||||
length: 1,
|
||||
|
@ -175,8 +174,7 @@ story.add('@mention', () => {
|
|||
|
||||
story.add('@mention regexp', () => {
|
||||
const props = createProps({
|
||||
body:
|
||||
'\uFFFC This is a (long) /text/ ^$ that is ... specially **crafted** to (test) our regexp escaping mechanism! Making sure that the code we write works in all sorts of scenarios',
|
||||
body: '\uFFFC This is a (long) /text/ ^$ that is ... specially **crafted** to (test) our regexp escaping mechanism! Making sure that the code we write works in all sorts of scenarios',
|
||||
bodyRanges: [
|
||||
{
|
||||
length: 1,
|
||||
|
@ -215,8 +213,7 @@ story.add('@mention no-matches', () => {
|
|||
|
||||
story.add('@mention no-matches', () => {
|
||||
const props = createProps({
|
||||
body:
|
||||
'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC',
|
||||
body: 'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC',
|
||||
bodyRanges: [
|
||||
{
|
||||
length: 1,
|
||||
|
|
|
@ -6,6 +6,5 @@ import React from 'react';
|
|||
|
||||
type PropsType = Record<string, never>;
|
||||
|
||||
export const SearchResultsLoadingFakeHeader: FunctionComponent<PropsType> = () => (
|
||||
<div className="module-SearchResultsLoadingFakeHeader" />
|
||||
);
|
||||
export const SearchResultsLoadingFakeHeader: FunctionComponent<PropsType> =
|
||||
() => <div className="module-SearchResultsLoadingFakeHeader" />;
|
||||
|
|
|
@ -353,9 +353,8 @@ data.forEach(emoji => {
|
|||
|
||||
if (skin_variations) {
|
||||
Object.entries(skin_variations).forEach(([tone, variation]) => {
|
||||
imageByEmoji[
|
||||
convertShortName(short_name, tone as SkinToneKey)
|
||||
] = makeImagePath(variation.image);
|
||||
imageByEmoji[convertShortName(short_name, tone as SkinToneKey)] =
|
||||
makeImagePath(variation.image);
|
||||
dataByEmoji[convertShortName(short_name, tone as SkinToneKey)] = emoji;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -62,7 +62,8 @@ export class LeftPaneChooseGroupMembersHelper extends LeftPaneHelper<LeftPaneCho
|
|||
this.candidateContacts = candidateContacts;
|
||||
this.cantAddContactForModal = cantAddContactForModal;
|
||||
this.isShowingMaximumGroupSizeModal = isShowingMaximumGroupSizeModal;
|
||||
this.isShowingRecommendedGroupSizeModal = isShowingRecommendedGroupSizeModal;
|
||||
this.isShowingRecommendedGroupSizeModal =
|
||||
isShowingRecommendedGroupSizeModal;
|
||||
this.searchTerm = searchTerm;
|
||||
this.selectedContacts = selectedContacts;
|
||||
|
||||
|
|
|
@ -161,9 +161,8 @@ export class LeftPaneSearchHelper extends LeftPaneHelper<LeftPaneSearchPropsType
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const conversationRowCount = getRowCountForLoadedSearchResults(
|
||||
conversationResults
|
||||
);
|
||||
const conversationRowCount =
|
||||
getRowCountForLoadedSearchResults(conversationResults);
|
||||
const contactRowCount = getRowCountForLoadedSearchResults(contactResults);
|
||||
const messageRowCount = getRowCountForLoadedSearchResults(messageResults);
|
||||
|
||||
|
|
|
@ -33,10 +33,8 @@ export const StickerManager = React.memo(
|
|||
i18n,
|
||||
}: Props) => {
|
||||
const focusRef = React.createRef<HTMLDivElement>();
|
||||
const [
|
||||
packToPreview,
|
||||
setPackToPreview,
|
||||
] = React.useState<StickerPackType | null>(null);
|
||||
const [packToPreview, setPackToPreview] =
|
||||
React.useState<StickerPackType | null>(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!knownPacks) {
|
||||
|
|
|
@ -237,7 +237,8 @@ export const StickerPicker = React.memo(
|
|||
'module-sticker-picker__header__button',
|
||||
'module-sticker-picker__header__button--add-pack',
|
||||
{
|
||||
'module-sticker-picker__header__button--hint': showPickerHint,
|
||||
'module-sticker-picker__header__button--hint':
|
||||
showPickerHint,
|
||||
}
|
||||
)}
|
||||
onClick={onClickAddPack}
|
||||
|
@ -300,8 +301,10 @@ export const StickerPicker = React.memo(
|
|||
className={classNames(
|
||||
'module-sticker-picker__body__content',
|
||||
{
|
||||
'module-sticker-picker__body__content--under-text': showText,
|
||||
'module-sticker-picker__body__content--under-long-text': showLongText,
|
||||
'module-sticker-picker__body__content--under-text':
|
||||
showText,
|
||||
'module-sticker-picker__body__content--under-long-text':
|
||||
showLongText,
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue