70 lines
2.3 KiB
Dart
70 lines
2.3 KiB
Dart
/// Shared formatting helpers. Anything a screen would otherwise write inline
|
|
/// to render a value belongs here, so the same quantity reads the same way
|
|
/// everywhere in the app.
|
|
library;
|
|
|
|
/// Human-friendly "time ago" formatting.
|
|
String timeAgo(DateTime t) {
|
|
final d = DateTime.now().difference(t);
|
|
if (d.inSeconds < 45) return 'just now';
|
|
if (d.inMinutes < 60) return '${d.inMinutes}m ago';
|
|
if (d.inHours < 24) return '${d.inHours}h ago';
|
|
if (d.inDays < 7) return '${d.inDays}d ago';
|
|
String two(int n) => n.toString().padLeft(2, '0');
|
|
return '${t.year}-${two(t.month)}-${two(t.day)}';
|
|
}
|
|
|
|
/// Byte count as B / KB / MB / GB.
|
|
String formatBytes(int bytes) {
|
|
if (bytes < 1024) return '$bytes B';
|
|
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
|
|
if (bytes < 1024 * 1024 * 1024) {
|
|
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
|
|
}
|
|
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
|
|
}
|
|
|
|
/// An integer with thousands separators — offsets and message counts are
|
|
/// unreadable without them.
|
|
String formatCount(int value) {
|
|
final digits = value.abs().toString();
|
|
final out = StringBuffer(value < 0 ? '-' : '');
|
|
for (var i = 0; i < digits.length; i++) {
|
|
if (i > 0 && (digits.length - i) % 3 == 0) out.write(',');
|
|
out.write(digits[i]);
|
|
}
|
|
return out.toString();
|
|
}
|
|
|
|
/// `HH:MM` for a timestamp.
|
|
String formatClock(DateTime t) {
|
|
String two(int n) => n.toString().padLeft(2, '0');
|
|
return '${two(t.hour)}:${two(t.minute)}';
|
|
}
|
|
|
|
/// The last segment of a path, accepting either separator and ignoring a
|
|
/// trailing slash.
|
|
String baseName(String path) {
|
|
final normalised = path.replaceAll('\\', '/').replaceAll(RegExp(r'/+$'), '');
|
|
final i = normalised.lastIndexOf('/');
|
|
return i >= 0 ? normalised.substring(i + 1) : normalised;
|
|
}
|
|
|
|
/// Joins a POSIX directory and a child name without doubling the separator.
|
|
String joinRemote(String base, String name) {
|
|
final trimmed = base.endsWith('/')
|
|
? base.substring(0, base.length - 1)
|
|
: base;
|
|
return '$trimmed/$name';
|
|
}
|
|
|
|
/// The parent of a POSIX path, bottoming out at `/`.
|
|
String parentRemote(String path) {
|
|
final trimmed = path.endsWith('/') && path.length > 1
|
|
? path.substring(0, path.length - 1)
|
|
: path;
|
|
final i = trimmed.lastIndexOf('/');
|
|
if (i <= 0) return '/';
|
|
return trimmed.substring(0, i);
|
|
}
|