Skip to content

Commit 424083f

Browse files
committed
lightbox: Use a friendlier format for the date
Changed the timestamp format from Mar 31, 2023 15:09:51 to a more readable Mar 31, 2023 at 3:09 PM. Added proper localization support to adapt timestamps to the user's language and region preferences. Optimized the timestamp logic to ensure accurate categorization into Today, Yesterday, or specific dates. Fixes: zulip#45
1 parent ab54810 commit 424083f

11 files changed

+363
-6
lines changed

assets/l10n/app_en.arb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,5 +680,29 @@
680680
"emojiPickerSearchEmoji": "Search emoji",
681681
"@emojiPickerSearchEmoji": {
682682
"description": "Hint text for the emoji picker search text field."
683+
},
684+
"aFewSecondsAgo": "A few seconds ago",
685+
"@aFewSecondsAgo": {
686+
"description": "Message displayed when the difference between the current time and the given time is less than 60 seconds."
687+
},
688+
"oneMinuteAgo": "1 minute ago",
689+
"@oneMinuteAgo": {
690+
"description": "Message displayed when the difference between the current time and the given time is exactly 1 minute."
691+
},
692+
"minutesAgo": "{minutes} minutes ago",
693+
"@minutesAgo": {
694+
"description": "Message displayed when the difference between the current time and the given time is in minutes. The placeholder {minutes} will be replaced with the actual number of minutes."
695+
},
696+
"todayAt": "Today at {time}",
697+
"@todayAt": {
698+
"description": "Message displayed when the given time is from today. The placeholder {time} will be replaced with the formatted time."
699+
},
700+
"yesterdayAt": "Yesterday at {time}",
701+
"@yesterdayAt": {
702+
"description": "Message displayed when the given time is from yesterday. The placeholder {time} will be replaced with the formatted time."
703+
},
704+
"dateAtTime": "{date} at {time}",
705+
"@dateAtTime": {
706+
"description": "Message displayed when the given time is older than yesterday. The placeholder {date} will be replaced with the formatted date and {time} with the formatted time."
683707
}
684708
}

lib/generated/l10n/zulip_localizations.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,42 @@ abstract class ZulipLocalizations {
10161016
/// In en, this message translates to:
10171017
/// **'Search emoji'**
10181018
String get emojiPickerSearchEmoji;
1019+
1020+
/// Message displayed when the difference between the current time and the given time is less than 60 seconds.
1021+
///
1022+
/// In en, this message translates to:
1023+
/// **'A few seconds ago'**
1024+
String get aFewSecondsAgo;
1025+
1026+
/// Message displayed when the difference between the current time and the given time is exactly 1 minute.
1027+
///
1028+
/// In en, this message translates to:
1029+
/// **'1 minute ago'**
1030+
String get oneMinuteAgo;
1031+
1032+
/// Message displayed when the difference between the current time and the given time is in minutes. The placeholder {minutes} will be replaced with the actual number of minutes.
1033+
///
1034+
/// In en, this message translates to:
1035+
/// **'{minutes} minutes ago'**
1036+
String minutesAgo(Object minutes);
1037+
1038+
/// Message displayed when the given time is from today. The placeholder {time} will be replaced with the formatted time.
1039+
///
1040+
/// In en, this message translates to:
1041+
/// **'Today at {time}'**
1042+
String todayAt(Object time);
1043+
1044+
/// Message displayed when the given time is from yesterday. The placeholder {time} will be replaced with the formatted time.
1045+
///
1046+
/// In en, this message translates to:
1047+
/// **'Yesterday at {time}'**
1048+
String yesterdayAt(Object time);
1049+
1050+
/// Message displayed when the given time is older than yesterday. The placeholder {date} will be replaced with the formatted date and {time} with the formatted time.
1051+
///
1052+
/// In en, this message translates to:
1053+
/// **'{date} at {time}'**
1054+
String dateAtTime(Object date, Object time);
10191055
}
10201056

