Fix action propagation to timeline items
This commit is contained in:
parent
9ffa29ca0d
commit
5a57e2b704
14 changed files with 71 additions and 66 deletions
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
|
@ -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 />;
|
||||||
|
|
|
@ -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 />;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 = [],
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
|
@ -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} />;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue