💄 Optimize check in widget and add today's countdown
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
@@ -57,11 +58,21 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
final todayResult = ref.watch(checkInResultTodayProvider);
|
final todayResult = ref.watch(checkInResultTodayProvider);
|
||||||
final nextNotableDay = ref.watch(nextNotableDayProvider);
|
final nextNotableDay = ref.watch(nextNotableDayProvider);
|
||||||
|
|
||||||
|
// Update time every second for live progress
|
||||||
|
final currentTime = useState(DateTime.now());
|
||||||
|
useEffect(() {
|
||||||
|
final timer = Timer.periodic(const Duration(seconds: 1), (_) {
|
||||||
|
currentTime.value = DateTime.now();
|
||||||
|
});
|
||||||
|
return timer.cancel;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
final now = currentTime.value;
|
||||||
|
|
||||||
final userinfo = ref.watch(userInfoProvider);
|
final userinfo = ref.watch(userInfoProvider);
|
||||||
final isAdult = useMemoized(() {
|
final isAdult = useMemoized(() {
|
||||||
final birthday = userinfo.value?.profile.birthday;
|
final birthday = userinfo.value?.profile.birthday;
|
||||||
if (birthday == null) return false;
|
if (birthday == null) return false;
|
||||||
final now = DateTime.now();
|
|
||||||
final age =
|
final age =
|
||||||
now.year -
|
now.year -
|
||||||
birthday.year -
|
birthday.year -
|
||||||
@@ -72,6 +83,12 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
return age >= 18;
|
return age >= 18;
|
||||||
}, [userinfo]);
|
}, [userinfo]);
|
||||||
|
|
||||||
|
final progress = (now.hour * 60.0 + now.minute) / (24 * 60);
|
||||||
|
final endOfDay = DateTime(now.year, now.month, now.day, 23, 59, 59);
|
||||||
|
final timeLeft = endOfDay.difference(now);
|
||||||
|
final timeLeftFormatted =
|
||||||
|
'${timeLeft.inHours.toString().padLeft(2, '0')}:${(timeLeft.inMinutes % 60).toString().padLeft(2, '0')}:${(timeLeft.inSeconds % 60).toString().padLeft(2, '0')}';
|
||||||
|
|
||||||
Future<void> checkIn({String? captchatTk}) async {
|
Future<void> checkIn({String? captchatTk}) async {
|
||||||
final client = ref.read(apiClientProvider);
|
final client = ref.read(apiClientProvider);
|
||||||
try {
|
try {
|
||||||
@@ -119,37 +136,22 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
fill: 1,
|
fill: 1,
|
||||||
size: 16,
|
size: 16,
|
||||||
).padding(right: 2),
|
).padding(right: 2),
|
||||||
Text(DateFormat('EEE').format(DateTime.now()))
|
Text(
|
||||||
.fontSize(16)
|
DateFormat('EEE').format(DateTime.now()),
|
||||||
.bold()
|
).fontSize(16).bold(),
|
||||||
.textColor(
|
Text(
|
||||||
Theme.of(context).colorScheme.onSecondaryContainer,
|
DateFormat('MM/dd').format(DateTime.now()),
|
||||||
|
).fontSize(16),
|
||||||
|
Tooltip(
|
||||||
|
message: timeLeftFormatted,
|
||||||
|
child: SizedBox(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
value: progress,
|
||||||
|
strokeWidth: 2,
|
||||||
),
|
),
|
||||||
Text(DateFormat('MM/dd').format(DateTime.now()))
|
),
|
||||||
.fontSize(16)
|
|
||||||
.textColor(
|
|
||||||
Theme.of(context).colorScheme.onSecondaryContainer,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: AnimatedSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
child: todayResult.when(
|
|
||||||
data: (result) {
|
|
||||||
return Text(
|
|
||||||
result == null
|
|
||||||
? 'checkInNone'
|
|
||||||
: 'checkInResultLevel${result.level}',
|
|
||||||
textAlign: TextAlign.start,
|
|
||||||
).tr().fontSize(15).bold();
|
|
||||||
},
|
|
||||||
loading:
|
|
||||||
() =>
|
|
||||||
Text('checkInNone').tr().fontSize(15).bold(),
|
|
||||||
error:
|
|
||||||
(err, stack) =>
|
|
||||||
Text('error').tr().fontSize(15).bold(),
|
|
||||||
),
|
|
||||||
).alignment(Alignment.centerLeft),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -171,7 +173,7 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Gap(6),
|
const Gap(2),
|
||||||
AnimatedSwitcher(
|
AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: todayResult.when(
|
child: todayResult.when(
|
||||||
@@ -224,31 +226,57 @@ class CheckInWidget extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton.outlined(
|
Column(
|
||||||
onPressed: () {
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
if (todayResult.valueOrNull == null) {
|
spacing: 3,
|
||||||
checkIn();
|
children: [
|
||||||
} else {
|
AnimatedSwitcher(
|
||||||
context.pushNamed(
|
duration: const Duration(milliseconds: 300),
|
||||||
'accountCalendar',
|
child: todayResult.when(
|
||||||
pathParameters: {'name': 'me'},
|
data: (result) {
|
||||||
);
|
return Text(
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: AnimatedSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
child: todayResult.when(
|
|
||||||
data:
|
|
||||||
(result) => Icon(
|
|
||||||
result == null
|
result == null
|
||||||
? Symbols.local_fire_department
|
? 'checkInNone'
|
||||||
: Symbols.event,
|
: 'checkInResultLevel${result.level}',
|
||||||
key: ValueKey(result != null),
|
textAlign: TextAlign.start,
|
||||||
),
|
).tr().fontSize(15).bold();
|
||||||
loading: () => const Icon(Symbols.refresh),
|
},
|
||||||
error: (_, _) => const Icon(Symbols.error),
|
loading: () => Text('checkInNone').tr().fontSize(15).bold(),
|
||||||
|
error: (err, stack) => Text('error').tr().fontSize(15).bold(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
IconButton.outlined(
|
||||||
|
iconSize: 16,
|
||||||
|
visualDensity: const VisualDensity(
|
||||||
|
horizontal: -3,
|
||||||
|
vertical: -2,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
if (todayResult.valueOrNull == null) {
|
||||||
|
checkIn();
|
||||||
|
} else {
|
||||||
|
context.pushNamed(
|
||||||
|
'accountCalendar',
|
||||||
|
pathParameters: {'name': 'me'},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: todayResult.when(
|
||||||
|
data:
|
||||||
|
(result) => Icon(
|
||||||
|
result == null
|
||||||
|
? Symbols.local_fire_department
|
||||||
|
: Symbols.event,
|
||||||
|
key: ValueKey(result != null),
|
||||||
|
),
|
||||||
|
loading: () => const Icon(Symbols.refresh),
|
||||||
|
error: (_, _) => const Icon(Symbols.error),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 12),
|
).padding(horizontal: 16, vertical: 12),
|
||||||
|
Reference in New Issue
Block a user