LCOV - code coverage report
Current view: top level - _classes/herald - app_sync.dart Coverage Total Hit
Test: lcov.info Lines: 0.0 % 83 0
Test Date: 2026-11-14 15:03:01 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/storage/app_preferences.dart';
       5              : import 'package:flutter/foundation.dart';
       6              : import 'package:flutter/widgets.dart';
       7              : import 'package:peerdart/peerdart.dart';
       8              : import 'package:uuid/uuid.dart';
       9              : 
      10              : class SyncStatus {
      11              :   final String id;
      12              :   bool? status;
      13            0 :   SyncStatus(this.id, [this.status]);
      14              : }
      15              : 
      16              : class SyncPeer extends SyncStatus {
      17              :   DataConnection connection;
      18            0 :   SyncPeer(super.id, this.connection);
      19              : }
      20              : 
      21              : class AppSync extends ChangeNotifier {
      22              :   late Peer peer;
      23              :   final Map<String, SyncPeer> _status = {};
      24              :   final Map<Type, Function> _cb = {};
      25              :   final Map<Type, Function> _cbBin = {};
      26              : 
      27            0 :   AppSync() : super() {
      28            0 :     final id = getUuid();
      29            0 :     if (id != null && id.isNotEmpty) {
      30            0 :       enable(id);
      31            0 :       connect();
      32              :     }
      33              :   }
      34              : 
      35            0 :   List<String> _get() {
      36            0 :     return AppPreferences.get(AppPreferences.prefP2P)?.split(',').where((e) => e.isNotEmpty).toList() ?? [];
      37              :   }
      38              : 
      39            0 :   void add(String uuid) {
      40            0 :     List<String> peers = _get();
      41            0 :     peers.add(uuid);
      42            0 :     AppPreferences.set(AppPreferences.prefP2P, (<String>{}..addAll(peers)).toList().join(',')).then((_) => connect());
      43              :   }
      44              : 
      45            0 :   void del(String uuid) {
      46            0 :     _status[uuid]?.connection.dispose();
      47            0 :     _status.remove(uuid);
      48            0 :     AppPreferences.set(AppPreferences.prefP2P, (_get()..remove(uuid)).join(',')).then((_) => notifyListeners());
      49              :   }
      50              : 
      51            0 :   void connect() {
      52            0 :     List<String> peers = _get();
      53            0 :     for (int i = 0; i < peers.length; i++) {
      54            0 :       trace(peers[i]);
      55              :     }
      56              :   }
      57              : 
      58            0 :   void trace(String id) {
      59            0 :     WidgetsBinding.instance.addPostFrameCallback((_) {
      60            0 :       if (peer == null) {
      61              :         return;
      62              :       }
      63            0 :       if (!peer.open) {
      64            0 :         peer.reconnect();
      65              :       }
      66            0 :       if (_status[id] == null) {
      67            0 :         _status[id] = SyncPeer(id, peer.connect(id));
      68            0 :       } else if (!_status[id]!.connection.open) {
      69            0 :         peer.removeConnection(_status[id]!.connection);
      70            0 :         _status[id]!.connection = peer.connect(id);
      71              :       }
      72              :       // TBD: when connected, verify postponed messages
      73            0 :       _status[id]!.connection.once('open').then((v) {
      74            0 :         WidgetsBinding.instance.addPostFrameCallback((_) {
      75            0 :           _status[id]!.status = true;
      76            0 :           notifyListeners();
      77              :         });
      78              :       });
      79            0 :       _status[id]!.connection.once('close').then((v) {
      80            0 :         WidgetsBinding.instance.addPostFrameCallback((_) {
      81            0 :           _status[id]!.status = false;
      82            0 :           notifyListeners();
      83              :         });
      84              :       });
      85              :     });
      86              :   }
      87              : 
      88            0 :   List<SyncStatus> getPeers() {
      89            0 :     return _status.values.map((e) => SyncStatus(e.id, e.status)).toList();
      90              :   }
      91              : 
      92            0 :   void follow(Type type, Function callback) => _cb[type] = callback;
      93              : 
      94            0 :   void followBinary(Type type, Function callback) => _cbBin[type] = callback;
      95              : 
      96            0 :   void unfollow(Type type) {
      97            0 :     _cb.remove(type);
      98            0 :     _cbBin.remove(type);
      99              :   }
     100              : 
     101            0 :   bool isActive() {
     102            0 :     return AppPreferences.get(AppPreferences.prefPeer) != null;
     103              :   }
     104              : 
     105            0 :   void disable() {
     106            0 :     WidgetsBinding.instance.addPostFrameCallback((_) {
     107            0 :       AppPreferences.clear(AppPreferences.prefPeer);
     108            0 :       AppPreferences.clear(AppPreferences.prefP2P);
     109            0 :       peer.dispose();
     110              :     });
     111              :   }
     112              : 
     113            0 :   void enable([String? id]) {
     114            0 :     WidgetsBinding.instance.addPostFrameCallback((_) {
     115            0 :       id ??= getUuid(true);
     116            0 :       peer = Peer(id: id);
     117            0 :       peer.on<DataConnection>('connection').listen((conn) {
     118            0 :         WidgetsBinding.instance.addPostFrameCallback((_) => _listen(conn));
     119              :       });
     120              :     });
     121              :   }
     122              : 
     123            0 :   _listen(DataConnection conn) {
     124            0 :     conn.on('data').listen((data) {
     125            0 :       WidgetsBinding.instance.addPostFrameCallback((_) {
     126            0 :         _cb.forEach((_, callback) => callback(data));
     127              :       });
     128              :     });
     129            0 :     conn.on('binary').listen((data) {
     130            0 :       WidgetsBinding.instance.addPostFrameCallback((_) {
     131            0 :         _cbBin.forEach((_, callback) => callback(data));
     132              :       });
     133              :     });
     134              :   }
     135              : 
     136            0 :   send(String data, [String? uuid]) {
     137            0 :     WidgetsBinding.instance.addPostFrameCallback((_) {
     138              :       if (uuid != null) {
     139            0 :         if (_status[uuid]?.status == true) {
     140            0 :           _status[uuid]!.connection.send(data);
     141              :         }
     142              :       } else {
     143            0 :         final keyList = _status.keys.toList();
     144            0 :         for (int i = 0; i < keyList.length; i++) {
     145            0 :           if (_status[keyList[i]]?.status == true && _status[keyList[i]]!.connection.open) {
     146            0 :             _status[keyList[i]]!.connection.send(data);
     147              :           } else {
     148              :             // .. postpone message
     149              :           }
     150              :         }
     151              :       }
     152              :     });
     153              :   }
     154              : 
     155            0 :   ping(String uuid) {
     156            0 :     WidgetsBinding.instance.addPostFrameCallback((_) {
     157            0 :       if (_status[uuid]?.status == true) {
     158            0 :         _status[uuid]!.connection.sendBinary(Uint8List.fromList(getUuid()?.codeUnits ?? [0]));
     159              :       }
     160              :     });
     161              :   }
     162              : 
     163            0 :   String getNewUuid() {
     164            0 :     return const Uuid().v4();
     165              :   }
     166              : 
     167            0 :   String? getUuid([bool force = false]) {
     168            0 :     String? id = AppPreferences.get(AppPreferences.prefPeer);
     169              :     if (force) {
     170            0 :       AppPreferences.set(AppPreferences.prefPeer, id = getNewUuid());
     171              :     }
     172              :     return id;
     173              :   }
     174              : }
        

Generated by: LCOV version 2.0-1