From 5a57e2b7043b5bfc8799b1b8ab9e931f39b32a16 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Sun, 12 Sep 2021 19:36:41 -0700 Subject: [PATCH] Fix action propagation to timeline items --- ts/components/CompositionArea.tsx | 45 ++++++++++--------- ts/components/ProfileEditorModal.tsx | 2 +- ts/components/conversation/AttachmentList.tsx | 6 +-- ts/components/conversation/Quote.tsx | 6 +-- ts/components/conversation/Timeline.tsx | 8 ++-- ts/state/selectors/conversations.ts | 13 +++++- ts/state/smart/CompositionArea.tsx | 14 +++--- ts/state/smart/GlobalModalContainer.tsx | 6 +-- ts/state/smart/LeftPane.tsx | 6 +-- ts/state/smart/MessageSearchResult.tsx | 2 +- ts/state/smart/ProfileEditorModal.ts | 3 +- ts/state/smart/Timeline.tsx | 4 +- ts/state/smart/TimelineItem.tsx | 6 +-- ts/types/Attachment.ts | 16 ++++--- 14 files changed, 71 insertions(+), 66 deletions(-) diff --git a/ts/components/CompositionArea.tsx b/ts/components/CompositionArea.tsx index 66b3781cd..50f96caaf 100644 --- a/ts/components/CompositionArea.tsx +++ b/ts/components/CompositionArea.tsx @@ -50,39 +50,42 @@ export type CompositionAPIType = { resetEmojiResults: InputApi['resetEmojiResults']; }; -export type OwnProps = { - readonly i18n: LocalizerType; - readonly areWePending?: boolean; - readonly areWePendingApproval?: boolean; - readonly announcementsOnly?: boolean; - readonly areWeAdmin?: boolean; - readonly groupAdmins: Array; - readonly groupVersion?: 1 | 2; - readonly isGroupV1AndDisabled?: boolean; - readonly isMissingMandatoryProfileSharing?: boolean; - readonly isSMSOnly?: boolean; - readonly isFetchingUUID?: boolean; - readonly left?: boolean; - readonly messageRequestsEnabled?: boolean; - readonly acceptedMessageRequest?: boolean; - readonly compositionApi?: React.MutableRefObject; - readonly micCellEl?: HTMLElement; - readonly draftAttachments: Array; - readonly shouldSendHighQualityAttachments: boolean; +export type OwnProps = Readonly<{ + i18n: LocalizerType; + areWePending?: boolean; + areWePendingApproval?: boolean; + announcementsOnly?: boolean; + areWeAdmin?: boolean; + groupAdmins: Array; + groupVersion?: 1 | 2; + isGroupV1AndDisabled?: boolean; + isMissingMandatoryProfileSharing?: boolean; + isSMSOnly?: boolean; + isFetchingUUID?: boolean; + left?: boolean; + messageRequestsEnabled?: boolean; + acceptedMessageRequest?: boolean; + compositionApi?: React.MutableRefObject; + micCellEl?: HTMLElement; + draftAttachments: ReadonlyArray; + shouldSendHighQualityAttachments: boolean; onChooseAttachment(): unknown; onAddAttachment(): unknown; onClickAttachment(): unknown; onCloseAttachment(): unknown; onClearAttachments(): unknown; onSelectMediaQuality(isHQ: boolean): unknown; - readonly quotedMessageProps?: QuoteProps; + quotedMessageProps?: Omit< + QuoteProps, + 'i18n' | 'onClick' | 'onClose' | 'withContentAbove' + >; onClickQuotedMessage(): unknown; setQuotedMessage(message: undefined): unknown; linkPreviewLoading: boolean; linkPreviewResult?: LinkPreviewWithDomain; onCloseLinkPreview(): unknown; openConversation(conversationId: string): unknown; -}; +}>; export type Props = Pick< CompositionInputProps, diff --git a/ts/components/ProfileEditorModal.tsx b/ts/components/ProfileEditorModal.tsx index 7701f604b..937bcdce1 100644 --- a/ts/components/ProfileEditorModal.tsx +++ b/ts/components/ProfileEditorModal.tsx @@ -23,7 +23,7 @@ type PropsType = { toggleProfileEditor: () => unknown; toggleProfileEditorHasError: () => unknown; } & PropsDataType & - ProfileEditorPropsType; + Omit; export const ProfileEditorModal = ({ hasError, diff --git a/ts/components/conversation/AttachmentList.tsx b/ts/components/conversation/AttachmentList.tsx index 9fb200e07..479558723 100644 --- a/ts/components/conversation/AttachmentList.tsx +++ b/ts/components/conversation/AttachmentList.tsx @@ -15,14 +15,14 @@ import { isVideoAttachment, } from '../../types/Attachment'; -export type Props = { - attachments: Array; +export type Props = Readonly<{ + attachments: ReadonlyArray; i18n: LocalizerType; onAddAttachment?: () => void; onClickAttachment?: (attachment: AttachmentType) => void; onClose?: () => void; onCloseAttachment: (attachment: AttachmentType) => void; -}; +}>; const IMAGE_WIDTH = 120; const IMAGE_HEIGHT = 120; diff --git a/ts/components/conversation/Quote.tsx b/ts/components/conversation/Quote.tsx index cd9cd7678..8cd77f19a 100644 --- a/ts/components/conversation/Quote.tsx +++ b/ts/components/conversation/Quote.tsx @@ -25,7 +25,7 @@ export type Props = { bodyRanges?: BodyRangesType; i18n: LocalizerType; isFromMe: boolean; - isIncoming: boolean; + isIncoming?: boolean; withContentAbove: boolean; onClick?: () => void; onClose?: () => void; @@ -33,7 +33,7 @@ export type Props = { rawAttachment?: QuotedAttachmentType; isViewOnce: boolean; referencedMessageNotFound: boolean; - doubleCheckMissingQuoteReference: () => unknown; + doubleCheckMissingQuoteReference?: () => unknown; }; type State = { @@ -133,7 +133,7 @@ export class Quote extends React.Component { } = this.props; if (referencedMessageNotFound) { - doubleCheckMissingQuoteReference(); + doubleCheckMissingQuoteReference?.(); } } diff --git a/ts/components/conversation/Timeline.tsx b/ts/components/conversation/Timeline.tsx index 04f845d74..d42526b9d 100644 --- a/ts/components/conversation/Timeline.tsx +++ b/ts/components/conversation/Timeline.tsx @@ -77,7 +77,7 @@ export type PropsDataType = { haveOldest: boolean; isLoadingMessages: boolean; isNearBottom?: boolean; - items: Array; + items: ReadonlyArray; loadCountdownStart?: number; messageHeightChangeIndex?: number; oldestUnreadIndex?: number; @@ -104,7 +104,7 @@ type PropsHousekeepingType = { i18n: LocalizerType; renderItem: (props: { - actions: PropsActionsType; + actionProps: PropsActionsType; containerElementRef: RefObject; conversationId: string; messageId: string; @@ -804,7 +804,7 @@ export class Timeline extends React.PureComponent { const nextMessageId: undefined | string = items[itemIndex + 1]; stableKey = messageId; - const actions = getActions(this.props); + const actionProps = getActions(this.props); rowContents = (
{ > window.showDebugLog()}> {renderItem({ - actions, + actionProps, containerElementRef: this.containerRef, conversationId: id, messageId, diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index 01d360dec..12271e953 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -888,10 +888,19 @@ export const getConversationMessagesSelector = createSelector( conversationMessagesSelector: CachedConversationMessagesSelectorType, messagesByConversation: MessagesByConversationType ) => { - return (id: string): TimelinePropsType | undefined => { + return (id: string): TimelinePropsType => { const conversation = messagesByConversation[id]; if (!conversation) { - return undefined; + // TODO: DESKTOP-2340 + return { + haveNewest: false, + haveOldest: false, + isLoadingMessages: false, + resetCounter: 0, + scrollToIndexCounter: 0, + totalUnread: 0, + items: [], + }; } return conversationMessagesSelector(conversation); diff --git a/ts/state/smart/CompositionArea.tsx b/ts/state/smart/CompositionArea.tsx index 71fbcd305..72e3a12ef 100644 --- a/ts/state/smart/CompositionArea.tsx +++ b/ts/state/smart/CompositionArea.tsx @@ -7,6 +7,7 @@ import { mapDispatchToProps } from '../actions'; import { CompositionArea } from '../../components/CompositionArea'; import { StateType } from '../reducer'; import { isConversationSMSOnly } from '../../util/isConversationSMSOnly'; +import { dropNull } from '../../util/dropNull'; import { selectRecentEmojis } from '../selectors/emojis'; import { getIntl, getUserConversationId } from '../selectors/user'; @@ -60,9 +61,10 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => { ['showStickersIntroduction'], false ); - const showPickerHint = + const showPickerHint = Boolean( get(state.items, ['showStickerPickerHint'], false) && - receivedPacks.length > 0; + receivedPacks.length > 0 + ); const { attachments: draftAttachments, @@ -77,8 +79,6 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => { return { // Base i18n: getIntl(state), - draftText, - draftBodyRanges, // AttachmentsList draftAttachments, // MediaQualitySelector @@ -123,6 +123,9 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => { announcementsOnly, areWeAdmin, groupAdmins: getGroupAdminsSelector(state)(conversation.id), + + draftText: dropNull(draftText), + draftBodyRanges, }; }; @@ -138,5 +141,4 @@ const dispatchPropsMap = { const smart = connect(mapStateToProps, dispatchPropsMap); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const SmartCompositionArea = smart(CompositionArea as any); +export const SmartCompositionArea = smart(CompositionArea); diff --git a/ts/state/smart/GlobalModalContainer.tsx b/ts/state/smart/GlobalModalContainer.tsx index f97c875a6..c148585f0 100644 --- a/ts/state/smart/GlobalModalContainer.tsx +++ b/ts/state/smart/GlobalModalContainer.tsx @@ -8,11 +8,7 @@ import { GlobalModalContainer } from '../../components/GlobalModalContainer'; import { StateType } from '../reducer'; import { SmartProfileEditorModal } from './ProfileEditorModal'; -// Workaround: A react component's required properties are filtering up through connect() -// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 -/* eslint-disable @typescript-eslint/no-explicit-any */ -const FilteredSmartProfileEditorModal = SmartProfileEditorModal as any; -/* eslint-enable @typescript-eslint/no-explicit-any */ +const FilteredSmartProfileEditorModal = SmartProfileEditorModal; function renderProfileEditor(): JSX.Element { return ; diff --git a/ts/state/smart/LeftPane.tsx b/ts/state/smart/LeftPane.tsx index a1fcb5425..f9bece5e1 100644 --- a/ts/state/smart/LeftPane.tsx +++ b/ts/state/smart/LeftPane.tsx @@ -47,11 +47,7 @@ import { SmartRelinkDialog } from './RelinkDialog'; import { SmartUpdateDialog } from './UpdateDialog'; import { SmartCaptchaDialog } from './CaptchaDialog'; -// Workaround: A react component's required properties are filtering up through connect() -// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 -/* eslint-disable @typescript-eslint/no-explicit-any */ -const FilteredSmartMessageSearchResult = SmartMessageSearchResult as any; -/* eslint-enable @typescript-eslint/no-explicit-any */ +const FilteredSmartMessageSearchResult = SmartMessageSearchResult; function renderExpiredBuildDialog(): JSX.Element { return ; diff --git a/ts/state/smart/MessageSearchResult.tsx b/ts/state/smart/MessageSearchResult.tsx index 7dc423851..b42832c69 100644 --- a/ts/state/smart/MessageSearchResult.tsx +++ b/ts/state/smart/MessageSearchResult.tsx @@ -13,7 +13,7 @@ import { getMessageSearchResultSelector } from '../selectors/search'; type SmartProps = { id: string; - style: CSSProperties; + style?: CSSProperties; }; function mapStateToProps(state: StateType, ourProps: SmartProps) { diff --git a/ts/state/smart/ProfileEditorModal.ts b/ts/state/smart/ProfileEditorModal.ts index 937fe01f8..ebe91e4c0 100644 --- a/ts/state/smart/ProfileEditorModal.ts +++ b/ts/state/smart/ProfileEditorModal.ts @@ -16,7 +16,8 @@ import { selectRecentEmojis } from '../selectors/emojis'; function mapStateToProps( state: StateType -): PropsDataType & ProfileEditorModalPropsType { +): Omit & + ProfileEditorModalPropsType { const { avatarPath, avatars: userAvatarData = [], diff --git a/ts/state/smart/Timeline.tsx b/ts/state/smart/Timeline.tsx index 777895474..a3c325695 100644 --- a/ts/state/smart/Timeline.tsx +++ b/ts/state/smart/Timeline.tsx @@ -243,6 +243,7 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => { const { id, ...actions } = props; const conversation = getConversationSelector(state)(id); + const conversationMessages = getConversationMessagesSelector(state)(id); const selectedMessage = getSelectedMessage(state); @@ -279,5 +280,4 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => { const smart = connect(mapStateToProps, mapDispatchToProps); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const SmartTimeline = smart(Timeline as any); +export const SmartTimeline = smart(Timeline); diff --git a/ts/state/smart/TimelineItem.tsx b/ts/state/smart/TimelineItem.tsx index c4b4adeed..8bc6ddd50 100644 --- a/ts/state/smart/TimelineItem.tsx +++ b/ts/state/smart/TimelineItem.tsx @@ -26,11 +26,7 @@ type ExternalProps = { previousMessageId: undefined | string; }; -// Workaround: A react component's required properties are filtering up through connect() -// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/31363 -/* eslint-disable @typescript-eslint/no-explicit-any */ -const FilteredSmartContactName = SmartContactName as any; -/* eslint-enable @typescript-eslint/no-explicit-any */ +const FilteredSmartContactName = SmartContactName; function renderContact(conversationId: string): JSX.Element { return ; diff --git a/ts/types/Attachment.ts b/ts/types/Attachment.ts index 36342016c..b3fc4c6a8 100644 --- a/ts/types/Attachment.ts +++ b/ts/types/Attachment.ts @@ -575,7 +575,7 @@ export function getExtensionForDisplay({ return undefined; } -export function isAudio(attachments?: Array): boolean { +export function isAudio(attachments?: ReadonlyArray): boolean { return Boolean( attachments && attachments[0] && @@ -585,7 +585,9 @@ export function isAudio(attachments?: Array): boolean { ); } -export function canDisplayImage(attachments?: Array): boolean { +export function canDisplayImage( + attachments?: ReadonlyArray +): boolean { const { height, width } = attachments && attachments[0] ? attachments[0] : { height: 0, width: 0 }; @@ -617,7 +619,7 @@ export function getUrl(attachment: AttachmentType): string | undefined { return attachment.url; } -export function isImage(attachments?: Array): boolean { +export function isImage(attachments?: ReadonlyArray): boolean { return Boolean( attachments && attachments[0] && @@ -644,7 +646,7 @@ export function canBeTranscoded( ); } -export function hasImage(attachments?: Array): boolean { +export function hasImage(attachments?: ReadonlyArray): boolean { return Boolean( attachments && attachments[0] && @@ -652,7 +654,7 @@ export function hasImage(attachments?: Array): boolean { ); } -export function isVideo(attachments?: Array): boolean { +export function isVideo(attachments?: ReadonlyArray): boolean { if (!attachments || attachments.length === 0) { return false; } @@ -732,7 +734,7 @@ export function getImageDimensions( } export function areAllAttachmentsVisual( - attachments?: Array + attachments?: ReadonlyArray ): boolean { if (!attachments) { return false; @@ -750,7 +752,7 @@ export function areAllAttachmentsVisual( } export function getGridDimensions( - attachments?: Array + attachments?: ReadonlyArray ): null | DimensionsType { if (!attachments || !attachments.length) { return null;