feat: merge expiring stopwatch and messagestatus together
parent
00f93a2754
commit
615722434b
@ -1,34 +1,188 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
import React from 'react';
|
||||
import { MessageRenderingProps } from '../../../../models/messageType';
|
||||
import { OutgoingMessageStatus } from './OutgoingMessageStatus';
|
||||
import { useMessageDirection, useMessageStatus } from '../../../../state/selectors';
|
||||
import styled from 'styled-components';
|
||||
import { useMessageExpirationPropsById } from '../../../../hooks/useParamSelector';
|
||||
import { useMessageStatus } from '../../../../state/selectors';
|
||||
|
||||
import { SpacerXS } from '../../../basic/Text';
|
||||
import { SessionIcon, SessionIconType } from '../../../icon';
|
||||
import { ExpireTimer } from '../../ExpireTimer';
|
||||
|
||||
type Props = {
|
||||
isCorrectSide: boolean;
|
||||
messageId: string;
|
||||
dataTestId?: string;
|
||||
dataTestId?: string | undefined;
|
||||
};
|
||||
|
||||
export type MessageStatusSelectorProps = Pick<MessageRenderingProps, 'direction' | 'status'>;
|
||||
|
||||
export const MessageStatus = (props: Props) => {
|
||||
const { isCorrectSide, dataTestId } = props;
|
||||
const direction = useMessageDirection(props.messageId);
|
||||
const { dataTestId, messageId } = props;
|
||||
const status = useMessageStatus(props.messageId);
|
||||
const selected = useMessageExpirationPropsById(props.messageId);
|
||||
|
||||
if (!props.messageId) {
|
||||
if (!props.messageId || !selected) {
|
||||
return null;
|
||||
}
|
||||
const isIncoming = selected.direction === 'incoming';
|
||||
|
||||
if (!isCorrectSide) {
|
||||
return null;
|
||||
if (isIncoming) {
|
||||
if (selected.isUnread || !selected.expirationDurationMs || !selected.expirationTimestamp) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<MessageStatusRead dataTestId={dataTestId} messageId={messageId} reserveDirection={true} />
|
||||
);
|
||||
}
|
||||
const isIncoming = direction === 'incoming';
|
||||
|
||||
const showStatus = !isIncoming && Boolean(status);
|
||||
if (!showStatus) {
|
||||
// this is the outgoing state: we display the text and the icon or the text and the expiretimer stopwatch when the message is expiring
|
||||
switch (status) {
|
||||
case 'sending':
|
||||
return <MessageStatusSending dataTestId={dataTestId} messageId={messageId} />;
|
||||
case 'sent':
|
||||
return <MessageStatusSent dataTestId={dataTestId} messageId={messageId} />;
|
||||
case 'read':
|
||||
return <MessageStatusRead dataTestId={dataTestId} messageId={messageId} />;
|
||||
case 'error':
|
||||
return <MessageStatusError dataTestId={dataTestId} messageId={messageId} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const MessageStatusContainer = styled.div<{ reserveDirection?: boolean }>`
|
||||
display: inline-block;
|
||||
align-self: flex-end;
|
||||
margin-bottom: 2px;
|
||||
margin-inline-start: 5px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-direction: ${props =>
|
||||
props.reserveDirection
|
||||
? 'row-reverse'
|
||||
: 'row'}; // we want {icon}{text} for incoming read messages, but {text}{icon} for outgoing messages
|
||||
`;
|
||||
|
||||
const StyledStatusText = styled.div`
|
||||
color: var(--text-secondary-color);
|
||||
font-size: small;
|
||||
`;
|
||||
|
||||
const TextDetails = ({ text }: { text: string }) => {
|
||||
return (
|
||||
<>
|
||||
<StyledStatusText>{text}</StyledStatusText>
|
||||
<SpacerXS />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
function IconDanger({ iconType }: { iconType: SessionIconType }) {
|
||||
return <SessionIcon iconColor={'var(--danger-color'} iconType={iconType} iconSize="tiny" />;
|
||||
}
|
||||
|
||||
function IconNormal({
|
||||
iconType,
|
||||
rotateDuration,
|
||||
}: {
|
||||
iconType: SessionIconType;
|
||||
rotateDuration?: number | undefined;
|
||||
}) {
|
||||
return (
|
||||
<SessionIcon
|
||||
rotateDuration={rotateDuration}
|
||||
iconColor={'var(--text-secondary-color)'}
|
||||
iconType={iconType}
|
||||
iconSize="tiny"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function useIsExpiring(messageId: string) {
|
||||
const selected = useMessageExpirationPropsById(messageId);
|
||||
return (
|
||||
selected && selected.expirationDurationMs && selected.expirationTimestamp && !selected.isExpired
|
||||
);
|
||||
}
|
||||
|
||||
function MessageStatusExpireTimer(props: Props) {
|
||||
const selected = useMessageExpirationPropsById(props.messageId);
|
||||
if (
|
||||
!selected ||
|
||||
!selected.expirationDurationMs ||
|
||||
!selected.expirationTimestamp ||
|
||||
selected.isExpired
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<ExpireTimer
|
||||
expirationDurationMs={selected.expirationDurationMs}
|
||||
expirationTimestamp={selected.expirationTimestamp}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const MessageStatusSending = ({ dataTestId }: Props) => {
|
||||
// while sending, we do not display the expire timer at all.
|
||||
return (
|
||||
<MessageStatusContainer data-testid={dataTestId} data-testtype="sending">
|
||||
<TextDetails text={window.i18n('sending')} />
|
||||
<IconNormal rotateDuration={2} iconType="sending" />
|
||||
</MessageStatusContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const MessageStatusSent = ({ dataTestId, messageId }: Props) => {
|
||||
const isExpiring = useIsExpiring(messageId);
|
||||
|
||||
return (
|
||||
<MessageStatusContainer data-testid={dataTestId} data-testtype="sent">
|
||||
<TextDetails text={window.i18n('sent')} />
|
||||
{isExpiring ? (
|
||||
<MessageStatusExpireTimer messageId={messageId} />
|
||||
) : (
|
||||
<IconNormal iconType="circleCheck" />
|
||||
)}
|
||||
</MessageStatusContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const MessageStatusRead = ({
|
||||
dataTestId,
|
||||
messageId,
|
||||
reserveDirection,
|
||||
}: Props & { reserveDirection?: boolean }) => {
|
||||
const isExpiring = useIsExpiring(messageId);
|
||||
return (
|
||||
<MessageStatusContainer
|
||||
data-testid={dataTestId}
|
||||
data-testtype="read"
|
||||
reserveDirection={reserveDirection}
|
||||
>
|
||||
<TextDetails text={window.i18n('read')} />
|
||||
{isExpiring ? (
|
||||
<MessageStatusExpireTimer messageId={messageId} />
|
||||
) : (
|
||||
<IconNormal iconType="doubleCheckCircleFilled" />
|
||||
)}
|
||||
</MessageStatusContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const MessageStatusError = ({ dataTestId }: Props) => {
|
||||
const showDebugLog = () => {
|
||||
ipcRenderer.send('show-debug-log');
|
||||
};
|
||||
// when on errro, we do not display the expire timer at all.
|
||||
|
||||
return <OutgoingMessageStatus dataTestId={dataTestId} status={status} />;
|
||||
return (
|
||||
<MessageStatusContainer
|
||||
data-testid={dataTestId}
|
||||
data-testtype="failed"
|
||||
onClick={showDebugLog}
|
||||
title={window.i18n('sendFailed')}
|
||||
>
|
||||
<TextDetails text={window.i18n('failed')} />
|
||||
<IconDanger iconType="error" />
|
||||
</MessageStatusContainer>
|
||||
);
|
||||
};
|
||||
|
@ -1,75 +0,0 @@
|
||||
import { ipcRenderer } from 'electron';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { LastMessageStatusType } from '../../../../state/ducks/conversations';
|
||||
import { SessionIcon } from '../../../icon';
|
||||
|
||||
const MessageStatusSendingContainer = styled.div`
|
||||
display: inline-block;
|
||||
align-self: flex-end;
|
||||
margin-bottom: 2px;
|
||||
margin-inline-start: 5px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const iconColor = 'var(--text-primary-color)';
|
||||
|
||||
const MessageStatusSending = ({ dataTestId }: { dataTestId?: string }) => {
|
||||
return (
|
||||
<MessageStatusSendingContainer data-testid={dataTestId} data-testtype="sending">
|
||||
<SessionIcon rotateDuration={2} iconColor={iconColor} iconType="sending" iconSize="tiny" />
|
||||
</MessageStatusSendingContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const MessageStatusSent = ({ dataTestId }: { dataTestId?: string }) => {
|
||||
return (
|
||||
<MessageStatusSendingContainer data-testid={dataTestId} data-testtype="sent">
|
||||
<SessionIcon iconColor={iconColor} iconType="circleCheck" iconSize="tiny" />
|
||||
</MessageStatusSendingContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const MessageStatusRead = ({ dataTestId }: { dataTestId?: string }) => {
|
||||
return (
|
||||
<MessageStatusSendingContainer data-testid={dataTestId} data-testtype="read">
|
||||
<SessionIcon iconColor={iconColor} iconType="doubleCheckCircleFilled" iconSize="tiny" />
|
||||
</MessageStatusSendingContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const MessageStatusError = ({ dataTestId }: { dataTestId?: string }) => {
|
||||
const showDebugLog = () => {
|
||||
ipcRenderer.send('show-debug-log');
|
||||
};
|
||||
|
||||
return (
|
||||
<MessageStatusSendingContainer
|
||||
data-testid={dataTestId}
|
||||
data-testtype="failed"
|
||||
onClick={showDebugLog}
|
||||
title={window.i18n('sendFailed')}
|
||||
>
|
||||
<SessionIcon iconColor={'var(--danger-color'} iconType="error" iconSize="tiny" />
|
||||
</MessageStatusSendingContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export const OutgoingMessageStatus = (props: {
|
||||
status: LastMessageStatusType | null;
|
||||
dataTestId?: string;
|
||||
}) => {
|
||||
const { status, dataTestId } = props;
|
||||
switch (status) {
|
||||
case 'sending':
|
||||
return <MessageStatusSending dataTestId={dataTestId} />;
|
||||
case 'sent':
|
||||
return <MessageStatusSent dataTestId={dataTestId} />;
|
||||
case 'read':
|
||||
return <MessageStatusRead dataTestId={dataTestId} />;
|
||||
case 'error':
|
||||
return <MessageStatusError dataTestId={dataTestId} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue