import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'main.dart' as Main; import 'newActivity.dart'; import 'Data.dart'; import 'User.dart' as User; class Activities extends StatefulWidget { const Activities({Key? key}) : super(key: key); @override _ActivitiesState createState() => _ActivitiesState(); } class _ActivitiesState extends State { //late ProgressDialog progressDialog; TextEditingController searchController = TextEditingController(); FocusNode _focus = FocusNode(); bool searching = false; void _onFocusChange() { print("FOCUS CHANGED! : ${_focus.hasFocus}"); if(!_focus.hasFocus){ searching=false; setState(() { }); } } var refreshSub; @override void initState() { // TODO: implement initState super.initState(); _focus.addListener(_onFocusChange); refreshSub = User.refreshStream.stream.listen((event) { if(!event){ setState(() { }); } }); //UpdateList(); //init(context); } @override void dispose() { // TODO: implement dispose super.dispose(); _focus.removeListener(_onFocusChange); _focus.dispose(); refreshSub?.closeStream(); } void UpdateList() async { try { //progressDialog.show(max: 100, msg: 'Loading Activities'); } catch (e) {} await User.refreshUserData(); setState(() {}); try { // progressDialog.update(value: 100); } catch (e) {} } @override Widget build(BuildContext context) { // progressDialog = ProgressDialog(context: context); List activities = PrintTasks(); return Scaffold( floatingActionButton: FloatingActionButton.extended( onPressed: () { Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewActivity())).then((value) => UpdateList()); }, label: Text("New Activity"), icon: Icon(Icons.add)), appBar: AppBar( toolbarHeight: (searching) ? 90 : null, title: (searching) ? Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( mainAxisSize: MainAxisSize.max, children: [ Expanded( child: TextField(onChanged: (text){setState(() { });},controller: searchController, focusNode: _focus, decoration: InputDecoration( filled: true, ),), ), InkWell( onTap: (){searching=false; searchController.clear(); setState(() { });}, child: Container( margin: EdgeInsets.all( 10), child: Icon(Icons.cancel), ), ) ], ), Text('searched time : ${Main.MinutesToTimeString(searchTime)}',style: TextStyle(fontSize: 15),) ], ) : Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row(children: [Icon(Icons.task, color: Theme.of(context).primaryColor), SizedBox(width: 10), Text('Activities')]), (selecting) ? Row(children: [ InkWell( onTap: () { DeleteSelectedTasks(); }, child: Container( margin: EdgeInsets.all(5), child: Icon( Icons.delete, size: 30, ), )), InkWell( onTap: () { setState(() { selecting = false; }); }, child: Container(margin:EdgeInsets.all(10),child: Icon(Icons.close, size: 30)), ) ]) : Row( children: [ InkWell( onTap: () { searching = true; _focus.requestFocus(); setState(() { }); }, child: Container( margin: EdgeInsets.all(10), child: Icon( Icons.search, size: 30, ), )), InkWell( onTap: () { UpdateList(); setState(() { }); }, child: Container(margin: EdgeInsets.all(10),child: Icon(Icons.refresh, size: 30)), ) ], ), ], )), drawer: Main.navDrawer(context, 2), body: Container( padding: EdgeInsets.all(0), child: ListView.builder( itemCount: activities.length, itemBuilder: (context, index){ return activities[index]; }) // SingleChildScrollView( // child: Column( // children: PrintTasks(), // )) )); } int searchTime = 0; List PrintTasks() { List _tasks = []; print('Priting cats : ' + User.taskTypes.length.toString()); String lastDate = ""; DateFormat dFormat = DateFormat("MM/dd"); Map productivtyActs = {}; Map unproductivtyActs = {}; Map totalMinutes = {}; for (var element in User.activities) { String thisDate = dFormat.format(element.startTime); int thisMinutes = element.endTime.difference(element.startTime).inMinutes; if (totalMinutes.containsKey(thisDate)) { if ((totalMinutes[thisDate] ?? 0) < thisMinutes) { totalMinutes[thisDate] = thisMinutes; } } else { totalMinutes.putIfAbsent(thisDate, () => thisMinutes); } if (element.taskType.cat?.productive ?? false) { if (productivtyActs.containsKey(thisDate)) { productivtyActs[thisDate] = (productivtyActs[thisDate]! + thisMinutes); } else { productivtyActs.putIfAbsent(thisDate, () => thisMinutes); } } else { if (unproductivtyActs.containsKey(thisDate)) { unproductivtyActs[thisDate] = (unproductivtyActs[thisDate]! + thisMinutes); } else { unproductivtyActs.putIfAbsent(thisDate, () => thisMinutes); } } } print(productivtyActs); //for (var element in User.activities) { searchTime=0; for(int i =0; i < User.activities.length; i++){ Activity element =User.activities[i]; if(searching){ bool matchMetadata = element.metadata!.toLowerCase().contains(searchController.text.toLowerCase()); bool matchTaskType=element.taskType.name.toLowerCase().contains(searchController.text.toLowerCase()); bool matchCategory = element.taskType.cat!.name.toLowerCase().contains(searchController.text.toLowerCase()); if(matchMetadata || matchTaskType || matchCategory){ //Good to go searchTime += element.endTime.difference(element.startTime).inMinutes; }else{ continue; } } String thisDate = dFormat.format(element.startTime); if (thisDate != lastDate) { int prodActs = productivtyActs[thisDate] ?? 0; int unProdActs = unproductivtyActs[thisDate] ?? 0; _tasks.add(DateSeperator(thisDate, prodActs, unProdActs)); lastDate = thisDate; } String name = element.taskType.name; if (element.taskType.cat == null) { print('Got some null cat : ${element.taskType.name}'); } else { Color color = Main.HexColor.fromHex(element.taskType.cat?.color ?? '#000000'); bool productive = element.taskType.cat?.productive ?? true; Widget task = ActivityCard(context, name, element.startTime, element.endTime, productive, color, element, totalMinutes[thisDate] ?? 0); // print('Activity : ${name} ,sTime: ${element.startTime}, eTime: ${element.endTime}'); _tasks.add(task); } //Check for gaps if(i < User.activities.length-1){ int gap = User.activities[i].trueStartTime.difference(User.activities[i+1].trueEndTime).inMinutes; if(gap > 10) { Widget addGap = timeGap(User.activities[i].trueStartTime, User.activities[i+1].trueEndTime); _tasks.add(addGap); } } } return _tasks; } Widget timeGap(DateTime sTime, DateTime eTime){ DateFormat dateFormat = DateFormat("HH:mm"); String gap = Main.MinutesToTimeString(sTime.difference(eTime).inMinutes); return Row( mainAxisSize: MainAxisSize.max, children: [ Container(padding: EdgeInsets.fromLTRB(10, 0, 5, 0), child: Column( mainAxisSize: MainAxisSize.max,mainAxisAlignment: MainAxisAlignment.center, children: [ // Text(dateFormat.format(activity.endTime)), SizedBox(width: 1, height: 30, child: Container(color: Colors.white)), Text(dateFormat.format(eTime))])), Expanded( child: InkWell( onTap: (){ Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewActivity(sTime: eTime,eTime: sTime))).then((value) => UpdateList()); }, child: Card( child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ Container( alignment: Alignment.bottomLeft, padding: const EdgeInsets.all(8), child: Text('Untracked period $gap'), ), Row( children: [ Icon(Icons.add,size:30), Text("Add Activity") ], ), ], ) ), ), ), ], ); } Widget DateSeperator(date, prodActs, unprodActs) { // double prodPercentage = (prodActs / (prodActs + unprodActs)) * 100; return FutureBuilder( future: Settings.getUntrackedUnproductive(), builder: (context, snapshot) { double prodPercentage = (User.ParseBool(snapshot.data))? ((prodActs / 1440) * 100) : ((prodActs / unprodActs) * 100); return Padding( padding: const EdgeInsets.fromLTRB(0, 20, 10, 0), child: Column( children: [ Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ SizedBox(width: 15,), Icon(Icons.circle), SizedBox( width: 10, ), Text( date, style: TextStyle(fontSize: 18), ), ], ), Row( children: [ Row( children: [ if(prodPercentage < 35)Text(Main.MinutesToTimeString(prodActs),), Container( child: Align( child: (prodPercentage >= 35) ?Text(Main.MinutesToTimeString(prodActs),) : Container(), alignment: Alignment.center, ), width: (prodPercentage) * 1, height: 20, decoration: BoxDecoration(color: Colors.green, borderRadius: BorderRadius.horizontal(left: Radius.circular(10))), ), Container( child: Align( child: (prodPercentage < 35) ?Text(Main.MinutesToTimeString(unprodActs)) :Container(), alignment: Alignment.center, ), width: (100 - prodPercentage) * 1, height: 20, decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.horizontal(right: Radius.circular(10))), ), if(prodPercentage >= 35)Text(Main.MinutesToTimeString(unprodActs)) ], ), SizedBox( width: 10, ), Text(prodPercentage.toStringAsFixed(1) + "%", style: TextStyle(color: Colors.green, fontWeight: FontWeight.bold, fontSize: 20)) ], ) // CustomPaint( // painter: MyPlayerBar(100, prodPercentage.toInt(), // background: Colors.green, fill: Colors.deepOrange), // child: Container( // alignment: Alignment.center, // height: 25.0, // width: 200, // child: Text( // "Productivity : ${prodPercentage.toStringAsFixed(1)}%", // style: TextStyle(fontWeight: FontWeight.bold),), // // ), // ), ], ) ], ), ); } ); } bool selecting = false; Widget ActivityCard(BuildContext context, String name, DateTime sTime, DateTime eTime, bool productive, Color color, Activity activity, int totalMinutes) { DateFormat dateFormat = DateFormat("HH:mm"); int thisMinutes = (activity.endTime.difference(activity.startTime).inMinutes); int timePercentage = ((thisMinutes / totalMinutes) * 100).toInt(); // print("$thisMinutes / $totalMinutes"); bool containsMetadata = ((activity.metadata ?? 'null') != 'null') && ((activity.metadata ?? '').isNotEmpty); var _timeSpan = eTime.difference(sTime); String timeSpan = ((_timeSpan.inHours > 0) ? _timeSpan.inHours.toString() + 'h ' : '') + ((_timeSpan.inMinutes % 60 > 0) ? (_timeSpan.inMinutes % 60).toString() + 'm' : ''); return Row(children: [ // Container(), (selecting) ? Checkbox( value: selectedActivities.contains(activity), onChanged: (value) { print('selected $name'); OnItemSelected(activity); setState(() {}); }) : Container(padding: EdgeInsets.fromLTRB(10, 0, 5, 0), child: Column( mainAxisSize: MainAxisSize.max,mainAxisAlignment: MainAxisAlignment.center, children: [ // Text(dateFormat.format(activity.endTime)), SizedBox(width: 1, height: 100, child: Container(color: Colors.white)), Text(dateFormat.format(activity.startTime)), ], )), Expanded( child: Column(children: [ Padding( padding: const EdgeInsets.fromLTRB(0, 0, 5, 0), child: Card( // color: color, elevation: 30, shadowColor: color, child: InkWell( onTap: () { //Open Respective Category if (selecting) { OnItemSelected(activity); } setState(() {}); }, onLongPress: () { print('gonna delete'); selecting = !selecting; selectedActivities = [activity]; setState(() {}); }, child: Container( padding: EdgeInsets.all(15), child: Column( children: [ Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, children: [ Text(name + " [$timeSpan]", style: TextStyle(fontSize: 17)), if (containsMetadata)Row(mainAxisAlignment:MainAxisAlignment.start,mainAxisSize: MainAxisSize.max,children: [ Icon( Icons.arrow_forward_outlined, size: 20, ), SizedBox( width: MediaQuery.of(context).size.width/3, child: Text( activity.metadata ?? '', ), ), ]), // Icon(Icons.analytics, color: color, size: 20,), ]), SizedBox( height: 5, ), Row(mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(dateFormat.format(sTime) + " - " + dateFormat.format(eTime)), SizedBox( width: 20, ), Row( children: [ (activity.taskType.relatedProject!= null) ?Container( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2), decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: Colors.black26), child: Text(activity.taskType.relatedProject!.name ?? 'n/a')) : Container(), SizedBox(width: 10,), Container( padding: EdgeInsets.symmetric(horizontal: 10, vertical: 2), decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: (productive) ? Colors.green : Colors.red), child: Text(activity.taskType.cat?.name ?? 'n/a')), ], ) // Icon(Icons.circle, // color: (productive) // ? Colors.green // : Colors.red) ]) ], )))), ), Container( margin: EdgeInsets.fromLTRB(15, 0, 15, 10), height: 2, child: Row( children: [Expanded(flex: timePercentage, child: Container(color: color)), Expanded(flex: 100 - timePercentage, child: Container())], )), ]), ), if(selecting)InkWell(child: Container(margin:EdgeInsets.all(10),child: Icon(Icons.edit)),onTap: (){ selecting=false; selectedActivities=[]; setState(() { }); Navigator.of(context).push(MaterialPageRoute(builder: (context) => NewActivity(sTime: activity.trueStartTime,eTime: activity.trueEndTime,metadata: activity.metadata,selectedTask: activity.taskType.name,))).then((value) => UpdateList()); },) ]); } void OnItemSelected(Activity activity) { if (!selectedActivities.contains(activity)) { selectedActivities.add(activity); } else { selectedActivities.remove(activity); } } void DeleteSelectedTasks() async { //progressDialog.show(max: 100, msg: 'Deleteing ${selectedActivities.length} Activities'); selectedActivities.forEach((element) async { await User.UserOperations.deleteActivity(element, bulk: true); }); await Future.delayed(Duration(seconds: 2)); await User.UserOperations.executeQueries(); await User.updateActList(); selectedActivities = []; selecting = false; setState(() { // progressDialog.update(value: 100); }); } } List selectedActivities = [];