feat: created getMessageExpirationProps selector and used in ExpirableReadableMessage

this stops us passing props down from the child components of ExpirableReadableMessage
pull/2660/head
William Grant 2 years ago
parent dd40fba132
commit 5d4238a3d8

@ -14,19 +14,7 @@ const StyledTimerNotification = styled(Flex)`
`; `;
export const TimerNotification = (props: PropsForExpirationTimer) => { export const TimerNotification = (props: PropsForExpirationTimer) => {
const { const { messageId, pubkey, profileName, expirationType, timespan, type, disabled } = props;
messageId,
receivedAt,
isUnread,
pubkey,
profileName,
expirationType,
expirationLength,
expirationTimestamp,
timespan,
type,
disabled,
} = props;
const contact = profileName || pubkey; const contact = profileName || pubkey;
// TODO legacy messages support will be removed in a future release // TODO legacy messages support will be removed in a future release
@ -64,14 +52,8 @@ export const TimerNotification = (props: PropsForExpirationTimer) => {
return ( return (
<ExpirableReadableMessage <ExpirableReadableMessage
convoId={props.convoId}
messageId={messageId} messageId={messageId}
direction={type === 'fromOther' ? 'incoming' : 'outgoing'} direction={type === 'fromOther' ? 'incoming' : 'outgoing'}
receivedAt={receivedAt}
isUnread={isUnread}
expirationLength={expirationLength}
expirationTimestamp={expirationTimestamp}
isExpired={props.isExpired}
isCentered={true} isCentered={true}
marginInlineStart={'calc(var(--margins-lg) + 6px)'} marginInlineStart={'calc(var(--margins-lg) + 6px)'}
marginInlineEnd={'calc(var(--margins-lg) + 6px)'} marginInlineEnd={'calc(var(--margins-lg) + 6px)'}

@ -7,18 +7,7 @@ import { SessionIcon } from '../../../icon';
import { ExpirableReadableMessage } from './ExpirableReadableMessage'; import { ExpirableReadableMessage } from './ExpirableReadableMessage';
export const DataExtractionNotification = (props: PropsForDataExtractionNotification) => { export const DataExtractionNotification = (props: PropsForDataExtractionNotification) => {
const { const { name, type, source, messageId } = props;
name,
type,
source,
messageId,
isUnread,
receivedAt,
direction,
expirationLength,
expirationTimestamp,
isExpired,
} = props;
let contentText: string; let contentText: string;
if (type === SignalService.DataExtractionNotification.Type.MEDIA_SAVED) { if (type === SignalService.DataExtractionNotification.Type.MEDIA_SAVED) {
@ -28,16 +17,7 @@ export const DataExtractionNotification = (props: PropsForDataExtractionNotifica
} }
return ( return (
<ExpirableReadableMessage <ExpirableReadableMessage messageId={messageId} key={`readable-message-${messageId}`}>
messageId={messageId}
receivedAt={receivedAt}
isUnread={isUnread}
direction={direction}
expirationLength={expirationLength}
expirationTimestamp={expirationTimestamp}
isExpired={isExpired}
key={`readable-message-${messageId}`}
>
<Flex <Flex
container={true} container={true}
flexDirection="row" flexDirection="row"

@ -1,14 +1,15 @@
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { useDispatch } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useInterval } from 'react-use'; import { useInterval } from 'react-use';
import styled from 'styled-components'; import styled from 'styled-components';
import { Data } from '../../../../data/data'; import { Data } from '../../../../data/data';
import { MessageModelType } from '../../../../models/messageType';
import { getConversationController } from '../../../../session/conversations'; import { getConversationController } from '../../../../session/conversations';
import { messagesExpired, PropsForExpiringMessage } from '../../../../state/ducks/conversations'; import { messagesExpired, PropsForExpiringMessage } from '../../../../state/ducks/conversations';
import { getIncrement } from '../../../../util/timer'; import { getIncrement } from '../../../../util/timer';
import { ExpireTimer } from '../../ExpireTimer'; import { ExpireTimer } from '../../ExpireTimer';
import { ReadableMessage, ReadableMessageProps } from './ReadableMessage'; import { ReadableMessage, ReadableMessageProps } from './ReadableMessage';
import { getMessageExpirationProps } from '../../../../state/selectors/conversations';
import { MessageModelType } from '../../../../models/messageType';
const EXPIRATION_CHECK_MINIMUM = 2000; const EXPIRATION_CHECK_MINIMUM = 2000;
@ -74,43 +75,57 @@ const StyledReadableMessage = styled(ReadableMessage)<{
`; `;
export interface ExpirableReadableMessageProps export interface ExpirableReadableMessageProps
extends ReadableMessageProps, extends Omit<ReadableMessageProps, 'receivedAt' | 'isUnread'> {
PropsForExpiringMessage { messageId: string;
direction: MessageModelType; // Note: this direction is used to override the message model direction in cases where it isn't set i.e. Timer Notifications rely on the 'type' prop to determine direction
direction?: MessageModelType;
isCentered?: boolean; isCentered?: boolean;
marginInlineStart?: string; marginInlineStart?: string;
marginInlineEnd?: string; marginInlineEnd?: string;
} }
export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) => { export const ExpirableReadableMessage = (props: ExpirableReadableMessageProps) => {
const selected = useSelector(state => getMessageExpirationProps(state as any, props.messageId));
if (!selected) {
return null;
}
const { const {
convoId, direction: overrideDirection,
messageId,
direction,
receivedAt,
isUnread,
expirationLength,
expirationTimestamp,
isCentered, isCentered,
marginInlineStart = '6px', marginInlineStart = '6px',
marginInlineEnd = '6px', marginInlineEnd = '6px',
} = props; } = props;
const expiringProps: PropsForExpiringMessage = { const {
convoId, convoId,
messageId, messageId,
direction: selectedDirection,
receivedAt,
isUnread,
expirationLength, expirationLength,
expirationTimestamp, expirationTimestamp,
isExpired: props.isExpired, isExpired: _isExpired,
} = selected;
const direction = overrideDirection || selectedDirection;
const { isExpired } = useIsExpired({
convoId,
messageId,
direction, direction,
}; expirationTimestamp,
const { isExpired } = useIsExpired(expiringProps); expirationLength,
const isIncoming = direction === 'incoming'; isExpired: _isExpired,
});
if (isExpired) { if (isExpired) {
return null; return null;
} }
const isIncoming = direction === 'incoming';
return ( return (
<StyledReadableMessage <StyledReadableMessage
messageId={messageId} messageId={messageId}

@ -22,10 +22,7 @@ export type GenericReadableMessageSelectorProps = Pick<
| 'conversationType' | 'conversationType'
| 'receivedAt' | 'receivedAt'
| 'isUnread' | 'isUnread'
| 'expirationLength'
| 'expirationTimestamp'
| 'isKickedFromGroup' | 'isKickedFromGroup'
| 'isExpired'
| 'convoId' | 'convoId'
| 'isDeleted' | 'isDeleted'
>; >;
@ -152,16 +149,7 @@ export const GenericReadableMessage = (props: Props) => {
isUnread={!!isUnread} isUnread={!!isUnread}
key={`readable-message-${messageId}`} key={`readable-message-${messageId}`}
> >
<ExpirableReadableMessage <ExpirableReadableMessage messageId={messageId}>
convoId={msgProps.convoId}
messageId={messageId}
direction={msgProps.direction}
receivedAt={receivedAt}
isUnread={Boolean(isUnread)}
expirationLength={msgProps.expirationLength}
expirationTimestamp={msgProps.expirationTimestamp}
isExpired={msgProps.isExpired}
>
<MessageContentWithStatuses <MessageContentWithStatuses
ctxMenuID={ctxMenuID} ctxMenuID={ctxMenuID}
messageId={messageId} messageId={messageId}

@ -12,7 +12,7 @@ const StyledIconContainer = styled.div`
`; `;
export const GroupInvitation = (props: PropsForGroupInvitation) => { export const GroupInvitation = (props: PropsForGroupInvitation) => {
const { messageId, receivedAt, isUnread } = props; const { messageId } = props;
const classes = ['group-invitation']; const classes = ['group-invitation'];
if (props.direction === 'outgoing') { if (props.direction === 'outgoing') {
@ -21,17 +21,7 @@ export const GroupInvitation = (props: PropsForGroupInvitation) => {
const openGroupInvitation = window.i18n('openGroupInvitation'); const openGroupInvitation = window.i18n('openGroupInvitation');
return ( return (
<ExpirableReadableMessage <ExpirableReadableMessage messageId={messageId} key={`readable-message-${messageId}`}>
convoId={props.convoId}
messageId={messageId}
direction={props.direction}
receivedAt={receivedAt}
isUnread={Boolean(isUnread)}
expirationLength={props.expirationLength}
expirationTimestamp={props.expirationTimestamp}
isExpired={props.isExpired}
key={`readable-message-${messageId}`}
>
<div className="group-invitation-container" id={`msg-${props.messageId}`}> <div className="group-invitation-container" id={`msg-${props.messageId}`}>
<div className={classNames(classes)}> <div className={classNames(classes)}>
<div className="contents"> <div className="contents">

@ -71,28 +71,10 @@ const ChangeItem = (change: PropsForGroupUpdateType): string => {
}; };
export const GroupUpdateMessage = (props: PropsForGroupUpdate) => { export const GroupUpdateMessage = (props: PropsForGroupUpdate) => {
const { const { change, messageId } = props;
change,
messageId,
receivedAt,
isUnread,
direction,
expirationLength,
expirationTimestamp,
isExpired,
} = props;
return ( return (
<ExpirableReadableMessage <ExpirableReadableMessage messageId={messageId} key={`readable-message-${messageId}`}>
messageId={messageId}
receivedAt={receivedAt}
isUnread={isUnread}
direction={direction}
expirationLength={expirationLength}
expirationTimestamp={expirationTimestamp}
isExpired={isExpired}
key={`readable-message-${messageId}`}
>
<NotificationBubble notificationText={ChangeItem(change)} iconType="users" /> <NotificationBubble notificationText={ChangeItem(change)} iconType="users" />
</ExpirableReadableMessage> </ExpirableReadableMessage>
); );

@ -36,16 +36,7 @@ const style: StyleType = {
}; };
export const CallNotification = (props: PropsForCallNotification) => { export const CallNotification = (props: PropsForCallNotification) => {
const { const { messageId, notificationType } = props;
messageId,
receivedAt,
isUnread,
notificationType,
direction,
expirationLength,
expirationTimestamp,
isExpired,
} = props;
const selectedConvoProps = useSelector(getSelectedConversation); const selectedConvoProps = useSelector(getSelectedConversation);
@ -63,16 +54,7 @@ export const CallNotification = (props: PropsForCallNotification) => {
const iconColor = styleItem.iconColor; const iconColor = styleItem.iconColor;
return ( return (
<ExpirableReadableMessage <ExpirableReadableMessage messageId={messageId} key={`readable-message-${messageId}`}>
messageId={messageId}
receivedAt={receivedAt}
direction={direction}
isUnread={isUnread}
expirationLength={expirationLength}
expirationTimestamp={expirationTimestamp}
isExpired={isExpired}
key={`readable-message-${messageId}`}
>
<NotificationBubble <NotificationBubble
notificationText={notificationText} notificationText={notificationText}
iconType={iconType} iconType={iconType}

@ -4,7 +4,7 @@ import { ConversationModel } from '../models/conversation';
import { PubKey } from '../session/types'; import { PubKey } from '../session/types';
import { UserUtils } from '../session/utils'; import { UserUtils } from '../session/utils';
import { StateType } from '../state/reducer'; import { StateType } from '../state/reducer';
import { getMessageReactsProps } from '../state/selectors/conversations'; import { getMessageExpirationProps, getMessageReactsProps } from '../state/selectors/conversations';
export function useAvatarPath(convoId: string | undefined) { export function useAvatarPath(convoId: string | undefined) {
const convoProps = useConversationPropsById(convoId); const convoProps = useConversationPropsById(convoId);
@ -184,6 +184,19 @@ export function useMessageReactsPropsById(messageId?: string) {
}); });
} }
export function useMessageExpirationPropsById(messageId?: string) {
return useSelector((state: StateType) => {
if (!messageId) {
return null;
}
const messageExpirationProps = getMessageExpirationProps(state, messageId);
if (!messageExpirationProps) {
return null;
}
return messageExpirationProps;
});
}
// TODO use env variable to toggle test values? // TODO use env variable to toggle test values?
// https://github.com/oxen-io/session-desktop/pull/2660/files#r1174823750 // https://github.com/oxen-io/session-desktop/pull/2660/files#r1174823750
export function useTimerOptionsByMode(disappearingMessageMode?: string, hasOnlyOneMode?: boolean) { export function useTimerOptionsByMode(disappearingMessageMode?: string, hasOnlyOneMode?: boolean) {

@ -149,6 +149,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
const propsForGroupInvitation = this.getPropsForGroupInvitation(); const propsForGroupInvitation = this.getPropsForGroupInvitation();
const propsForGroupUpdateMessage = this.getPropsForGroupUpdateMessage(); const propsForGroupUpdateMessage = this.getPropsForGroupUpdateMessage();
const propsForTimerNotification = this.getPropsForTimerNotification(); const propsForTimerNotification = this.getPropsForTimerNotification();
const propsForExpiringMessage = this.getPropsForExpiringMessage();
const propsForMessageRequestResponse = this.getPropsForMessageRequestResponse(); const propsForMessageRequestResponse = this.getPropsForMessageRequestResponse();
const callNotificationType = this.get('callNotificationType'); const callNotificationType = this.get('callNotificationType');
const messageProps: MessageModelPropsWithoutConvoProps = { const messageProps: MessageModelPropsWithoutConvoProps = {
@ -170,10 +171,13 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
messageProps.propsForTimerNotification = propsForTimerNotification; messageProps.propsForTimerNotification = propsForTimerNotification;
} }
if (propsForExpiringMessage) {
messageProps.propsForExpiringMessage = propsForExpiringMessage;
}
if (callNotificationType) { if (callNotificationType) {
messageProps.propsForCallNotification = { messageProps.propsForCallNotification = {
notificationType: callNotificationType, notificationType: callNotificationType,
messageId: this.id,
receivedAt: this.get('received_at') || Date.now(), receivedAt: this.get('received_at') || Date.now(),
isUnread: this.isUnread(), isUnread: this.isUnread(),
...this.getPropsForExpiringMessage(), ...this.getPropsForExpiringMessage(),
@ -278,7 +282,7 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
await deleteExternalMessageFiles(this.attributes); await deleteExternalMessageFiles(this.attributes);
} }
public getPropsForExpiringMessage(): PropsForExpiringMessage | { direction: MessageModelType } { public getPropsForExpiringMessage(): PropsForExpiringMessage {
const expirationType = this.get('expirationType'); const expirationType = this.get('expirationType');
const expirationLength = this.get('expireTimer') const expirationLength = this.get('expireTimer')
? this.get('expireTimer') * DURATION.SECONDS ? this.get('expireTimer') * DURATION.SECONDS
@ -323,7 +327,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
timespan, timespan,
disabled, disabled,
type: fromSync ? 'fromSync' : UserUtils.isUsFromCache(source) ? 'fromMe' : 'fromOther', type: fromSync ? 'fromSync' : UserUtils.isUsFromCache(source) ? 'fromMe' : 'fromOther',
messageId: this.id,
receivedAt: this.get('received_at'), receivedAt: this.get('received_at'),
isUnread: this.isUnread(), isUnread: this.isUnread(),
expirationType: expirationType || 'off', expirationType: expirationType || 'off',
@ -351,7 +354,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
serverName: invitation.name, serverName: invitation.name,
url: serverAddress, url: serverAddress,
acceptUrl: invitation.url, acceptUrl: invitation.url,
messageId: this.id as string,
receivedAt: this.get('received_at'), receivedAt: this.get('received_at'),
isUnread: this.isUnread(), isUnread: this.isUnread(),
...this.getPropsForExpiringMessage(), ...this.getPropsForExpiringMessage(),
@ -374,7 +376,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
return { return {
...dataExtractionNotification, ...dataExtractionNotification,
name: contact.profileName || contact.name || dataExtractionNotification.source, name: contact.profileName || contact.name || dataExtractionNotification.source,
messageId: this.id,
receivedAt: this.get('received_at'), receivedAt: this.get('received_at'),
isUnread: this.isUnread(), isUnread: this.isUnread(),
...this.getPropsForExpiringMessage(), ...this.getPropsForExpiringMessage(),
@ -414,7 +415,6 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
} }
const sharedProps = { const sharedProps = {
messageId: this.id,
isUnread: this.isUnread(), isUnread: this.isUnread(),
receivedAt: this.get('received_at'), receivedAt: this.get('received_at'),
...this.getPropsForExpiringMessage(), ...this.getPropsForExpiringMessage(),

@ -1,10 +1,6 @@
import { defaultsDeep } from 'lodash'; import { defaultsDeep } from 'lodash';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { import { CallNotificationType, PropsForMessageWithConvoProps } from '../state/ducks/conversations';
CallNotificationType,
PropsForExpiringMessage,
PropsForMessageWithConvoProps,
} from '../state/ducks/conversations';
import { AttachmentTypeWithPath } from '../types/Attachment'; import { AttachmentTypeWithPath } from '../types/Attachment';
import { Reaction, ReactionList, SortedReactionList } from '../types/Reaction'; import { Reaction, ReactionList, SortedReactionList } from '../types/Reaction';
import { DisappearingMessageType } from '../util/expiringMessages'; import { DisappearingMessageType } from '../util/expiringMessages';
@ -139,13 +135,9 @@ export enum MessageDirection {
any = '%', any = '%',
} }
export interface PropsForDataExtractionNotification export interface PropsForDataExtractionNotification extends DataExtractionNotificationMsg {
extends DataExtractionNotificationMsg,
PropsForExpiringMessage {
name: string; name: string;
messageId: string; messageId: string;
receivedAt?: number;
isUnread: boolean;
} }
export type PropsForMessageRequestResponse = MessageRequestResponseMsg & { export type PropsForMessageRequestResponse = MessageRequestResponseMsg & {

@ -23,14 +23,14 @@ import {
export type CallNotificationType = 'missed-call' | 'started-call' | 'answered-a-call'; export type CallNotificationType = 'missed-call' | 'started-call' | 'answered-a-call';
export type PropsForCallNotification = PropsForExpiringMessage & { export type PropsForCallNotification = {
notificationType: CallNotificationType; notificationType: CallNotificationType;
receivedAt: number; messageId: string;
isUnread: boolean;
}; };
export type MessageModelPropsWithoutConvoProps = { export type MessageModelPropsWithoutConvoProps = {
propsForMessage: PropsForMessageWithoutConvoProps; propsForMessage: PropsForMessageWithoutConvoProps;
propsForExpiringMessage?: PropsForExpiringMessage;
propsForGroupInvitation?: PropsForGroupInvitation; propsForGroupInvitation?: PropsForGroupInvitation;
propsForTimerNotification?: PropsForExpirationTimer; propsForTimerNotification?: PropsForExpirationTimer;
propsForDataExtractionNotification?: PropsForDataExtractionNotification; propsForDataExtractionNotification?: PropsForDataExtractionNotification;
@ -79,12 +79,14 @@ export type PropsForExpiringMessage = {
convoId?: string; convoId?: string;
messageId: string; messageId: string;
direction: MessageModelType; direction: MessageModelType;
receivedAt?: number;
isUnread?: boolean;
expirationTimestamp?: number | null; expirationTimestamp?: number | null;
expirationLength?: number | null; expirationLength?: number | null;
isExpired?: boolean; isExpired?: boolean;
}; };
export type PropsForExpirationTimer = PropsForExpiringMessage & { export type PropsForExpirationTimer = {
expirationType: DisappearingMessageConversationType; expirationType: DisappearingMessageConversationType;
timespan: string; timespan: string;
disabled: boolean; disabled: boolean;
@ -95,8 +97,6 @@ export type PropsForExpirationTimer = PropsForExpiringMessage & {
title: string | null; title: string | null;
type: 'fromMe' | 'fromSync' | 'fromOther'; type: 'fromMe' | 'fromSync' | 'fromOther';
messageId: string; messageId: string;
isUnread: boolean;
receivedAt: number | undefined;
}; };
export type PropsForGroupUpdateGeneral = { export type PropsForGroupUpdateGeneral = {
@ -130,20 +130,17 @@ export type PropsForGroupUpdateType =
| PropsForGroupUpdateName | PropsForGroupUpdateName
| PropsForGroupUpdateLeft; | PropsForGroupUpdateLeft;
export type PropsForGroupUpdate = PropsForExpiringMessage & { export type PropsForGroupUpdate = {
change: PropsForGroupUpdateType; change: PropsForGroupUpdateType;
messageId: string; messageId: string;
receivedAt: number | undefined;
isUnread: boolean;
}; };
export type PropsForGroupInvitation = PropsForExpiringMessage & { export type PropsForGroupInvitation = {
serverName: string; serverName: string;
url: string; url: string;
direction: MessageModelType;
acceptUrl: string; acceptUrl: string;
messageId: string; messageId: string;
receivedAt?: number;
isUnread: boolean;
}; };
export type PropsForAttachment = { export type PropsForAttachment = {

@ -8,6 +8,7 @@ import {
MessageModelPropsWithConvoProps, MessageModelPropsWithConvoProps,
MessageModelPropsWithoutConvoProps, MessageModelPropsWithoutConvoProps,
MessagePropsDetails, MessagePropsDetails,
PropsForExpiringMessage,
ReduxConversationType, ReduxConversationType,
SortedMessageModelProps, SortedMessageModelProps,
} from '../ducks/conversations'; } from '../ducks/conversations';
@ -1089,6 +1090,29 @@ export const getMessageAttachmentProps = createSelector(getMessagePropsByMessage
return msgProps; return msgProps;
}); });
export const getMessageExpirationProps = createSelector(getMessagePropsByMessageId, (props):
| PropsForExpiringMessage
| undefined => {
if (!props || isEmpty(props)) {
return undefined;
}
const msgProps: PropsForExpiringMessage = {
...pick(props.propsForMessage, [
'convoId',
'direction',
'receivedAt',
'isUnread',
'expirationTimestamp',
'expirationLength',
'isExpired',
]),
messageId: props.propsForMessage.id,
};
return msgProps;
});
export const getIsMessageSelected = createSelector( export const getIsMessageSelected = createSelector(
getMessagePropsByMessageId, getMessagePropsByMessageId,
getSelectedMessageIds, getSelectedMessageIds,
@ -1151,10 +1175,6 @@ export const getGenericReadableMessageSelectorProps = createSelector(
'convoId', 'convoId',
'direction', 'direction',
'conversationType', 'conversationType',
'expirationType',
'expirationLength',
'expirationTimestamp',
'isExpired',
'isUnread', 'isUnread',
'receivedAt', 'receivedAt',
'isKickedFromGroup', 'isKickedFromGroup',

Loading…
Cancel
Save