10211057
class _ZulipLocalizationsDelegate extends LocalizationsDelegate<ZulipLocalizations> {

lib/generated/l10n/zulip_localizations_ar.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,4 +537,30 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
537537

538538
@override
539539
String get emojiPickerSearchEmoji => 'Search emoji';
540+
541+
@override
542+
String get aFewSecondsAgo => 'A few seconds ago';
543+
544+
@override
545+
String get oneMinuteAgo => '1 minute ago';
546+
547+
@override
548+
String minutesAgo(Object minutes) {
549+
return '$minutes minutes ago';
550+
}
551+
552+
@override
553+
String todayAt(Object time) {
554+
return 'Today at $time';
555+
}
556+
557+
@override
558+
String yesterdayAt(Object time) {
559+
return 'Yesterday at $time';
560+
}
561+
562+
@override
563+
String dateAtTime(Object date, Object time) {
564+
return '$date at $time';
565+
}
540566
}

lib/generated/l10n/zulip_localizations_en.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,4 +537,30 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
537537

538538
@override
539539
String get emojiPickerSearchEmoji => 'Search emoji';
540+
541+
@override
542+
String get aFewSecondsAgo => 'A few seconds ago';
543+
544+
@override
545+
String get oneMinuteAgo => '1 minute ago';
546+
547+
@override
548+
String minutesAgo(Object minutes) {
549+
return '$minutes minutes ago';
550+
}
551+
552+
@override
553+
String todayAt(Object time) {
554+
return 'Today at $time';
555+
}
556+
557+
@override
558+
String yesterdayAt(Object time) {
559+
return 'Yesterday at $time';
560+
}
561+
562+
@override
563+
String dateAtTime(Object date, Object time) {
564+
return '$date at $time';
565+
}
540566
}

lib/generated/l10n/zulip_localizations_ja.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,4 +537,30 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
537537

538538
@override
539539
String get emojiPickerSearchEmoji => 'Search emoji';
540+
541+
@override
542+
String get aFewSecondsAgo => 'A few seconds ago';
543+
544+
@override
545+
String get oneMinuteAgo => '1 minute ago';
546+
547+
@override
548+
String minutesAgo(Object minutes) {
549+
return '$minutes minutes ago';
550+
}
551+
552+
@override
553+
String todayAt(Object time) {
554+
return 'Today at $time';
555+
}
556+
557+
@override
558+
String yesterdayAt(Object time) {
559+
return 'Yesterday at $time';
560+
}
561+
562+
@override
563+
String dateAtTime(Object date, Object time) {
564+
return '$date at $time';
565+
}
540566
}

lib/generated/l10n/zulip_localizations_nb.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,4 +537,30 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
537537

538538
@override
539539
String get emojiPickerSearchEmoji => 'Search emoji';
540+
541+
@override
542+
String get aFewSecondsAgo => 'A few seconds ago';
543+
544+
@override
545+
String get oneMinuteAgo => '1 minute ago';
546+
547+
@override
548+
String minutesAgo(Object minutes) {
549+
return '$minutes minutes ago';
550+
}
551+
552+
@override
553+
String todayAt(Object time) {
554+
return 'Today at $time';
555+
}
556+
557+
@override
558+
String yesterdayAt(Object time) {
559+
return 'Yesterday at $time';
560+
}
561+
562+
@override
563+
String dateAtTime(Object date, Object time) {
564+
return '$date at $time';
565+
}
540566
}

lib/generated/l10n/zulip_localizations_pl.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,4 +537,30 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
537537

538538
@override
539539
String get emojiPickerSearchEmoji => 'Szukaj emoji';
540+
541+
@override
542+
String get aFewSecondsAgo => 'A few seconds ago';
543+
544+
@override
545+
String get oneMinuteAgo => '1 minute ago';
546+
547+
@override
548+
String minutesAgo(Object minutes) {
549+
return '$minutes minutes ago';
550+
}
551+
552+
@override
553+
String todayAt(Object time) {
554+
return 'Today at $time';
555+
}
556+
557+
@override
558+
String yesterdayAt(Object time) {
559+
return 'Yesterday at $time';
560+
}
561+
562+
@override
563+
String dateAtTime(Object date, Object time) {
564+
return '$date at $time';
565+
}
540566
}

lib/generated/l10n/zulip_localizations_ru.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,4 +537,30 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
537537

