From ca0fe7dd61a97a61f87f96ccd7b6012d7cc46bc1 Mon Sep 17 00:00:00 2001 From: Ryan Pandya Date: Sat, 3 Jun 2023 16:36:01 -0400 Subject: [PATCH] Realtime updating works! --- ltx_flutter/lib/app_properties_bloc.dart | 24 ++++ ltx_flutter/lib/appwrite/categories_api.dart | 4 +- ltx_flutter/lib/appwrite/database_api.dart | 51 +++++++-- ltx_flutter/lib/main.dart | 1 + ltx_flutter/lib/pages/tabs_page.dart | 29 ++++- ltx_flutter/lib/pages/today_page.dart | 111 +++++++++++++------ 6 files changed, 175 insertions(+), 45 deletions(-) create mode 100644 ltx_flutter/lib/app_properties_bloc.dart diff --git a/ltx_flutter/lib/app_properties_bloc.dart b/ltx_flutter/lib/app_properties_bloc.dart new file mode 100644 index 0000000..ebaa118 --- /dev/null +++ b/ltx_flutter/lib/app_properties_bloc.dart @@ -0,0 +1,24 @@ +import 'dart:async'; + +final appBloc = AppPropertiesBloc(); + +class AppPropertiesBloc { + StreamController _title = StreamController(); + StreamController _editable = StreamController(); + + Stream get titleStream => _title.stream; + Stream get editable => _editable.stream; + + updateTitle(String newTitle) { + _title.sink.add(newTitle); + } + + toggleEditable(bool editable) { + _editable.sink.add(!editable); + } + + dispose() { + _title.close(); + _editable.close(); + } +} diff --git a/ltx_flutter/lib/appwrite/categories_api.dart b/ltx_flutter/lib/appwrite/categories_api.dart index d13bad9..9a0fb93 100644 --- a/ltx_flutter/lib/appwrite/categories_api.dart +++ b/ltx_flutter/lib/appwrite/categories_api.dart @@ -50,7 +50,7 @@ class Category { } Color _getForegroundColor(String colorStr) { - return Color(int.parse("0xff$colorStr")).computeLuminance() > 0.5 + return Color(int.parse("0xff$colorStr")).computeLuminance() > 0.2 ? Colors.black : Colors.white; } @@ -99,7 +99,6 @@ class CategoriesAPI extends ChangeNotifier { if (!_ready) { return false; } - print(_ready); return Category(_categories.singleWhere((e) { return e.data['number'] == double.parse(n.toString()); })); @@ -174,7 +173,6 @@ class CategoriesAPI extends ChangeNotifier { 'parent': parentId, 'description': description, }); - print(x); return x; } catch (e) { print(e); diff --git a/ltx_flutter/lib/appwrite/database_api.dart b/ltx_flutter/lib/appwrite/database_api.dart index 4e1926c..e847ee3 100644 --- a/ltx_flutter/lib/appwrite/database_api.dart +++ b/ltx_flutter/lib/appwrite/database_api.dart @@ -4,11 +4,13 @@ import 'package:flutter/material.dart'; import 'package:ltx_flutter/appwrite/auth_api.dart'; import 'package:ltx_flutter/constants/constants.dart'; import 'package:intl/intl.dart'; +import 'package:ltx_flutter/constants/constants.dart'; class DatabaseAPI extends ChangeNotifier { Client client = Client(); late final Account account; late final Databases databases; + late final Realtime realtime; final AuthAPI auth = AuthAPI(); late List _entries = []; @@ -26,15 +28,52 @@ class DatabaseAPI extends ChangeNotifier { DatabaseAPI() { init(); getAll(); + subscribeRealtime(); } init() { - client - .setEndpoint(APPWRITE_URL) - .setProject(APPWRITE_PROJECT_ID) - .setSelfSigned(); + client.setEndpoint(APPWRITE_URL).setProject(APPWRITE_PROJECT_ID); account = Account(client); databases = Databases(client); + realtime = Realtime(client); + } + + elDate(dynamic el) { + return formatter.format(DateTime.parse(el.data['date'])); + } + + sortByDate(entries) { + return entries.sort( + (a, b) => a.$id.compareTo(b.$id), + ); + } + + subscribeRealtime() { + final subscription = realtime.subscribe( + ['databases.$APPWRITE_DATABASE_ID.collections.$COLLECTION.documents']); + + print("Subscribed to realtime"); + subscription.stream.listen((response) { + String dateISO = + formatter.format(DateTime.parse(response.payload['date'])); + if (response.events.contains( + 'databases.$APPWRITE_DATABASE_ID.collections.$COLLECTION.documents.*.update')) { + // Entry was updated + + int entryIndex = + _entries.indexWhere((element) => elDate(element) == dateISO); + Document newEntry = _entries[entryIndex]; + newEntry.data['hours'] = response.payload['hours']; + newEntry.data['mood'] = response.payload['mood']; + newEntry.data['comments'] = response.payload['comments']; + + _entries.removeAt(entryIndex); + _entries.add(newEntry); + notifyListeners(); + // _entries.add(entry); + // sortByDate(_entries); + } + }); } int total() { @@ -81,9 +120,7 @@ class DatabaseAPI extends ChangeNotifier { ); _entries.add(response.first); - _entries.sort( - (a, b) => a.$id.compareTo(b.$id), - ); + notifyListeners(); return response.first; } diff --git a/ltx_flutter/lib/main.dart b/ltx_flutter/lib/main.dart index c7e4a10..93125bf 100644 --- a/ltx_flutter/lib/main.dart +++ b/ltx_flutter/lib/main.dart @@ -25,6 +25,7 @@ class LifetrackerApp extends StatelessWidget { Widget build(BuildContext context) { final loginStatus = context.watch().status; return MaterialApp( + debugShowCheckedModeBanner: false, scrollBehavior: const MaterialScrollBehavior().copyWith(dragDevices: { PointerDeviceKind.mouse, PointerDeviceKind.touch, diff --git a/ltx_flutter/lib/pages/tabs_page.dart b/ltx_flutter/lib/pages/tabs_page.dart index 608f608..9ad730d 100644 --- a/ltx_flutter/lib/pages/tabs_page.dart +++ b/ltx_flutter/lib/pages/tabs_page.dart @@ -2,6 +2,7 @@ import 'package:ltx_flutter/pages/account_page.dart'; import 'package:ltx_flutter/pages/categories_page.dart'; import 'package:ltx_flutter/pages/today_page.dart'; import 'package:flutter/material.dart'; +import '../app_properties_bloc.dart'; class TabsPage extends StatefulWidget { const TabsPage({Key? key}) : super(key: key); @@ -25,7 +26,33 @@ class _TabsPageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Flutter"), + title: StreamBuilder( + stream: appBloc.titleStream, + initialData: "Flutter", + builder: (context, snapshot) { + return Text(snapshot.data.toString()); + }), + actions: [ + Padding( + padding: const EdgeInsets.only(right: 28.0), + child: Row( + children: [ + Text("Edit"), + StreamBuilder( + 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( diff --git a/ltx_flutter/lib/pages/today_page.dart b/ltx_flutter/lib/pages/today_page.dart index f1d8a0b..f6e15a8 100644 --- a/ltx_flutter/lib/pages/today_page.dart +++ b/ltx_flutter/lib/pages/today_page.dart @@ -25,7 +25,7 @@ String hourString(int e) { return "${hour.toString()} $meridien"; } -List generateHours(entry) { +List generateHours(entry, bool edit) { if (entry == null) { return [Center(child: RefreshProgressIndicator())]; } @@ -35,24 +35,34 @@ List generateHours(entry) { List reduced = []; int counter = 0; - 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 { + + 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, + }); } - counter++; } - print(reduced); return reduced.map( (e) { - double height = double.parse((e['num'] * 35).toString()); + double height = double.parse((e['num'] * 36).toString()); return Consumer( builder: (context, categories, child) { Category category = categories.lookUp(e['val'].toString()); @@ -63,10 +73,38 @@ List generateHours(entry) { child: Padding( padding: const EdgeInsets.only(left: 10), child: Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - style: TextStyle(color: category.foregroundColor), - hourString(e['hour'])), + 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( @@ -85,6 +123,26 @@ List generateHours(entry) { ).toList(); } +Color moodColor(mood) { + if (mood == null) { + return Colors.transparent; + } + if (mood >= 8) { + return Colors.green; + } + if (mood > 5) { + return Colors.blue; + } + if (mood == 5) { + return Colors.yellow; + } + if (mood >= 3) { + return Colors.amber; + } else { + return Colors.red; + } +} + class TodayPage extends StatefulWidget { const TodayPage({Key? key}) : super(key: key); @@ -175,7 +233,6 @@ class _DayViewState extends State { super.didChangeDependencies(); database = context.watch(); categories = context.watch(); - print(categories.lookUp(9)); entries = database.entries; } @@ -190,22 +247,6 @@ class _DayViewState extends State { } } - // Document? grabEntry(DateTime date) { - // DatabaseAPI database = context.watch(); - // entries = database.entries; - // String formattedDate = formatDate( - // dateISO: date.toIso8601String(), - // format: "yyyy-MM-dd", - // ); - - // try { - // dayEntry = entries.singleWhere((element) => element.$id == formattedDate); - // return dayEntry; - // } catch (e) { - // database.getOne(date: formattedDate).then((value) => dayEntry = value); - // } - // } - @override Widget build(BuildContext context) { List entries = database.entries; @@ -292,7 +333,7 @@ class _DayViewState extends State { Expanded( child: ListView( children: categories.ready - ? generateHours(dayEntry) + ? generateHours(dayEntry, true) : [CircularProgressIndicator()], ), ), @@ -304,7 +345,9 @@ class _DayViewState extends State { SizedBox.square( dimension: 50, child: Container( - color: Colors.amber, child: Center(child: moodWidget)), + color: moodColor(mood), + child: Center(child: moodWidget), + ), ), SizedBox(width: 30), Expanded(