Add app source and tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
69
lib/Peach/utils/CommonUtils.dart
Normal file
69
lib/Peach/utils/CommonUtils.dart
Normal file
@@ -0,0 +1,69 @@
|
||||
/// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user