diff --git a/packages/neon/neon/lib/src/utils/relative_time.dart b/packages/neon/neon/lib/src/utils/relative_time.dart new file mode 100644 index 00000000..293667de --- /dev/null +++ b/packages/neon/neon/lib/src/utils/relative_time.dart @@ -0,0 +1,36 @@ +import 'package:meta/meta.dart'; + +/// Extension for formating the differnce between two [DateTime]s. +@internal +extension RelativeTimeFormat on DateTime { + /// Format the relative time between this and [to]. + /// + /// If unspecified [DateTime.now] will be used. + String formatRelative([final DateTime? to]) { + final now = to ?? DateTime.now(); + var diff = now.difference(toLocal()); + final text = StringBuffer(); + + // Sometimes something can be messed up... + if (diff.isNegative) { + if (diff.inMinutes >= 1) { + text.write('-'); + } + diff = Duration(microseconds: -diff.inMicroseconds); + } + + if (diff.inMinutes < 1) { + text.write('now'); + } else if (diff.inHours < 1) { + text.write('${diff.inMinutes}m'); + } else if (diff.inDays < 1) { + text.write('${diff.inHours}h'); + } else if (diff.inDays < 365) { + text.write('${diff.inDays}d'); + } else { + text.write('${diff.inDays ~/ 365}y'); + } + + return text.toString(); + } +} diff --git a/packages/neon/neon/lib/src/widgets/relative_time.dart b/packages/neon/neon/lib/src/widgets/relative_time.dart index 63837739..d4af7961 100644 --- a/packages/neon/neon/lib/src/widgets/relative_time.dart +++ b/packages/neon/neon/lib/src/widgets/relative_time.dart @@ -1,49 +1,52 @@ +import 'dart:async'; + import 'package:flutter/widgets.dart'; +import 'package:neon/src/utils/relative_time.dart'; -class RelativeTime extends StatelessWidget { +/// Shows the time elapsed since a [DateTime] and periodically updates itself. +class RelativeTime extends StatefulWidget { + /// Creates a new relative DateTime widget. const RelativeTime({ required this.date, this.style, super.key, }); + /// The timestamp to be displayed. final DateTime date; + + /// The text style of the calculated time. + /// + /// If not specified the nearest [TextStyle] will be used. final TextStyle? style; - static String format(final DateTime date) { - final now = DateTime.now(); - var diff = now.difference(date.toLocal()); - final text = StringBuffer(); - - // Sometimes something can be messed up... - if (diff.isNegative) { - if (diff.inMinutes >= 1) { - text.write('-'); - } - diff = Duration(microseconds: -diff.inMicroseconds); - } - - if (diff.inMinutes < 1) { - text.write('now'); - } else if (diff.inHours < 1) { - text.write('${diff.inMinutes}m'); - } else if (diff.inDays < 1) { - text.write('${diff.inHours}h'); - } else if (diff.inDays < 365) { - text.write('${diff.inDays}d'); - } else { - text.write('${diff.inDays ~/ 365}y'); - } - - return text.toString(); + @override + State createState() => _RelativeTimeState(); +} + +class _RelativeTimeState extends State { + late final Timer timer; + + @override + void initState() { + timer = Timer.periodic( + const Duration(minutes: 1), + (final _) => setState(() {}), + ); + + super.initState(); + } + + @override + void dispose() { + timer.cancel(); + + super.dispose(); } @override - Widget build(final BuildContext context) => StreamBuilder( - stream: Stream.periodic(const Duration(minutes: 1)), - builder: (final context, final _) => Text( - format(date), - style: style, - ), + Widget build(final BuildContext context) => Text( + widget.date.formatRelative(), + style: widget.style, ); }