feat: right click on expiring msg shows timer left

pull/3019/head
Audric Ackermann 4 months ago
parent ad9905d2ac
commit 7573152e8b

@ -203,7 +203,6 @@ export const MessageContextMenu = (props: Props) => {
const attachments = useMessageAttachments(messageId);
const timestamp = useMessageTimestamp(messageId);
const serverTimestamp = useMessageServerTimestamp(messageId);
const sender = useMessageSender(messageId);
const isOutgoing = direction === 'outgoing';
@ -368,7 +367,11 @@ export const MessageContextMenu = (props: Props) => {
<Menu id={contextMenuId} onVisibilityChange={onVisibilityChange} animation="fade">
{enableReactions && (
// eslint-disable-next-line @typescript-eslint/no-misused-promises
<MessageReactBar action={onEmojiClick} additionalAction={onShowEmoji} />
<MessageReactBar
action={onEmojiClick}
additionalAction={onShowEmoji}
messageId={messageId}
/>
)}
{attachments?.length ? (
<Item onClick={saveAttachment}>{window.i18n('downloadAttachment')}</Item>

@ -1,16 +1,18 @@
import { isEqual } from 'lodash';
import React, { ReactElement, useState } from 'react';
import useMount from 'react-use/lib/useMount';
import React, { ReactElement } from 'react';
import styled from 'styled-components';
import { RecentReactions } from '../../../../types/Reaction';
import { isEmpty } from 'lodash';
import moment from 'moment';
import { useMessageExpirationPropsById } from '../../../../hooks/useParamSelector';
import { nativeEmojiData } from '../../../../util/emoji';
import { getRecentReactions } from '../../../../util/storage';
import { SessionIconButton } from '../../../icon';
import { SpacerSM } from '../../../basic/Text';
import { SessionIcon, SessionIconButton } from '../../../icon';
type Props = {
action: (...args: Array<any>) => void;
additionalAction: (...args: Array<any>) => void;
messageId: string;
};
const StyledMessageReactBar = styled.div`
@ -18,8 +20,6 @@ const StyledMessageReactBar = styled.div`
border-radius: 25px;
box-shadow: 0 2px 16px 0 rgba(0, 0, 0, 0.2), 0 0px 20px 0 rgba(0, 0, 0, 0.19);
position: absolute;
top: -56px;
padding: 4px 8px;
white-space: nowrap;
width: 302px;
@ -51,45 +51,133 @@ const ReactButton = styled.span`
}
`;
export const MessageReactBar = (props: Props): ReactElement => {
const { action, additionalAction } = props;
const [recentReactions, setRecentReactions] = useState<RecentReactions>();
const StyledContainer = styled.div<{ expirationTimestamp: number | null }>`
position: absolute;
top: ${props => (props.expirationTimestamp ? '-106px' : '-56px')};
display: flex;
flex-direction: column;
min-width: 0;
align-items: flex-start;
left: -1px;
`;
useMount(() => {
const reactions = new RecentReactions(getRecentReactions());
if (reactions && !isEqual(reactions, recentReactions)) {
setRecentReactions(reactions);
}
});
const StyledExpiresIn = styled.div`
border-radius: 8px;
padding: 10px;
white-space: nowrap;
color: var(--text-primary-color);
size: var(--font-size-sm);
background-color: var(--context-menu-background-color);
box-shadow: 0px 0px 9px 0px var(--context-menu-shadow-color);
margin-top: 7px;
display: flex;
align-items: center;
min-width: 0;
`;
function useIsRenderedExpiresInItem(messageId: string) {
const expiryDetails = useMessageExpirationPropsById(messageId);
if (
!expiryDetails ||
isEmpty(expiryDetails) ||
!expiryDetails.expirationDurationMs ||
expiryDetails.isExpired ||
!expiryDetails.expirationTimestamp
) {
return null;
}
return expiryDetails.expirationTimestamp;
}
function formatExpiry({ expirationTimestamp }: { expirationTimestamp: number }) {
const diffMs = expirationTimestamp - Date.now();
const diff = moment(diffMs).utc();
if (diffMs <= 0) {
return `0s`;
}
const prefix = 'Message will expire in';
if (!recentReactions) {
return <></>;
if (diff.isBefore(moment.utc(0).add(1, 'minute'))) {
return `${prefix} ${diff.seconds()}s`;
}
if (diff.isBefore(moment.utc(0).add(1, 'hour'))) {
const extraUnit = diff.seconds() ? ` ${diff.seconds()}s` : '';
return `${prefix} ${diff.minutes()}m${extraUnit}`;
}
if (diff.isBefore(moment.utc(0).add(1, 'day'))) {
const extraUnit = diff.minutes() ? ` ${diff.minutes()}m` : '';
return `${prefix} ${diff.hours()}h${extraUnit}`;
}
if (diff.isBefore(moment.utc(0).add(7, 'day'))) {
const extraUnit = diff.hours() ? ` ${diff.hours()}h` : '';
return `${prefix} ${diff.dayOfYear() - 1}d${extraUnit}`;
}
if (diff.isBefore(moment.utc(0).add(31, 'day'))) {
const days = diff.dayOfYear() - 1;
const weeks = Math.floor(days / 7);
const daysLeft = days % 7;
const extraUnit = daysLeft ? ` ${daysLeft}d` : '';
return `${prefix} ${weeks}w${extraUnit}`;
}
return '...';
}
const ExpiresInItem = ({ expirationTimestamp }: { expirationTimestamp?: number | null }) => {
if (!expirationTimestamp) {
return null;
}
return (
<StyledExpiresIn>
<SessionIcon iconSize={'small'} iconType="stopwatch" />
<SpacerSM />
<span>{formatExpiry({ expirationTimestamp })}</span>
</StyledExpiresIn>
);
};
export const MessageReactBar = ({ action, additionalAction, messageId }: Props): ReactElement => {
const recentReactions = getRecentReactions();
const expirationTimestamp = useIsRenderedExpiresInItem(messageId);
return (
<StyledMessageReactBar>
{recentReactions &&
recentReactions.items.map(emoji => (
<ReactButton
key={emoji}
role={'img'}
aria-label={nativeEmojiData?.ariaLabels ? nativeEmojiData.ariaLabels[emoji] : undefined}
onClick={() => {
action(emoji);
}}
>
{emoji}
</ReactButton>
))}
<SessionIconButton
iconColor={'var(--emoji-reaction-bar-icon-color)'}
iconPadding={'8px'}
iconSize={'huge'}
iconType="plusThin"
backgroundColor={'var(--emoji-reaction-bar-icon-background-color)'}
borderRadius="300px"
onClick={additionalAction}
/>
</StyledMessageReactBar>
<StyledContainer expirationTimestamp={expirationTimestamp}>
<StyledMessageReactBar>
{recentReactions &&
recentReactions.map(emoji => (
<ReactButton
key={emoji}
role={'img'}
aria-label={
nativeEmojiData?.ariaLabels ? nativeEmojiData.ariaLabels[emoji] : undefined
}
onClick={() => {
action(emoji);
}}
>
{emoji}
</ReactButton>
))}
<SessionIconButton
iconColor={'var(--emoji-reaction-bar-icon-color)'}
iconPadding={'8px'}
iconSize={'huge'}
iconType="plusThin"
backgroundColor={'var(--emoji-reaction-bar-icon-background-color)'}
borderRadius="300px"
onClick={additionalAction}
/>
</StyledMessageReactBar>
<ExpiresInItem expirationTimestamp={expirationTimestamp} />
</StyledContainer>
);
};

@ -13,6 +13,8 @@ export const DURATION = {
HOURS: hours,
/** 24 * 60 * 60 * 1000 = 86,400,000 ms */
DAYS: days,
/** 7 * 24 * 60 * 60 * 1000 = 604,800,000 ms */
WEEKS: days * 7,
};
export const TTL_DEFAULT = {

Loading…
Cancel
Save