Fix action propagation to timeline items

This commit is contained in:
Fedor Indutny 2021-09-12 19:36:41 -07:00 committed by GitHub
parent 9ffa29ca0d
commit 5a57e2b704
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 71 additions and 66 deletions

View file

@ -50,39 +50,42 @@ export type CompositionAPIType = {
resetEmojiResults: InputApi['resetEmojiResults']; resetEmojiResults: InputApi['resetEmojiResults'];
}; };
export type OwnProps = { export type OwnProps = Readonly<{
readonly i18n: LocalizerType; i18n: LocalizerType;
readonly areWePending?: boolean; areWePending?: boolean;
readonly areWePendingApproval?: boolean; areWePendingApproval?: boolean;
readonly announcementsOnly?: boolean; announcementsOnly?: boolean;
readonly areWeAdmin?: boolean; areWeAdmin?: boolean;
readonly groupAdmins: Array<ConversationType>; groupAdmins: Array<ConversationType>;
readonly groupVersion?: 1 | 2; groupVersion?: 1 | 2;
readonly isGroupV1AndDisabled?: boolean; isGroupV1AndDisabled?: boolean;
readonly isMissingMandatoryProfileSharing?: boolean; isMissingMandatoryProfileSharing?: boolean;
readonly isSMSOnly?: boolean; isSMSOnly?: boolean;
readonly isFetchingUUID?: boolean; isFetchingUUID?: boolean;
readonly left?: boolean; left?: boolean;
readonly messageRequestsEnabled?: boolean; messageRequestsEnabled?: boolean;
readonly acceptedMessageRequest?: boolean; acceptedMessageRequest?: boolean;
readonly compositionApi?: React.MutableRefObject<CompositionAPIType>; compositionApi?: React.MutableRefObject<CompositionAPIType>;
readonly micCellEl?: HTMLElement; micCellEl?: HTMLElement;
readonly draftAttachments: Array<AttachmentType>; draftAttachments: ReadonlyArray<AttachmentType>;
readonly shouldSendHighQualityAttachments: boolean; shouldSendHighQualityAttachments: boolean;
onChooseAttachment(): unknown; onChooseAttachment(): unknown;
onAddAttachment(): unknown; onAddAttachment(): unknown;
onClickAttachment(): unknown; onClickAttachment(): unknown;
onCloseAttachment(): unknown; onCloseAttachment(): unknown;
onClearAttachments(): unknown; onClearAttachments(): unknown;
onSelectMediaQuality(isHQ: boolean): unknown; onSelectMediaQuality(isHQ: boolean): unknown;
readonly quotedMessageProps?: QuoteProps; quotedMessageProps?: Omit<
QuoteProps,
'i18n' | 'onClick' | 'onClose' | 'withContentAbove'
>;
onClickQuotedMessage(): unknown; onClickQuotedMessage(): unknown;
setQuotedMessage(message: undefined): unknown; setQuotedMessage(message: undefined): unknown;
linkPreviewLoading: boolean; linkPreviewLoading: boolean;
linkPreviewResult?: LinkPreviewWithDomain; linkPreviewResult?: LinkPreviewWithDomain;
onCloseLinkPreview(): unknown; onCloseLinkPreview(): unknown;
openConversation(conversationId: string): unknown; openConversation(conversationId: string): unknown;
}; }>;
export type Props = Pick< export type Props = Pick<
CompositionInputProps, CompositionInputProps,

View file

