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