LCOV - code coverage report
Current view: top level - lib/pages/bill - bill_page.dart Coverage Total Hit
Test: lcov.info Lines: 97.8 % 91 89
Test Date: 2026-11-14 15:08:11 Functions: - 0 0

            Line data    Source code
       1              : // Copyright 2023 The terCAD team. All rights reserved.
       2              : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
       3              : 
       4              : import 'package:app_finance/_classes/controller/iterator_controller.dart';
       5              : import 'package:app_finance/_classes/herald/app_start_of_month.dart';
       6              : import 'package:app_finance/_classes/storage/app_data.dart';
       7              : import 'package:app_finance/_classes/herald/app_locale.dart';
       8              : import 'package:app_finance/_classes/structure/bill_app_data.dart';
       9              : import 'package:app_finance/_configs/screen_helper.dart';
      10              : import 'package:app_finance/_configs/theme_helper.dart';
      11              : import 'package:app_finance/_classes/structure/navigation/app_route.dart';
      12              : import 'package:app_finance/_ext/date_time_ext.dart';
      13              : import 'package:app_finance/design/wrapper/background_wrapper.dart';
      14              : import 'package:app_finance/pages/_interfaces/abstract_page_state.dart';
      15              : import 'package:app_finance/pages/bill/widgets/bill_line_widget.dart';
      16              : import 'package:app_finance/pages/bill/widgets/sliver_header_delegate.dart';
      17              : import 'package:app_finance/design/generic/base_header_widget.dart';
      18              : import 'package:app_finance/design/generic/base_swipe_widget.dart';
      19              : import 'package:material_ui/material_ui.dart';
      20              : 
      21              : class BillPage extends StatefulWidget {
      22            1 :   const BillPage({super.key});
      23              : 
      24            1 :   @override
      25            1 :   BillPageState createState() => BillPageState();
      26              : }
      27              : 
      28              : class BillPageState<T extends StatefulWidget> extends AbstractPageState<T> {
      29              :   InterfaceIterator? stream;
      30              :   List<Widget> itemsShown = [];
      31              :   DateTime timer = DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day);
      32              :   final _scrollController = ScrollController();
      33              :   final batch = 25;
      34              : 
      35            1 :   @override
      36              :   void initState() {
      37            1 :     super.initState();
      38            3 :     _scrollController.addListener(() {
      39            6 :       if (_scrollController.position.extentAfter < 200 && !stream!.isFinished) {
      40            0 :         setState(() => _addItems());
      41              :       }
      42              :     });
      43              :   }
      44              : 
      45            1 :   @override
      46              :   void dispose() {
      47            2 :     _scrollController.dispose();
      48            1 :     super.dispose();
      49              :   }
      50              : 
      51            1 :   Widget addHeaderWidget() {
      52            3 :     final width = ScreenHelper.state().width - ThemeHelper.getIndent(4);
      53            3 :     DateTime startingDay = DateTime.now().getStartingDay(AppStartOfMonth.get());
      54            1 :     return SliverToBoxAdapter(
      55            1 :       child: BaseHeaderWidget(
      56              :         route: AppRoute.homeRoute,
      57            2 :         tooltip: AppLocale.labels.homeTooltip,
      58              :         width: width,
      59            2 :         total: state.getTotal(AppDataType.bills),
      60            4 :         title: '${AppLocale.labels.billHeadline}, ${startingDay.fullMonth()}',
      61              :         searchRoute: AppRoute.billSearchRoute,
      62              :       ),
      63              :     );
      64              :   }
      65              : 
      66            1 :   Widget addLineWidget(dynamic item) {
      67            3 :     final width = ScreenHelper.state().width - ThemeHelper.getIndent(4);
      68            3 :     final account = state.getByUuid(item.account);
      69            3 :     final budget = state.getByUuid(item.category);
      70            2 :     final countWidth = ThemeHelper.getWidthCount(null, context);
      71            1 :     return BaseSwipeWidget(
      72              :       routePath: AppRoute.billEditRoute,
      73            1 :       uuid: item.uuid!,
      74            1 :       child: BillLineWidget(
      75            1 :         uuid: item.uuid!,
      76              :         count: countWidth,
      77            1 :         title: item.title,
      78            3 :         description: account != null ? '${account.title} (${account.description})' : '',
      79            1 :         descriptionColor: account?.color ?? Colors.transparent,
      80            1 :         details: item.detailsFormatted,
      81            1 :         progress: item.progress,
      82            1 :         color: budget?.color ?? Colors.transparent,
      83            1 :         icon: budget?.icon ?? Icons.radio_button_unchecked_sharp,
      84            1 :         iconTooltip: budget?.title ?? '?',
      85            1 :         hidden: item.hidden,
      86              :         width: width,
      87              :         route: AppRoute.billViewRoute,
      88              :       ),
      89              :     );
      90              :   }
      91              : 
      92            1 :   List<dynamic> getItems(DateTime timer) {
      93            4 :     return stream!.getTill(0.0 + timer.millisecondsSinceEpoch);
      94              :   }
      95              : 
      96            1 :   void _addItems() {
      97            2 :     if (itemsShown.isEmpty) {
      98            3 :       itemsShown.add(addHeaderWidget());
      99              :     }
     100            4 :     if (stream == null || stream?.isFinished == true) {
     101              :       return;
     102              :     }
     103              :     String marker = '';
     104            1 :     List<dynamic> items = [];
     105              :     do {
     106            2 :       marker = timer.yMEd();
     107            2 :       items = getItems(timer);
     108            3 :       timer = timer.add(const Duration(days: -1));
     109            1 :     } while (items.isEmpty && !stream!.isFinished);
     110              : 
     111            2 :     itemsShown.add(
     112            1 :       SliverMainAxisGroup(
     113            1 :         slivers: [
     114            1 :           SliverPersistentHeader(
     115              :             pinned: true,
     116            1 :             delegate: SliverHeaderDelegate(marker),
     117              :           ),
     118            1 :           SliverPadding(
     119            2 :             padding: EdgeInsets.symmetric(vertical: ThemeHelper.getIndent(0.5)),
     120            1 :             sliver: SliverList.builder(
     121            1 :               itemCount: items.length,
     122            1 :               itemBuilder: (context, int index) {
     123            1 :                 final item = items[index];
     124            1 :                 return BackgroundWrapper(
     125              :                   index: index,
     126            1 :                   child: addLineWidget(item),
     127              :                 );
     128              :               },
     129              :             ),
     130              :           ),
     131              :         ],
     132              :       ),
     133              :     );
     134            3 :     WidgetsBinding.instance.addPostFrameCallback((_) {
     135            4 :       if (_scrollController.position.maxScrollExtent == 0) {
     136            3 :         setState(() => _addItems());
     137              :       }
     138              :     });
     139              :   }
     140              : 
     141            1 :   @override
     142            2 :   String getTitle() => AppLocale.labels.billHeadline;
     143              : 
     144            1 :   @override
     145            2 :   String getButtonName() => AppLocale.labels.addMainTooltip;
     146              : 
     147            1 :   @override
     148              :   Widget buildButton(BuildContext context, BoxConstraints constraints) {
     149            1 :     NavigatorState nav = Navigator.of(context);
     150            1 :     return FloatingActionButton(
     151              :       heroTag: 'bill_page',
     152            0 :       onPressed: () => nav.pushNamed(AppRoute.billAddRoute),
     153            1 :       tooltip: getButtonName(),
     154              :       child: const Icon(Icons.add),
     155              :     );
     156              :   }
     157              : 
     158            3 :   InterfaceIterator getContentStream() => state.getStream<BillAppData>(AppDataType.bills);
     159              : 
     160            1 :   void clearState() {
     161            2 :     itemsShown = [];
     162            1 :     final now = DateTime.now();
     163            5 :     timer = DateTime(now.year, now.month, now.day);
     164            1 :     stream = null;
     165              :   }
     166              : 
     167            1 :   @override
     168              :   Widget buildContent(BuildContext context, BoxConstraints constraints) {
     169            3 :     state.addListener(clearState);
     170            1 :     if (stream == null) {
     171            2 :       WidgetsBinding.instance.addPostFrameCallback(
     172            3 :         (_) => setState(() {
     173            2 :           stream = getContentStream();
     174            1 :           _addItems();
     175              :         }),
     176              :       );
     177              :     }
     178            1 :     return Padding(
     179            2 :       padding: EdgeInsets.all(ThemeHelper.getIndent()),
     180            1 :       child: CustomScrollView(
     181            1 :         controller: _scrollController,
     182            1 :         slivers: itemsShown,
     183              :       ),
     184              :     );
     185              :   }
     186              : }
        

Generated by: LCOV version 2.0-1