Show full timestamp on hover (#714)
* Show full timestamp on hover * Not always display time * Always show full timestamp in search
This commit is contained in:
parent
04f910ee03
commit
21726b63f8
5 changed files with 73 additions and 17 deletions
44
src/app/atoms/time/Time.jsx
Normal file
44
src/app/atoms/time/Time.jsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import dateFormat from 'dateformat';
|
||||||
|
import { isInSameDay } from '../../../util/common';
|
||||||
|
|
||||||
|
function Time({ timestamp, fullTime }) {
|
||||||
|
const date = new Date(timestamp);
|
||||||
|
|
||||||
|
const formattedFullTime = dateFormat(date, 'dd mmmm yyyy, hh:MM TT');
|
||||||
|
let formattedDate = formattedFullTime;
|
||||||
|
|
||||||
|
if (!fullTime) {
|
||||||
|
const compareDate = new Date();
|
||||||
|
const isToday = isInSameDay(date, compareDate);
|
||||||
|
compareDate.setDate(compareDate.getDate() - 1);
|
||||||
|
const isYesterday = isInSameDay(date, compareDate);
|
||||||
|
|
||||||
|
formattedDate = dateFormat(date, isToday || isYesterday ? 'hh:MM TT' : 'dd/mm/yyyy');
|
||||||
|
if (isYesterday) {
|
||||||
|
formattedDate = `Yesterday, ${formattedDate}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<time
|
||||||
|
dateTime={date.toISOString()}
|
||||||
|
title={formattedFullTime}
|
||||||
|
>
|
||||||
|
{formattedDate}
|
||||||
|
</time>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Time.defaultProps = {
|
||||||
|
fullTime: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
Time.propTypes = {
|
||||||
|
timestamp: PropTypes.number.isRequired,
|
||||||
|
fullTime: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Time;
|
|
@ -24,6 +24,7 @@ import Tooltip from '../../atoms/tooltip/Tooltip';
|
||||||
import Input from '../../atoms/input/Input';
|
import Input from '../../atoms/input/Input';
|
||||||
import Avatar from '../../atoms/avatar/Avatar';
|
import Avatar from '../../atoms/avatar/Avatar';
|
||||||
import IconButton from '../../atoms/button/IconButton';
|
import IconButton from '../../atoms/button/IconButton';
|
||||||
|
import Time from '../../atoms/time/Time';
|
||||||
import ContextMenu, { MenuHeader, MenuItem, MenuBorder } from '../../atoms/context-menu/ContextMenu';
|
import ContextMenu, { MenuHeader, MenuItem, MenuBorder } from '../../atoms/context-menu/ContextMenu';
|
||||||
import * as Media from '../media/Media';
|
import * as Media from '../media/Media';
|
||||||
|
|
||||||
|
@ -67,7 +68,7 @@ const MessageAvatar = React.memo(({
|
||||||
));
|
));
|
||||||
|
|
||||||
const MessageHeader = React.memo(({
|
const MessageHeader = React.memo(({
|
||||||
userId, username, time,
|
userId, username, timestamp, fullTime,
|
||||||
}) => (
|
}) => (
|
||||||
<div className="message__header">
|
<div className="message__header">
|
||||||
<Text
|
<Text
|
||||||
|
@ -81,14 +82,20 @@ const MessageHeader = React.memo(({
|
||||||
<span>{twemojify(userId)}</span>
|
<span>{twemojify(userId)}</span>
|
||||||
</Text>
|
</Text>
|
||||||
<div className="message__time">
|
<div className="message__time">
|
||||||
<Text variant="b3">{time}</Text>
|
<Text variant="b3">
|
||||||
|
<Time timestamp={timestamp} fullTime={fullTime} />
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
));
|
));
|
||||||
|
MessageHeader.defaultProps = {
|
||||||
|
fullTime: false,
|
||||||
|
};
|
||||||
MessageHeader.propTypes = {
|
MessageHeader.propTypes = {
|
||||||
userId: PropTypes.string.isRequired,
|
userId: PropTypes.string.isRequired,
|
||||||
username: PropTypes.string.isRequired,
|
username: PropTypes.string.isRequired,
|
||||||
time: PropTypes.string.isRequired,
|
timestamp: PropTypes.number.isRequired,
|
||||||
|
fullTime: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
function MessageReply({ name, color, body }) {
|
function MessageReply({ name, color, body }) {
|
||||||
|
@ -690,7 +697,7 @@ function getEditedBody(editedMEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function Message({
|
function Message({
|
||||||
mEvent, isBodyOnly, roomTimeline, focus, time,
|
mEvent, isBodyOnly, roomTimeline, focus, fullTime,
|
||||||
}) {
|
}) {
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
const roomId = mEvent.getRoomId();
|
const roomId = mEvent.getRoomId();
|
||||||
|
@ -751,7 +758,12 @@ function Message({
|
||||||
}
|
}
|
||||||
<div className="message__main-container">
|
<div className="message__main-container">
|
||||||
{!isBodyOnly && (
|
{!isBodyOnly && (
|
||||||
<MessageHeader userId={senderId} username={username} time={time} />
|
<MessageHeader
|
||||||
|
userId={senderId}
|
||||||
|
username={username}
|
||||||
|
timestamp={mEvent.getTs()}
|
||||||
|
fullTime={fullTime}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{roomTimeline && isReply && (
|
{roomTimeline && isReply && (
|
||||||
<MessageReplyWrapper
|
<MessageReplyWrapper
|
||||||
|
@ -799,13 +811,14 @@ Message.defaultProps = {
|
||||||
isBodyOnly: false,
|
isBodyOnly: false,
|
||||||
focus: false,
|
focus: false,
|
||||||
roomTimeline: null,
|
roomTimeline: null,
|
||||||
|
fullTime: false,
|
||||||
};
|
};
|
||||||
Message.propTypes = {
|
Message.propTypes = {
|
||||||
mEvent: PropTypes.shape({}).isRequired,
|
mEvent: PropTypes.shape({}).isRequired,
|
||||||
isBodyOnly: PropTypes.bool,
|
isBodyOnly: PropTypes.bool,
|
||||||
roomTimeline: PropTypes.shape({}),
|
roomTimeline: PropTypes.shape({}),
|
||||||
focus: PropTypes.bool,
|
focus: PropTypes.bool,
|
||||||
time: PropTypes.string.isRequired,
|
fullTime: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
export { Message, MessageReply, PlaceholderMessage };
|
export { Message, MessageReply, PlaceholderMessage };
|
||||||
|
|
|
@ -4,6 +4,7 @@ import './TimelineChange.scss';
|
||||||
|
|
||||||
import Text from '../../atoms/text/Text';
|
import Text from '../../atoms/text/Text';
|
||||||
import RawIcon from '../../atoms/system-icons/RawIcon';
|
import RawIcon from '../../atoms/system-icons/RawIcon';
|
||||||
|
import Time from '../../atoms/time/Time';
|
||||||
|
|
||||||
import JoinArraowIC from '../../../../public/res/ic/outlined/join-arrow.svg';
|
import JoinArraowIC from '../../../../public/res/ic/outlined/join-arrow.svg';
|
||||||
import LeaveArraowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
|
import LeaveArraowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
|
||||||
|
@ -12,7 +13,7 @@ import InviteCancelArraowIC from '../../../../public/res/ic/outlined/invite-canc
|
||||||
import UserIC from '../../../../public/res/ic/outlined/user.svg';
|
import UserIC from '../../../../public/res/ic/outlined/user.svg';
|
||||||
|
|
||||||
function TimelineChange({
|
function TimelineChange({
|
||||||
variant, content, time, onClick,
|
variant, content, timestamp, onClick,
|
||||||
}) {
|
}) {
|
||||||
let iconSrc;
|
let iconSrc;
|
||||||
|
|
||||||
|
@ -48,7 +49,9 @@ function TimelineChange({
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<div className="timeline-change__time">
|
<div className="timeline-change__time">
|
||||||
<Text variant="b3">{time}</Text>
|
<Text variant="b3">
|
||||||
|
<Time timestamp={timestamp} />
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
@ -68,7 +71,7 @@ TimelineChange.propTypes = {
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.node,
|
PropTypes.node,
|
||||||
]).isRequired,
|
]).isRequired,
|
||||||
time: PropTypes.string.isRequired,
|
timestamp: PropTypes.number.isRequired,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -120,14 +120,13 @@ function RoomSearch({ roomId }) {
|
||||||
const renderTimeline = (timeline) => (
|
const renderTimeline = (timeline) => (
|
||||||
<div className="room-search__result-item" key={timeline[0].getId()}>
|
<div className="room-search__result-item" key={timeline[0].getId()}>
|
||||||
{ timeline.map((mEvent) => {
|
{ timeline.map((mEvent) => {
|
||||||
const time = dateFormat(mEvent.getDate(), 'dd/mm/yyyy - hh:MM TT');
|
|
||||||
const id = mEvent.getId();
|
const id = mEvent.getId();
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={id}>
|
<React.Fragment key={id}>
|
||||||
<Message
|
<Message
|
||||||
mEvent={mEvent}
|
mEvent={mEvent}
|
||||||
isBodyOnly={false}
|
isBodyOnly={false}
|
||||||
time={time}
|
fullTime
|
||||||
/>
|
/>
|
||||||
<Button onClick={() => selectRoom(roomId, id)}>View</Button>
|
<Button onClick={() => selectRoom(roomId, id)}>View</Button>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
|
|
@ -125,10 +125,7 @@ function renderEvent(roomTimeline, mEvent, prevMEvent, isFocus = false) {
|
||||||
&& prevMEvent.getType() !== 'm.room.create'
|
&& prevMEvent.getType() !== 'm.room.create'
|
||||||
&& diffMinutes(mEvent.getDate(), prevMEvent.getDate()) <= MAX_MSG_DIFF_MINUTES
|
&& diffMinutes(mEvent.getDate(), prevMEvent.getDate()) <= MAX_MSG_DIFF_MINUTES
|
||||||
);
|
);
|
||||||
const mDate = mEvent.getDate();
|
const timestamp = mEvent.getTs();
|
||||||
const isToday = isInSameDay(mDate, new Date());
|
|
||||||
|
|
||||||
const time = dateFormat(mDate, isToday ? 'hh:MM TT' : 'dd/mm/yyyy');
|
|
||||||
|
|
||||||
if (mEvent.getType() === 'm.room.member') {
|
if (mEvent.getType() === 'm.room.member') {
|
||||||
const timelineChange = parseTimelineChange(mEvent);
|
const timelineChange = parseTimelineChange(mEvent);
|
||||||
|
@ -138,7 +135,7 @@ function renderEvent(roomTimeline, mEvent, prevMEvent, isFocus = false) {
|
||||||
key={mEvent.getId()}
|
key={mEvent.getId()}
|
||||||
variant={timelineChange.variant}
|
variant={timelineChange.variant}
|
||||||
content={timelineChange.content}
|
content={timelineChange.content}
|
||||||
time={time}
|
timestamp={timestamp}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -149,7 +146,7 @@ function renderEvent(roomTimeline, mEvent, prevMEvent, isFocus = false) {
|
||||||
isBodyOnly={isBodyOnly}
|
isBodyOnly={isBodyOnly}
|
||||||
roomTimeline={roomTimeline}
|
roomTimeline={roomTimeline}
|
||||||
focus={isFocus}
|
focus={isFocus}
|
||||||
time={time}
|
fullTime={false}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue