import 'dart:async'; import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/models/enums.dart'; import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/views/monthly/monthly_list_state.dart'; import 'package:didvan/views/widgets/date_picker_button.dart'; import 'package:didvan/views/widgets/didvan/checkbox.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/home_app_bar.dart'; import 'package:didvan/views/widgets/item_title.dart'; import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:provider/provider.dart'; class MonthlyListPage extends StatefulWidget { const MonthlyListPage({super.key}); @override State createState() => _MonthlyListPageState(); } class _MonthlyListPageState extends State { Timer? _timer; final _focusNode = FocusNode(); @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { final state = context.read(); state.initCategories(); state.fetchMonthlyList(); }); } void _onChanged(String value) { if (_timer != null) { _timer!.cancel(); } _timer = Timer(const Duration(milliseconds: 500), () { context.read().search(value); }); } Future _showFilterBottomSheet() async { final state = context.read(); await ActionSheetUtils(context).showBottomSheet( data: ActionSheetData( title: 'فیلتر جستجو', smallDismissButton: true, titleIcon: DidvanIcons.filter_regular, dismissTitle: 'حذف فیلتر', confrimTitle: 'نمایش نتایج', onDismissed: () => state.resetFilters(false), onConfirmed: () { state.filtering = state.startDate != null || state.endDate != null || state.selectedCats.isNotEmpty; state.fetchMonthlyList(); }, content: StatefulBuilder( builder: (context, setState) => Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ItemTitle( title: 'تاریخ ایجاد', style: Theme.of(context).textTheme.bodyMedium, icon: DidvanIcons.calendar_range_regular, ), const SizedBox(height: 8), Row( children: [ DatePickerButton( initialValue: state.startDate, emptyText: 'از تاریخ', onPicked: (date) => setState(() => state.startDate = date), lastDate: state.endDate, ), const SizedBox(width: 8), DatePickerButton( initialValue: state.endDate, emptyText: 'تا تاریخ', onPicked: (date) => setState(() => state.endDate = date), firstDate: state.startDate, ), ], ), const SizedBox(height: 28), if (state.categories.isNotEmpty) ...[ ItemTitle( title: 'دسته بندی', icon: DidvanIcons.category_regular, style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: 12), Wrap( children: [ for (var category in state.categories) SizedBox( width: (MediaQuery.of(context).size.width - 40) / 2, child: DidvanCheckbox( title: category.label, value: state.selectedCats .any((cat) => cat.id == category.id), onChanged: (value) { setState(() { if (value) { state.selectedCats.add(category); } else { state.selectedCats.removeWhere( (cat) => cat.id == category.id); } }); }, ), ), ], ), ], ], ), ), ), ); } @override void dispose() { _timer?.cancel(); _focusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final state = context.watch(); final theme = Theme.of(context); final colorScheme = theme.colorScheme; final isDark = theme.brightness == Brightness.dark; return DidvanScaffold( padding: EdgeInsets.zero, appBarData: null, slivers: [ SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(top: 12.0), child: HomeAppBar( showBackButton: false, showSearchField: state.appState != AppState.failed, onSearchChanged: _onChanged, onFilterPressed: _showFilterBottomSheet, searchFocusNode: _focusNode, isFiltered: state.filtering, searchValue: state.searchQuery, ), ), ), SliverAppBar( pinned: true, automaticallyImplyLeading: false, backgroundColor: colorScheme.surface, elevation: 0, centerTitle: false, titleSpacing: 16, title: Padding( padding: const EdgeInsets.all(8.0), child: Text( 'ماهنامه تحلیلی دیدوان', style: theme.textTheme.headlineSmall?.copyWith( color: isDark ? Colors.white : DesignConfig.isDark ? const Color.fromARGB(255, 0, 90, 119) : const Color.fromARGB(255, 0, 53, 70), fontWeight: FontWeight.bold, fontSize: 19), ), ), actions: [ IconButton( onPressed: () => Navigator.of(context).pop(), icon: SvgPicture.asset( 'lib/assets/icons/arrow-left.svg', width: 30, height: 30, colorFilter: ColorFilter.mode( isDark ? Colors.white70 : Theme.of(context).colorScheme.caption, BlendMode.srcIn), ), ), const SizedBox(width: 8), ], ), SliverPadding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), sliver: SliverStateHandler( onRetry: () => state.fetchMonthlyList(), state: state, childCount: state.monthlyItems.length, builder: (context, state, index) { final item = state.monthlyItems[index]; return TweenAnimationBuilder( tween: Tween(begin: 0.0, end: 1.0), duration: const Duration(milliseconds: 375), builder: (BuildContext context, double value, Widget? child) { return Opacity( opacity: value, child: Transform.translate( offset: Offset(0.0, 50.0 * (1.0 - value)), child: child, ), ); }, child: Padding( padding: const EdgeInsets.only(bottom: 12), child: GestureDetector( onTap: () { final pdfUrl = item.file; debugPrint('Opening PDF URL: $pdfUrl'); Navigator.of(context).pushNamed( Routes.pdfViewer, arguments: { 'pdfUrl': pdfUrl, 'title': item.title, }, ); }, child: Container( decoration: BoxDecoration( color: DesignConfig.isDark ? const Color.fromARGB(255, 36, 36, 36) : Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all( color: const Color.fromARGB(255, 184, 184, 184), ), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(8.0), child: ClipRRect( borderRadius: BorderRadius.circular(16), child: SkeletonImage( imageUrl: '${RequestHelper.baseUrl}${item.image}', width: 120, height: 120, borderRadius: BorderRadius.circular(16), ), ), ), Expanded( child: Padding( padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ DidvanText( item.title, style: TextStyle( color: DesignConfig.isDark ? Colors.white : Colors.black, fontWeight: FontWeight.bold, fontSize: 15, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 10), Row( children: [ SvgPicture.asset( 'lib/assets/icons/calendar.svg', width: 16, height: 16, ), const SizedBox(width: 5), Expanded( child: DidvanText( DateTime.parse(item.publishedAt) .toPersianDateStr(), style: const TextStyle( color: Color.fromARGB( 255, 102, 102, 102), fontSize: 12, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ), const SizedBox(height: 10), Row( children: [ SvgPicture.asset( 'lib/assets/icons/PDF_Page.svg', width: 16, height: 16, ), const SizedBox(width: 5), DidvanText( '${item.pageNumber} صفحه', style: const TextStyle( color: Color.fromARGB( 255, 102, 102, 102), fontSize: 12, ), ), ], ), const SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ // GestureDetector( // onTap: () { // context // .read() // .toggleLike(item.id); // }, // child: SvgPicture.asset( // item.isLiked // ? 'lib/assets/icons/heart_fill.svg' // : 'lib/assets/icons/heart2.svg', // width: 20, // height: 20, // colorFilter: ColorFilter.mode( // item.isLiked // ? const Color.fromARGB( // 255, 175, 4, 54) // : const Color.fromARGB( // 255, 102, 102, 102), // BlendMode.srcIn, // ), // ), // ), // const SizedBox(width: 16), GestureDetector( onTap: () { context .read() .toggleBookmark(item.id); }, child: SvgPicture.asset( item.isBookmarked ? 'lib/assets/icons/bookmark_fill.svg' : 'lib/assets/icons/archive-tick.svg', width: 20, height: 20, colorFilter: ColorFilter.mode( item.isBookmarked ? const Color.fromARGB( 255, 0, 126, 167) : const Color.fromARGB( 255, 102, 102, 102), BlendMode.srcIn, ), ), ), ], ), ], ), ), ), ], ), ), ), ), ); }, ), ), ], ); } }