POLISHING STUFF and Smarter AI, and alot i mean.. ALOT OF BUG FIX AND OPTIMIZE
This commit is contained in:
+152
-111
@@ -13,6 +13,9 @@ import '../../app/injection_container.dart';
|
||||
import '../../core/errors/friendly_error.dart';
|
||||
import '../../core/services/tts_service.dart';
|
||||
import '../../core/theme/app_colors.dart';
|
||||
import '../../core/theme/app_decorations.dart';
|
||||
import '../../core/theme/app_text_styles.dart';
|
||||
import '../../shared/widgets/animations/animations.dart';
|
||||
import 'application/notification_cubit.dart';
|
||||
import 'domain/entities/guardian_notification.dart';
|
||||
|
||||
@@ -124,84 +127,105 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
||||
builder: (context, state) {
|
||||
final items = state.items.map(_NotifItem.fromEntity).toList();
|
||||
final unreadCount = items.where((n) => !n.isRead).length;
|
||||
return SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Notifications',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineSmall
|
||||
?.copyWith(fontWeight: FontWeight.w800),
|
||||
),
|
||||
if (unreadCount > 0) ...[
|
||||
const SizedBox(width: 8),
|
||||
_UnreadBadge(count: unreadCount),
|
||||
],
|
||||
],
|
||||
),
|
||||
const Text(
|
||||
'Pesan dari Guardian kamu',
|
||||
style: TextStyle(color: AppColors.muted),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (unreadCount > 0)
|
||||
TextButton.icon(
|
||||
onPressed: state.markingAll ? null : _markAllRead,
|
||||
icon: state.markingAll
|
||||
? const SizedBox(
|
||||
width: 14,
|
||||
height: 14,
|
||||
child: CircularProgressIndicator(strokeWidth: 2))
|
||||
: const Icon(Icons.done_all, size: 18),
|
||||
label: const Text('Baca semua'),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: _load,
|
||||
icon: const Icon(Icons.refresh),
|
||||
tooltip: 'Refresh',
|
||||
),
|
||||
],
|
||||
return DecoratedBox(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [AppColors.softBlueBg, Colors.white],
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Body
|
||||
Expanded(
|
||||
child: state.loading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: state.error != null
|
||||
? _ErrorPanel(message: state.error!, onRetry: _load)
|
||||
: items.isEmpty
|
||||
? const _EmptyPanel()
|
||||
: RefreshIndicator(
|
||||
onRefresh: _load,
|
||||
child: ListView.separated(
|
||||
itemCount: items.length,
|
||||
separatorBuilder: (_, __) =>
|
||||
const SizedBox(height: 10),
|
||||
itemBuilder: (ctx, i) => _NotifCard(
|
||||
notif: items[i],
|
||||
onMarkRead: () => _markRead(items[i].id),
|
||||
onReadAloud: () => _readAloud(items[i]),
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: FadeSlideWrapper(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Header
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'Notifications',
|
||||
style: AppTextStyles.heading,
|
||||
),
|
||||
if (unreadCount > 0) ...[
|
||||
const SizedBox(width: 8),
|
||||
_UnreadBadge(count: unreadCount),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const Text(
|
||||
'Pesan dari Guardian kamu',
|
||||
style: TextStyle(color: AppColors.muted),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (unreadCount > 0)
|
||||
TextButton.icon(
|
||||
onPressed: state.markingAll ? null : _markAllRead,
|
||||
icon: state.markingAll
|
||||
? const SizedBox(
|
||||
width: 14,
|
||||
height: 14,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2))
|
||||
: const Icon(Icons.done_all, size: 18),
|
||||
label: const Text('Baca semua'),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: _load,
|
||||
icon: const Icon(Icons.refresh),
|
||||
tooltip: 'Refresh',
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Body
|
||||
Expanded(
|
||||
child: state.loading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: state.error != null
|
||||
? _ErrorPanel(
|
||||
message: state.error!, onRetry: _load)
|
||||
: items.isEmpty
|
||||
? const _EmptyPanel()
|
||||
: RefreshIndicator(
|
||||
onRefresh: _load,
|
||||
child: ListView(
|
||||
children: [
|
||||
StaggerWrapper(
|
||||
children: [
|
||||
for (final item in items)
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(
|
||||
bottom: 10),
|
||||
child: _NotifCard(
|
||||
notif: item,
|
||||
onMarkRead: () =>
|
||||
_markRead(item.id),
|
||||
onReadAloud: () =>
|
||||
_readAloud(item),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -271,7 +295,7 @@ class _NotifCard extends StatelessWidget {
|
||||
duration: const Duration(milliseconds: 300),
|
||||
decoration: BoxDecoration(
|
||||
color: unread ? const Color(0xFFEFF6FF) : Colors.white,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
borderRadius: AppDecorations.cardRadius,
|
||||
border: Border.all(
|
||||
color: unread
|
||||
? AppColors.primary.withValues(alpha: 0.3)
|
||||
@@ -280,9 +304,9 @@ class _NotifCard extends StatelessWidget {
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.04),
|
||||
blurRadius: 8,
|
||||
offset: const Offset(0, 2),
|
||||
color: Colors.black.withValues(alpha: 0.08),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -301,7 +325,7 @@ class _NotifCard extends StatelessWidget {
|
||||
color: isVoice
|
||||
? AppColors.success.withValues(alpha: 0.12)
|
||||
: AppColors.primary.withValues(alpha: 0.12),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
),
|
||||
child: Icon(
|
||||
isVoice ? Icons.mic : Icons.message,
|
||||
@@ -413,6 +437,7 @@ class _UnreadBadge extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary,
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
boxShadow: AppDecorations.cardShadow,
|
||||
),
|
||||
child: Text(
|
||||
'$count',
|
||||
@@ -431,21 +456,29 @@ class _ErrorPanel extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.wifi_off, size: 48, color: AppColors.muted),
|
||||
const SizedBox(height: 12),
|
||||
Text(message,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: AppColors.muted)),
|
||||
const SizedBox(height: 16),
|
||||
FilledButton.icon(
|
||||
onPressed: onRetry,
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Coba lagi'),
|
||||
),
|
||||
],
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.cardWhite,
|
||||
borderRadius: AppDecorations.cardRadius,
|
||||
boxShadow: AppDecorations.cardShadow,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.wifi_off, size: 48, color: AppColors.muted),
|
||||
const SizedBox(height: 12),
|
||||
Text(message,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: AppColors.muted)),
|
||||
const SizedBox(height: 16),
|
||||
FilledButton.icon(
|
||||
onPressed: onRetry,
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Coba lagi'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -456,21 +489,29 @@ class _EmptyPanel extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.notifications_none, size: 64, color: AppColors.muted),
|
||||
SizedBox(height: 12),
|
||||
Text('Belum ada notifikasi',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.muted)),
|
||||
SizedBox(height: 4),
|
||||
Text('Guardian belum mengirim pesan.',
|
||||
style: TextStyle(color: AppColors.muted)),
|
||||
],
|
||||
return Center(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.cardWhite,
|
||||
borderRadius: AppDecorations.cardRadius,
|
||||
boxShadow: AppDecorations.cardShadow,
|
||||
),
|
||||
child: const Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.notifications_none, size: 64, color: AppColors.muted),
|
||||
SizedBox(height: 12),
|
||||
Text('Belum ada notifikasi',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.muted)),
|
||||
SizedBox(height: 4),
|
||||
Text('Guardian belum mengirim pesan.',
|
||||
style: TextStyle(color: AppColors.muted)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user