9dfa06ffee
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
175 lines
5.6 KiB
Dart
175 lines
5.6 KiB
Dart
import 'dart:math' as math;
|
|
|
|
import 'package:intl/intl.dart';
|
|
|
|
import '../../shared/l10n/l10n.dart';
|
|
import 'channel.dart';
|
|
|
|
class EphemeralChannelDisplay {
|
|
final String? detailLabel;
|
|
final String tooltipLabel;
|
|
|
|
const EphemeralChannelDisplay({
|
|
required this.detailLabel,
|
|
required this.tooltipLabel,
|
|
});
|
|
}
|
|
|
|
bool isEphemeralChannel(Channel channel) =>
|
|
channel.ttlSeconds != null || channel.ttlDeadline != null;
|
|
|
|
EphemeralChannelDisplay? ephemeralChannelDisplay(
|
|
Channel channel, {
|
|
DateTime? now,
|
|
AppLocalizations? l10n,
|
|
}) {
|
|
if (!isEphemeralChannel(channel)) return null;
|
|
|
|
final deadline = channel.ttlDeadline;
|
|
final remainingSeconds = deadline == null
|
|
? null
|
|
: ((deadline.millisecondsSinceEpoch -
|
|
(now ?? DateTime.now()).millisecondsSinceEpoch) /
|
|
1000)
|
|
.ceil();
|
|
|
|
if (remainingSeconds == null) {
|
|
final ttlSeconds = channel.ttlSeconds;
|
|
return EphemeralChannelDisplay(
|
|
detailLabel: ttlSeconds == null
|
|
? null
|
|
: formatCompactTtl(ttlSeconds, l10n: l10n),
|
|
tooltipLabel: ttlSeconds == null
|
|
? l10n?.ephemeralAutoCleanup ??
|
|
'Ephemeral channel. Cleans up automatically after inactivity.'
|
|
: l10n?.ephemeralCleanupAfter(
|
|
formatVerboseTtl(ttlSeconds, l10n: l10n),
|
|
) ??
|
|
'Ephemeral channel. Cleans up after ${formatVerboseTtl(ttlSeconds)} of inactivity.',
|
|
);
|
|
}
|
|
|
|
final compactRemaining = formatCompactRemaining(
|
|
remainingSeconds,
|
|
l10n: l10n,
|
|
);
|
|
final verboseRemaining = formatVerboseRemaining(
|
|
remainingSeconds,
|
|
l10n: l10n,
|
|
);
|
|
final absoluteDeadlineLabel = formatAbsoluteDeadline(deadline!, l10n: l10n);
|
|
|
|
return EphemeralChannelDisplay(
|
|
detailLabel: compactRemaining,
|
|
tooltipLabel: remainingSeconds <= 0
|
|
? l10n?.ephemeralCleanupDueTooltip ??
|
|
'Ephemeral channel. Cleanup is due now.'
|
|
: l10n?.ephemeralScheduled(verboseRemaining, absoluteDeadlineLabel) ??
|
|
'Ephemeral channel. Cleans up $verboseRemaining. Scheduled for $absoluteDeadlineLabel.',
|
|
);
|
|
}
|
|
|
|
String formatCompactRemaining(
|
|
int remainingSeconds, {
|
|
AppLocalizations? l10n,
|
|
}) {
|
|
if (remainingSeconds <= 0) return l10n?.ephemeralCleanupDue ?? 'Cleanup due';
|
|
if (remainingSeconds <= 60) return l10n?.ephemeralOneMinuteLeft ?? '1m left';
|
|
if (remainingSeconds < 60 * 60) {
|
|
final minutes = math.max(1, (remainingSeconds / 60).ceil());
|
|
return l10n?.ephemeralMinutesLeft(minutes) ?? '${minutes}m left';
|
|
}
|
|
if (remainingSeconds < 60 * 60 * 24) {
|
|
final hours = math.max(1, (remainingSeconds / (60 * 60)).ceil());
|
|
return l10n?.ephemeralHoursLeft(hours) ?? '${hours}h left';
|
|
}
|
|
final days = math.max(1, (remainingSeconds / (60 * 60 * 24)).ceil());
|
|
return l10n?.ephemeralDaysLeft(days) ?? '${days}d left';
|
|
}
|
|
|
|
String formatVerboseRemaining(
|
|
int remainingSeconds, {
|
|
AppLocalizations? l10n,
|
|
}) {
|
|
if (remainingSeconds <= 0) return l10n?.timeNow ?? 'now';
|
|
if (remainingSeconds <= 60) {
|
|
return l10n?.ephemeralInOneMinute ?? 'in 1 minute';
|
|
}
|
|
if (remainingSeconds < 60 * 60) {
|
|
final minutes = math.max(1, (remainingSeconds / 60).ceil());
|
|
return l10n?.ephemeralInMinutes(minutes) ??
|
|
'in $minutes minute${minutes == 1 ? '' : 's'}';
|
|
}
|
|
if (remainingSeconds < 60 * 60 * 24) {
|
|
final hours = math.max(1, (remainingSeconds / (60 * 60)).ceil());
|
|
return l10n?.ephemeralInHours(hours) ??
|
|
'in $hours hour${hours == 1 ? '' : 's'}';
|
|
}
|
|
final days = math.max(1, (remainingSeconds / (60 * 60 * 24)).ceil());
|
|
return l10n?.ephemeralInDays(days) ??
|
|
'in $days day${days == 1 ? '' : 's'}';
|
|
}
|
|
|
|
String formatCompactTtl(int ttlSeconds, {AppLocalizations? l10n}) {
|
|
if (ttlSeconds < 60) {
|
|
final seconds = math.max(1, ttlSeconds);
|
|
return l10n?.ephemeralSeconds(seconds) ?? '${seconds}s TTL';
|
|
}
|
|
if (ttlSeconds < 60 * 60) {
|
|
final minutes = math.max(1, (ttlSeconds / 60).ceil());
|
|
return l10n?.ephemeralMinutes(minutes) ?? '${minutes}m TTL';
|
|
}
|
|
if (ttlSeconds < 60 * 60 * 24) {
|
|
final hours = math.max(1, (ttlSeconds / (60 * 60)).ceil());
|
|
return l10n?.ephemeralHours(hours) ?? '${hours}h TTL';
|
|
}
|
|
final days = math.max(1, (ttlSeconds / (60 * 60 * 24)).ceil());
|
|
return l10n?.ephemeralDays(days) ?? '${days}d TTL';
|
|
}
|
|
|
|
String formatVerboseTtl(int ttlSeconds, {AppLocalizations? l10n}) {
|
|
if (ttlSeconds < 60) {
|
|
final seconds = math.max(1, ttlSeconds);
|
|
return l10n?.ephemeralSeconds(seconds) ??
|
|
'$seconds second${seconds == 1 ? '' : 's'}';
|
|
}
|
|
if (ttlSeconds < 60 * 60) {
|
|
final minutes = math.max(1, (ttlSeconds / 60).ceil());
|
|
return l10n?.ephemeralMinutes(minutes) ??
|
|
'$minutes minute${minutes == 1 ? '' : 's'}';
|
|
}
|
|
if (ttlSeconds < 60 * 60 * 24) {
|
|
final hours = math.max(1, (ttlSeconds / (60 * 60)).ceil());
|
|
return l10n?.ephemeralHours(hours) ??
|
|
'$hours hour${hours == 1 ? '' : 's'}';
|
|
}
|
|
final days = math.max(1, (ttlSeconds / (60 * 60 * 24)).ceil());
|
|
return l10n?.ephemeralDays(days) ??
|
|
'$days day${days == 1 ? '' : 's'}';
|
|
}
|
|
|
|
String formatAbsoluteDeadline(DateTime deadline, {AppLocalizations? l10n}) {
|
|
if (l10n != null) {
|
|
return DateFormat.yMMMd(l10n.localeName).add_jm().format(deadline.toLocal());
|
|
}
|
|
const months = [
|
|
'Jan',
|
|
'Feb',
|
|
'Mar',
|
|
'Apr',
|
|
'May',
|
|
'Jun',
|
|
'Jul',
|
|
'Aug',
|
|
'Sep',
|
|
'Oct',
|
|
'Nov',
|
|
'Dec',
|
|
];
|
|
final local = deadline.toLocal();
|
|
final hour12 = local.hour % 12 == 0 ? 12 : local.hour % 12;
|
|
final minute = local.minute.toString().padLeft(2, '0');
|
|
final period = local.hour < 12 ? 'AM' : 'PM';
|
|
return '${months[local.month - 1]} ${local.day}, $hour12:$minute $period';
|
|
}
|