58 lines
1.7 KiB
Dart
58 lines
1.7 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
|
|
import '../../../core/data/server_clock.dart';
|
|
|
|
/// 타임어택 남은 시간 — 초마다 다시 그린다. 마감은 서버 값(`quests.activeTo`)이라
|
|
/// 화면을 다시 열어도 처음부터 세지 않는다. 기준은 [ServerClock] 이다. 마감이 지나면
|
|
/// [builder] 가 null 을 받고 타이머를 멈춘다.
|
|
///
|
|
/// 표시용이다. 참여 수락/거부는 서버 시각으로 다시 판정한다(`fn-quest-timeattack`).
|
|
class TimeAttackCountdown extends StatefulWidget {
|
|
const TimeAttackCountdown({
|
|
super.key,
|
|
required this.endsAt,
|
|
required this.builder,
|
|
});
|
|
|
|
final DateTime endsAt;
|
|
final Widget Function(BuildContext context, Duration? left) builder;
|
|
|
|
@override
|
|
State<TimeAttackCountdown> createState() => _TimeAttackCountdownState();
|
|
}
|
|
|
|
class _TimeAttackCountdownState extends State<TimeAttackCountdown> {
|
|
late final Timer _timer;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_timer = Timer.periodic(const Duration(seconds: 1), (_) => setState(() {}));
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_timer.cancel();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final left = widget.endsAt.difference(ServerClock.now());
|
|
final closed = left.isNegative || left == Duration.zero;
|
|
if (closed) _timer.cancel();
|
|
return widget.builder(context, closed ? null : left);
|
|
}
|
|
}
|
|
|
|
/// `H:MM:SS`(1시간 이상) 또는 `MM:SS`.
|
|
String formatLeft(Duration left) {
|
|
String two(int n) => n.toString().padLeft(2, '0');
|
|
final h = left.inHours;
|
|
final m = two(left.inMinutes % 60);
|
|
final s = two(left.inSeconds % 60);
|
|
return h > 0 ? '$h:$m:$s' : '$m:$s';
|
|
}
|