Big update: can now update entries from flutter

This commit is contained in:
Ryan Pandya 2023-06-05 00:37:07 -04:00
parent ca0fe7dd61
commit 0ea14e9c02
4 changed files with 417 additions and 133 deletions

View File

@ -137,6 +137,49 @@ class DatabaseAPI extends ChangeNotifier {
return response.documents;
}
Future<Document> updateEntry(
{String? dateISO,
List<dynamic>? hours,
int? mood,
String? comments}) async {
String date = formatter.format(DateTime.parse(dateISO!));
int entryIndex = _entries.indexWhere((element) => elDate(element) == date);
hours ??= _entries[entryIndex].data['hours'];
comments ??= _entries[entryIndex].data['comments'];
mood ??= _entries[entryIndex].data['mood'];
Document newEntry = await databases.updateDocument(
databaseId: APPWRITE_DATABASE_ID,
collectionId: COLLECTION,
documentId: date,
data: {'hours': hours, 'mood': mood, 'comments': comments},
);
_entries.removeAt(entryIndex);
_entries.add(newEntry);
notifyListeners();
return newEntry;
}
updateHours(dayEntry, index, value) {
List<dynamic> hours = dayEntry.data['hours'];
try {
hours[index] = num.parse(value);
} catch (e) {
if (hours.length == index) {
hours.add(num.parse(value));
} else {
print(List.generate(index - hours.length, (i) => -1));
hours.addAll(List.generate(index - hours.length, (i) => -1));
hours.add(num.parse(value));
}
}
updateEntry(dateISO: dayEntry.data['date'], hours: hours);
}
Future<Document> addEntry(
{required String date,
List hours = const [],

View File

@ -32,27 +32,6 @@ class _TabsPageState extends State<TabsPage> {
builder: (context, snapshot) {
return Text(snapshot.data.toString());
}),
actions: [
Padding(
padding: const EdgeInsets.only(right: 28.0),
child: Row(
children: [
Text("Edit"),
StreamBuilder<Object>(
stream: appBloc.editable,
initialData: true,
builder: (context, snapshot) {
return Switch(
value: bool.parse(snapshot.data.toString()),
onChanged: (value) {
// print(value);
},
);
}),
],
),
)
],
),
body: _widgets.elementAt(_selectedIndex),
bottomNavigationBar: BottomNavigationBar(

View File

@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:intl/intl.dart';
import 'package:appwrite/models.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest.dart' as tz;
String formatDate({String format = "", String? dateISO}) {
final DateFormat dateFormatter = DateFormat(format);
@ -12,117 +14,35 @@ String formatDate({String format = "", String? dateISO}) {
}
String hourString(int e) {
try {
tz.getLocation('America/Los_Angeles');
} catch (e) {
tz.initializeTimeZones();
}
int pacificTime = tz.TZDateTime.now(tz.getLocation('America/Los_Angeles'))
.timeZoneOffset
.inHours;
int localTime = DateTime.now().timeZoneOffset.inHours;
e = e + localTime - pacificTime;
var meridien = "AM";
var hour = 12;
if (e > 12) {
if (e > 24) {
hour = e - 24;
} else if (e > 12) {
hour = e - 12;
} else if (e > 0) {
hour = e;
}
if (e > 11) {
if (e > 11 && e < 24) {
meridien = "PM";
}
return "${hour.toString()} $meridien";
}
List<Widget> generateHours(entry, bool edit) {
if (entry == null) {
return [Center(child: RefreshProgressIndicator())];
}
List<dynamic> hours = entry.data['hours'];
// print(hours);
List reduced = [];
int counter = 0;
if (edit) {
for (int val in hours) {
if (reduced.isEmpty) {
reduced.add({'val': val, 'num': 1, 'hour': counter});
} else {
if (reduced.last['val'] == val) {
reduced.last['num']++;
reduced.last['hour'] = counter;
} else {
reduced.add({'val': val, 'num': 1, 'hour': counter});
}
}
counter++;
}
} else {
for (int val in hours) {
reduced.add({
'hour': counter,
'val': val,
'num': 1,
});
}
}
return reduced.map(
(e) {
double height = double.parse((e['num'] * 36).toString());
return Consumer<CategoriesAPI>(
builder: (context, categories, child) {
Category category = categories.lookUp(e['val'].toString());
return SizedBox(
height: height,
child: Card(
color: category.backgroundColor,
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
e['num'] == 1
? Center(
child: Text(
style:
TextStyle(color: category.foregroundColor),
hourString(e['hour'])),
)
: Padding(
padding: const EdgeInsets.only(top: 10, bottom: 10),
child: Column(
children: [
Text(
style: TextStyle(
color: category.foregroundColor),
hourString(e['hour'] - e['num'] + 1)),
Expanded(
child: VerticalDivider(
indent: 10,
endIndent: 10,
color: category.foregroundColor,
width: 4,
),
),
Text(
style: TextStyle(
color: category.foregroundColor),
hourString(e['hour'] + 1))
],
),
),
Expanded(
child: Center(
child: Text(
style: TextStyle(color: category.foregroundColor),
category.name),
),
),
],
),
),
),
);
},
);
},
).toList();
}
Color moodColor(mood) {
if (mood == null) {
return Colors.transparent;
@ -228,6 +148,8 @@ class _DayViewState extends State<DayView> {
late num? mood = 0;
late String comments = "";
bool _editable = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
@ -277,8 +199,8 @@ class _DayViewState extends State<DayView> {
Widget moodWidget = mood == null
? Icon(
size: 30,
Icons.star_rate_outlined,
color: Colors.black,
Icons.star_outline,
color: Colors.white,
)
: Text(
style: TextStyle(fontSize: 20, color: Colors.black),
@ -290,7 +212,7 @@ class _DayViewState extends State<DayView> {
height: 20,
),
Padding(
padding: const EdgeInsets.all(8.0),
padding: const EdgeInsets.all(8),
child: GestureDetector(
onTap: () => _setDate(DateTime.now()),
onLongPress: () {
@ -326,16 +248,28 @@ class _DayViewState extends State<DayView> {
icon: Icon(Icons.arrow_right),
onPressed: () => _incrementDate(1),
),
Expanded(
child: SizedBox(width: 5),
),
Text("Edit"),
Switch(
value: _editable,
onChanged: (value) => setState(() {
_editable = !_editable!;
}),
),
],
),
),
),
Expanded(
child: ListView(
children: categories.ready
? generateHours(dayEntry, true)
: [CircularProgressIndicator()],
),
child: categories.ready
? HourGenerator(
dayEntry: dayEntry,
editable: _editable,
)
: ListView(
children: [Center(child: CircularProgressIndicator())]),
),
Padding(
padding: const EdgeInsets.only(top: 30, right: 30, left: 10),
@ -344,9 +278,45 @@ class _DayViewState extends State<DayView> {
SizedBox(width: 10),
SizedBox.square(
dimension: 50,
child: Container(
color: moodColor(mood),
child: Center(child: moodWidget),
child: GestureDetector(
onTapDown: (event) => showMenu(
initialValue: mood,
context: context,
position: RelativeRect.fromLTRB(
event.globalPosition.dx + 25,
event.globalPosition.dy - 50,
MediaQuery.of(context).size.width -
event.globalPosition.dx,
MediaQuery.of(context).size.height -
event.globalPosition.dy +
0,
),
items: List.generate(
10,
(index) => PopupMenuItem(
onTap: () => database.updateEntry(
dateISO: dayEntry!.data['date'], mood: index),
value: index,
child: Text(
style: TextStyle(
fontWeight: FontWeight.bold,
color: moodColor(index),
),
index.toString(),
),
),
),
),
child: mood == null
? Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.white)),
child: Center(child: moodWidget),
)
: Container(
color: moodColor(mood),
child: Center(child: moodWidget),
),
),
),
SizedBox(width: 30),
@ -356,12 +326,20 @@ class _DayViewState extends State<DayView> {
smartQuotesType: SmartQuotesType.enabled,
enableInteractiveSelection: true,
controller: commentsController,
minLines: 2,
maxLines: 4,
onFieldSubmitted: (value) {
database.updateEntry(
dateISO: dayEntry!.data['date'], comments: value);
},
),
),
SizedBox(width: 30),
IconButton(
tooltip: "Save",
onPressed: () => print("Save"),
onPressed: () => database.updateEntry(
dateISO: dayEntry!.data['date'],
comments: commentsController.value.text.toString()),
icon: Icon(Icons.save),
)
],
@ -372,3 +350,286 @@ class _DayViewState extends State<DayView> {
);
}
}
class HourGenerator extends StatefulWidget {
const HourGenerator(
{super.key, required this.dayEntry, required this.editable});
final Document? dayEntry;
final bool editable;
@override
State<HourGenerator> createState() => _HourGeneratorState();
}
class _HourGeneratorState extends State<HourGenerator> {
@override
Widget build(BuildContext context) {
List<Widget> generateHours(entry, bool edit) {
if (entry == null) {
return [Center(child: RefreshProgressIndicator())];
}
List<dynamic> hours = entry.data['hours'];
// print(hours);
List reduced = [];
int counter = 0;
if (!edit) {
for (int val in hours) {
if (reduced.isEmpty) {
reduced.add({'val': val, 'num': 1, 'hour': counter});
} else {
if (reduced.last['val'] == val) {
reduced.last['num']++;
reduced.last['hour'] = counter;
} else {
reduced.add({'val': val, 'num': 1, 'hour': counter});
}
}
counter++;
}
} else {
for (int val in hours) {
reduced.add({
'hour': counter,
'val': val,
'num': 1,
});
if (edit) counter++;
}
}
List<Widget> hourWidgets = reduced.map(
(e) {
double height = double.parse((e['num'] * 36).toString());
return Consumer<CategoriesAPI>(
builder: (context, categories, child) {
Category category = categories.lookUp(e['val'].toString());
return SizedBox(
height: height,
child: GestureDetector(
onTap: () {},
child: Card(
color: category.backgroundColor,
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
e['num'] == 1
? SizedBox(
width: 50,
child: Center(
child: Text(
style: TextStyle(
color: category.foregroundColor),
hourString(e['hour']),
),
),
)
: Padding(
padding: const EdgeInsets.only(
top: 10, bottom: 10),
child: Column(
children: [
Text(
style: TextStyle(
color: category.foregroundColor),
hourString(e['hour'] - e['num'] + 1)),
Expanded(
child: VerticalDivider(
indent: 2,
endIndent: 2,
color: category.foregroundColor,
thickness: 2,
),
),
Text(
style: TextStyle(
color: category.foregroundColor),
hourString(e['hour'] + 1))
],
),
),
Expanded(
child: Center(
child: edit
? HourFormField(
category: category,
index: e['hour'],
dayEntry: entry,
)
: Text(
style: TextStyle(
color: category.foregroundColor),
category.name),
),
),
],
),
),
),
),
);
},
);
},
).toList();
List<Consumer<CategoriesAPI>> remainingHours =
List.generate(24 - counter, (index) {
return Consumer<CategoriesAPI>(builder: (context, value, child) {
return SizedBox(
height: 35,
child: Card(
color: Colors.black,
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 50,
child: Center(
child: Text(
style: TextStyle(color: Colors.white),
hourString(counter + index)),
),
),
Expanded(
child: Center(
child: HourFormField(
index: index + counter,
dayEntry: entry,
),
),
),
],
),
),
),
);
});
});
List<Consumer<CategoriesAPI>> remainingHoursConsolidated = [
Consumer<CategoriesAPI>(
builder: (context, value, child) {
return SizedBox(
height: (24 - counter) * 30,
child: Card(
color: Colors.black,
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 10, bottom: 10),
child: Column(
children: [
Text(
style: TextStyle(color: Colors.white),
hourString(counter)),
Expanded(
child: VerticalDivider(
indent: 2,
endIndent: 2,
color: Colors.white,
thickness: 2,
),
),
Text(
style: TextStyle(color: Colors.white),
hourString(24))
],
),
),
Expanded(
child: Center(
child: Icon(Icons.question_mark),
),
),
],
),
),
),
);
},
)
];
edit || counter == 23
? hourWidgets.addAll(remainingHours)
: hourWidgets.addAll(remainingHoursConsolidated);
;
return hourWidgets;
}
return ListView(children: generateHours(widget.dayEntry, widget.editable));
}
}
class HourFormField extends StatefulWidget {
HourFormField({
super.key,
this.category,
required this.index,
required this.dayEntry,
});
final Category? category;
int index;
Document dayEntry;
@override
State<HourFormField> createState() => _HourFormFieldState();
}
class _HourFormFieldState extends State<HourFormField> {
Color fgColor = Colors.white;
String catNum = "";
String catName = "[Empty]";
@override
Widget build(BuildContext context) {
if (widget.category != null) {
catNum = widget.category!.number.toString();
fgColor = widget.category!.foregroundColor;
catName = widget.category!.name;
}
return Row(
children: [
SizedBox(
width: 135,
),
SizedBox(
width: 50,
child: Consumer<DatabaseAPI>(builder: (context, database, child) {
return TextFormField(
showCursor: false,
onFieldSubmitted: (value) {
database.updateHours(widget.dayEntry, widget.index, value);
},
style: TextStyle(
color: fgColor, fontFamily: "Monospace", height: 35),
textAlign: TextAlign.center,
keyboardType: TextInputType.numberWithOptions(
signed: false,
),
initialValue: catNum,
);
}),
),
SizedBox(width: 40),
Text(
style: TextStyle(color: fgColor, fontFamily: "Monospace"), catName),
],
);
}
}

View File

@ -19,6 +19,7 @@ dependencies:
pluto_grid: ^7.0.2
string_to_hex: ^0.2.2
flutter_form_builder: ^9.0.0
timezone: ^0.9.2
dev_dependencies:
flutter_test: