import 'package:flutter/material.dart'; import 'datamodel.dart'; import 'sensorconnector.dart'; import 'databaseconnector.dart'; class ViewEvents extends StatefulWidget { const ViewEvents({Key? key}) : super(key: key); @override _ViewEvents createState() => _ViewEvents(); } class _ViewEvents extends State<ViewEvents> { // Get singleton to access locally stored events: final EventStoreInstance _events = EventStoreInstance(); String _status = ''; TextStyle _statusStyle = const TextStyle(color: Colors.black); String _syncButtonText = 'Start Sync'; String _appBarText = ''; int _selectedRow = -1; bool _cancelSync = false; //Flag to cancel sync process bool _currentlySyncing = false; // State flag to indicate if sync is ongoing var database = DatabaseInstance(); List<Event> _localEvents = []; Future<void> fetchEventsFromDb() async{ final EventStoreInstance event = EventStoreInstance(); int pendingEvents = 0; if (event.showAllEvents == false) { _appBarText = 'Pending Events'; //Get only pending events _localEvents = await database.getPendingEvents(); pendingEvents = _localEvents.length; }else { _appBarText = 'All Events'; _localEvents = await database.getEvents(); pendingEvents = await database.getPendingEventCnt(); } _status = pendingEvents.toString() + ' event(s) pending'; _statusStyle = const TextStyle(color: Colors.black); setState(() {}); //Got events from database, so update UI } @override void initState() { super.initState(); fetchEventsFromDb(); } @override void dispose() async { _cancelSync = true; /*Async update current configuration to shared preferences*/ final EventStoreInstance event = EventStoreInstance(); event.storeToSharedPrefs(); super.dispose(); debugPrint("View Events disposed"); } Future<void> syncEvents() async { _cancelSync = false; final ConfigurationStoreInstance configuration = ConfigurationStoreInstance(); SensorConnector connection = SensorConnector(); List<Event> events = await database.getPendingEvents(); int syncCounter = events.length; debugPrint('Pending Events'); for (var event in events){ debugPrint(event.toString()); } if(syncCounter > 0) { try { String? token = await connection.getAuthToken( configuration.loginInformation.mail, configuration.loginInformation.password); var index = 0; for (var event in events) { if(_cancelSync == true){ _currentlySyncing = false; _syncButtonText = 'Start Sync'; debugPrint('Sync process canceled'); return; } _currentlySyncing = true; _syncButtonText = 'Syncing ...'; if(mounted) { setState(() {}); } debugPrint('Idx: ' + index.toString() + ' ' + event.toSensorJson().toString()); index++; if (await connection.putEvent(event, token) == true) { syncCounter--; debugPrint('put success, now pending: ' + syncCounter.toString()); event.status = 'EXPORTED'; //Update event to export only once database.updateEvent(event); //Update Event as exported in SQL Database debugPrint('updated database'); ////Update view list TODO: this is bad for the sync performance! await fetchEventsFromDb(); if(mounted) { setState(() {}); } } else { throw Exception('Sync for ' + event.urn + 'failed'); } } _status = 'export success'; _statusStyle = const TextStyle(color: Colors.green); } catch (e) { String errorText = e.toString(); errorText = errorText.substring(10, errorText.length); //Remove 'Exception' from string. _status = errorText; _statusStyle = const TextStyle(color: Colors.red); if(mounted) { setState(() {}); } } }else{ debugPrint('No PENDING event(s)'); } _currentlySyncing = false; _syncButtonText = 'Start Sync'; if(mounted) { setState(() {}); } } @override Widget build(BuildContext context) { final EventStoreInstance event = EventStoreInstance(); return Scaffold( appBar: AppBar( title: const Text("View & Sync"), actions: <Widget>[ Text(_appBarText, style: const TextStyle(fontStyle: FontStyle.italic),), Switch( //Enable showing all or only pending events. Default is to show only pending events value: event.showAllEvents, onChanged: (value) { event.showAllEvents = value; fetchEventsFromDb(); setState(() {}); }), ], ), body: Container( margin: const EdgeInsets.symmetric(horizontal: 5.0), child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: SingleChildScrollView( scrollDirection: Axis.vertical, child: DataTable( columns: const <DataColumn>[ DataColumn( label: Text( '(ID) URN', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Label', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Type', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Description', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'StartDate', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'EndDate', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Latitude', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Longitude', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Elevation', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Status', style: TextStyle(fontStyle: FontStyle.italic), ), ), DataColumn( label: Text( 'Data', style: TextStyle(fontStyle: FontStyle.italic), ), ), ], rows: <DataRow>[ //for (var event in _localEvents) for (var i = 0; i < _localEvents.length; i++) DataRow( selected: i == _selectedRow, onLongPress: () { if(_currentlySyncing == true){ _cancelSync = true; return; //Cancel syncing before editing } debugPrint("Selected Row: " + i.toString()); var eventCopy = Event.fromEvent(_localEvents[i]); if(eventCopy.status == 'PENDING'){ Navigator.pushNamed(context, '/fifth', arguments: {'event': eventCopy} ).then((_) => setState(() {fetchEventsFromDb();})); }else { debugPrint('Can not edit already exported events.'); //TODO: display popup or grey out"Exported Events are read only, Edit via Sensor.awi.de" when trying to edit. } }, cells: <DataCell>[ //DataCell(Text(_localEvents[i].urnId.toString())), DataCell(Text('('+_localEvents[i].urnId.toString()+') ' + _localEvents[i].urn)), DataCell( Text(_localEvents[i].label), ), DataCell(Text(_localEvents[i].type)), DataCell(Text(_localEvents[i].description)), //Do not show microseconds DataCell(Text(_localEvents[i].startDate.substring(0, 19) + 'Z')), DataCell(Text(_localEvents[i].endDate.substring(0, 19) + 'Z')), DataCell(Text(_localEvents[i].latitude)), DataCell(Text(_localEvents[i].longitude)), DataCell(Text(_localEvents[i].elevation == '' ? '' : double.parse(_localEvents[i].elevation).toStringAsFixed(2))), DataCell(Text(_localEvents[i].status)), DataCell(Text(_localEvents[i].data)), ], ), ], ), ), ), ), bottomNavigationBar: Container( margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 5.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const SizedBox(width: 1), Text(_status, style: _statusStyle), FloatingActionButton.extended( heroTag: null, tooltip: 'Upload Events', icon: null, label: Text(_syncButtonText), onPressed: () { if(_currentlySyncing == false){ _currentlySyncing = true; syncEvents(); }else{ debugPrint('Sync already running, canceling sync'); _cancelSync = true; } }, ), ], ), ), ); } }