@ -23,7 +23,7 @@ type PropsType = {
toggleProfileEditor: () => unknown; toggleProfileEditor: () => unknown;
toggleProfileEditorHasError: () => unknown; toggleProfileEditorHasError: () => unknown;
} & PropsDataType & } & PropsDataType &
ProfileEditorPropsType; Omit<ProfileEditorPropsType, 'onEditStateChanged' | 'onProfileChanged'>;
export const ProfileEditorModal = ({ export const ProfileEditorModal = ({
hasError, hasError,

View file

@ -15,14 +15,14 @@ import {
isVideoAttachment, isVideoAttachment,
} from '../../types/Attachment'; } from '../../types/Attachment';
export type Props = { export type Props = Readonly<{
attachments: Array<AttachmentType>; attachments: ReadonlyArray<AttachmentType>;
i18n: LocalizerType; i18n: LocalizerType;
onAddAttachment?: () => void; onAddAttachment?: () => void;
onClickAttachment?: (attachment: AttachmentType) => void; onClickAttachment?: (attachment: AttachmentType) => void;
onClose?: () => void; onClose?: () => void;
onCloseAttachment: (attachment: AttachmentType) => void; onCloseAttachment: (attachment: AttachmentType) => void;
}; }>;
const IMAGE_WIDTH = 120; const IMAGE_WIDTH = 120;
const IMAGE_HEIGHT = 120; const IMAGE_HEIGHT = 120;

View file

@ -25,7 +25,7 @@ export type Props = {
bodyRanges?: BodyRangesType; bodyRanges?: BodyRangesType;
i18n: LocalizerType; i18n: LocalizerType;
isFromMe: boolean; isFromMe: boolean;
isIncoming: boolean; isIncoming?: boolean;
withContentAbove: boolean; withContentAbove: boolean;
onClick?: () => void; onClick?: () => void;
onClose?: () => void; onClose?: () => void;
@ -33,7 +33,7 @@ export type Props = {
rawAttachment?: QuotedAttachmentType; rawAttachment?: QuotedAttachmentType;
isViewOnce: boolean; isViewOnce: boolean;
referencedMessageNotFound: boolean; referencedMessageNotFound: boolean;
doubleCheckMissingQuoteReference: () => unknown; doubleCheckMissingQuoteReference?: () => unknown;
}; };
type State = { type State = {
@ -133,7 +133,7 @@ export class Quote extends React.Component<Props, State> {
} = this.props; } = this.props;
if (referencedMessageNotFound) { if (referencedMessageNotFound) {
doubleCheckMissingQuoteReference(); doubleCheckMissingQuoteReference?.();
} }
} }

View file

@ -77,7 +77,7 @@ export type PropsDataType = {
haveOldest: boolean; haveOldest: boolean;
isLoadingMessages: boolean; isLoadingMessages: boolean;
isNearBottom?: boolean; isNearBottom?: boolean;
items: Array<string>; items: ReadonlyArray<string>;
loadCountdownStart?: number; loadCountdownStart?: number;
messageHeightChangeIndex?: number; messageHeightChangeIndex?: number;
oldestUnreadIndex?: number; oldestUnreadIndex?: number;
@ -104,7 +104,7 @@ type PropsHousekeepingType = {
i18n: LocalizerType; i18n: LocalizerType;
renderItem: (props: { renderItem: (props: {
actions: PropsActionsType; actionProps: PropsActionsType;
containerElementRef: RefObject<HTMLElement>; containerElementRef: RefObject<HTMLElement>;
conversationId: string; conversationId: string;
messageId: string; messageId: string;
@ -804,7 +804,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
const nextMessageId: undefined | string = items[itemIndex + 1]; const nextMessageId: undefined | string = items[itemIndex + 1];
stableKey = messageId; stableKey = messageId;
const actions = getActions(this.props); const actionProps = getActions(this.props);
rowContents = ( rowContents = (
<div <div
@ -816,7 +816,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
> >
<ErrorBoundary i18n={i18n} showDebugLog={() => window.showDebugLog()}> <ErrorBoundary i18n={i18n} showDebugLog={() => window.showDebugLog()}>
{renderItem({ {renderItem({
actions, actionProps,
containerElementRef: this.containerRef, containerElementRef: this.containerRef,
conversationId: id, conversationId: id,
messageId, messageId,

View file

@ -888,10 +888,19 @@ export const getConversationMessagesSelector = createSelector(
conversationMessagesSelector: CachedConversationMessagesSelectorType, conversationMessagesSelector: CachedConversationMessagesSelectorType,
messagesByConversation: MessagesByConversationType messagesByConversation: MessagesByConversationType
) => { ) => {
return (id: string): TimelinePropsType | undefined => { return (id: string): TimelinePropsType => {
const conversation = messagesByConversation[id]; const conversation = messagesByConversation[id];
if (!conversation) { if (!conversation) {
return undefined; // TODO: DESKTOP-2340
return {
haveNewest: false,
haveOldest: false,
isLoadingMessages: false,
resetCounter: 0,
scrollToIndexCounter: 0,
totalUnread: 0,
items: [],
};
} }
return conversationMessagesSelector(conversation); return conversationMessagesSelector(conversation);

View file

@ -7,6 +7,7 @@ import { mapDispatchToProps } from '../actions';
import { CompositionArea } from '../../components/CompositionArea'; import { CompositionArea } from '../../components/CompositionArea';
import { StateType } from '../reducer'; import { StateType } from '../reducer';
import { isConversationSMSOnly } from '../../util/isConversationSMSOnly'; import { isConversationSMSOnly } from '../../util/isConversationSMSOnly';
import { dropNull } from '../../util/dropNull';
import { selectRecentEmojis } from '../selectors/emojis'; import { selectRecentEmojis } from '../selectors/emojis';
import { getIntl, getUserConversationId } from '../selectors/user'; import { getIntl, getUserConversationId } from '../selectors/user';
@ -60,9 +61,10 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
['showStickersIntroduction'], ['showStickersIntroduction'],
false false
); );
const showPickerHint = const showPickerHint = Boolean(
get(state.items, ['showStickerPickerHint'], false) && get(state.items, ['showStickerPickerHint'], false) &&
receivedPacks.length > 0; receivedPacks.length > 0
);
const { const {
attachments: draftAttachments, attachments: draftAttachments,
@ -77,8 +79,6 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
return { return {
// Base // Base
i18n: getIntl(state), i18n: getIntl(state),
draftText,
draftBodyRanges,
// AttachmentsList // AttachmentsList
draftAttachments, draftAttachments,
// MediaQualitySelector // MediaQualitySelector
@ -123,6 +123,9 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
announcementsOnly, announcementsOnly,
areWeAdmin, areWeAdmin,
groupAdmins: getGroupAdminsSelector(state)(conversation.id), groupAdmins: getGroupAdminsSelector(state)(conversation.id),
draftText: dropNull(draftText),
draftBodyRanges,
}; };
}; };
@ -138,5 +141,4 @@ const dispatchPropsMap = {
const smart = connect(mapStateToProps, dispatchPropsMap); const smart = connect(mapStateToProps, dispatchPropsMap);
// eslint-disable-next-line @typescript-eslint/no-explicit-any export const SmartCompositionArea = smart(CompositionArea);
export const SmartCompositionArea = smart(CompositionArea as any);

View file

@ -8,11 +8,7 @@ import { GlobalModalContainer } from '../../components/GlobalModalContainer';
import { StateType } from '../reducer'; import { StateType } from '../reducer';
import { SmartProfileEditorModal } from './ProfileEditorModal'; import { SmartProfileEditorModal } from './ProfileEditorModal';
// Workaround: A react component's required properties are filtering up through connect() const FilteredSmartProfileEditorModal = SmartProfileEditorModal;
// 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 */
function renderProfileEditor(): JSX.Element { function renderProfileEditor(): JSX.Element {
return <FilteredSmartProfileEditorModal />; return <FilteredSmartProfileEditorModal />;

View file

@ -47,11 +47,7 @@ import { SmartRelinkDialog } from './RelinkDialog';
import { SmartUpdateDialog } from './UpdateDialog'; import { SmartUpdateDialog } from './UpdateDialog';
import { SmartCaptchaDialog } from './CaptchaDialog'; import { SmartCaptchaDialog } from './CaptchaDialog';
// Workaround: A react component's required properties are filtering up through connect() const FilteredSmartMessageSearchResult = SmartMessageSearchResult;
// 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 */
function renderExpiredBuildDialog(): JSX.Element { function renderExpiredBuildDialog(): JSX.Element {
return <SmartExpiredBuildDialog />; return <SmartExpiredBuildDialog />;

View file

@ -13,7 +13,7 @@ import { getMessageSearchResultSelector } from '../selectors/search';
type SmartProps = { type SmartProps = {
id: string; id: string;
style: CSSProperties; style?: CSSProperties;
}; };
function mapStateToProps(state: StateType, ourProps: SmartProps) { function mapStateToProps(state: StateType, ourProps: SmartProps) {

View file

@ -16,7 +16,8 @@ import { selectRecentEmojis } from '../selectors/emojis';
function mapStateToProps( function mapStateToProps(
state: StateType state: StateType
): PropsDataType & ProfileEditorModalPropsType { ): Omit<PropsDataType, 'onEditStateChange' | 'onProfileChanged'> &
ProfileEditorModalPropsType {
const { const {
avatarPath, avatarPath,
avatars: userAvatarData = [], avatars: userAvatarData = [],

View file

@ -243,6 +243,7 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
const { id, ...actions } = props; const { id, ...actions } = props;
const conversation = getConversationSelector(state)(id); const conversation = getConversationSelector(state)(id);
const conversationMessages = getConversationMessagesSelector(state)(id); const conversationMessages = getConversationMessagesSelector(state)(id);
const selectedMessage = getSelectedMessage(state); const selectedMessage = getSelectedMessage(state);
@ -279,5 +280,4 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
const smart = connect(mapStateToProps, mapDispatchToProps); const smart = connect(mapStateToProps, mapDispatchToProps);
// eslint-disable-next-line @typescript-eslint/no-explicit-any export const SmartTimeline = smart(Timeline);
export const SmartTimeline = smart(Timeline as any);

View file

@ -26,11 +26,7 @@ type ExternalProps = {
previousMessageId: undefined | string; previousMessageId: undefined | string;
}; };
// Workaround: A react component's required properties are filtering up through connect() const FilteredSmartContactName = SmartContactName;
// 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 */
function renderContact(conversationId: string): JSX.Element { function renderContact(conversationId: string): JSX.Element {
return <FilteredSmartContactName conversationId={conversationId} />; return <FilteredSmartContactName conversationId={conversationId} />;

View file

@ -575,7 +575,7 @@ export function getExtensionForDisplay({
return undefined; return undefined;
} }
export function isAudio(attachments?: Array<AttachmentType>): boolean { export function isAudio(attachments?: ReadonlyArray<AttachmentType>): boolean {
return Boolean( return Boolean(
attachments && attachments &&
attachments[0] && attachments[0] &&
@ -585,7 +585,9 @@ export function isAudio(attachments?: Array<AttachmentType>): boolean {
); );
} }
export function canDisplayImage(attachments?: Array<AttachmentType>): boolean { export function canDisplayImage(
attachments?: ReadonlyArray<AttachmentType>
): boolean {
const { height, width } = const { height, width } =
attachments && attachments[0] ? attachments[0] : { height: 0, width: 0 }; attachments && attachments[0] ? attachments[0] : { height: 0, width: 0 };
@ -617,7 +619,7 @@ export function getUrl(attachment: AttachmentType): string | undefined {
return attachment.url; return attachment.url;
} }
export function isImage(attachments?: Array<AttachmentType>): boolean { export function isImage(attachments?: ReadonlyArray<AttachmentType>): boolean {
return Boolean( return Boolean(
attachments && attachments &&
attachments[0] && attachments[0] &&
@ -644,7 +646,7 @@ export function canBeTranscoded(
); );
} }
export function hasImage(attachments?: Array<AttachmentType>): boolean { export function hasImage(attachments?: ReadonlyArray<AttachmentType>): boolean {
return Boolean( return Boolean(
attachments && attachments &&
attachments[0] && attachments[0] &&
@ -652,7 +654,7 @@ export function hasImage(attachments?: Array<AttachmentType>): boolean {
); );
} }
export function isVideo(attachments?: Array<AttachmentType>): boolean { export function isVideo(attachments?: ReadonlyArray<AttachmentType>): boolean {
if (!attachments || attachments.length === 0) { if (!attachments || attachments.length === 0) {
return false; return false;
} }
@ -732,7 +734,7 @@ export function getImageDimensions(
} }
export function areAllAttachmentsVisual( export function areAllAttachmentsVisual(
attachments?: Array<AttachmentType> attachments?: ReadonlyArray<AttachmentType>
): boolean { ): boolean {
if (!attachments) { if (!attachments) {
return false; return false;
@ -750,7 +752,7 @@ export function areAllAttachmentsVisual(
} }
export function getGridDimensions( export function getGridDimensions(
attachments?: Array<AttachmentType> attachments?: ReadonlyArray<AttachmentType>
): null | DimensionsType { ): null | DimensionsType {
if (!attachments || !attachments.length) { if (!attachments || !attachments.length) {
return null; return null;