From 1445c205c0a6ab9a5d43ede5e31dc79c4383f49a Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Sun, 24 Jan 2021 21:02:05 +0100 Subject: [PATCH 01/14] basic UI --- lib/view/page/sprints_details.dart | 252 +++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 lib/view/page/sprints_details.dart diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart new file mode 100644 index 0000000..d7ff641 --- /dev/null +++ b/lib/view/page/sprints_details.dart @@ -0,0 +1,252 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:idea_tracker/model/sprint.dart'; + +class SprintsDetails extends StatefulWidget { + + //final Sprint sprint; + //SprintsDetails(this.sprint) + + @override + _SprintsDetailsState createState() => _SprintsDetailsState(); +} + +class _SprintsDetailsState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: Icon(Icons.arrow_back), + tooltip: 'go to the previous page', + onPressed: () => Navigator.pop(context), + ), + actions: [ + IconButton( + icon: Icon(Icons.share), + tooltip: 'share this sprint', + onPressed: () => null, + ), + IconButton( + icon: Icon(Icons.delete), + tooltip: 'delete this sprint', + onPressed: () => null, + ), + IconButton( + icon: Icon(Icons.more_vert), + onPressed: () => null, + ) + ], + backgroundColor: Colors.black87, + ), + body: AnnotatedRegion( + value: SystemUiOverlayStyle.light, + child: GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Stack( + children: [ + Container( + height: double.infinity, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + ), + ), + Container( + height: double.infinity, + child: SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + padding: EdgeInsets.fromLTRB(0, 15.0, 0, 30.0), + child: Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Column( + children: [ + Row( + children: [ + Icon( + Icons.account_circle, + size: 15.0, + color: Colors.black.withOpacity(0.6), + ), + SizedBox(width: 10.0,), + Text( + 'DanielQuick', + style: TextStyle(color: Colors.black.withOpacity(0.6), fontSize: 12.0), + ), + ], + ), + SizedBox(height: 3.0,), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Idea tracker', + maxLines: 1, + style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold), + ), + SizedBox(width: 15.0,), + Text( + '- 24/01/2021', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ], + ), + SizedBox(height: 10.0,), + Row( + children: [ + Flexible( + child: Container( + width: 365, + child: Text( + 'Create an idea - Describe the idea, maybe under predefined requirements or templates - Resources for implementing the idea - Voting', + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ), + SizedBox(height: 10.0,), + Row( + children: [ + Icon(Icons.star_border , size: 18.0, color: Colors.black.withOpacity(0.6),), + SizedBox(width: 5.0,), + Text( + '69 Stars', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + SizedBox(width: 25.0,), + Icon(Icons.person_outline , size: 18.0, color: Colors.black.withOpacity(0.6),), + SizedBox(width: 5.0,), + Text( + '7 contributers', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ], + ), + Row( + children: [ + ButtonBar( + mainAxisSize: MainAxisSize.max, + children: [ + RaisedButton( + child: Row( + children: [ + Icon(Icons.star_border), + SizedBox(width: 5.0,), + Text('STAR') + ], + ), + onPressed: () {}, + color: Colors.white, + ), + RaisedButton( + child: Row( + children: [ + Icon(Icons.hail), + SizedBox(width: 5.0,), + Text('I\'M IN'), + ], + ), + onPressed: () {}, + color: Colors.white, + ), + ], + ), + ], + ), + ], + ), + ), + Divider( + height: 20.0, + color: Colors.grey, + ), + Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Column( + children: [ + Row( + children: [ + Text('Members', style: TextStyle(fontSize: 25.0),), + ], + ), + SizedBox(height: 10.0,), + ListTile( + leading: Icon(Icons.military_tech), + title: Text("DanielQuick"), + subtitle: Text("Teamleader"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("OsamaBinNaughty-hub"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("labody"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("xopherw"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("Abdul-Aziz"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("manuelvargastapia"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("taracell"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("DanielQuick"), + subtitle: Text("Contributer"), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ), + ); + } + Widget _memberCards(String contributer){ + return Card( + clipBehavior: Clip.antiAlias, + child: Column( + children: [ + ListTile( + leading: Icon(Icons.account_circle), + title: Text(contributer), + subtitle: Text( + 'Team leader: }', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ), + ], + ), + ); + } +} From a6a5c9da553b315b9bbc19ba0b8a4589d321521a Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Mon, 25 Jan 2021 21:38:22 +0100 Subject: [PATCH 02/14] SprintDetails is now generic --- lib/view/page/sprints_details.dart | 95 +++++++++--------------------- 1 file changed, 29 insertions(+), 66 deletions(-) diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index d7ff641..7ab3d03 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -4,10 +4,10 @@ import 'package:idea_tracker/model/sprint.dart'; class SprintsDetails extends StatefulWidget { - //final Sprint sprint; - //SprintsDetails(this.sprint) + final Sprint sprint; + SprintsDetails(this.sprint); - @override + @override _SprintsDetailsState createState() => _SprintsDetailsState(); } @@ -27,14 +27,12 @@ class _SprintsDetailsState extends State { tooltip: 'share this sprint', onPressed: () => null, ), - IconButton( - icon: Icon(Icons.delete), - tooltip: 'delete this sprint', - onPressed: () => null, - ), - IconButton( - icon: Icon(Icons.more_vert), - onPressed: () => null, + Padding( + padding: const EdgeInsets.only(left:8.0), + child: IconButton( + icon: Icon(Icons.more_vert), + onPressed: () => null, + ), ) ], backgroundColor: Colors.black87, @@ -76,7 +74,7 @@ class _SprintsDetailsState extends State { ), SizedBox(width: 10.0,), Text( - 'DanielQuick', + widget.sprint.teamLeader, style: TextStyle(color: Colors.black.withOpacity(0.6), fontSize: 12.0), ), ], @@ -86,13 +84,13 @@ class _SprintsDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( - 'Idea tracker', + widget.sprint.title, maxLines: 1, style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold), ), SizedBox(width: 15.0,), Text( - '- 24/01/2021', + '- ${widget.sprint.createdAt}', style: TextStyle(color: Colors.black.withOpacity(0.6)), ), ], @@ -104,7 +102,7 @@ class _SprintsDetailsState extends State { child: Container( width: 365, child: Text( - 'Create an idea - Describe the idea, maybe under predefined requirements or templates - Resources for implementing the idea - Voting', + widget.sprint.description, maxLines: 5, overflow: TextOverflow.ellipsis, ), @@ -118,6 +116,7 @@ class _SprintsDetailsState extends State { Icon(Icons.star_border , size: 18.0, color: Colors.black.withOpacity(0.6),), SizedBox(width: 5.0,), Text( + //TODO: make it work with button. '69 Stars', style: TextStyle(color: Colors.black.withOpacity(0.6)), ), @@ -125,7 +124,7 @@ class _SprintsDetailsState extends State { Icon(Icons.person_outline , size: 18.0, color: Colors.black.withOpacity(0.6),), SizedBox(width: 5.0,), Text( - '7 contributers', + '${widget.sprint.members.length} contributers', style: TextStyle(color: Colors.black.withOpacity(0.6)), ), ], @@ -180,43 +179,11 @@ class _SprintsDetailsState extends State { SizedBox(height: 10.0,), ListTile( leading: Icon(Icons.military_tech), - title: Text("DanielQuick"), + title: Text(widget.sprint.teamLeader), subtitle: Text("Teamleader"), ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("OsamaBinNaughty-hub"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("labody"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("xopherw"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("Abdul-Aziz"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("manuelvargastapia"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("taracell"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("DanielQuick"), - subtitle: Text("Contributer"), + Column( + children: _loadMembersOnListTiles(widget.sprint), ), ], ), @@ -232,21 +199,17 @@ class _SprintsDetailsState extends State { ), ); } - Widget _memberCards(String contributer){ - return Card( - clipBehavior: Clip.antiAlias, - child: Column( - children: [ - ListTile( - leading: Icon(Icons.account_circle), - title: Text(contributer), - subtitle: Text( - 'Team leader: }', - style: TextStyle(color: Colors.black.withOpacity(0.6)), - ), - ), - ], - ), + + List _loadMembersOnListTiles(Sprint sprint){ + List members = sprint.members; + return members.map((e) => _members(e)).toList(); + } + + Widget _members(String contributer){ + return ListTile( + leading: Icon(Icons.account_circle), + title: Text(contributer), + subtitle: Text("Contributer"), ); } } From 2a3e6fa7f5ffa6e15c07ee0354506082279baafe Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Tue, 26 Jan 2021 12:10:46 +0100 Subject: [PATCH 03/14] Connection SprintsPage & SprintDetails --- lib/view/page/sprints_details.dart | 74 ++++++++++++++++++++++++------ lib/view/page/sprints_page.dart | 60 +++++++++++++++++++++--- 2 files changed, 113 insertions(+), 21 deletions(-) diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index 7ab3d03..d743972 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -74,7 +74,7 @@ class _SprintsDetailsState extends State { ), SizedBox(width: 10.0,), Text( - widget.sprint.teamLeader, + _teamleader(widget.sprint), style: TextStyle(color: Colors.black.withOpacity(0.6), fontSize: 12.0), ), ], @@ -89,10 +89,7 @@ class _SprintsDetailsState extends State { style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold), ), SizedBox(width: 15.0,), - Text( - '- ${widget.sprint.createdAt}', - style: TextStyle(color: Colors.black.withOpacity(0.6)), - ), + _createdAt(widget.sprint), ], ), SizedBox(height: 10.0,), @@ -124,7 +121,7 @@ class _SprintsDetailsState extends State { Icon(Icons.person_outline , size: 18.0, color: Colors.black.withOpacity(0.6),), SizedBox(width: 5.0,), Text( - '${widget.sprint.members.length} contributers', + '${_numberOfContributers(widget.sprint.members, widget.sprint)} contributers', style: TextStyle(color: Colors.black.withOpacity(0.6)), ), ], @@ -177,14 +174,7 @@ class _SprintsDetailsState extends State { ], ), SizedBox(height: 10.0,), - ListTile( - leading: Icon(Icons.military_tech), - title: Text(widget.sprint.teamLeader), - subtitle: Text("Teamleader"), - ), - Column( - children: _loadMembersOnListTiles(widget.sprint), - ), + _membersListTile(widget.sprint), ], ), ), @@ -199,7 +189,34 @@ class _SprintsDetailsState extends State { ), ); } - + + Widget _membersListTile(Sprint sprint){ + if(sprint.members == null){ + return Row( + children: [ + Text( + 'There are no contributers', + style: TextStyle(color: Colors.black.withOpacity(0.6),), + ), + ], + ); + } else { + return Column( + children: [ + ListTile( + leading: Icon(Icons.military_tech), + title: Text(sprint.teamLeader), + subtitle: Text("Teamleader"), + ), + Column( + children: _loadMembersOnListTiles(sprint), + ), + + ], + ); + } + } + List _loadMembersOnListTiles(Sprint sprint){ List members = sprint.members; return members.map((e) => _members(e)).toList(); @@ -212,4 +229,31 @@ class _SprintsDetailsState extends State { subtitle: Text("Contributer"), ); } + + String _numberOfContributers(List members, Sprint sprint){ + if(sprint.members == null || sprint.members.length == 0){ + return '0'; + } else { + return '${sprint.members.length}'; + } + } + + String _teamleader(Sprint sprint){ + if(sprint.teamLeader == null){ + return 'There is no teamleader'; + } else { + return sprint.teamLeader; + } + } + + Widget _createdAt(Sprint sprint){ + if(sprint.createdAt == null){ + return null; + } else { + return Text( + '- ${sprint.createdAt}', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ); + } + } } diff --git a/lib/view/page/sprints_page.dart b/lib/view/page/sprints_page.dart index e1f4318..b6965e6 100644 --- a/lib/view/page/sprints_page.dart +++ b/lib/view/page/sprints_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:idea_tracker/controller/page/sprints_page_controller.dart'; import 'package:idea_tracker/model/sprint.dart'; +import 'package:idea_tracker/view/page/sprints_details.dart'; import 'package:idea_tracker/view/widget/state_management/base_view.dart'; class SprintsPage extends StatefulWidget { @@ -52,7 +53,6 @@ class _SprintsPageState extends State { ), Divider( height: 20.0, - color: Colors.grey, ), Column( @@ -75,12 +75,36 @@ class _SprintsPageState extends State { } List _loadActiveSprintsOnCard(List sprints, BuildContext ctx){ - return sprints - .map((e) => _cardmaker(e.title, e.teamLeader, e.description, ctx)).toList(); + if(sprints.isNotEmpty) { + return sprints + .map((e) => _cardmaker(e.title, e.teamLeader, e.description,e.members, ctx, e)) + .toList(); + } else { + return [ + Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Row( + children: [ + SizedBox(width: 15.0,), + Text( + 'There are no active sprints', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ], + ), + )]; + } } + String _numberOfContributers(List members, Sprint sprint){ + if(sprint.members == null || sprint.members.length == 0){ + return '0'; + } else { + return '${sprint.members.length}'; + } + } - Widget _cardmaker(String title, String teamleader, String description, BuildContext ctx){ + Widget _cardmaker(String title, String teamleader, String description, List members, BuildContext ctx, Sprint sprint){ return Card( clipBehavior: Clip.antiAlias, child: Column( @@ -89,7 +113,7 @@ class _SprintsPageState extends State { leading: Icon(Icons.run_circle), title: Text(title), subtitle: Text( - 'Team leader: ${teamleader}', + 'Team leader: ${_teamleader(sprint)}', style: TextStyle(color: Colors.black.withOpacity(0.6)), ), ), @@ -100,12 +124,28 @@ class _SprintsPageState extends State { style: TextStyle(color: Colors.black.withOpacity(0.6)), ), ), + Padding( + padding: const EdgeInsets.only(left:14.0), + child: Row( + children: [ + Icon(Icons.person_outline , size: 18.0, color: Colors.black.withOpacity(0.6),), + SizedBox(width: 5.0,), + Text( + '${_numberOfContributers(members, sprint)} contributers', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ], + ), + ), ButtonBar( alignment: MainAxisAlignment.start, children: [ FlatButton( onPressed: () { - // Perform some action + Navigator.push( + ctx, + MaterialPageRoute(builder: (context) => SprintsDetails(sprint)), + ); }, child: const Text('DETAILS'), ), @@ -115,4 +155,12 @@ class _SprintsPageState extends State { ), ); } + + String _teamleader(Sprint sprint){ + if(sprint.teamLeader == null){ + return ''; + } else { + return sprint.teamLeader; + } + } } From 871844d8b13b3eb4c45af778e83f1b84188c96f6 Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Tue, 26 Jan 2021 18:00:10 +0100 Subject: [PATCH 04/14] teamleader posts --- lib/view/page/sprints_details.dart | 118 ++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index d743972..19ff2c7 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -160,6 +160,16 @@ class _SprintsDetailsState extends State { ], ), ), + + // NOT A USED FEATURE!! IGNORE !! + + //Divider( + //height: 20.0, + //color: Colors.grey, + //), + //_latestTeamleaderPost(), + //SizedBox(height: 10.0,), + Divider( height: 20.0, color: Colors.grey, @@ -178,6 +188,14 @@ class _SprintsDetailsState extends State { ], ), ), + + // NOT A USED FEATURE!! IGNORE !! + + //Divider( + //height: 20.0, + //color: Colors.grey, + //), + //_teamleaderPosts(), ], ), ), @@ -211,7 +229,6 @@ class _SprintsDetailsState extends State { Column( children: _loadMembersOnListTiles(sprint), ), - ], ); } @@ -256,4 +273,103 @@ class _SprintsDetailsState extends State { ); } } + + Widget _teamleaderPost(String teamleader, String timeSincePosted, String post){ + return Padding( + padding: const EdgeInsets.only(right:15.0), + child: Card( + clipBehavior: Clip.antiAlias, + child: Padding( + padding: EdgeInsets.fromLTRB(10.0, 10.0, 0.0, 10.0), + child: Column( + children: [ + Row( + children: [ + Icon( + Icons.account_circle, + size: 40.0, + ), + Padding( + padding: const EdgeInsets.only(left: 15.0,), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + teamleader, + style: TextStyle( + fontSize: 20.0, + ), + ), + SizedBox(width: 10.0,), + Text( + '- ${timeSincePosted}', + style: TextStyle( + color: Colors.black.withOpacity(0.6), + ), + ), + ], + ), + Container( + width: 280, + child: Text( + post, + ), + ), + ], + ), + ), + ], + ), + ], + ), + ), + ), + ); + } + + // NOT A USED FEATURE !! IGNORE !! + Widget _latestTeamleaderPost(){ + return Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Column( + children: [ + Row( + children: [ + Text('Latest Teamleader Post', style: TextStyle(fontSize: 25.0),), + ], + ), + SizedBox(height: 10.0,), + _teamleaderPost( + 'DanielQuick', + '3d', + 'sup' + ), + ], + ), + ); + } + + // NOT A USED FEATURE !! IGNORE !! + Widget _teamleaderPosts(){ + return Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Column( + children: [ + Row( + children: [ + Text('Teamleader Post\'s', style: TextStyle(fontSize: 25.0),), + ], + ), + SizedBox(height: 10.0,), + _teamleaderPost('DanielQuick', '6d', 'hey'), + _teamleaderPost('DanielQuick', '7d', 'heyyyy'), + _teamleaderPost('DanielQuick', '8d', 'hallo'), + _teamleaderPost('DanielQuick', '9d', 'bonjour'), + ], + ), + ); + } + } From a20356ddae82aa27eca63a923999654d9e62b78b Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Tue, 26 Jan 2021 18:07:16 +0100 Subject: [PATCH 05/14] Revert "Merge branch 'develop' into sprints-details" This reverts commit 0b883e75bf909924997af5094e4126325fd45a5d, reversing changes made to 871844d8b13b3eb4c45af778e83f1b84188c96f6. --- ios/Podfile.lock | 8 +- ios/Runner.xcodeproj/project.pbxproj | 14 +- .../page/create_idea_page_controller.dart | 5 +- .../idea_edit_details_page_controller.dart | 19 +- lib/locator.dart | 7 +- lib/model/idea.dart | 38 +- lib/model/models.dart | 3 - lib/model/placeholder_model.dart | 1 + lib/model/sprint.dart | 17 - lib/model/user.dart | 25 +- lib/service/authentication_service.dart | 176 ++------ lib/service/firebase_service.dart | 17 - lib/service/idea_service.dart | 312 ++------------- lib/service/services.dart | 8 - lib/service/sprint_service.dart | 378 ++---------------- lib/service/user_service.dart | 138 +------ lib/view/dialog/confirm_logout_dialog.dart | 22 - lib/view/page/idea_details_page.dart | 2 +- lib/view/page/idea_edit_details_page.dart | 2 +- lib/view/page/main_page.dart | 4 +- lib/view/page/splash_page.dart | 42 +- pubspec.lock | 6 +- pubspec.yaml | 4 +- test/test_services.dart | 239 ----------- 24 files changed, 166 insertions(+), 1321 deletions(-) delete mode 100644 lib/model/models.dart create mode 100644 lib/model/placeholder_model.dart delete mode 100644 lib/service/firebase_service.dart delete mode 100644 lib/service/services.dart delete mode 100644 lib/view/dialog/confirm_logout_dialog.dart delete mode 100644 test/test_services.dart diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 20434b8..0af1b9b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -29,14 +29,14 @@ PODS: - Firebase/Analytics (= 7.3.0) - firebase_core - Flutter - - firebase_auth (0.20.0-1): + - firebase_auth (0.20.0): - Firebase/Auth (= 7.3.0) - firebase_core - Flutter - firebase_core (0.7.0): - Firebase/CoreOnly (= 7.3.0) - Flutter - - firebase_crashlytics (0.4.0-1): + - firebase_crashlytics (0.4.0): - Firebase/Crashlytics (= 7.3.0) - firebase_core - firebase_dynamic_links (0.7.0-1): @@ -182,9 +182,9 @@ SPEC CHECKSUMS: cloud_firestore: 433cf1aece50c727965355ae00e8dbeeceae66b1 Firebase: 26223c695fe322633274198cb19dca8cb7e54416 firebase_analytics: 41043f6e14fc9bc27c3f05217ab7f2b0ad9b41eb - firebase_auth: b55e5136528dced982022ee073ed4b48f15c055c + firebase_auth: 5439c15aabe321600b6bec4f83b5fec587a9c370 firebase_core: 91b27774a52f41f8b58484a75edf71197ac01c59 - firebase_crashlytics: e6ecb908f6249ce3757d18a812819a41495bf1cb + firebase_crashlytics: f510009fff758b0388061f55073611c6cba02ee0 firebase_dynamic_links: 14bf04008446c471b5c9f6e6542e14210bf26bd5 firebase_storage: 554b8ce9192bfe0bd35f63da42d0d1f6ae819ccc FirebaseAnalytics: 2580c2d62535ae7b644143d48941fcc239ea897a diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 361c44b..a876acd 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -10,7 +10,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 25068FB8297957B0140A6673 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5470DB219173EBFA0F995B72 /* Pods_Runner.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 571FA46825BB723300BC28C9 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 571FA46725BB723300BC28C9 /* GoogleService-Info.plist */; }; + 5706341D25AE2A4200B99F2A /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5706341C25AE2A4200B99F2A /* GoogleService-Info.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -36,7 +36,7 @@ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 5156ACB70525C5130F1A42EA /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 5470DB219173EBFA0F995B72 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 571FA46725BB723300BC28C9 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 5706341C25AE2A4200B99F2A /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -95,7 +95,7 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( - 571FA46725BB723300BC28C9 /* GoogleService-Info.plist */, + 5706341C25AE2A4200B99F2A /* GoogleService-Info.plist */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, @@ -194,7 +194,7 @@ files = ( 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 571FA46825BB723300BC28C9 /* GoogleService-Info.plist in Resources */, + 5706341D25AE2A4200B99F2A /* GoogleService-Info.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); @@ -441,6 +441,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -448,7 +449,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -465,7 +466,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -496,6 +497,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/lib/controller/page/create_idea_page_controller.dart b/lib/controller/page/create_idea_page_controller.dart index 2c64918..0189406 100644 --- a/lib/controller/page/create_idea_page_controller.dart +++ b/lib/controller/page/create_idea_page_controller.dart @@ -38,12 +38,13 @@ class CreateIdeaPageController extends ChangeNotifier { void createIdea() async { final idea = await _ideaService.create( - Idea( + idea: Idea( title: _ideaTitle, description: _ideaDescription, ), ); if (onIdeaCreated != null) onIdeaCreated("Success!"); - print(idea.toString()); + print( + "Idea created => id: ${idea.id} | title: ${idea.title} | description: ${idea.description} | createdAt: ${idea.createdAt} | updatedAt: ${idea.updatedAt} | votes: ${idea.votes}"); } } diff --git a/lib/controller/page/idea_edit_details_page_controller.dart b/lib/controller/page/idea_edit_details_page_controller.dart index 54d1535..e0ec4f5 100644 --- a/lib/controller/page/idea_edit_details_page_controller.dart +++ b/lib/controller/page/idea_edit_details_page_controller.dart @@ -12,8 +12,6 @@ class IdeaEditDetailsPageController extends ChangeNotifier { Idea get currentIdea => _currentIdea; - String get updateString => null; - set currentIdea(Idea idea) { _currentIdea = idea; } @@ -44,18 +42,11 @@ class IdeaEditDetailsPageController extends ChangeNotifier { return null; } - void saveTitleChange() async { - final idea = await _ideaService - .update(_currentIdea, [UpdateIdea.title], [updateString]); - if (onDataUpdated != null) onDataUpdated("Success!"); - print("Idea updated => ${idea.toString()}"); - } - - void saveDescriptionChange() async { - final idea = await _ideaService - .update(_currentIdea, [UpdateIdea.description], [updateString]); + void saveChanges() async { + final idea = await _ideaService.update(idea: _currentIdea); if (onDataUpdated != null) onDataUpdated("Success!"); - print("Idea updated => ${idea.toString()}"); + print( + "Idea updated => id: ${idea.id} | title: ${idea.title} | description: ${idea.description} | createdAt: ${idea.createdAt} | updatedAt: ${idea.updatedAt} | votes: ${idea.votes}"); } void openDeleteDialog() async { @@ -65,7 +56,7 @@ class IdeaEditDetailsPageController extends ChangeNotifier { // Check with "==" because "confirmDelete" will be "null" // if dialog is dismissed if (confirmDelete == true) { - await _ideaService.delete(_currentIdea); + await _ideaService.delete(idea: _currentIdea); print("Idea deleted"); if (onDataUpdated != null) onDataUpdated("Success!"); } diff --git a/lib/locator.dart b/lib/locator.dart index 54cded7..cff4cbc 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -5,8 +5,11 @@ import 'package:idea_tracker/controller/page/create_idea_page_controller.dart'; import 'package:idea_tracker/controller/page/idea_edit_details_page_controller.dart'; import 'package:idea_tracker/controller/page/ideas_main_page_controller.dart'; import 'package:idea_tracker/controller/page/main_page_controller.dart'; -import 'service/services.dart'; import 'package:idea_tracker/controller/page/sprints_page_controller.dart'; +import 'package:idea_tracker/service/authentication_service.dart'; +import 'package:idea_tracker/service/idea_service.dart'; +import 'package:idea_tracker/service/sprint_service.dart'; +import 'package:idea_tracker/service/user_service.dart'; GetIt locator = GetIt.instance; @@ -19,7 +22,7 @@ void setupLocator() { locator.registerFactory(() => LandingPageRecoverPasswordDialogController()); locator.registerFactory(() => SprintsPageController()); - /// Services + // Services locator.registerSingleton(IdeaService()); locator.registerSingleton(SprintService()); locator.registerSingleton(AuthenticationService()); diff --git a/lib/model/idea.dart b/lib/model/idea.dart index dff52e0..58aee27 100644 --- a/lib/model/idea.dart +++ b/lib/model/idea.dart @@ -2,60 +2,38 @@ class Idea { final String id; final String title; final String description; - final String creatorId; + final String userId; final int createdAt; final int updatedAt; - final int voteYes; - final int voteNo; - final List voters; + final List votes; Idea({ this.id, this.title, this.description, - this.creatorId, + this.userId, this.createdAt, this.updatedAt, - this.voteYes, - this.voteNo, - this.voters, + this.votes, }); Idea copyWith({ String id, String title, String description, - String creatorId, + String userId, int createdAt, int updatedAt, - int voteYes, - int voteNo, - List voters, + List votes, }) { return Idea( id: id ?? this.id, title: title ?? this.title, description: description ?? this.description, - creatorId: creatorId ?? this.creatorId, + userId: userId ?? this.userId, createdAt: createdAt ?? this.createdAt, updatedAt: updatedAt ?? this.updatedAt, - voteYes: voteYes ?? this.voteYes, - voteNo: voteNo ?? this.voteNo, - voters: voters ?? this.voters, + votes: votes ?? this.votes, ); } - - /// print the current stored idea to a string for debugging purposes - toString() { - return 'Idea: id: ${this.id}, ' - 'title: ${this.title}, ' - 'description: ${this.description}, ' - 'creatorId: ${this.creatorId}, ' - 'createdAt: ${this.createdAt}, ' - 'updatedAt: ${this.updatedAt}, ' - 'creatorId: ${this.creatorId}, ' - 'voteYes: ${this.voteYes}, ' - 'voteNo: ${this.voteNo}, ' - 'voters: ${this.voters}'; - } } diff --git a/lib/model/models.dart b/lib/model/models.dart deleted file mode 100644 index ef22c7c..0000000 --- a/lib/model/models.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'idea.dart'; -export 'sprint.dart'; -export 'user.dart'; \ No newline at end of file diff --git a/lib/model/placeholder_model.dart b/lib/model/placeholder_model.dart new file mode 100644 index 0000000..7e5cf10 --- /dev/null +++ b/lib/model/placeholder_model.dart @@ -0,0 +1 @@ +// Git does not track empty directories. After a new file exists in this directory, this file can safely be deleted. diff --git a/lib/model/sprint.dart b/lib/model/sprint.dart index 11b9b67..a062416 100644 --- a/lib/model/sprint.dart +++ b/lib/model/sprint.dart @@ -45,23 +45,6 @@ class Sprint { posts: posts ?? this.posts, ); } - - ///Convert a Sprint object to string for debug purposes - toString() { - String toString = 'Sprint: id: ${this.id}, ' - 'title: ${this.title}, ' - 'description: ${this.description}, ' - 'createdAt: ${this.createdAt}, ' - 'updatedAt: ${this.updatedAt}, ' - 'members: ${this.members}, ' - 'potentialLeaders: ${this.potentialLeaders}, ' - 'teamLeader: ${this.teamLeader}, ' - 'posts: '; - this.posts.forEach((sprintPost) => toString = toString + - 'SprintPost: ${sprintPost.id}, ${sprintPost.title}, ' - '${sprintPost.content}, ${sprintPost.createdAt}'); - return toString; - } } class SprintPost { diff --git a/lib/model/user.dart b/lib/model/user.dart index 02d0e73..8c33181 100644 --- a/lib/model/user.dart +++ b/lib/model/user.dart @@ -1,31 +1,16 @@ class User { - String id; - String email; - String userName; - String photoURL; + final String id; + final String username; User({ this.id, - this.email, - this.userName, - this.photoURL, + this.username, }); - User copyWith({String id, String email, String userName, String photoURL}) { + User copyWith({String id, String username}) { return User( id: id ?? this.id, - email: email ?? this.email, - userName: userName ?? this.userName, - photoURL: photoURL ?? this.photoURL ?? '_', + username: username ?? this.username, ); } - - @override - ///returns the user string - toString() { - return 'User: id: ${this.id ?? 'null'}, ' - 'email: ${this.email ?? 'null'}, ' - 'userName: ${this.userName ?? 'null'}, ' - 'photoURL: ${this.photoURL ?? 'null'}'; - } } diff --git a/lib/service/authentication_service.dart b/lib/service/authentication_service.dart index f221008..64f41c1 100644 --- a/lib/service/authentication_service.dart +++ b/lib/service/authentication_service.dart @@ -1,156 +1,32 @@ -import 'dart:core'; -import 'package:firebase_auth/firebase_auth.dart' as auth; -import 'package:flutter/cupertino.dart'; -import '../locator.dart'; -import '../model/user.dart'; -import 'services.dart'; +import 'package:idea_tracker/model/user.dart'; class AuthenticationService { - ///create variable instances for use with Firebase - auth.FirebaseAuth _auth; - UserService _userService; - - ///this initializes the class variables - initialize() { - _auth = auth.FirebaseAuth.instance; - _userService = locator(); - } - - ///Sign up with email/password - Future signUp( - String email, String userName, String password, String passwordVerify) async { - if (password == passwordVerify) { - try { - auth.UserCredential credential = await _auth - .createUserWithEmailAndPassword(email: email, password: password); - print('${credential.user.metadata}'); - User user = (User( - id: credential.user.uid, - email: email, - userName: userName, - photoURL: '_')); - _userService.getUser(user); - print('credential: ${credential.user.uid}'); - debugPrint('Signed Up'); - return 'Signed Up'; - } on auth.FirebaseAuthException catch (e) { - if (e.code == 'weak-password') { - debugPrint('The password provided is too weak.'); - return 'The password provided is too weak.'; - } else if (e.code == 'email-already-in-use') { - debugPrint('The account already exists for that email.'); - return 'The account already exists for that email.'; - } else { - debugPrint('Something went wrong, please try again.'); - return 'Something went wrong, please try again.'; - } - } catch (e) { - print(e); - } - } - return 'Passwords do not Match'; - } - - ///Sign in user with e-mail - Future signIn(String email, String password) async { - try { - auth.UserCredential credential = await _auth.signInWithEmailAndPassword( - email: email, password: password); - await new Future.delayed(const Duration(microseconds: 5)); - User user = (User( - id: credential.user.uid, - email: email, - userName: email, - photoURL: '_')); - print('credential: ${credential.user.uid}'); - _userService.getUser(user); - await new Future.delayed(const Duration(microseconds: 5)); - debugPrint('Signed In'); - return 'Signed In'; - } on auth.FirebaseAuthException catch (e) { - if (e.code == 'weak-password') { - debugPrint('The password provided is too weak.'); - return 'The password provided is too weak.'; - } else if (e.code == 'email-already-in-use') { - debugPrint('The account already exists for that email.'); - return 'The account already exists for that email.'; - } else { - debugPrint('Something went wrong, please try again.'); - return 'Something went wrong, please try again.'; - } - } catch (e) { - print(e); - } - return null; - } - - ///sign out + User _signedInUser; + User get signedInUser => _signedInUser; + + /// Sign in user + /// + /// Returns the [User] model if successful + Future signIn({ + String username, + String password, + }) async { + _signedInUser = User( + id: "id", + username: "username", + ); + return _signedInUser; + } + + /// Sign out [_signedInUser] Future signOut() async { - await _auth.signOut(); - debugPrint('Signed Out'); - } - - ///returns the current authenticated user as User object - User authenticatedUser() { - User _authenticatedUser; - auth.User user = _auth.currentUser; - if (user == null) { - _authenticatedUser = null; - print('User is currently signed out!'); - return _authenticatedUser; - } else { - _authenticatedUser = User( - id: user.uid,); - new Future.delayed(const Duration(microseconds: 20)); - _authenticatedUser = _userService.getUser(_authenticatedUser); - print('2. User from _authenticatedUser function: '+ _authenticatedUser.toString()); - print('User is signed in!'); - return _authenticatedUser; - } - } - - ///return - bool isSignedIn(){ - auth.User user = _auth.currentUser; - if (user == null) { - return false; - } else { - return true; - } - } - - ///password verification - bool verifyPassword(String password, String passwordVerify) { - if (password == passwordVerify) { - return true; - } else { - return false; - } - } - - ///enables send user a password reset by e-mail...Link to forgot password logic - Future forgotPassword(String email) async { - await _auth.sendPasswordResetEmail(email: email); + _signedInUser = null; } - ///enable a change password from within app - Future changePassword(String password, String passwordVerify) async { - ///Create an instance of the current user. - auth.User user = _auth.currentUser; - if (password == passwordVerify) { - ///Pass in the password to updatePassword. - user.updatePassword(password).then((_) { - debugPrint('Your password changed Successfully!'); - return 'Your password changed Successfully!'; - }).catchError((err) { - ///This might happen, when the wrong password is in, the user isn't found, or if the user hasn't logged in recently. - debugPrint("You can't change the Password" + err.toString()); - return "You can't change the Password" + err.toString(); - }); - } else { - debugPrint('Your passwords do not match, please try again.'); - return 'Your passwords do not match, please try again.'; - } - return null; - } + /// Sign up new user + Future signUp({ + String username, + String password, + String confirmPassword, + }) async {} } diff --git a/lib/service/firebase_service.dart b/lib/service/firebase_service.dart deleted file mode 100644 index 702410d..0000000 --- a/lib/service/firebase_service.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_crashlytics/firebase_crashlytics.dart'; -import 'package:flutter/cupertino.dart'; - -setupFirebase() async { - ///initalize Firebase - await Firebase.initializeApp(); - debugPrint('Firebase initialized...'); - - /// Enable FirebaseCrashlytics - await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true); - debugPrint('Firebase Crashlytics collection enabled...'); - - /// Pass all uncaught errors from the framework to Crashlytics. - FlutterError.onError = await FirebaseCrashlytics.instance.recordFlutterError; - debugPrint('Firebase Crashlytics flutter errors enabled...'); -} diff --git a/lib/service/idea_service.dart b/lib/service/idea_service.dart index f9492b6..b30e017 100644 --- a/lib/service/idea_service.dart +++ b/lib/service/idea_service.dart @@ -1,289 +1,47 @@ -import 'package:flutter/cupertino.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:idea_tracker/service/services.dart'; import 'package:idea_tracker/model/idea.dart'; -import 'package:idea_tracker/model/user.dart'; - -import '../locator.dart'; - -///for this service class, users of this class do not have to worry about the updatedAt -///or createdAt variables within the Idea object - -///Use this enum to update parts of the Idea Object -enum UpdateIdea { title, description, vote } class IdeaService { - ///create variable instances for use - CollectionReference _ideaRef; - - ///this initializes the class - initialize() { - _ideaRef = FirebaseFirestore.instance.collection("ideas"); + /// Create [idea] + Future create({Idea idea}) async { + return idea.copyWith( + createdAt: DateTime.now().millisecondsSinceEpoch, + updatedAt: DateTime.now().millisecondsSinceEpoch, + ); } - /// adds the idea to the database and returns that idea - Future create(Idea idea) async { - DocumentReference docReference = _ideaRef.doc(); - await _ideaRef - .doc(docReference.id) - .set(await _toJson(idea, docReference.id)); - idea = idea.copyWith( - id: docReference.id, createdAt: DateTime.now().millisecondsSinceEpoch); - debugPrint('Added idea id: ${idea.id} to DB'); - return idea; - } - - ///gets user from Authentication Service - User _getUser() { - AuthenticationService _auth = locator(); - User user = _auth.authenticatedUser(); - print(user.id); - return user; - } - - Future update( - Idea idea, List updates, List updateStrings) async { - for (var i = 0; i < updates.length; i++) { - idea = await _update(idea, updates[i], updateStrings[i]); - } - - return idea; - } + /// Delete [idea] + Future delete({Idea idea}) async {} - ///Switch to update idea object...this uses the Enum UpdateIdea at the top of - ///this class. - ///use case: _ideaService.update(idea, Update.title, "new Title'); - ///returns the updated idea - Future _update( - Idea idea, UpdateIdea update, String updateString) async { - User user = _getUser(); - Idea updatedIdea = idea; - switch (update) { - case UpdateIdea.title: - { - if (user.id == idea.creatorId) { - updatedIdea = await _updateTitle(idea, updateString); - } - return updatedIdea; - } - break; - case UpdateIdea.description: - { - if (user.id == idea.creatorId) { - updatedIdea = await _updateDescription(idea, updateString); - } - return updatedIdea; - } - break; - case UpdateIdea.vote: - { - ///checks to see if user already voted - if (!updatedIdea.voters.contains(user.id)) { - ///adds logged in user to voter list - updatedIdea = await _updateVoters(idea); - if (updateString.toLowerCase() == 'yes') { - updatedIdea = await _updateVoteYes(idea); - } else if (updateString.toLowerCase() == 'no') { - updatedIdea = await _updateVoteNo(idea); - } else { - print('Vote must be yes or no'); - } - } else { - print('User ${user.id} has already voted for this idea.'); - } - return updatedIdea; - } - break; - default: - { - print('Nothing was updated, please try again.'); - updatedIdea = await get(idea.id); - return updatedIdea; - } - } + /// Update [idea] + Future update({Idea idea}) async { + return idea.copyWith(updatedAt: DateTime.now().millisecondsSinceEpoch); } - /// The idea here is to get a document ID from a Stream of multiple documents - /// then store it to the current _idea for use of display and updates for Idea Services. + /// Return Idea with [id] if available /// - /// To get the document ID to pass to this function: - /// 1. StreamBuilder -> store as AsyncSnapshot snapshot using - /// getAllIdeasFromDBStream(), getIdeasFromDBForCurrentMonthStream(), or searchIdeasByTitle() - /// 2. ListView -> children: snapshot.data.docs.map((document) widgets for each document) - /// 3. access a specific idea's details "onTapped" with this method passing "document.id" - /// to this method - Future get(String documentId) async { - Idea idea; - await _ideaRef.doc(documentId).get().then((document) { - if (document.exists) { - idea = _fromFirestore(document); - } else { - print('Document does not exist on the database'); - } - }); - print(idea.toString()); - return idea; - } - - /// returns all ideas that were created for the current month as Future List + /// Returns [null] if none found + Future get({String id}) async { + return Idea( + id: "id", + title: "title", + description: "Description", + createdAt: 0, + updatedAt: 0, + ); + } + + /// Return all ideas created this month Future> getAll() async { - DateTime dateTime = DateTime.now(); - Duration days = new Duration(days: dateTime.day - 1); - debugPrint('getAll() performing...'); - QuerySnapshot querySnapshot = await _ideaRef - .where('createdAt', - isGreaterThanOrEqualTo: - dateTime.subtract(days).millisecondsSinceEpoch) - .get(); - return querySnapshot.docs.map((doc) => _fromFirestore(doc)).toList(); - } - - /// removes idea from the database and returns that idea - Future delete(Idea idea) async { - await _ideaRef.doc(idea.id).delete(); - debugPrint('removed idea ${idea.id} from DB'); - idea = null; - } - - ///used to input the idea into the database. - ///This is only used the first time an idea is input into the database - Future> _toJson(Idea idea, String id) async { - User user = _getUser(); - return { - "id": id, - "title": idea.title, - "titleArray": idea.title.toLowerCase().split(new RegExp('\\s+')).toList(), - "creatorId": user.id, - "description": idea.description, - "createdAt": idea.createdAt ?? DateTime.now().millisecondsSinceEpoch, - "updatedAt": idea.updatedAt ?? DateTime.now().millisecondsSinceEpoch, - "voteYes": idea.voteYes ?? 0, - "voteNo": idea.voteNo ?? 0, - "voters": idea.voters ?? new List(), - }; - } - - ///Create Idea object from Firestore DocumentSnapshot - Idea _fromFirestore(DocumentSnapshot doc) { - Idea idea = new Idea( - id: doc.id, - title: doc.data()["title"], - description: doc.data()["description"], - creatorId: doc.data()["creatorId"], - createdAt: doc.data()["createdAt"], - updatedAt: doc.data()["updatedAt"], - voteYes: doc.data()["voteYes"], - voteNo: doc.data()["voteNo"], - voters: doc.data()["voters"].cast()); - return idea; - } - - /// updates _idea udpatedAt in the database and updates _idea with that update - /// is called when each field is updated - Future _updateUpdatedAt(Idea idea) async { - await _ideaRef - .doc(idea.id) - .update({'updatedAt': DateTime.now().millisecondsSinceEpoch}); - idea = idea.copyWith(updatedAt: DateTime.now().millisecondsSinceEpoch); - debugPrint('updated idea ${idea.id} updatedAt DB'); - return idea; - } - - /// updates _idea title and searchable title array in the database and updates _idea with that title - Future _updateTitle(Idea idea, String title) async { - await _ideaRef - .doc(idea.id) - .update({'title': title}) - .then((value) => print("Title Updated for ${idea.id}")) - .catchError( - (error) => print("Failed to update title for ${idea.id}: $error")); - await _ideaRef - .doc(idea.id) - .update({ - "titleArray": title.toLowerCase().split(new RegExp('\\s+')).toList() - }) - .then((value) => print("Title Array ${idea.id}")) - .catchError( - (error) => print("Failed to update ${idea.id} titleArray: $error")); - _updateUpdatedAt(idea); - debugPrint('updated idea ${idea.id} title in DB'); - return idea = await get(idea.id); - } - - /// updates _idea description in the database and updates _idea with that description - Future _updateDescription(Idea idea, String description) async { - await _ideaRef - .doc(idea.id) - .update({'description': description}) - .then((value) => print("description Updated for ${idea.id}")) - .catchError((error) => print("Failed to update description: $error")); - _updateUpdatedAt(idea); - return idea = await get(idea.id); - } - - /// updates _idea in the database and udpates _idea with that update - /// Please note that every user stored in the list must be unique - /// voters can only vote once per idea - Future _updateVoters(Idea idea) async { - User user = _getUser(); - await _ideaRef.doc(idea.id).update({ - "voters": FieldValue.arrayUnion([user.id]) - }).then((_) async { - idea = await _updateUpdatedAt(idea); - await new Future.delayed(const Duration(microseconds: 5)); - debugPrint('Added voter: $user.id to idea ${idea.id}'); - }); - return idea = await get(idea.id); - } - - Future _updateVoteYes(Idea idea) async { - await _ideaRef.doc(idea.id).update({'voteYes': FieldValue.increment(1)}); - return idea = await get(idea.id); - } - - Future _updateVoteNo(Idea idea) async { - await _ideaRef.doc(idea.id).update({'voteNo': FieldValue.increment(1)}); - return idea = await get(idea.id); - } - - ///get _idea as a stream, returns Instance of '_MapStream' - ///suggested use: - /// StreamBuilder( - /// stream: _ideaService.getIdeasFromDBForCurrentMonthStream(), - /// builder: - /// (BuildContext context, AsyncSnapshot snapshot) { - /// ... - /// },) - getAsStream(Idea idea) async { - debugPrint('getCurrentIdeaFromDBStream() performing...'); - return _ideaRef.doc(idea.id).get().asStream(); - } - - ///get all idea documents from database,returns Instance of '_MapStream' - ///suggested use: - /// StreamBuilder( - /// stream: _ideaService.getIdeasFromDBForCurrentMonthStream(), - /// builder: - /// (BuildContext context, AsyncSnapshot snapshot) { - /// ... - /// },) - getAllIdeas() async { - debugPrint('getAllIdeasFromDBStream() performing...'); - return _ideaRef.snapshots(); - } - - ///Used to search all ideas by title and returns Instance of 'Future>' - ///suggested use: - /// StreamBuilder( - /// stream: _ideaService.searchIdeasByTitle(String searchTerm), - /// builder: - /// (BuildContext context, AsyncSnapshot snapshot) { - /// ... - /// },) - searchByTitle(String search) { - debugPrint('searchIdeasByTitle() performing...'); - return _ideaRef - .where('titleArray', arrayContains: search.toLowerCase()) - .snapshots(); + return [0, 1, 2, 3, 4, 5] + .map( + (e) => Idea( + id: e.toString(), + title: "title $e", + description: "Description $e", + createdAt: e, + updatedAt: e, + ), + ) + .toList(); } } diff --git a/lib/service/services.dart b/lib/service/services.dart deleted file mode 100644 index 9445784..0000000 --- a/lib/service/services.dart +++ /dev/null @@ -1,8 +0,0 @@ -export 'authentication_service.dart'; -export 'idea_service.dart'; -export 'sprint_service.dart'; -export 'user_service.dart'; -export 'firebase_service.dart'; - - - diff --git a/lib/service/sprint_service.dart b/lib/service/sprint_service.dart index 761bd5f..71d84ad 100644 --- a/lib/service/sprint_service.dart +++ b/lib/service/sprint_service.dart @@ -1,359 +1,47 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/cupertino.dart'; -import '../model/sprint.dart'; - -///Use these enums to update parts of the Sprint Object -enum UpdateSprint { - title, - description, - addPotentialLeader, - deletePotentialLeader, - addMember, - deleteMember, - teamLeader -} -enum UpdatePost { create, delete } +import 'package:idea_tracker/model/sprint.dart'; class SprintService { - ///create variable instances for use - static CollectionReference sprintRef; - - ///this initializes the class variables - initialize() { - sprintRef = FirebaseFirestore.instance.collection("sprints"); - } - - ///adds Sprint sprint to the database - Future create(Sprint sprint) async { - DocumentReference docReference = sprintRef.doc(); - sprint = sprint.copyWith(id: docReference.id); - await sprintRef - .doc(docReference.id) - .set(_sprintToJson(sprint, docReference.id)); - debugPrint('Added sprint id: ${sprint.id} to DB'); - return sprint = await get(sprint.id); - } - - ///removes/deletes class Sprint sprint from the database - Future delete(Sprint sprint) async { - await sprintRef.doc(sprint.id).delete(); - debugPrint('removed Sprint ${sprint.id} from DB'); - } - - Future update(Sprint sprint, List updates, - List updateStrings) async { - for (var i = 0; i < updates.length; i++) { - sprint = await _update(sprint, updates[i], updateStrings[i]); - } - - return sprint; + /// Create [sprint] + Future create({Sprint sprint}) async { + return sprint.copyWith( + createdAt: DateTime.now().millisecondsSinceEpoch, + updatedAt: DateTime.now().millisecondsSinceEpoch, + ); } - ///Switch to update sprint object...use case: sprintService.update(idea, UpdateSprint.title, 'new Title'); - ///returns the updated Sprint - Future _update( - Sprint sprint, UpdateSprint update, String updateString) async { - switch (update) { - case UpdateSprint.title: - { - sprint = await _updateTitle(sprint, updateString); - return sprint; - } - break; - case UpdateSprint.description: - { - sprint = await _updateDescription(sprint, updateString); - return sprint; - } - break; - case UpdateSprint.teamLeader: - { - sprint = await _updateTeamLeader(sprint, updateString); - return sprint; - } - break; - case UpdateSprint.addPotentialLeader: - { - sprint = await _addPotentialLeaders(sprint, updateString); - return sprint; - } - break; - case UpdateSprint.deletePotentialLeader: - { - sprint = await _deletePotentialLeaders(sprint, updateString); - return sprint; - } - break; - case UpdateSprint.addMember: - { - sprint = await _addMember(sprint, updateString); - return sprint; - } - break; - case UpdateSprint.deleteMember: - { - sprint = await _deleteMember(sprint, updateString); - return sprint; - } - break; - default: - { - print( - "Nothing was updated, please use Enum UpdateSprint your Sprint ${sprint.id}."); - return sprint; - } - } + /// Update [sprint] + Future update({Sprint sprint}) async { + return sprint.copyWith(updatedAt: DateTime.now().millisecondsSinceEpoch); } - ///update posts within Sprint - ///...to update a post first delete post then create new post - Future updatePost( - Sprint sprint, UpdatePost updatePost, SprintPost sprintPost) async { - switch (updatePost) { - case UpdatePost.create: - { - sprint = await _createPost(sprint, sprintPost); - return sprint; - } - break; - case UpdatePost.delete: - { - sprint = await _deletePost(sprint, sprintPost); - return sprint; - } - break; - default: - { - print( - "Nothing was updated, please use Enum UpdatePost to update your Sprint ${sprint.id}."); - return sprint; - } - break; - } - } + /// Delete [sprint] + Future delete({Sprint sprint}) async {} - /// The idea here is to get a document ID from a Stream of multiple documents - /// then store it to the current sprint for use of display and updates for Sprint Services. + /// Get Sprint with [id] /// - /// To get the document ID to pass to this function: - /// 1. StreamBuilder -> store as AsyncSnapshot snapshot using - /// getAllIdeasFromDBStream(), getIdeasFromDBForCurrentMonthStream(), or searchIdeasByTitle() - /// 2. ListView -> children: snapshot.data.docs.map((document) widgets for each document) - /// 3. access a specific sprint's details "onTapped" with this method passing "document.id" - /// to this method - Future get(String documentId) async { - DocumentSnapshot doc; - Sprint sprint; - await sprintRef.doc(documentId).get().then((document) async { - if (document.exists) { - doc = document; - sprint = _fromFirestore(doc); - } else { - print('Document does not exist on the database'); - } - }); - return sprint; - } - - ///Create Sprint object from a Firestore DocumentSnapshot - Sprint _fromFirestore(DocumentSnapshot doc) { - ///converts _InternalLinkedHashMap to List - List> sprintPostDataMaps = - List>.from(doc.data()['posts']); - List sprintPostList = sprintPostDataMaps - .map((post) => _sprintPostFromFirestore(post)) - .toList() - .cast(); - Sprint sprint = new Sprint( - id: doc.id, - title: doc.data()["title"], - description: doc.data()["description"], - teamLeader: doc.data()['teamLeader'], - createdAt: doc.data()["createdAt"], - updatedAt: doc.data()["updatedAt"], - members: doc.data()['members'].cast(), - potentialLeaders: doc.data()['potentialLeaders'].cast(), - posts: sprintPostList, - ); - return sprint; - } - - ///convert a SprintPost object from Json Map for Firestore consumption - SprintPost _sprintPostFromFirestore(post) { - SprintPost sprintPost = new SprintPost( - id: post['id'], - title: post['title'], - content: post['content'], - createdAt: post['createdAt'], + /// Return null if none found + Future get({String id}) async { + return Sprint( + id: "id", + title: "title", + description: "description", + createdAt: 0, + updatedAt: 0, ); - return sprintPost; - } - - ///convert SprintPost object to Json Map for Firestore consumption - _sprintPostToJson(SprintPost post) { - return { - 'id': post.id, - 'title': post.title, - 'content': post.content, - 'createdAt': post.createdAt, - }; - } - - ///convert Sprint object to Json Map for Firestore consumption - _sprintToJson(Sprint sprint, String id) { - return { - 'id': sprint.id ?? id, - "title": sprint.title, - "titleArray": - sprint.title.toLowerCase().split(new RegExp('\\s+')).toList(), - "description": sprint.description, - "createdAt": DateTime.now().millisecondsSinceEpoch, - "teamLeader": sprint.teamLeader, - "updatedAt": sprint.updatedAt, - "potentialLeaders": sprint.potentialLeaders ?? new List(), - "members": sprint.members ?? new List(), - "posts": sprint.posts ?? new List(), - }; - } - - ///returns int for updateUpdatedAt(); - _getUpdatedAt() { - return DateTime.now().millisecondsSinceEpoch; - } - - /// updates Sprint sprint title in database - Future _updateTitle(Sprint sprint, String title) async { - await sprintRef.doc(sprint.id).update({'title': title}); - await sprintRef.doc(sprint.id).update( - {"titleArray": title.toLowerCase().split(new RegExp('\\s+')).toList()}); - await _updateUpdatedAt(sprint); - debugPrint('updated sprint ${sprint.id} title in DB'); - return sprint = await get(sprint.id); - } - - /// updates Sprint sprint description in database - Future _updateDescription(Sprint sprint, String description) async { - await sprintRef.doc(sprint.id).update({'description': description}); - sprint = sprint.copyWith(description: description); - await _updateUpdatedAt(sprint); - debugPrint('updated description DB'); - return sprint = await get(sprint.id); - } - - /// updates class sprint updatedAt in database - Future _updateUpdatedAt(Sprint sprint) async { - await sprintRef.doc(sprint.id).update({'updatedAt': _getUpdatedAt()}); - sprint = sprint.copyWith(updatedAt: _getUpdatedAt()); - debugPrint('updated updatedAt DB'); - return sprint = await get(sprint.id); - } - - /// updates class sprint updateTeamLeader in database - Future _updateTeamLeader(Sprint sprint, String teamLeader) async { - await sprintRef.doc(sprint.id).update({'teamLeader': teamLeader}); - sprint = sprint.copyWith(teamLeader: teamLeader); - _updateUpdatedAt(sprint); - debugPrint('updated teamLeader DB'); - return sprint = await get(sprint.id); - } - - /// updates class sprint potentialLeaders in database - /// Please note that every potentialLeader stored in the list must be unique, - /// or firestore will overwrite stored String potentialLeaders with - /// this String potentialLeaders - Future _addPotentialLeaders( - Sprint sprint, String potentialLeader) async { - sprintRef.doc(sprint.id).update({ - "potentialLeaders": FieldValue.arrayUnion(['$potentialLeader']) - }).then((_) { - debugPrint( - 'Added potentialLeader: $potentialLeader to sprint ${sprint.id}'); - }); - _updateUpdatedAt(sprint); - - ///to get and store correct properties into class sprint object - return sprint = await get(sprint.id); - } - - Future _deletePotentialLeaders( - Sprint sprint, String potentialLeader) async { - sprintRef.doc(sprint.id).update({ - "potentialLeaders": FieldValue.arrayRemove(['$potentialLeader']) - }).then((_) { - debugPrint( - 'Deleted potentialLeaders: $potentialLeader to sprint ${sprint.id}'); - }); - _updateUpdatedAt(sprint); - - ///to get and store correct properties into class sprint object - return sprint = await get(sprint.id); - } - - /// updates sprint in database - /// Please note that every member stored in the list must be unique, - /// or firestore will overwrite stored String member with - /// this String member - Future _addMember(Sprint sprint, String member) async { - sprintRef.doc(sprint.id).update({ - "members": FieldValue.arrayUnion(['$member']) - }).then((_) { - debugPrint('Added member: $member to sprint ${sprint.id}'); - }); - _updateUpdatedAt(sprint); - - ///return sprint to user - return sprint = await get(sprint.id); - } - - /// remove sprint members from database - /// Please note that every member stored in the list must be unique, - /// or firestore will overwrite stored String member with - /// this String member - Future _deleteMember(Sprint sprint, String member) async { - sprintRef.doc(sprint.id).update({ - "members": FieldValue.arrayRemove(['$member']) - }).then((_) { - debugPrint('removed member: $member from sprint ${sprint.id}'); - }); - _updateUpdatedAt(sprint); - - ///to get and store correct properties into class sprint object - return sprint = await get(sprint.id); - } - - /// adds SprintPost object to sprint in database - /// Please note that every SprintPost stored in the list must be unique, - /// or firestore will overwrite stored SprintPost with - /// this SprintPost - Future _createPost(Sprint sprint, SprintPost post) async { - sprintRef.doc(sprint.id).update({ - "posts": FieldValue.arrayUnion([_sprintPostToJson(post)]) - }).then((_) { - debugPrint('Added post: $post to sprint ${sprint.id}'); - }); - _updateUpdatedAt(sprint); - - ///to get and store correct properties into class sprint object - return sprint = await get(sprint.id); - } - - /// deletes SprintPost object to sprint in database - Future _deletePost(Sprint sprint, SprintPost post) async { - sprintRef.doc(sprint.id).update({ - "posts": FieldValue.arrayRemove([_sprintPostToJson(post)]) - }).then((_) { - debugPrint('Removed post: $post from sprint ${sprint.id}'); - }); - _updateUpdatedAt(sprint); - - ///to get and store correct properties into class sprint object - return sprint = await get(sprint.id); } - ///get all sprint documents from database,returns List' + /// Get all sprints for this month Future> getAll() async { - debugPrint('getAllSprintsFromDBStream() performing...'); - QuerySnapshot querySnapshots = await sprintRef.get(); - return querySnapshots.docs.map((doc) => _fromFirestore(doc)).toList(); + return [0, 1, 2, 3, 4, 5] + .map( + (e) => Sprint( + id: e.toString(), + title: "title $e", + description: "description $e", + createdAt: e, + updatedAt: e, + ), + ) + .toList(); } } diff --git a/lib/service/user_service.dart b/lib/service/user_service.dart index c39e7c2..f79891b 100644 --- a/lib/service/user_service.dart +++ b/lib/service/user_service.dart @@ -1,133 +1,11 @@ -import 'package:cloud_firestore/cloud_firestore.dart'; -import 'package:flutter/cupertino.dart'; -import '../model/user.dart'; -enum UpdateUser {userName, photo} -class UserService { - ///create variable instances for use - CollectionReference _userRef; - User _user; - - ///this initializes the collection reference - initialize() { - _userRef = FirebaseFirestore.instance.collection("users"); - } - - ///method to put the user into the database properly - _toJson(User _user) { - return { - "id": _user.id, - "email": _user.email, - "userName": _user.userName ?? _user.email, - "photoURL": _user.photoURL ?? "_", - }; - } - - ///Method to convert Firestore DocumentSnapshot to a User Object - User _fromFirestore(DocumentSnapshot doc) { - User user = new User( - id: doc.id , - email: doc.data()['email'] ?? 'null', - userName: doc.data()['userName'] ?? 'null', - photoURL: doc.data()['photoURL'] ?? 'null'); - return user; - } - - Future update(User user, UpdateUser update, String updateString) async { - User updatedUser = user; - switch (update) { - case UpdateUser.userName: - { - updatedUser = await _updateUserName(user); - return updatedUser; - } - break; - case UpdateUser.photo: - { - updatedUser = await _updateUserPhotoURL(user); - return updatedUser; - } - break; - default: - { - print( - "Nothing was updated, please use Enum UpdateUser to update your User ${user.id}."); - return user; - } - break; - } - } - - ///Current _user is always set from setUserFromFirestore() - ///this is performed within authentication service upon sign up/in - void _setUser(User user) { - _user = user; - debugPrint('setCurrentUser(User user): ${_user.id}'); - } - - ///Returns the latest setUserFromFirestore() user object - User getUser(User user) { - _setUserFromFirestore(user); - return _user; - } - - ///This method can be used to remove the current user from the database. - ///This method should be used with caution, - ///if you remove yourself you can be added back if you log back in - Future delete(User user) async { - await _userRef.doc(user.id).delete(); - debugPrint('User ${user.id} removed from DB'); - } - - ///adds user to the database - ///can be used to replace entire user in database ...Be careful - Future _add(User user) async { - print('replace user ${user.id}'); - return await _userRef.doc(user.id).set(_toJson(user)); - } - - ///to update the user name in the database - Future _updateUserName(User user) async { - print("updateUserName(String userName): ${user.id} / ${user.userName}"); - await _userRef.doc(user.id).update({'userName': user.userName}); - debugPrint('User ${user.id} updated userName: ${user.userName} DB'); - await _setUserFromFirestore(user); - return getUser(user); - } - - ///used to update a photoURL if someone wants to include a picture with their - ///user profile - Future _updateUserPhotoURL(User user) async { - await _userRef.doc(user.id).update({'photoURL': user.photoURL}); - debugPrint('User ${user.id} updated PhotoUrl DB'); - await _setUserFromFirestore(user); - return getUser(user); - } +import 'package:idea_tracker/model/user.dart'; - ///used to get the document to show user attributes within the database - /// - ///user parameter stores the User object to _user. - ///this creates user in database if the user doesn't exist yet - ///if the document is found within the User is stored - /// - Future _setUserFromFirestore(User user) async { - DocumentSnapshot snapshot = await _userRef.doc(user.id).get(); - User userFromDocument; - if (snapshot.exists) { - userFromDocument = _fromFirestore(snapshot); - _setUser(userFromDocument); - print('Found User: ${snapshot.data()['id']}'); - } else { - print('Document does not exist on the database. Loading to database...'); - await _add(user); - print ('reload getUserDocument()...'); - _setUserFromFirestore(user); - } - print(snapshot.data()); - return snapshot; - } - - ///Used to get the user as a stream - Future getCurrentUserAsStream(User user) async { - return _userRef.doc(user.id).get().asStream(); +class UserService { + /// Get User with [id] + Future get({String id}) async { + return User( + id: "id", + username: "username", + ); } } diff --git a/lib/view/dialog/confirm_logout_dialog.dart b/lib/view/dialog/confirm_logout_dialog.dart deleted file mode 100644 index 382cd3e..0000000 --- a/lib/view/dialog/confirm_logout_dialog.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:idea_tracker/view/page/splash_page.dart'; - -class Confim_Logout_Dialog extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text('Logging Out'), - content: Text('Are you sure you want to log out?'), - actions: [ - TextButton( - onPressed: () => Navigator.popAndPushNamed(context, "/landing"), - child: Text('Yes'), - ), - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: Text('No'), - ), - ], - ); - } -} diff --git a/lib/view/page/idea_details_page.dart b/lib/view/page/idea_details_page.dart index b8a155b..dd1221e 100644 --- a/lib/view/page/idea_details_page.dart +++ b/lib/view/page/idea_details_page.dart @@ -9,7 +9,7 @@ class IdeaDetailsPage extends StatelessWidget { IdeaDetailsPage({this.idea}); Widget getEditButton(String userId, BuildContext context) { - if (userId == idea.creatorId) { + if (userId == idea.userId) { return IconButton( icon: Icon(Icons.edit), onPressed: () { diff --git a/lib/view/page/idea_edit_details_page.dart b/lib/view/page/idea_edit_details_page.dart index b3d8b50..4b969d2 100644 --- a/lib/view/page/idea_edit_details_page.dart +++ b/lib/view/page/idea_edit_details_page.dart @@ -104,7 +104,7 @@ class IdeaEditDetailsPage extends StatelessWidget { textColor: Colors.white, onPressed: () { if (_formKey.currentState.validate()) { - //controller.saveChanges(); + controller.saveChanges(); } }, ), diff --git a/lib/view/page/main_page.dart b/lib/view/page/main_page.dart index 856c3c7..4f68bae 100644 --- a/lib/view/page/main_page.dart +++ b/lib/view/page/main_page.dart @@ -20,7 +20,9 @@ class MainPage extends StatelessWidget { final sprintsTab = Navigator( initialRoute: "/", onGenerateRoute: (settings) { - return MaterialPageRoute(builder: (context) => SprintsPage()); + return MaterialPageRoute( + builder: (context) => SprintsPage() + ); }, ); final profileTab = Navigator( diff --git a/lib/view/page/splash_page.dart b/lib/view/page/splash_page.dart index 46e0c2e..32fefd9 100644 --- a/lib/view/page/splash_page.dart +++ b/lib/view/page/splash_page.dart @@ -1,8 +1,8 @@ +import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:idea_tracker/service/services.dart'; -import 'package:idea_tracker/locator.dart'; class SplashPage extends StatefulWidget { final Function onComplete; @@ -13,8 +13,6 @@ class SplashPage extends StatefulWidget { } class _SplashPageState extends State { - - @override void initState() { super.initState(); @@ -22,36 +20,26 @@ class _SplashPageState extends State { } _initialize() async { - /// Initialize all app dependencies - - ///initialize Firebase and related services - await setupFirebase().then((_){ - /// this is here to ensure initialization of each Firebase Service happens - /// after Firebase.initializeApp() - UserService _userService = locator(); - IdeaService _ideaService = locator(); - SprintService _sprintService = locator(); - AuthenticationService _authenticationService = - locator(); - _userService.initialize(); - _authenticationService.initialize(); - _ideaService.initialize(); - _sprintService.initialize(); - //TestServices testServices = new TestServices(); - //testServices.testServices(); - }); - + // Initialize all app dependencies + + /// initalize Firebase + await Firebase.initializeApp(); + debugPrint('Firebase initalized...'); + /// Enable FirebaseCrashlytics + FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true); + debugPrint('Firebase Crashlytics collection enabled...'); + /// Pass all uncaught errors from the framework to Crashlytics. + FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError; + debugPrint('Firebase Crashlytics flutter errors enabled...'); + SchedulerBinding.instance.addPostFrameCallback((timeStamp) { - /// ensure the onComplete callback cannot be called in the same frame + // ensure the onComplete callback cannot be called in the same frame widget.onComplete(); }); - - } @override Widget build(BuildContext context) { - return Scaffold( body: Center( child: CircularProgressIndicator(), diff --git a/pubspec.lock b/pubspec.lock index 2fe539d..967c709 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -126,7 +126,7 @@ packages: name: firebase_auth url: "https://pub.dartlang.org" source: hosted - version: "0.20.0+1" + version: "0.20.0" firebase_auth_platform_interface: dependency: transitive description: @@ -168,7 +168,7 @@ packages: name: firebase_crashlytics url: "https://pub.dartlang.org" source: hosted - version: "0.4.0+1" + version: "0.4.0" firebase_crashlytics_platform_interface: dependency: transitive description: @@ -302,7 +302,7 @@ packages: name: provider url: "https://pub.dartlang.org" source: hosted - version: "4.3.2+4" + version: "4.3.2+3" quiver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0500897..60d3614 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,10 +19,10 @@ dependencies: # firebase plugins firebase_core: ^0.7.0 - firebase_auth: ^0.20.0+1 + firebase_auth: ^0.20.0 # for database cloud_firestore: ^0.16.0 - firebase_crashlytics: ^0.4.0+1 + firebase_crashlytics: ^0.4.0 firebase_analytics: ^7.0.1 firebase_storage: ^7.0.0 # Passwordless authentication with email link use: https://medium.com/firebase-developers/dive-into-firebase-auth-on-flutter-email-and-link-sign-in-e51603eb08f8#id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjI1MmZjYjk3ZGY1YjZiNGY2ZDFhODg1ZjFlNjNkYzRhOWNkMjMwYzUiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2MTAxNzE0ODQsImF1ZCI6IjIxNjI5NjAzNTgzNC1rMWs2cWUwNjBzMnRwMmEyamFtNGxqZGNtczAwc3R0Zy5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjEwMjQ2MzU0NDQ2MDM4OTg5NTYwOSIsImVtYWlsIjoidGFyYWNlbGxlcnlAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF6cCI6IjIxNjI5NjAzNTgzNC1rMWs2cWUwNjBzMnRwMmEyamFtNGxqZGNtczAwc3R0Zy5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsIm5hbWUiOiJUYXJhIENlbGxlcnkiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDYuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1xT2NZVm9HUjNyby9BQUFBQUFBQUFBSS9BQUFBQUFBQUFBQS9BTVp1dWNtR3hZTGZkeVdaZnhqR2F4ZzRvZW5lMllVWWl3L3M5Ni1jL3Bob3RvLmpwZyIsImdpdmVuX25hbWUiOiJUYXJhIiwiZmFtaWx5X25hbWUiOiJDZWxsZXJ5IiwiaWF0IjoxNjEwMTcxNzg0LCJleHAiOjE2MTAxNzUzODQsImp0aSI6IjIyMGFmMWEyNGNkODc0MjZkNjQ5NTExMmZiOTI4Y2ViNWM4ZjJmMTYifQ.jgD6alVVFRBK8gkPOv6FpghHHfjxJwflLrx8TDMfIRz9yWlJIalLQy7MXZ68VNIQgvume8BnORrY2bQSF4vDU5UcHdzehZwxnEe3UcYdaYP-NKduYfPzqiwE2HxLgd4bEgvSCje5xqX20V6l3UZYkal3izqeSydj_FIcA7ANymy74aPBehS1-x3huXg39FGU5k-xu2dTs4QWVJTRn1GArHt86cdHuEr2vAyEzCLGYT_2kfwKE99HooBHUusImokpTui9QRApUcfR0DmMw-FhW-xRddi1jLribT1FODujPREjX02BnIMX1jzd9JgOCYMBdHQoYMUUhSl534izKrt2aQ diff --git a/test/test_services.dart b/test/test_services.dart deleted file mode 100644 index 88a45dc..0000000 --- a/test/test_services.dart +++ /dev/null @@ -1,239 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import '../lib/locator.dart'; -import '../lib/service/services.dart'; -import '../lib/model/models.dart'; - -class TestServices { - UserService _userService = locator(); - IdeaService _ideaService = locator(); - SprintService _sprintService = locator(); - AuthenticationService _authenticationService = - locator(); - User user; - - testServices() async { - bool isSignedIn; - //User user = _authenticationService.authenticatedUser(); - //print(user.toString()); - await _authenticationService.signUp("test@user.com", "Test User", "testUser123", "testUser123"); - //await new Future.delayed(const Duration(seconds: 3)); - //await _authenticationService.signOut(); - isSignedIn = _authenticationService.isSignedIn(); - print(isSignedIn); - await new Future.delayed(const Duration(seconds: 3)); - //await _authenticationService.forgotPassword('fluttersprints@gmail.com'); - await new Future.delayed(const Duration(seconds: 3)); - isSignedIn = _authenticationService.isSignedIn(); - print(isSignedIn); - //await _authenticationService.signIn("test@user.com", "testUser123"); - isSignedIn = _authenticationService.isSignedIn(); - print(isSignedIn); - await new Future.delayed(const Duration(seconds: 3)); - user = _authenticationService.authenticatedUser(); - await new Future.delayed(const Duration(seconds: 3)); - print('User from Test file auth test: '+user.toString()); - // print( - //'Authentication Sign In Test Complete...Next line should show user details - // for ZIE1S79L3CRPNn3EaR0yi3sWMOO2'); - await new Future.delayed(const Duration(seconds: 3)); - //await runUserServiceTest(); - await new Future.delayed(const Duration(seconds: 3)); - await runIdeaServiceTest(); - await new Future.delayed(const Duration(seconds: 6)); - await runSprintServicesTest(); - } - - runUserServiceTest() async { - ///existing user for testing - ///_authenticationService.signIn("foobar@test.com", "123qweASD#\$%"); - User _testUser = User( - id: "ZIE1S79L3CRPNn3EaR0yi3sWMOO2", - email: "foobar@test.com", - userName: "foobar@test.com", - photoURL: "_"); - - _userService.getUser(_testUser); - _testUser = _userService.getUser(_testUser); - await _userService - .update(_testUser,UpdateUser.userName, 'foo bar'); - await new Future.delayed(const Duration(seconds: 3)); - await _userService.update( - _testUser, UpdateUser.photo, 'some url to a photo'); - await new Future.delayed(const Duration(seconds: 3)); - _testUser = _userService.getUser(_testUser); - print(_testUser.toString()); - await new Future.delayed(const Duration(seconds: 3)); - var userStream = await _userService.getCurrentUserAsStream(_testUser); - await new Future.delayed(const Duration(seconds: 3)); - print('getCurrentUserAsStream(): ${userStream.toString()}'); - await new Future.delayed(const Duration(seconds: 3)); - } - - ///testing all functions of this service class - runIdeaServiceTest() async { - Idea _testIdea = new Idea( - description: 'Test description', - title: 'Test title', - ); - - ///This is the Idea used for testing with minimal input for operation within - /// firestore it already exists - /* - Idea _testIdea1 = new Idea(id: 'hqw103o7xuRJ8ObUYijZ'); - - _testIdea = await _ideaService.create(_testIdea); - //_testIdea = await _ideaService.get(_testIdea.id); - await new Future.delayed(const Duration(seconds: 3)); - _testIdea = await _ideaService.update( - _testIdea, UpdateIdea.title, 'New Idea Title'); - await new Future.delayed(const Duration(seconds: 3)); - _testIdea = - await _ideaService.update(_testIdea, UpdateIdea.title, 'Updated Title'); - await new Future.delayed(const Duration(seconds: 3)); - _testIdea = await _ideaService.update( - _testIdea, UpdateIdea.title, 'Updated Title Again'); - await new Future.delayed(const Duration(seconds: 3)); - print(_testIdea.toString()); - await new Future.delayed(const Duration(seconds: 3)); - _testIdea = await _ideaService.update( - _testIdea, UpdateIdea.description, "New Description"); - await new Future.delayed(const Duration(seconds: 3)); - _testIdea = await _ideaService.update( - _testIdea, UpdateIdea.description, "Updated Description"); - await new Future.delayed(const Duration(seconds: 3)); - _testIdea = await _ideaService.update( - _testIdea, UpdateIdea.description, 'Updated Description Again'); - await new Future.delayed(const Duration(seconds: 3)); - print(_testIdea.toString()); - _testIdea = await _ideaService.update(_testIdea, UpdateIdea.vote, "Yes"); - await new Future.delayed(const Duration(seconds: 3)); - - ///shouldn't let vote go through user already voted this idea - _testIdea = await _ideaService.update(_testIdea, UpdateIdea.vote, "No"); - await new Future.delayed(const Duration(seconds: 3)); - - ///shouldn't let vote go through user already voted this idea - _testIdea = await _ideaService.update(_testIdea, UpdateIdea.vote, "No"); - print('{Should have 1 yes and 0 No: ${_testIdea.toString()}...'); - await _ideaService.delete(_testIdea); - - _testIdea1 = await _ideaService.get(_testIdea1.id); - - //_testIdea = await _ideaService.get(_testIdea.id); - await new Future.delayed(const Duration(seconds: 3)); - var number = _ideaService.getAsStream(_testIdea).toString(); - await new Future.delayed(const Duration(seconds: 3)); - print('from getCurrentIdeaFromDBAsStream(): $number'); - var titles = await _ideaService.searchByTitle('TiTle'); - print('from searchIdeasByTitle(): $titles'); - await new Future.delayed(const Duration(seconds: 3)); - - */ - var title0 = await _ideaService.getAll(); - print('from getAll(): $title0'); - await new Future.delayed(const Duration(seconds: 3)); - // var title1 = await _ideaService.getAllIdeas(); - //print('from getAllIdeasFromDBStream(): ${title1.toString()}'); - } //end test - - runSprintServicesTest() async { - /* - ///Test Sprint objects - SprintPost _post0 = new SprintPost( - id: 0, - title: 'Sprint Post 0', - content: 'Content 0', - createdAt: DateTime.now().millisecondsSinceEpoch); - SprintPost _post1 = new SprintPost( - id: 1, - title: 'Sprint Post 1', - content: 'Content 1', - createdAt: DateTime.now().millisecondsSinceEpoch); - SprintPost _post2 = new SprintPost( - id: 2, - title: 'Sprint Post 2', - content: 'Content 2', - createdAt: DateTime.now().millisecondsSinceEpoch); - - ///full test Sprint Objects - Sprint _sprintTest = new Sprint( - title: "Sprint Title", - description: "test description", - members: ['member0', 'member1', 'member2'], - potentialLeaders: ['potentialLeaders0', 'potentialLeaders1'], - teamLeader: 'teamLeader', - createdAt: DateTime.now().microsecondsSinceEpoch, - updatedAt: DateTime.now().microsecondsSinceEpoch, - posts: List(), - ); - - Sprint _sprintTest1 = new Sprint( - id: 'TodjI69eQV4xwSkuQx2T', - title: "Sprint Title", - description: "test description", - teamLeader: 'teamLeader', - members: ['member0', 'member1', 'member2'], - potentialLeaders: ['potentialLeaders0', 'potentialLeaders1'], - createdAt: DateTime.now().microsecondsSinceEpoch, - updatedAt: DateTime.now().microsecondsSinceEpoch, - posts: List(), - ); - - _sprintTest = await _sprintService.create(_sprintTest); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.updatePost( - _sprintTest1, UpdatePost.create, _post0); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.updatePost( - _sprintTest1, UpdatePost.create, _post1); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.updatePost( - _sprintTest1, UpdatePost.create, _post2); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest = await _sprintService.update( - _sprintTest, UpdateSprint.title, "Update Sprint Title"); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest = await _sprintService.update( - _sprintTest, UpdateSprint.description, "Update Sprint Description"); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest = await _sprintService.update( - _sprintTest, UpdateSprint.addMember, "member3"); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest = await _sprintService.update( - _sprintTest, UpdateSprint.teamLeader, "potentialLeader2"); - await new Future.delayed(const Duration(seconds: 3)); - print(_sprintTest.toString()); - _sprintService.delete(_sprintTest); - await new Future.delayed(const Duration(seconds: 3)); - - _sprintTest1 = await _sprintService.get("TodjI69eQV4xwSkuQx2T"); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.update( - _sprintTest1, UpdateSprint.addPotentialLeader, "added leader"); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.update(_sprintTest1, - UpdateSprint.deletePotentialLeader, _sprintTest1.potentialLeaders[2]); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.updatePost( - _sprintTest1, UpdatePost.create, _post0); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.updatePost( - _sprintTest1, UpdatePost.create, _post1); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.updatePost( - _sprintTest1, UpdatePost.create, _post2); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.updatePost( - _sprintTest1, UpdatePost.delete, _sprintTest1.posts[1]); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.update( - _sprintTest1, UpdateSprint.addMember, 'member4'); - await new Future.delayed(const Duration(seconds: 3)); - _sprintTest1 = await _sprintService.update( - _sprintTest1, UpdateSprint.deleteMember, 'member4'); - await new Future.delayed(const Duration(seconds: 3)); - */ - var title0 = await _sprintService.getAll(); - print('Sprint from getAll(): $title0'); - } -} From 2615f123821b659a900a5afb435ca99b6b6c9498 Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Tue, 26 Jan 2021 20:13:59 +0100 Subject: [PATCH 06/14] SprintsController set-up --- .../page/sprints_details_controller.dart | 20 +++++++++++++++++++ lib/view/page/sprints_details.dart | 5 ++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 lib/controller/page/sprints_details_controller.dart diff --git a/lib/controller/page/sprints_details_controller.dart b/lib/controller/page/sprints_details_controller.dart new file mode 100644 index 0000000..3e00abf --- /dev/null +++ b/lib/controller/page/sprints_details_controller.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:idea_tracker/locator.dart'; +import 'package:idea_tracker/model/sprint.dart'; +import 'package:idea_tracker/service/sprint_service.dart'; + +class SprintsDetailsController extends ChangeNotifier { + Sprint _currentSprint; + final _sprintService = locator(); + + Sprint get currentSprint => _currentSprint; + + set currentSprint(Sprint sprint){ + _currentSprint = sprint; + } + + void addSprintMember(String member){ + + } + +} \ No newline at end of file diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index 19ff2c7..277d9b3 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -145,7 +145,10 @@ class _SprintsDetailsState extends State { RaisedButton( child: Row( children: [ - Icon(Icons.hail), + Icon( + Icons.hail, + //color: Colors.lightGreen, + ), SizedBox(width: 5.0,), Text('I\'M IN'), ], From 866b5a4ed553b6d013edd7ecc259f353149ad941 Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Sun, 24 Jan 2021 21:02:05 +0100 Subject: [PATCH 07/14] basic UI --- lib/view/page/sprints_details.dart | 252 +++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 lib/view/page/sprints_details.dart diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart new file mode 100644 index 0000000..d7ff641 --- /dev/null +++ b/lib/view/page/sprints_details.dart @@ -0,0 +1,252 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:idea_tracker/model/sprint.dart'; + +class SprintsDetails extends StatefulWidget { + + //final Sprint sprint; + //SprintsDetails(this.sprint) + + @override + _SprintsDetailsState createState() => _SprintsDetailsState(); +} + +class _SprintsDetailsState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: Icon(Icons.arrow_back), + tooltip: 'go to the previous page', + onPressed: () => Navigator.pop(context), + ), + actions: [ + IconButton( + icon: Icon(Icons.share), + tooltip: 'share this sprint', + onPressed: () => null, + ), + IconButton( + icon: Icon(Icons.delete), + tooltip: 'delete this sprint', + onPressed: () => null, + ), + IconButton( + icon: Icon(Icons.more_vert), + onPressed: () => null, + ) + ], + backgroundColor: Colors.black87, + ), + body: AnnotatedRegion( + value: SystemUiOverlayStyle.light, + child: GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Stack( + children: [ + Container( + height: double.infinity, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + ), + ), + Container( + height: double.infinity, + child: SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + padding: EdgeInsets.fromLTRB(0, 15.0, 0, 30.0), + child: Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Column( + children: [ + Row( + children: [ + Icon( + Icons.account_circle, + size: 15.0, + color: Colors.black.withOpacity(0.6), + ), + SizedBox(width: 10.0,), + Text( + 'DanielQuick', + style: TextStyle(color: Colors.black.withOpacity(0.6), fontSize: 12.0), + ), + ], + ), + SizedBox(height: 3.0,), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Idea tracker', + maxLines: 1, + style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold), + ), + SizedBox(width: 15.0,), + Text( + '- 24/01/2021', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ], + ), + SizedBox(height: 10.0,), + Row( + children: [ + Flexible( + child: Container( + width: 365, + child: Text( + 'Create an idea - Describe the idea, maybe under predefined requirements or templates - Resources for implementing the idea - Voting', + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ), + SizedBox(height: 10.0,), + Row( + children: [ + Icon(Icons.star_border , size: 18.0, color: Colors.black.withOpacity(0.6),), + SizedBox(width: 5.0,), + Text( + '69 Stars', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + SizedBox(width: 25.0,), + Icon(Icons.person_outline , size: 18.0, color: Colors.black.withOpacity(0.6),), + SizedBox(width: 5.0,), + Text( + '7 contributers', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ], + ), + Row( + children: [ + ButtonBar( + mainAxisSize: MainAxisSize.max, + children: [ + RaisedButton( + child: Row( + children: [ + Icon(Icons.star_border), + SizedBox(width: 5.0,), + Text('STAR') + ], + ), + onPressed: () {}, + color: Colors.white, + ), + RaisedButton( + child: Row( + children: [ + Icon(Icons.hail), + SizedBox(width: 5.0,), + Text('I\'M IN'), + ], + ), + onPressed: () {}, + color: Colors.white, + ), + ], + ), + ], + ), + ], + ), + ), + Divider( + height: 20.0, + color: Colors.grey, + ), + Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Column( + children: [ + Row( + children: [ + Text('Members', style: TextStyle(fontSize: 25.0),), + ], + ), + SizedBox(height: 10.0,), + ListTile( + leading: Icon(Icons.military_tech), + title: Text("DanielQuick"), + subtitle: Text("Teamleader"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("OsamaBinNaughty-hub"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("labody"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("xopherw"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("Abdul-Aziz"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("manuelvargastapia"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("taracell"), + subtitle: Text("Contributer"), + ), + ListTile( + leading: Icon(Icons.account_circle), + title: Text("DanielQuick"), + subtitle: Text("Contributer"), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ), + ); + } + Widget _memberCards(String contributer){ + return Card( + clipBehavior: Clip.antiAlias, + child: Column( + children: [ + ListTile( + leading: Icon(Icons.account_circle), + title: Text(contributer), + subtitle: Text( + 'Team leader: }', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ), + ], + ), + ); + } +} From b4d4dd4c87ee29931e63e72af9457f646ce4b0da Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Mon, 25 Jan 2021 21:38:22 +0100 Subject: [PATCH 08/14] SprintDetails is now generic --- lib/view/page/sprints_details.dart | 95 +++++++++--------------------- 1 file changed, 29 insertions(+), 66 deletions(-) diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index d7ff641..7ab3d03 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -4,10 +4,10 @@ import 'package:idea_tracker/model/sprint.dart'; class SprintsDetails extends StatefulWidget { - //final Sprint sprint; - //SprintsDetails(this.sprint) + final Sprint sprint; + SprintsDetails(this.sprint); - @override + @override _SprintsDetailsState createState() => _SprintsDetailsState(); } @@ -27,14 +27,12 @@ class _SprintsDetailsState extends State { tooltip: 'share this sprint', onPressed: () => null, ), - IconButton( - icon: Icon(Icons.delete), - tooltip: 'delete this sprint', - onPressed: () => null, - ), - IconButton( - icon: Icon(Icons.more_vert), - onPressed: () => null, + Padding( + padding: const EdgeInsets.only(left:8.0), + child: IconButton( + icon: Icon(Icons.more_vert), + onPressed: () => null, + ), ) ], backgroundColor: Colors.black87, @@ -76,7 +74,7 @@ class _SprintsDetailsState extends State { ), SizedBox(width: 10.0,), Text( - 'DanielQuick', + widget.sprint.teamLeader, style: TextStyle(color: Colors.black.withOpacity(0.6), fontSize: 12.0), ), ], @@ -86,13 +84,13 @@ class _SprintsDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( - 'Idea tracker', + widget.sprint.title, maxLines: 1, style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold), ), SizedBox(width: 15.0,), Text( - '- 24/01/2021', + '- ${widget.sprint.createdAt}', style: TextStyle(color: Colors.black.withOpacity(0.6)), ), ], @@ -104,7 +102,7 @@ class _SprintsDetailsState extends State { child: Container( width: 365, child: Text( - 'Create an idea - Describe the idea, maybe under predefined requirements or templates - Resources for implementing the idea - Voting', + widget.sprint.description, maxLines: 5, overflow: TextOverflow.ellipsis, ), @@ -118,6 +116,7 @@ class _SprintsDetailsState extends State { Icon(Icons.star_border , size: 18.0, color: Colors.black.withOpacity(0.6),), SizedBox(width: 5.0,), Text( + //TODO: make it work with button. '69 Stars', style: TextStyle(color: Colors.black.withOpacity(0.6)), ), @@ -125,7 +124,7 @@ class _SprintsDetailsState extends State { Icon(Icons.person_outline , size: 18.0, color: Colors.black.withOpacity(0.6),), SizedBox(width: 5.0,), Text( - '7 contributers', + '${widget.sprint.members.length} contributers', style: TextStyle(color: Colors.black.withOpacity(0.6)), ), ], @@ -180,43 +179,11 @@ class _SprintsDetailsState extends State { SizedBox(height: 10.0,), ListTile( leading: Icon(Icons.military_tech), - title: Text("DanielQuick"), + title: Text(widget.sprint.teamLeader), subtitle: Text("Teamleader"), ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("OsamaBinNaughty-hub"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("labody"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("xopherw"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("Abdul-Aziz"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("manuelvargastapia"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("taracell"), - subtitle: Text("Contributer"), - ), - ListTile( - leading: Icon(Icons.account_circle), - title: Text("DanielQuick"), - subtitle: Text("Contributer"), + Column( + children: _loadMembersOnListTiles(widget.sprint), ), ], ), @@ -232,21 +199,17 @@ class _SprintsDetailsState extends State { ), ); } - Widget _memberCards(String contributer){ - return Card( - clipBehavior: Clip.antiAlias, - child: Column( - children: [ - ListTile( - leading: Icon(Icons.account_circle), - title: Text(contributer), - subtitle: Text( - 'Team leader: }', - style: TextStyle(color: Colors.black.withOpacity(0.6)), - ), - ), - ], - ), + + List _loadMembersOnListTiles(Sprint sprint){ + List members = sprint.members; + return members.map((e) => _members(e)).toList(); + } + + Widget _members(String contributer){ + return ListTile( + leading: Icon(Icons.account_circle), + title: Text(contributer), + subtitle: Text("Contributer"), ); } } From ff9bde1d4c6df02d2c479a80b443fc39194baa23 Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Tue, 26 Jan 2021 12:10:46 +0100 Subject: [PATCH 09/14] Connection SprintsPage & SprintDetails --- lib/view/page/sprints_details.dart | 74 ++++++++++++++++++++++++------ lib/view/page/sprints_page.dart | 60 +++++++++++++++++++++--- 2 files changed, 113 insertions(+), 21 deletions(-) diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index 7ab3d03..d743972 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -74,7 +74,7 @@ class _SprintsDetailsState extends State { ), SizedBox(width: 10.0,), Text( - widget.sprint.teamLeader, + _teamleader(widget.sprint), style: TextStyle(color: Colors.black.withOpacity(0.6), fontSize: 12.0), ), ], @@ -89,10 +89,7 @@ class _SprintsDetailsState extends State { style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold), ), SizedBox(width: 15.0,), - Text( - '- ${widget.sprint.createdAt}', - style: TextStyle(color: Colors.black.withOpacity(0.6)), - ), + _createdAt(widget.sprint), ], ), SizedBox(height: 10.0,), @@ -124,7 +121,7 @@ class _SprintsDetailsState extends State { Icon(Icons.person_outline , size: 18.0, color: Colors.black.withOpacity(0.6),), SizedBox(width: 5.0,), Text( - '${widget.sprint.members.length} contributers', + '${_numberOfContributers(widget.sprint.members, widget.sprint)} contributers', style: TextStyle(color: Colors.black.withOpacity(0.6)), ), ], @@ -177,14 +174,7 @@ class _SprintsDetailsState extends State { ], ), SizedBox(height: 10.0,), - ListTile( - leading: Icon(Icons.military_tech), - title: Text(widget.sprint.teamLeader), - subtitle: Text("Teamleader"), - ), - Column( - children: _loadMembersOnListTiles(widget.sprint), - ), + _membersListTile(widget.sprint), ], ), ), @@ -199,7 +189,34 @@ class _SprintsDetailsState extends State { ), ); } - + + Widget _membersListTile(Sprint sprint){ + if(sprint.members == null){ + return Row( + children: [ + Text( + 'There are no contributers', + style: TextStyle(color: Colors.black.withOpacity(0.6),), + ), + ], + ); + } else { + return Column( + children: [ + ListTile( + leading: Icon(Icons.military_tech), + title: Text(sprint.teamLeader), + subtitle: Text("Teamleader"), + ), + Column( + children: _loadMembersOnListTiles(sprint), + ), + + ], + ); + } + } + List _loadMembersOnListTiles(Sprint sprint){ List members = sprint.members; return members.map((e) => _members(e)).toList(); @@ -212,4 +229,31 @@ class _SprintsDetailsState extends State { subtitle: Text("Contributer"), ); } + + String _numberOfContributers(List members, Sprint sprint){ + if(sprint.members == null || sprint.members.length == 0){ + return '0'; + } else { + return '${sprint.members.length}'; + } + } + + String _teamleader(Sprint sprint){ + if(sprint.teamLeader == null){ + return 'There is no teamleader'; + } else { + return sprint.teamLeader; + } + } + + Widget _createdAt(Sprint sprint){ + if(sprint.createdAt == null){ + return null; + } else { + return Text( + '- ${sprint.createdAt}', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ); + } + } } diff --git a/lib/view/page/sprints_page.dart b/lib/view/page/sprints_page.dart index e1f4318..b6965e6 100644 --- a/lib/view/page/sprints_page.dart +++ b/lib/view/page/sprints_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:idea_tracker/controller/page/sprints_page_controller.dart'; import 'package:idea_tracker/model/sprint.dart'; +import 'package:idea_tracker/view/page/sprints_details.dart'; import 'package:idea_tracker/view/widget/state_management/base_view.dart'; class SprintsPage extends StatefulWidget { @@ -52,7 +53,6 @@ class _SprintsPageState extends State { ), Divider( height: 20.0, - color: Colors.grey, ), Column( @@ -75,12 +75,36 @@ class _SprintsPageState extends State { } List _loadActiveSprintsOnCard(List sprints, BuildContext ctx){ - return sprints - .map((e) => _cardmaker(e.title, e.teamLeader, e.description, ctx)).toList(); + if(sprints.isNotEmpty) { + return sprints + .map((e) => _cardmaker(e.title, e.teamLeader, e.description,e.members, ctx, e)) + .toList(); + } else { + return [ + Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Row( + children: [ + SizedBox(width: 15.0,), + Text( + 'There are no active sprints', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ], + ), + )]; + } } + String _numberOfContributers(List members, Sprint sprint){ + if(sprint.members == null || sprint.members.length == 0){ + return '0'; + } else { + return '${sprint.members.length}'; + } + } - Widget _cardmaker(String title, String teamleader, String description, BuildContext ctx){ + Widget _cardmaker(String title, String teamleader, String description, List members, BuildContext ctx, Sprint sprint){ return Card( clipBehavior: Clip.antiAlias, child: Column( @@ -89,7 +113,7 @@ class _SprintsPageState extends State { leading: Icon(Icons.run_circle), title: Text(title), subtitle: Text( - 'Team leader: ${teamleader}', + 'Team leader: ${_teamleader(sprint)}', style: TextStyle(color: Colors.black.withOpacity(0.6)), ), ), @@ -100,12 +124,28 @@ class _SprintsPageState extends State { style: TextStyle(color: Colors.black.withOpacity(0.6)), ), ), + Padding( + padding: const EdgeInsets.only(left:14.0), + child: Row( + children: [ + Icon(Icons.person_outline , size: 18.0, color: Colors.black.withOpacity(0.6),), + SizedBox(width: 5.0,), + Text( + '${_numberOfContributers(members, sprint)} contributers', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ], + ), + ), ButtonBar( alignment: MainAxisAlignment.start, children: [ FlatButton( onPressed: () { - // Perform some action + Navigator.push( + ctx, + MaterialPageRoute(builder: (context) => SprintsDetails(sprint)), + ); }, child: const Text('DETAILS'), ), @@ -115,4 +155,12 @@ class _SprintsPageState extends State { ), ); } + + String _teamleader(Sprint sprint){ + if(sprint.teamLeader == null){ + return ''; + } else { + return sprint.teamLeader; + } + } } From 39c28343526924ba9cc6f1d92a24396db07098a7 Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Tue, 26 Jan 2021 18:00:10 +0100 Subject: [PATCH 10/14] teamleader posts --- lib/view/page/sprints_details.dart | 118 ++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 1 deletion(-) diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index d743972..19ff2c7 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -160,6 +160,16 @@ class _SprintsDetailsState extends State { ], ), ), + + // NOT A USED FEATURE!! IGNORE !! + + //Divider( + //height: 20.0, + //color: Colors.grey, + //), + //_latestTeamleaderPost(), + //SizedBox(height: 10.0,), + Divider( height: 20.0, color: Colors.grey, @@ -178,6 +188,14 @@ class _SprintsDetailsState extends State { ], ), ), + + // NOT A USED FEATURE!! IGNORE !! + + //Divider( + //height: 20.0, + //color: Colors.grey, + //), + //_teamleaderPosts(), ], ), ), @@ -211,7 +229,6 @@ class _SprintsDetailsState extends State { Column( children: _loadMembersOnListTiles(sprint), ), - ], ); } @@ -256,4 +273,103 @@ class _SprintsDetailsState extends State { ); } } + + Widget _teamleaderPost(String teamleader, String timeSincePosted, String post){ + return Padding( + padding: const EdgeInsets.only(right:15.0), + child: Card( + clipBehavior: Clip.antiAlias, + child: Padding( + padding: EdgeInsets.fromLTRB(10.0, 10.0, 0.0, 10.0), + child: Column( + children: [ + Row( + children: [ + Icon( + Icons.account_circle, + size: 40.0, + ), + Padding( + padding: const EdgeInsets.only(left: 15.0,), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + teamleader, + style: TextStyle( + fontSize: 20.0, + ), + ), + SizedBox(width: 10.0,), + Text( + '- ${timeSincePosted}', + style: TextStyle( + color: Colors.black.withOpacity(0.6), + ), + ), + ], + ), + Container( + width: 280, + child: Text( + post, + ), + ), + ], + ), + ), + ], + ), + ], + ), + ), + ), + ); + } + + // NOT A USED FEATURE !! IGNORE !! + Widget _latestTeamleaderPost(){ + return Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Column( + children: [ + Row( + children: [ + Text('Latest Teamleader Post', style: TextStyle(fontSize: 25.0),), + ], + ), + SizedBox(height: 10.0,), + _teamleaderPost( + 'DanielQuick', + '3d', + 'sup' + ), + ], + ), + ); + } + + // NOT A USED FEATURE !! IGNORE !! + Widget _teamleaderPosts(){ + return Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Column( + children: [ + Row( + children: [ + Text('Teamleader Post\'s', style: TextStyle(fontSize: 25.0),), + ], + ), + SizedBox(height: 10.0,), + _teamleaderPost('DanielQuick', '6d', 'hey'), + _teamleaderPost('DanielQuick', '7d', 'heyyyy'), + _teamleaderPost('DanielQuick', '8d', 'hallo'), + _teamleaderPost('DanielQuick', '9d', 'bonjour'), + ], + ), + ); + } + } From 1ef7c3001358dbc727cf145e32eebf63a372a909 Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Tue, 26 Jan 2021 20:13:59 +0100 Subject: [PATCH 11/14] SprintsController set-up --- .../page/sprints_details_controller.dart | 20 +++++++++++++++++++ lib/view/page/sprints_details.dart | 5 ++++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 lib/controller/page/sprints_details_controller.dart diff --git a/lib/controller/page/sprints_details_controller.dart b/lib/controller/page/sprints_details_controller.dart new file mode 100644 index 0000000..3e00abf --- /dev/null +++ b/lib/controller/page/sprints_details_controller.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:idea_tracker/locator.dart'; +import 'package:idea_tracker/model/sprint.dart'; +import 'package:idea_tracker/service/sprint_service.dart'; + +class SprintsDetailsController extends ChangeNotifier { + Sprint _currentSprint; + final _sprintService = locator(); + + Sprint get currentSprint => _currentSprint; + + set currentSprint(Sprint sprint){ + _currentSprint = sprint; + } + + void addSprintMember(String member){ + + } + +} \ No newline at end of file diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index 19ff2c7..277d9b3 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -145,7 +145,10 @@ class _SprintsDetailsState extends State { RaisedButton( child: Row( children: [ - Icon(Icons.hail), + Icon( + Icons.hail, + //color: Colors.lightGreen, + ), SizedBox(width: 5.0,), Text('I\'M IN'), ], From ee902b16b14eb4786a30dbae2e5600b43b99f50d Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Fri, 29 Jan 2021 13:59:16 +0100 Subject: [PATCH 12/14] SprintsDetailsController & Epoch time converter --- .../page/sprints_details_controller.dart | 7 ++- lib/model/placeholder_model.dart | 1 + lib/view/page/sprints_details.dart | 27 +++++++++-- pubspec.lock | 47 +++++++++++-------- pubspec.yaml | 3 ++ 5 files changed, 58 insertions(+), 27 deletions(-) create mode 100644 lib/model/placeholder_model.dart diff --git a/lib/controller/page/sprints_details_controller.dart b/lib/controller/page/sprints_details_controller.dart index 3e00abf..5e53793 100644 --- a/lib/controller/page/sprints_details_controller.dart +++ b/lib/controller/page/sprints_details_controller.dart @@ -1,20 +1,23 @@ import 'package:flutter/material.dart'; import 'package:idea_tracker/locator.dart'; import 'package:idea_tracker/model/sprint.dart'; +import 'package:idea_tracker/service/authentication_service.dart'; import 'package:idea_tracker/service/sprint_service.dart'; class SprintsDetailsController extends ChangeNotifier { Sprint _currentSprint; final _sprintService = locator(); + final _authService = locator(); Sprint get currentSprint => _currentSprint; set currentSprint(Sprint sprint){ _currentSprint = sprint; + notifyListeners(); } - void addSprintMember(String member){ - + void addSprintMember() async { + currentSprint = await _sprintService.update(currentSprint, [UpdateSprint.addMember], [_authService.getAuthenticatedUser().id]); } } \ No newline at end of file diff --git a/lib/model/placeholder_model.dart b/lib/model/placeholder_model.dart new file mode 100644 index 0000000..7e5cf10 --- /dev/null +++ b/lib/model/placeholder_model.dart @@ -0,0 +1 @@ +// Git does not track empty directories. After a new file exists in this directory, this file can safely be deleted. diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index 277d9b3..a4aaa21 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:idea_tracker/model/sprint.dart'; +import 'package:time_formatter/time_formatter.dart'; class SprintsDetails extends StatefulWidget { @@ -84,7 +85,7 @@ class _SprintsDetailsState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( - widget.sprint.title, + _title(widget.sprint), maxLines: 1, style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold), ), @@ -99,7 +100,7 @@ class _SprintsDetailsState extends State { child: Container( width: 365, child: Text( - widget.sprint.description, + _description(widget.sprint), maxLines: 5, overflow: TextOverflow.ellipsis, ), @@ -226,7 +227,7 @@ class _SprintsDetailsState extends State { children: [ ListTile( leading: Icon(Icons.military_tech), - title: Text(sprint.teamLeader), + title: Text(_teamleader(sprint)), subtitle: Text("Teamleader"), ), Column( @@ -258,9 +259,25 @@ class _SprintsDetailsState extends State { } } + String _title(Sprint sprint){ + if(sprint.title == null){ + return 'Nameless Sprint'; + } else { + return sprint.title; + } + } + + String _description(Sprint sprint){ + if(sprint.description == null){ + return 'No description'; + } else { + return sprint.description; + } + } + String _teamleader(Sprint sprint){ if(sprint.teamLeader == null){ - return 'There is no teamleader'; + return 'No teamleader'; } else { return sprint.teamLeader; } @@ -271,7 +288,7 @@ class _SprintsDetailsState extends State { return null; } else { return Text( - '- ${sprint.createdAt}', + '- ${formatTime(sprint.createdAt)}', style: TextStyle(color: Colors.black.withOpacity(0.6)), ); } diff --git a/pubspec.lock b/pubspec.lock index 1fd5ac5..62b02ab 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,35 +7,35 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.1" + version: "2.5.0-nullsafety.3" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0-nullsafety.3" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0-nullsafety.5" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.3" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0-nullsafety.3" cloud_firestore: dependency: "direct main" description: @@ -63,7 +63,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.3" + version: "1.15.0-nullsafety.5" convert: dependency: transitive description: @@ -91,7 +91,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.3" firebase: dependency: transitive description: @@ -253,21 +253,21 @@ packages: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.2" + version: "0.6.3-nullsafety.3" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.1" + version: "0.12.10-nullsafety.3" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0-nullsafety.6" nested: dependency: transitive description: @@ -281,7 +281,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.1" + version: "1.8.0-nullsafety.3" pedantic: dependency: transitive description: @@ -321,56 +321,63 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.2" + version: "1.8.0-nullsafety.4" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.1" + version: "1.10.0-nullsafety.6" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.1" + version: "2.1.0-nullsafety.3" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.1" + version: "1.1.0-nullsafety.3" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.1" + version: "1.2.0-nullsafety.3" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.2" + version: "0.2.19-nullsafety.6" + time_formatter: + dependency: "direct main" + description: + name: time_formatter + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0+5" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.3" + version: "1.3.0-nullsafety.5" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0-nullsafety.5" sdks: - dart: ">=2.10.0-110 <2.11.0" + dart: ">=2.12.0-0.0 <3.0.0" flutter: ">=1.20.0 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0500897..b548092 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,6 +28,9 @@ dependencies: # Passwordless authentication with email link use: https://medium.com/firebase-developers/dive-into-firebase-auth-on-flutter-email-and-link-sign-in-e51603eb08f8#id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjI1MmZjYjk3ZGY1YjZiNGY2ZDFhODg1ZjFlNjNkYzRhOWNkMjMwYzUiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2MTAxNzE0ODQsImF1ZCI6IjIxNjI5NjAzNTgzNC1rMWs2cWUwNjBzMnRwMmEyamFtNGxqZGNtczAwc3R0Zy5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjEwMjQ2MzU0NDQ2MDM4OTg5NTYwOSIsImVtYWlsIjoidGFyYWNlbGxlcnlAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF6cCI6IjIxNjI5NjAzNTgzNC1rMWs2cWUwNjBzMnRwMmEyamFtNGxqZGNtczAwc3R0Zy5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsIm5hbWUiOiJUYXJhIENlbGxlcnkiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDYuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1xT2NZVm9HUjNyby9BQUFBQUFBQUFBSS9BQUFBQUFBQUFBQS9BTVp1dWNtR3hZTGZkeVdaZnhqR2F4ZzRvZW5lMllVWWl3L3M5Ni1jL3Bob3RvLmpwZyIsImdpdmVuX25hbWUiOiJUYXJhIiwiZmFtaWx5X25hbWUiOiJDZWxsZXJ5IiwiaWF0IjoxNjEwMTcxNzg0LCJleHAiOjE2MTAxNzUzODQsImp0aSI6IjIyMGFmMWEyNGNkODc0MjZkNjQ5NTExMmZiOTI4Y2ViNWM4ZjJmMTYifQ.jgD6alVVFRBK8gkPOv6FpghHHfjxJwflLrx8TDMfIRz9yWlJIalLQy7MXZ68VNIQgvume8BnORrY2bQSF4vDU5UcHdzehZwxnEe3UcYdaYP-NKduYfPzqiwE2HxLgd4bEgvSCje5xqX20V6l3UZYkal3izqeSydj_FIcA7ANymy74aPBehS1-x3huXg39FGU5k-xu2dTs4QWVJTRn1GArHt86cdHuEr2vAyEzCLGYT_2kfwKE99HooBHUusImokpTui9QRApUcfR0DmMw-FhW-xRddi1jLribT1FODujPREjX02BnIMX1jzd9JgOCYMBdHQoYMUUhSl534izKrt2aQ firebase_dynamic_links: ^0.7.0+1 + # UNIX timestamps and converts them to readable time formats https://pub.dev/packages/time_formatter + time_formatter: ^1.0.0+5 + dev_dependencies: flutter_test: From 904d50e5282e9f64bfd54983ff4f602b3b1776c8 Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Fri, 29 Jan 2021 17:57:36 +0100 Subject: [PATCH 13/14] CreateSprintPageController --- .../page/create_sprint_page_controller.dart | 62 ++++ .../page/sprints_details_controller.dart | 19 +- lib/locator.dart | 4 + lib/view/page/create_sprint_page.dart | 0 lib/view/page/sprints_details.dart | 347 +++++++++--------- 5 files changed, 261 insertions(+), 171 deletions(-) create mode 100644 lib/controller/page/create_sprint_page_controller.dart create mode 100644 lib/view/page/create_sprint_page.dart diff --git a/lib/controller/page/create_sprint_page_controller.dart b/lib/controller/page/create_sprint_page_controller.dart new file mode 100644 index 0000000..2dd64fa --- /dev/null +++ b/lib/controller/page/create_sprint_page_controller.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:idea_tracker/locator.dart'; +import 'package:idea_tracker/model/sprint.dart'; +import 'package:idea_tracker/service/sprint_service.dart'; + +class CreateSprintPageController extends ChangeNotifier{ + String _sprintTitle; + String _sprintDescription; + String _sprintTeamleader; + Function(String) onSprintCreated; + + final _sprintService = locator(); + + void setSprintTitle(String title){ + _sprintTitle = title; + } + + void setSprintDescription(String description){ + _sprintDescription = description; + } + + void setSprintTeamleader(String teamleader){ + _sprintTeamleader = teamleader; + } + + String validateTitle(String value) { + if (value.isEmpty) { + return 'Please enter idea title'; + } else if (value.length < 3) { + return 'Title must be at least 3 characters'; + } + return null; + } + + String validateDescription(String value) { + if (value.isEmpty) { + return 'Please enter idea description'; + } else if (value.length < 3) { + return 'Description must be at least 3 characters'; + } + return null; + } + + String validateTeamleader(String value) { + if (value.isEmpty) { + return 'Please enter a teamleader'; + } + return null; + } + + void createSprint() async { + final sprint = await _sprintService.create( + Sprint( + title: _sprintTitle, + description: _sprintDescription, + teamLeader: _sprintTeamleader, + ), + ); + if(onSprintCreated != null) onSprintCreated("Succes!"); + print(sprint.toString()); + } +} \ No newline at end of file diff --git a/lib/controller/page/sprints_details_controller.dart b/lib/controller/page/sprints_details_controller.dart index 5e53793..3e8c138 100644 --- a/lib/controller/page/sprints_details_controller.dart +++ b/lib/controller/page/sprints_details_controller.dart @@ -16,8 +16,23 @@ class SprintsDetailsController extends ChangeNotifier { notifyListeners(); } - void addSprintMember() async { - currentSprint = await _sprintService.update(currentSprint, [UpdateSprint.addMember], [_authService.getAuthenticatedUser().id]); + addOrDeleteSprintMember() async { + if(currentSprint.members.contains(_authService.getAuthenticatedUser().id)) { + currentSprint = await _sprintService.update( + currentSprint, [UpdateSprint.deleteMember], [_authService + .getAuthenticatedUser() + .id + ]); + } else { + currentSprint = await _sprintService.update( + currentSprint, [UpdateSprint.addMember], [_authService + .getAuthenticatedUser() + .id + ]); + } } + initialize(){ + + } } \ No newline at end of file diff --git a/lib/locator.dart b/lib/locator.dart index 26f597e..ef1e882 100644 --- a/lib/locator.dart +++ b/lib/locator.dart @@ -2,9 +2,11 @@ import 'package:get_it/get_it.dart'; import 'package:idea_tracker/controller/dialog/idea_edit_details_page_delete_dialog_controller.dart'; import 'package:idea_tracker/controller/dialog/landing_page_recover_password_dialog_controller.dart'; import 'package:idea_tracker/controller/page/create_idea_page_controller.dart'; +import 'package:idea_tracker/controller/page/create_sprint_page_controller.dart'; import 'package:idea_tracker/controller/page/idea_edit_details_page_controller.dart'; import 'package:idea_tracker/controller/page/ideas_main_page_controller.dart'; import 'package:idea_tracker/controller/page/main_page_controller.dart'; +import 'package:idea_tracker/controller/page/sprints_details_controller.dart'; import 'service/services.dart'; import 'package:idea_tracker/controller/page/sprints_page_controller.dart'; @@ -21,6 +23,8 @@ void setupLocator() { locator.registerFactory(() => LandingPageRecoverPasswordDialogController()); locator.registerFactory(() => ProfilePageController()); locator.registerFactory(() => SprintsPageController()); + locator.registerFactory(() => SprintsDetailsController()); + locator.registerFactory(() => CreateSprintPageController()); /// Services locator.registerSingleton(IdeaService()); diff --git a/lib/view/page/create_sprint_page.dart b/lib/view/page/create_sprint_page.dart new file mode 100644 index 0000000..e69de29 diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index a4aaa21..933df88 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:idea_tracker/controller/page/sprints_details_controller.dart'; import 'package:idea_tracker/model/sprint.dart'; +import 'package:idea_tracker/view/widget/state_management/base_view.dart'; import 'package:time_formatter/time_formatter.dart'; class SprintsDetails extends StatefulWidget { @@ -15,200 +17,208 @@ class SprintsDetails extends StatefulWidget { class _SprintsDetailsState extends State { @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - leading: IconButton( - icon: Icon(Icons.arrow_back), - tooltip: 'go to the previous page', - onPressed: () => Navigator.pop(context), - ), - actions: [ - IconButton( - icon: Icon(Icons.share), - tooltip: 'share this sprint', - onPressed: () => null, - ), - Padding( - padding: const EdgeInsets.only(left:8.0), - child: IconButton( - icon: Icon(Icons.more_vert), - onPressed: () => null, + return BaseView( + onControllerReady: (controller){ + controller.initialize(); + }, + builder: (context, controller, child){ + return Scaffold( + appBar: AppBar( + leading: IconButton( + icon: Icon(Icons.arrow_back), + tooltip: 'go to the previous page', + onPressed: () => Navigator.pop(context), ), - ) - ], - backgroundColor: Colors.black87, - ), - body: AnnotatedRegion( - value: SystemUiOverlayStyle.light, - child: GestureDetector( - onTap: () => FocusScope.of(context).unfocus(), - child: Stack( - children: [ - Container( - height: double.infinity, - width: double.infinity, - decoration: BoxDecoration( - color: Colors.white, - ), + actions: [ + IconButton( + icon: Icon(Icons.share), + tooltip: 'share this sprint', + onPressed: () => null, ), - Container( - height: double.infinity, - child: SingleChildScrollView( - physics: AlwaysScrollableScrollPhysics(), - padding: EdgeInsets.fromLTRB(0, 15.0, 0, 30.0), - child: Padding( - padding: const EdgeInsets.only(top: 10.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.only(left: 15.0), - child: Column( - children: [ - Row( - children: [ - Icon( - Icons.account_circle, - size: 15.0, - color: Colors.black.withOpacity(0.6), - ), - SizedBox(width: 10.0,), - Text( - _teamleader(widget.sprint), - style: TextStyle(color: Colors.black.withOpacity(0.6), fontSize: 12.0), - ), - ], - ), - SizedBox(height: 3.0,), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - _title(widget.sprint), - maxLines: 1, - style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold), - ), - SizedBox(width: 15.0,), - _createdAt(widget.sprint), - ], - ), - SizedBox(height: 10.0,), - Row( + Padding( + padding: const EdgeInsets.only(left:8.0), + child: IconButton( + icon: Icon(Icons.more_vert), + onPressed: () => null, + ), + ) + ], + backgroundColor: Colors.black87, + ), + body: AnnotatedRegion( + value: SystemUiOverlayStyle.light, + child: GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + child: Stack( + children: [ + Container( + height: double.infinity, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.white, + ), + ), + Container( + height: double.infinity, + child: SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + padding: EdgeInsets.fromLTRB(0, 15.0, 0, 30.0), + child: Padding( + padding: const EdgeInsets.only(top: 10.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Column( children: [ - Flexible( - child: Container( - width: 365, - child: Text( - _description(widget.sprint), - maxLines: 5, - overflow: TextOverflow.ellipsis, + Row( + children: [ + Icon( + Icons.account_circle, + size: 15.0, + color: Colors.black.withOpacity(0.6), ), - ), - ), - ], - ), - SizedBox(height: 10.0,), - Row( - children: [ - Icon(Icons.star_border , size: 18.0, color: Colors.black.withOpacity(0.6),), - SizedBox(width: 5.0,), - Text( - //TODO: make it work with button. - '69 Stars', - style: TextStyle(color: Colors.black.withOpacity(0.6)), + SizedBox(width: 10.0,), + Text( + _teamleader(widget.sprint), + style: TextStyle(color: Colors.black.withOpacity(0.6), fontSize: 12.0), + ), + ], ), - SizedBox(width: 25.0,), - Icon(Icons.person_outline , size: 18.0, color: Colors.black.withOpacity(0.6),), - SizedBox(width: 5.0,), - Text( - '${_numberOfContributers(widget.sprint.members, widget.sprint)} contributers', - style: TextStyle(color: Colors.black.withOpacity(0.6)), + SizedBox(height: 3.0,), + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + _title(widget.sprint), + maxLines: 1, + style: TextStyle(fontSize: 25.0, fontWeight: FontWeight.bold), + ), + SizedBox(width: 15.0,), + _createdAt(widget.sprint), + ], ), - ], - ), - Row( - children: [ - ButtonBar( - mainAxisSize: MainAxisSize.max, + SizedBox(height: 10.0,), + Row( children: [ - RaisedButton( - child: Row( - children: [ - Icon(Icons.star_border), - SizedBox(width: 5.0,), - Text('STAR') - ], + Flexible( + child: Container( + width: 365, + child: Text( + _description(widget.sprint), + maxLines: 5, + overflow: TextOverflow.ellipsis, + ), ), - onPressed: () {}, - color: Colors.white, ), - RaisedButton( - child: Row( - children: [ - Icon( - Icons.hail, - //color: Colors.lightGreen, + ], + ), + SizedBox(height: 10.0,), + Row( + children: [ + Icon(Icons.star_border , size: 18.0, color: Colors.black.withOpacity(0.6),), + SizedBox(width: 5.0,), + Text( + //TODO: make it work with button. + '69 Stars', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + SizedBox(width: 25.0,), + Icon(Icons.person_outline , size: 18.0, color: Colors.black.withOpacity(0.6),), + SizedBox(width: 5.0,), + Text( + '${_numberOfContributers(widget.sprint.members, widget.sprint)} contributers', + style: TextStyle(color: Colors.black.withOpacity(0.6)), + ), + ], + ), + Row( + children: [ + ButtonBar( + mainAxisSize: MainAxisSize.max, + children: [ + RaisedButton( + child: Row( + children: [ + Icon(Icons.star_border), + SizedBox(width: 5.0,), + Text('STAR') + ], ), - SizedBox(width: 5.0,), - Text('I\'M IN'), - ], - ), - onPressed: () {}, - color: Colors.white, + onPressed: () {}, + color: Colors.white, + ), + RaisedButton( + child: Row( + children: [ + Icon( + Icons.hail, + //color: Colors.lightGreen, + ), + SizedBox(width: 5.0,), + Text('I\'M IN'), + ], + ), + onPressed: //() => controller.addOrDeleteSprintMember(), + (){}, + color: Colors.white, + ), + ], ), ], ), ], ), - ], - ), - ), + ), - // NOT A USED FEATURE!! IGNORE !! + // NOT A USED FEATURE!! IGNORE !! - //Divider( - //height: 20.0, - //color: Colors.grey, - //), - //_latestTeamleaderPost(), - //SizedBox(height: 10.0,), + //Divider( + //height: 20.0, + //color: Colors.grey, + //), + //_latestTeamleaderPost(), + //SizedBox(height: 10.0,), - Divider( - height: 20.0, - color: Colors.grey, - ), - Padding( - padding: const EdgeInsets.only(left: 15.0), - child: Column( - children: [ - Row( + Divider( + height: 20.0, + color: Colors.grey, + ), + Padding( + padding: const EdgeInsets.only(left: 15.0), + child: Column( children: [ - Text('Members', style: TextStyle(fontSize: 25.0),), + Row( + children: [ + Text('Members', style: TextStyle(fontSize: 25.0),), + ], + ), + SizedBox(height: 10.0,), + _membersListTile(widget.sprint), ], ), - SizedBox(height: 10.0,), - _membersListTile(widget.sprint), - ], - ), - ), + ), - // NOT A USED FEATURE!! IGNORE !! + // NOT A USED FEATURE!! IGNORE !! - //Divider( - //height: 20.0, - //color: Colors.grey, - //), - //_teamleaderPosts(), - ], + //Divider( + //height: 20.0, + //color: Colors.grey, + //), + //_teamleaderPosts(), + ], + ), + ), ), ), - ), + ], ), - ], + ), ), - ), - ), + ); + }, ); } @@ -391,5 +401,4 @@ class _SprintsDetailsState extends State { ), ); } - -} +} \ No newline at end of file From a4659904284117ca57ba2d18573fc6caf5f89b03 Mon Sep 17 00:00:00 2001 From: OsamaBinNaughty-hub <57219918+OsamaBinNaughty-hub@users.noreply.github.com> Date: Sat, 13 Feb 2021 12:05:42 +0100 Subject: [PATCH 14/14] updating details controller --- .../page/sprints_details_controller.dart | 20 ++-- lib/service/sprint_service.dart | 98 +++++++++++-------- lib/view/page/sprints_details.dart | 4 +- 3 files changed, 66 insertions(+), 56 deletions(-) diff --git a/lib/controller/page/sprints_details_controller.dart b/lib/controller/page/sprints_details_controller.dart index 3e8c138..7a468e6 100644 --- a/lib/controller/page/sprints_details_controller.dart +++ b/lib/controller/page/sprints_details_controller.dart @@ -16,20 +16,12 @@ class SprintsDetailsController extends ChangeNotifier { notifyListeners(); } - addOrDeleteSprintMember() async { - if(currentSprint.members.contains(_authService.getAuthenticatedUser().id)) { - currentSprint = await _sprintService.update( - currentSprint, [UpdateSprint.deleteMember], [_authService - .getAuthenticatedUser() - .id - ]); - } else { - currentSprint = await _sprintService.update( - currentSprint, [UpdateSprint.addMember], [_authService - .getAuthenticatedUser() - .id - ]); - } + UpdateSprintMember() async { + currentSprint = await _sprintService.update( + currentSprint, [UpdateSprint.member], [_authService + .getAuthenticatedUser() + .id + ]); } initialize(){ diff --git a/lib/service/sprint_service.dart b/lib/service/sprint_service.dart index eeacf83..16934d3 100644 --- a/lib/service/sprint_service.dart +++ b/lib/service/sprint_service.dart @@ -1,6 +1,6 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/cupertino.dart'; -import 'package:idea_tracker/model/sprint.dart'; +import '../model/sprint.dart'; ///Use these enums to update parts of the Sprint Object enum UpdateSprint { @@ -8,8 +8,7 @@ enum UpdateSprint { description, addPotentialLeader, deletePotentialLeader, - addMember, - deleteMember, + member, teamLeader } enum UpdatePost { create, delete } @@ -40,73 +39,92 @@ class SprintService { debugPrint('removed Sprint ${sprint.id} from DB'); } + /// Uses the switch to update multiple properties within a sprint + /// Requires use of enum above this class + /// Use Case example: + /// _sprintServices.update(sprint, [UpdateSprint.title, + /// UpdateSprint.description], ['New Title', 'New Description sequence']) Future update(Sprint sprint, List updates, List updateStrings) async { - for (var i = 0; i < updates.length; i++) { - sprint = await _update(sprint, updates[i], updateStrings[i]); + if (updates.length == updateStrings.length) { + for (var i = 0; i < updates.length; i++) { + sprint = await _update(sprint, updates[i], updateStrings[i]); + } + return sprint; + } else { + debugPrint( + 'The update will not work unless both lists have the same length'); } - return sprint; } - ///Switch to update sprint object...use case: sprintService.update(idea, UpdateSprint.title, 'new Title'); - ///returns the updated Sprint + /// Switch to update sprint object... + /// use case: sprintService.update(idea, UpdateSprint.title, 'new Title'); + /// returns the updated Sprint Future _update( Sprint sprint, UpdateSprint update, String updateString) async { + Sprint updatedSprint = sprint; switch (update) { case UpdateSprint.title: { - sprint = await _updateTitle(sprint, updateString); - return sprint; + updatedSprint = await _updateTitle(updatedSprint, updateString); + return updatedSprint; } break; case UpdateSprint.description: { - sprint = await _updateDescription(sprint, updateString); - return sprint; + updatedSprint = await _updateDescription(updatedSprint, updateString); + return updatedSprint; } break; case UpdateSprint.teamLeader: { - sprint = await _updateTeamLeader(sprint, updateString); - return sprint; + updatedSprint = await _updateTeamLeader(updatedSprint, updateString); + return updatedSprint; } break; case UpdateSprint.addPotentialLeader: { - sprint = await _addPotentialLeaders(sprint, updateString); - return sprint; + updatedSprint = await _addPotentialLeaders(updatedSprint, updateString); + return updatedSprint; } break; case UpdateSprint.deletePotentialLeader: { - sprint = await _deletePotentialLeaders(sprint, updateString); - return sprint; + updatedSprint = await _deletePotentialLeaders(updatedSprint, updateString); + return updatedSprint; } break; - case UpdateSprint.addMember: + case UpdateSprint.member: { - sprint = await _addMember(sprint, updateString); - return sprint; - } - break; - case UpdateSprint.deleteMember: - { - sprint = await _deleteMember(sprint, updateString); - return sprint; + /// This checks to see if user already a member. + /// To get the number of members use list.length(); on the member list + /// within the Sprint object + if (!updatedSprint.members.contains(updateString)) { + updatedSprint = await _addMember(updatedSprint, updateString); + debugPrint('User $updateString added to member list for this sprint.'); + } else { + debugPrint('User $updateString is already member of this sprint.'); + updatedSprint = await _deleteMember(updatedSprint, updateString); + debugPrint('User $updateString removed from voters for this idea.'); + } + return updatedSprint; } break; default: { print( - "Nothing was updated, please use Enum UpdateSprint your Sprint ${sprint.id}."); - return sprint; + "Nothing was updated, please use Enum UpdateSprint your Sprint ${updatedSprint.id}."); + return updatedSprint; } } } - ///update posts within Sprint + /// update posts within Sprint ///...to update a post first delete post then create new post + /// list class here just adds new posts to the end of the list, + /// Firestore can only use arrays for lists so it follows typical array + /// addition and deletion functionality Future updatePost( Sprint sprint, UpdatePost updatePost, SprintPost sprintPost) async { switch (updatePost) { @@ -174,7 +192,7 @@ class SprintService { debugPrint('getAllCurrent() performing...'); QuerySnapshot querySnapshot = await sprintRef .where('createdAt', - isGreaterThanOrEqualTo: startOfMonth.millisecondsSinceEpoch) + isGreaterThanOrEqualTo: startOfMonth.millisecondsSinceEpoch) .get() .catchError( (error) => print("Failed to get all Current Sprints: $error")); @@ -185,7 +203,7 @@ class SprintService { Sprint _fromFirestore(DocumentSnapshot doc) { ///converts _InternalLinkedHashMap to List List> sprintPostDataMaps = - List>.from(doc.data()['posts']); + List>.from(doc.data()['posts']); List sprintPostList = sprintPostDataMaps .map((post) => _sprintPostFromFirestore(post)) .toList() @@ -229,13 +247,13 @@ class SprintService { _sprintToJson(Sprint sprint, String id) { return { 'id': sprint.id ?? id, - "title": sprint.title, + "title": sprint.title ?? '', "titleArray": - sprint.title.toLowerCase().split(new RegExp('\\s+')).toList(), - "description": sprint.description, + sprint.title.toLowerCase().split(new RegExp('\\s+')).toList(), + "description": sprint.description ?? '', "createdAt": DateTime.now().millisecondsSinceEpoch, - "teamLeader": sprint.teamLeader, - "updatedAt": sprint.updatedAt, + "teamLeader": sprint.teamLeader ?? '', + "updatedAt": sprint.updatedAt ?? DateTime.now().millisecondsSinceEpoch, "potentialLeaders": sprint.potentialLeaders ?? new List(), "members": sprint.members ?? new List(), "posts": sprint.posts ?? new List(), @@ -301,7 +319,7 @@ class SprintService { 'Added potentialLeader: $potentialLeader to sprint ${sprint.id}'); await _updateUpdatedAt(sprint); }).catchError( - (error) => debugPrint("Failed to add potential leader: $error")); + (error) => debugPrint("Failed to add potential leader: $error")); ///to get and store correct properties into class sprint object return sprint = await get(sprint.id); @@ -318,7 +336,7 @@ class SprintService { 'Deleted potentialLeaders: $potentialLeader to sprint ${sprint.id}'); await _updateUpdatedAt(sprint); }).catchError( - (error) => debugPrint("Failed to delete potential leader $error")); + (error) => debugPrint("Failed to delete potential leader $error")); ///to get and store correct properties into class sprint object return sprint = await get(sprint.id); @@ -385,4 +403,4 @@ class SprintService { return sprint = await get(sprint.id); } -} +} \ No newline at end of file diff --git a/lib/view/page/sprints_details.dart b/lib/view/page/sprints_details.dart index 933df88..4a75169 100644 --- a/lib/view/page/sprints_details.dart +++ b/lib/view/page/sprints_details.dart @@ -161,8 +161,8 @@ class _SprintsDetailsState extends State { Text('I\'M IN'), ], ), - onPressed: //() => controller.addOrDeleteSprintMember(), - (){}, + onPressed: () => controller.UpdateSprintMember(), + color: Colors.white, ), ],