LCOV - code coverage report
Current view: top level - lib/pages/manager - manager_page.dart Coverage Total Hit
Test: lcov.info Lines: 1.6 % 63 1
Test Date: 2026-11-14 15:08:11 Functions: - 0 0

            Line data    Source code
       1              : // Copyright 2026 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 'dart:math';
       5              : 
       6              : import 'package:app_finance/_classes/herald/app_locale.dart';
       7              : import 'package:app_finance/_classes/storage/transaction_log.dart';
       8              : import 'package:app_finance/_classes/structure/navigation/app_route.dart';
       9              : import 'package:app_finance/_configs/theme_helper.dart';
      10              : import 'package:app_finance/design/generic/loading_widget.dart';
      11              : import 'package:app_finance/design/wrapper/background_wrapper.dart';
      12              : import 'package:app_finance/design/wrapper/confirmation_wrapper.dart';
      13              : import 'package:app_finance/pages/_interfaces/abstract_page_state.dart';
      14              : import 'package:app_finance/pages/manager/widgets/log_entry.dart';
      15              : import 'package:app_finance/pages/manager/widgets/log_entry_widget.dart';
      16              : import 'package:material_ui/material_ui.dart';
      17              : 
      18              : class ManagerPage extends StatefulWidget {
      19            1 :   const ManagerPage({super.key});
      20              : 
      21            0 :   @override
      22            0 :   ManagerPageState createState() => ManagerPageState();
      23              : }
      24              : 
      25              : class ManagerPageState extends AbstractPageState<ManagerPage> {
      26              :   static const batch = 25;
      27              :   final ScrollController scrollController = ScrollController();
      28              :   final List<LogEntry> entries = [];
      29              :   int visible = batch;
      30              :   bool isLoading = true;
      31              :   bool isChanged = false;
      32              : 
      33            0 :   @override
      34              :   void initState() {
      35            0 :     super.initState();
      36            0 :     scrollController.addListener(_scrollListener);
      37            0 :     _load();
      38              :   }
      39              : 
      40            0 :   @override
      41              :   void dispose() {
      42            0 :     scrollController.removeListener(_scrollListener);
      43            0 :     scrollController.dispose();
      44            0 :     super.dispose();
      45              :   }
      46              : 
      47            0 :   Future<void> _load() async {
      48            0 :     final lines = <LogEntry>[];
      49              :     int index = 0;
      50            0 :     await for (final line in TransactionLog.read()) {
      51            0 :       if (line.trim().isNotEmpty) {
      52            0 :         lines.add(LogEntry(index, line));
      53              :       }
      54            0 :       index++;
      55              :     }
      56            0 :     if (!mounted) {
      57              :       return;
      58              :     }
      59            0 :     setState(() {
      60            0 :       entries.addAll(lines.reversed);
      61            0 :       isLoading = false;
      62              :     });
      63              :   }
      64              : 
      65            0 :   void _scrollListener() {
      66            0 :     if (scrollController.position.extentAfter < 200 && visible < entries.length) {
      67            0 :       setState(() => visible += batch);
      68              :     }
      69              :   }
      70              : 
      71            0 :   void _reorder(int oldIndex, int newIndex) {
      72            0 :     if (oldIndex == newIndex) {
      73              :       return;
      74              :     }
      75            0 :     setState(() {
      76            0 :       entries.insert(newIndex, entries.removeAt(oldIndex));
      77            0 :       isChanged = true;
      78              :     });
      79              :   }
      80              : 
      81            0 :   void _delete(LogEntry entry) {
      82            0 :     setState(() {
      83            0 :       entries.remove(entry);
      84            0 :       isChanged = true;
      85              :     });
      86              :   }
      87              : 
      88            0 :   Future<void> _save(NavigatorState nav) async {
      89            0 :     setState(() => isLoading = true);
      90            0 :     TransactionLog.rewrite(entries.reversed.map((e) => e.raw).toList());
      91            0 :     await super.state.flush();
      92            0 :     nav.pushNamedAndRemoveUntil(AppRoute.homeRoute, (_) => false);
      93              :   }
      94              : 
      95            0 :   @override
      96            0 :   String getTitle() => AppLocale.labels.managerHeadline;
      97              : 
      98            0 :   @override
      99            0 :   String getButtonName() => isChanged ? AppLocale.labels.saveSettingsTooltip : '';
     100              : 
     101            0 :   @override
     102              :   Widget buildButton(BuildContext context, BoxConstraints constraints) {
     103            0 :     if (!isChanged || isLoading) {
     104              :       return ThemeHelper.emptyBox;
     105              :     }
     106            0 :     final nav = Navigator.of(context);
     107            0 :     return FloatingActionButton(
     108              :       heroTag: 'manager_page',
     109            0 :       onPressed: () => ConfirmationWrapper.show(context, () => _save(nav)),
     110            0 :       tooltip: getButtonName(),
     111              :       child: const Icon(Icons.save),
     112              :     );
     113              :   }
     114              : 
     115            0 :   @override
     116              :   Widget buildContent(BuildContext context, BoxConstraints constraints) {
     117            0 :     if (isLoading) {
     118            0 :       return Center(child: LoadingWidget(isLoading: isLoading));
     119              :     }
     120            0 :     final count = min(visible, entries.length);
     121            0 :     return ReorderableListView.builder(
     122            0 :       scrollController: scrollController,
     123              :       buildDefaultDragHandles: false,
     124              :       itemCount: count,
     125            0 :       onReorderItem: _reorder,
     126            0 :       footer: visible < entries.length ? const Center(child: CircularProgressIndicator()) : ThemeHelper.formEndBox,
     127            0 :       itemBuilder: (context, index) {
     128            0 :         final entry = entries[index];
     129            0 :         return BackgroundWrapper(
     130            0 :           key: ValueKey(entry.index),
     131              :           index: index,
     132            0 :           child: LogEntryWidget(
     133              :             entry: entry,
     134              :             position: index,
     135            0 :             onToggle: () => setState(() => entry.isExpanded = !entry.isExpanded),
     136            0 :             onDelete: () => _delete(entry),
     137              :           ),
     138              :         );
     139              :       },
     140              :     );
     141              :   }
     142              : }
        

Generated by: LCOV version 2.0-1