518 lines
20 KiB
Dart
518 lines
20 KiB
Dart
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<Activities> {
|
|
//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<Widget> 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(
|
|
title: (searching)
|
|
? 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),
|
|
),
|
|
)
|
|
],
|
|
)
|
|
: 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(),
|
|
// ))
|
|
));
|
|
}
|
|
|
|
|
|
List<Widget> PrintTasks() {
|
|
List<Widget> _tasks = [];
|
|
print('Priting cats : ' + User.taskTypes.length.toString());
|
|
String lastDate = "";
|
|
DateFormat dFormat = DateFormat("MM/dd");
|
|
Map<String, int> productivtyActs = <String, int>{};
|
|
Map<String, int> unproductivtyActs = <String, int>{};
|
|
Map<String, int> totalMinutes = <String, int>{};
|
|
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) {
|
|
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
|
|
}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;
|
|
double prodPercentage = (prodActs / 1440) * 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,
|
|
),
|
|
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<Activity> selectedActivities = [];
|