-
+
+ {'12:01am today -- '}
+
-
-
+ {'11:59pm yesterday - adds day name -- '}
+
-
-
+ {'24 hours ago -- '}
+
-
-
+ {'Two days ago -- '}
+
-
-
+ {'Seven days ago - adds month -- '}
+
-
-
+ {'Thirty days ago -- '}
+
-
-
+
+ {'January 1st at 12:01am -- '}
+
-
-
+
+ {'December 31st at 11:59pm - adds year -- '}
+
-
-
+ {'One year ago -- '}
+
-;
+
;
+```
+
+### All major transitions: Normal
+
+```jsx
+function get1201() {
+ const d = new Date();
+ d.setHours(0, 0, 1, 0);
+ return d.getTime();
+}
+function getYesterday1159() {
+ return get1201() - 2 * 60 * 1000;
+}
+function getJanuary1201() {
+ const now = new Date();
+ const d = new Date(now.getFullYear(), 0, 1, 0, 1);
+ return d.getTime();
+}
+function getDecember1159() {
+ return getJanuary1201() - 2 * 60 * 1000;
+}
+
+
+
+ {"500ms ago - all below 1 minute are 'now' -- "}
+
+
+
+ {'Five seconds ago -- '}
+
+
+
+ {'30 seconds ago -- '}
+
+
+
+ {'One minute ago - in minutes -- '}
+
+
+
+ {'30 minutes ago -- '}
+
+
+
+ {'45 minutes ago (used to round up to 1 hour with moment) -- '}
+
+
+
+ {'One hour ago - in hours -- '}
+
+
+
+ {'12:01am today -- '}
+
+
+
+ {'11:59pm yesterday - adds day name -- '}
+
+
+
+ {'24 hours ago -- '}
+
+
+
+ {'Two days ago -- '}
+
+
+
+ {'Seven days ago - adds month -- '}
+
+
+
+ {'Thirty days ago -- '}
+
+
+
+ {'January 1st at 12:01am -- '}
+
+
+
+ {'December 31st at 11:59pm - adds year -- '}
+
+
+
+ {'One year ago -- '}
+
+
+
;
```
diff --git a/ts/components/conversation/Timestamp.tsx b/ts/components/conversation/Timestamp.tsx
index d9a3a283d4ec..8abe5bd24670 100644
--- a/ts/components/conversation/Timestamp.tsx
+++ b/ts/components/conversation/Timestamp.tsx
@@ -13,6 +13,7 @@ interface Props {
withImageNoCaption?: boolean;
withSticker?: boolean;
withTapToViewExpired?: boolean;
+ withUnread?: boolean;
direction?: 'incoming' | 'outgoing';
i18n: LocalizerType;
}
@@ -52,6 +53,7 @@ export class Timestamp extends React.Component
{
withImageNoCaption,
withSticker,
withTapToViewExpired,
+ withUnread,
extended,
} = this.props;
const moduleName = module || 'module-timestamp';
@@ -69,7 +71,8 @@ export class Timestamp extends React.Component {
? `${moduleName}--${direction}-with-tap-to-view-expired`
: null,
withImageNoCaption ? `${moduleName}--with-image-no-caption` : null,
- withSticker ? `${moduleName}--with-sticker` : null
+ withSticker ? `${moduleName}--with-sticker` : null,
+ withUnread ? `${moduleName}--with-unread` : null
)}
title={moment(timestamp).format('llll')}
>
diff --git a/ts/components/conversation/TypingBubble.tsx b/ts/components/conversation/TypingBubble.tsx
index cb40d0218de5..1f8d87dc144b 100644
--- a/ts/components/conversation/TypingBubble.tsx
+++ b/ts/components/conversation/TypingBubble.tsx
@@ -42,7 +42,7 @@ export class TypingBubble extends React.PureComponent {
name={name}
phoneNumber={phoneNumber}
profileName={profileName}
- size={36}
+ size={28}
/>
);
diff --git a/ts/components/conversation/_contactUtil.tsx b/ts/components/conversation/_contactUtil.tsx
index e37a2c3cb5cc..62f684437947 100644
--- a/ts/components/conversation/_contactUtil.tsx
+++ b/ts/components/conversation/_contactUtil.tsx
@@ -17,7 +17,7 @@ export function renderAvatar({
}: {
contact: ContactType;
i18n: LocalizerType;
- size: number;
+ size: 28 | 52 | 80;
direction?: 'outgoing' | 'incoming';
}) {
const { avatar } = contact;
diff --git a/ts/util/formatRelativeTime.ts b/ts/util/formatRelativeTime.ts
index 80796dc513ef..9727414b8740 100644
--- a/ts/util/formatRelativeTime.ts
+++ b/ts/util/formatRelativeTime.ts
@@ -1,6 +1,12 @@
import moment from 'moment';
import { LocalizerType } from '../types/Util';
+// Only applies in the english locales, but it ensures that the format
+// is what we want.
+function replaceSuffix(time: string) {
+ return time.replace(/ PM$/, 'pm').replace(/ AM$/, 'am');
+}
+
const getExtendedFormats = (i18n: LocalizerType) => ({
y: 'lll',
M: `${i18n('timestampFormat_M') || 'MMM D'} LT`,
@@ -38,19 +44,15 @@ export function formatRelativeTime(
const diff = moment.duration(now.diff(timestamp));
if (diff.years() >= 1 || !isYear(timestamp)) {
- return timestamp.format(formats.y);
+ return replaceSuffix(timestamp.format(formats.y));
} else if (diff.months() >= 1 || diff.days() > 6) {
- return timestamp.format(formats.M);
+ return replaceSuffix(timestamp.format(formats.M));
} else if (diff.days() >= 1 || !isToday(timestamp)) {
- return timestamp.format(formats.d);
+ return replaceSuffix(timestamp.format(formats.d));
} else if (diff.hours() >= 1) {
- const key = extended ? 'hoursAgo' : 'hoursAgoShort';
-
- return i18n(key, [String(diff.hours())]);
+ return i18n('hoursAgo', [String(diff.hours())]);
} else if (diff.minutes() >= 1) {
- const key = extended ? 'minutesAgo' : 'minutesAgoShort';
-
- return i18n(key, [String(diff.minutes())]);
+ return i18n('minutesAgo', [String(diff.minutes())]);
}
return i18n('justNow');