538538
@override
539539
String get emojiPickerSearchEmoji => 'Поиск эмодзи';
540+
541+
@override
542+
String get aFewSecondsAgo => 'A few seconds ago';
543+
544+
@override
545+
String get oneMinuteAgo => '1 minute ago';
546+
547+
@override
548+
String minutesAgo(Object minutes) {
549+
return '$minutes minutes ago';
550+
}
551+
552+
@override
553+
String todayAt(Object time) {
554+
return 'Today at $time';
555+
}
556+
557+
@override
558+
String yesterdayAt(Object time) {
559+
return 'Yesterday at $time';
560+
}
561+
562+
@override
563+
String dateAtTime(Object date, Object time) {
564+
return '$date at $time';
565+
}
540566
}

lib/generated/l10n/zulip_localizations_sk.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,4 +537,30 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
537537

538538
@override
539539
String get emojiPickerSearchEmoji => 'Hľadať emotikon';
540+
541+
@override
542+
String get aFewSecondsAgo => 'A few seconds ago';
543+
544+
@override
545+
String get oneMinuteAgo => '1 minute ago';
546+
547+
@override
548+
String minutesAgo(Object minutes) {
549+
return '$minutes minutes ago';
550+
}
551+
552+
@override
553+
String todayAt(Object time) {
554+
return 'Today at $time';
555+
}
556+
557+
@override
558+
String yesterdayAt(Object time) {
559+
return 'Yesterday at $time';
560+
}
561+
562+
@override
563+
String dateAtTime(Object date, Object time) {
564+
return '$date at $time';
565+
}
540566
}

lib/widgets/lightbox.dart

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,42 @@ class _LightboxPageLayoutState extends State<_LightboxPageLayout> {
152152

153153
PreferredSizeWidget? appBar;
154154
if (_headerFooterVisible) {
155-
// TODO(#45): Format with e.g. "Yesterday at 4:47 PM"
156-
final timestampText = DateFormat
157-
.yMMMd(/* TODO(#278): Pass selected language here, I think? */)
158-
.add_Hms()
159-
.format(DateTime.fromMillisecondsSinceEpoch(widget.message.timestamp * 1000));
155+
final zulipLocalizations = ZulipLocalizations.of(context);
156+
157+
String formatLocalizedTimestamp(DateTime date) {
158+
final now = DateTime.now();
159+
final nowDateOnly = DateTime(now.year, now.month, now.day);
160+
final messageDateOnly = DateTime(date.year, date.month, date.day);
161+
162+
final differenceInSeconds = now.difference(date).inSeconds;
163+
final differenceInMinutes = now.difference(date).inMinutes;
164+
final differenceInDays = nowDateOnly.difference(messageDateOnly).inDays;
165+
166+
if (differenceInSeconds < 60) {
167+
return zulipLocalizations.aFewSecondsAgo;
168+
} else if (differenceInMinutes < 60) {
169+
return Intl.plural(
170+
differenceInMinutes,
171+
one: zulipLocalizations.oneMinuteAgo,
172+
other: zulipLocalizations.minutesAgo(differenceInMinutes),
173+
locale: zulipLocalizations.localeName,
174+
);
175+
} else if (differenceInDays == 0) {
176+
final time = DateFormat.jm(zulipLocalizations.localeName).format(date);
177+
return zulipLocalizations.todayAt(time);
178+
} else if (differenceInDays == 1) {
179+
final time = DateFormat.jm(zulipLocalizations.localeName).format(date);
180+
return zulipLocalizations.yesterdayAt(time);
181+
} else {
182+
final dateStr = DateFormat('MMM d, yyyy', zulipLocalizations.localeName).format(date);
183+
final timeStr = DateFormat('hh:mm a', zulipLocalizations.localeName).format(date);
184+
return zulipLocalizations.dateAtTime(dateStr, timeStr);
185+
}
186+
}
187+
188+
final timestampText = formatLocalizedTimestamp(
189+
DateTime.fromMillisecondsSinceEpoch(widget.message.timestamp * 1000),
190+
);
160191

161192
// We use plain [AppBar] instead of [ZulipAppBar], even though this page
162193
// has a [PerAccountStore], because:

0 commit comments

Comments
 (0)