From e4213d36a077386f96c8debd922187380fcec72f Mon Sep 17 00:00:00 2001 From: Cathryn Li <60448964+cathli66@users.noreply.github.com> Date: Wed, 1 May 2024 21:14:49 -0400 Subject: [PATCH] Cl893/fix prod bugs (#219) * ui fixes * fix joining orgs * fix completer bug * Fix events issues * fix challenge completed page ui * Delete cascades * fix i've arrived race condition and reversed copied event * fix more ui * updated progress bars * Fix tests + launcher * edit color of challenge completed progress bar --------- Co-authored-by: neketka --- admin/src/components/Events.tsx | 10 +- game/lib/achievements/achievement_cell.dart | 98 +--- game/lib/api/game_server_api.dart | 8 +- game/lib/challenges/challenge_cell.dart | 17 +- game/lib/challenges/challenges_page.dart | 2 +- game/lib/gameplay/challenge_completed.dart | 432 +++++++++--------- game/lib/gameplay/gameplay_map.dart | 89 ++-- game/lib/gameplay/gameplay_page.dart | 6 +- .../global_leaderboard_widget.dart | 4 +- game/lib/journeys/journey_cell.dart | 200 +++++--- game/lib/model/user_model.dart | 1 - game/lib/navigation_page/home_navbar.dart | 13 +- .../navigation_page/search_filter_home.dart | 143 +++--- game/lib/preview/preview.dart | 115 +++-- game/lib/profile/achievement_cell.dart | 108 ----- game/lib/profile/completed_cell.dart | 23 +- game/lib/profile/profile_page.dart | 15 +- game/lib/profile/settings_page.dart | 10 +- game/lib/widget/leaderboard_cell.dart | 10 +- game/lib/widget/progress_bar.dart | 75 +++ server/prisma/schema.prisma | 2 +- server/src/challenge/challenge.gateway.ts | 4 +- server/src/event/event.e2e-spec.ts | 14 +- server/src/event/event.service.ts | 13 + .../src/organization/organization.service.ts | 2 +- 25 files changed, 766 insertions(+), 648 deletions(-) delete mode 100644 game/lib/profile/achievement_cell.dart create mode 100644 game/lib/widget/progress_bar.dart diff --git a/admin/src/components/Events.tsx b/admin/src/components/Events.tsx index 5371d240..a88cb29e 100644 --- a/admin/src/components/Events.tsx +++ b/admin/src/components/Events.tsx @@ -58,8 +58,8 @@ function EventCard(props: { props.event.difficulty === "Easy" ? "Easy" : props.event.difficulty === "Normal" - ? "Normal" - : "Hard"; + ? "Normal" + : "Hard"; let categoryInput = props.event.category as string; const categoryType = @@ -158,8 +158,8 @@ function fromForm(form: EntryForm[], id: string): EventDto { (form[5] as OptionEntryForm).value === 0 ? EventDifficultyDto.Easy : (form[5] as OptionEntryForm).value === 1 - ? EventDifficultyDto.Normal - : EventDifficultyDto.Hard, + ? EventDifficultyDto.Normal + : EventDifficultyDto.Hard, latitudeF: 0, longitudeF: 0, @@ -290,7 +290,7 @@ export function Events() { setCopyModalOpen(false); return; } - for (const chalId of ev.challenges!) { + for (const chalId of [...ev.challenges!].reverse()!) { const chal = serverData.challenges.get(chalId)!; serverData.updateChallenge({ ...chal, diff --git a/game/lib/achievements/achievement_cell.dart b/game/lib/achievements/achievement_cell.dart index 81fd17e5..96c268c8 100644 --- a/game/lib/achievements/achievement_cell.dart +++ b/game/lib/achievements/achievement_cell.dart @@ -2,77 +2,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; - -class LoadingBar extends StatelessWidget { - final int totalTasks; - final int tasksFinished; - - const LoadingBar( - this.tasksFinished, - this.totalTasks, - ); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - Container( - width: 200, - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return Stack(children: [ - Container( - width: constraints.maxWidth, - height: 13, - alignment: Alignment.centerLeft, - child: Container( - decoration: new BoxDecoration( - color: Color.fromARGB(255, 241, 241, 241), - shape: BoxShape.rectangle, - borderRadius: BorderRadius.all(Radius.circular(16.0)), - ), - ), - ), - Container( - width: (totalTasks > 0 ? tasksFinished / totalTasks : 0) * - constraints.maxWidth, - height: 13, - alignment: Alignment.centerLeft, - child: Container( - decoration: new BoxDecoration( - color: Color.fromARGB(197, 237, 86, 86), - shape: BoxShape.rectangle, - borderRadius: BorderRadius.all(Radius.circular(16.0)), - ), - ), - ), - Container( - height: 3, - width: max( - (totalTasks > 0 ? tasksFinished / totalTasks : 0) * - constraints.maxWidth - - 16, - 0), - margin: EdgeInsets.only(left: 8, top: 3), - alignment: Alignment.centerLeft, - decoration: new BoxDecoration( - color: Color(0x99F3C6C6), - shape: BoxShape.rectangle, - borderRadius: BorderRadius.all(Radius.circular(5.0)), - ), - ), - ]); - })), - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Text( - tasksFinished.toString() + "/" + totalTasks.toString(), - ), - ), - ], - ); - } -} +import 'package:game/widget/progress_bar.dart'; class AchievementCell extends StatefulWidget { final SvgPicture thumbnail; @@ -109,7 +39,9 @@ class _AchievementCellState extends State { return GestureDetector( onTap: () async {}, child: Container( - padding: EdgeInsets.all(5), + width: MediaQuery.sizeOf(context).width * 0.85, + height: MediaQuery.sizeOf(context).height * 0.11, + //padding: EdgeInsets.all(5), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15), @@ -122,8 +54,7 @@ class _AchievementCellState extends State { ], ), child: Container( - margin: EdgeInsets.all(10), - height: 64, + padding: EdgeInsets.all(10), child: Row( children: [ Container(margin: EdgeInsets.only(right: 12), child: thumbnail), @@ -134,13 +65,18 @@ class _AchievementCellState extends State { children: [ Align( alignment: Alignment.centerLeft, - child: Text( - description, - style: TextStyle( - color: Color.fromARGB(204, 0, 0, 0), - fontSize: 14, - fontFamily: 'Poppins', - fontWeight: FontWeight.w500, + child: Container( + width: MediaQuery.sizeOf(context).width * 0.45, + child: Text( + description, + maxLines: 2, + overflow: TextOverflow.clip, + style: TextStyle( + color: Color.fromARGB(204, 0, 0, 0), + fontSize: 14, + fontFamily: 'Poppins', + fontWeight: FontWeight.w500, + ), ), ), ), diff --git a/game/lib/api/game_server_api.dart b/game/lib/api/game_server_api.dart index 3dc1c3f0..c59cfe49 100644 --- a/game/lib/api/game_server_api.dart +++ b/game/lib/api/game_server_api.dart @@ -45,8 +45,12 @@ class GameServerApi { completer.complete(arg); }; - Future.delayed(Duration(seconds: 5)) - .then((value) => completer.complete(null)); + Future.delayed(Duration(seconds: 5)).then((value) { + if (completer.isCompleted) { + return; + } + completer.complete(null); + }); _refreshEv = ev; _refreshDat = data; diff --git a/game/lib/challenges/challenge_cell.dart b/game/lib/challenges/challenge_cell.dart index 8ab78f78..73a185a2 100644 --- a/game/lib/challenges/challenge_cell.dart +++ b/game/lib/challenges/challenge_cell.dart @@ -105,9 +105,10 @@ class _ChallengeCellState extends State { ), ], ), - height: 135.0, + height: MediaQuery.sizeOf(context).height * 0.15, + width: MediaQuery.sizeOf(context).width * 0.85, child: Padding( - padding: EdgeInsets.all(16.0), + padding: EdgeInsets.all(16), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -116,7 +117,9 @@ class _ChallengeCellState extends State { child: ClipRRect( borderRadius: BorderRadius.all(Radius.circular(4.6)), child: Image.network(imgUrl, - width: 100, height: 100, fit: BoxFit.cover), + width: MediaQuery.sizeOf(context).height * 0.1, + height: MediaQuery.sizeOf(context).height * 0.1, + fit: BoxFit.cover), ), ), Expanded( @@ -139,9 +142,7 @@ class _ChallengeCellState extends State { ), ], ), - SizedBox( - height: 4, - ), + Spacer(), Text( challengeName, style: TextStyle( @@ -151,9 +152,7 @@ class _ChallengeCellState extends State { fontWeight: FontWeight.w600, ), ), - SizedBox( - height: 12, - ), + Spacer(), Row( mainAxisAlignment: MainAxisAlignment.start, children: [ diff --git a/game/lib/challenges/challenges_page.dart b/game/lib/challenges/challenges_page.dart index 769442b0..589bb22f 100644 --- a/game/lib/challenges/challenges_page.dart +++ b/game/lib/challenges/challenges_page.dart @@ -86,7 +86,7 @@ class _ChallengesPageState extends State { color: Color.fromARGB(255, 255, 248, 241), ), child: Padding( - padding: EdgeInsets.all(30), + padding: EdgeInsets.all(16), child: Stack( children: [ Align( diff --git a/game/lib/gameplay/challenge_completed.dart b/game/lib/gameplay/challenge_completed.dart index 673ca41d..96f817ff 100644 --- a/game/lib/gameplay/challenge_completed.dart +++ b/game/lib/gameplay/challenge_completed.dart @@ -11,98 +11,99 @@ import 'package:game/model/event_model.dart'; import 'package:game/model/tracker_model.dart'; import 'package:game/model/group_model.dart'; import 'package:game/model/challenge_model.dart'; +import 'dart:math'; import 'package:flutter_svg/flutter_svg.dart'; -class ChallengeCompletedPage extends StatefulWidget { - const ChallengeCompletedPage({ - Key? key, - }) : super(key: key); - - @override - State createState() => _ChallengeCompletedState(); -} - class LoadingBar extends StatelessWidget { - final int num_challenges; - final int num_completed; + final int totalTasks; + final int tasksFinished; const LoadingBar( - this.num_completed, - this.num_challenges, + this.tasksFinished, + this.totalTasks, ); @override Widget build(BuildContext context) { - double progress = num_completed / num_challenges; - return Row(mainAxisSize: MainAxisSize.max, children: [ - Expanded( - flex: 8, - child: Stack(children: [ - Container( - width: MediaQuery.sizeOf(context).width * 0.66, - height: 24, - alignment: Alignment.centerLeft, - child: Container( - decoration: new BoxDecoration( - color: Color.fromARGB(255, 241, 241, 241), - shape: BoxShape.rectangle, - borderRadius: BorderRadius.all(Radius.circular(16.0)), + return Row( + children: [ + Container( + width: MediaQuery.sizeOf(context).width * 0.66, + height: 20, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Stack(children: [ + Container( + width: constraints.maxWidth, + height: constraints.maxHeight, + alignment: Alignment.centerLeft, + child: Container( + decoration: new BoxDecoration( + color: Color.fromARGB(220, 237, 86, 86), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(16.0)), + ), + ), + ), + Container( + width: (totalTasks > 0 ? tasksFinished / totalTasks : 0) * + constraints.maxWidth, + height: constraints.maxHeight, + alignment: Alignment.centerLeft, + child: Container( + decoration: new BoxDecoration( + color: Color.fromARGB(197, 237, 86, 86), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(16.0)), + ), + ), + ), + Container( + height: 5, + width: max( + (totalTasks > 0 ? tasksFinished / totalTasks : 0) * + constraints.maxWidth - + 16, + 0), + margin: EdgeInsets.only(left: 8, top: 3), + alignment: Alignment.centerLeft, + decoration: new BoxDecoration( + color: Color(0x99F3C6C6), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(5.0)), + ), ), - ), - ), - Container( - width: - (progress + 0.05) * MediaQuery.sizeOf(context).width * 0.66, - height: 24, - alignment: Alignment.centerLeft, - child: Container( - decoration: new BoxDecoration( - color: Color(0xE6ED5656), - shape: BoxShape.rectangle, - borderRadius: BorderRadius.all(Radius.circular(16.0)), + ]); + })), + Expanded( + flex: 2, + child: Row(children: [ + Text(" "), + SvgPicture.asset("assets/icons/pin.svg"), + Text( + " " + tasksFinished.toString() + "/" + totalTasks.toString(), + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + fontWeight: FontWeight.bold, ), - ), - ), - ]) - // child: Container( - // clipBehavior: Clip.hardEdge, - // decoration: BoxDecoration( - // borderRadius: BorderRadius.circular(15.0), - // ), - // height: 20.0, - // width: double.infinity, - // child: Stack( - // alignment: Alignment.centerLeft, - // children: [ - // Positioned.fill( - // child: LinearProgressIndicator( - // value: progress, - // color: Color(0xE6ED5656), - // backgroundColor: Color(0xFFF1F1F1)), - // ), - // ], - // ), - // ), - ), - Expanded( - flex: 2, - child: Row(children: [ - Text(" "), - SvgPicture.asset("assets/icons/pin.svg"), - Text( - num_completed.toString() + "/" + num_challenges.toString(), - style: TextStyle( - color: Colors.white, - fontSize: 16.0, - fontWeight: FontWeight.bold, - ), - ) - ])) - ]); + ) + ])) + ], + ); } } +class ChallengeCompletedPage extends StatefulWidget { + const ChallengeCompletedPage({ + Key? key, + }) : super(key: key); + + @override + State createState() => _ChallengeCompletedState(); +} + class _ChallengeCompletedState extends State { bool journeyPage = false; bool journeyCompleted = false; @@ -366,139 +367,160 @@ class _ChallengeCompletedState extends State { fontSize: 25.0, fontWeight: FontWeight.bold, ), - ) - ], - )), - journeyPage - ? Container( - alignment: Alignment.bottomCenter, - margin: EdgeInsets.all(40), - child: journeyCompleted - ? ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color.fromARGB(255, 237, 86, 86), - padding: EdgeInsets.only( - right: 15, left: 15, top: 10, bottom: 10), - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(10), // button's shape, - ), - ), - child: Row(mainAxisSize: MainAxisSize.min, children: [ - Text( - "Return Home ", - style: TextStyle( - fontFamily: 'Poppins', - fontSize: 21, - fontWeight: FontWeight.w400, - color: Color(0xFFFFFFFF)), - ), - SvgPicture.asset("assets/icons/forwardcarrot.svg") - ]), - onPressed: () { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => BottomNavBar())); - }, - ) - : Row( - children: [ - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: - Color.fromARGB(0, 255, 255, 255), - shadowColor: Color.fromARGB(0, 255, 255, 255), - padding: EdgeInsets.only( - right: 15, left: 15, top: 10, bottom: 10), - shape: RoundedRectangleBorder( - side: BorderSide(color: Colors.white), - borderRadius: BorderRadius.circular( - 10), // button's shape, + ), + Spacer(), + journeyPage + ? Container( + alignment: Alignment.bottomCenter, + margin: EdgeInsets.only( + bottom: MediaQuery.sizeOf(context).height * 0.05), + child: journeyCompleted + ? ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + Color.fromARGB(255, 237, 86, 86), + padding: EdgeInsets.only( + right: 15, left: 15, top: 10, bottom: 10), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 10), // button's shape, + ), ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Return Home ", + style: TextStyle( + fontFamily: 'Poppins', + fontSize: 21, + fontWeight: FontWeight.w400, + color: Color(0xFFFFFFFF)), + ), + SvgPicture.asset( + "assets/icons/forwardcarrot.svg") + ]), + onPressed: () { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + BottomNavBar())); + }, + ) + : Row( + children: [ + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + Color.fromARGB(0, 255, 255, 255), + shadowColor: + Color.fromARGB(0, 255, 255, 255), + padding: EdgeInsets.only( + right: 15, + left: 15, + top: 10, + bottom: 10), + shape: RoundedRectangleBorder( + side: BorderSide(color: Colors.white), + borderRadius: BorderRadius.circular( + 10), // button's shape, + ), + ), + onPressed: () => + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + BottomNavBar())), + child: Text( + "Leave", + style: TextStyle( + fontFamily: 'Poppins', + fontSize: 20, + fontWeight: FontWeight.w400, + color: Color(0xFFFFFFFF)), + )), + Spacer(), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: + Color.fromARGB(255, 237, 86, 86), + padding: EdgeInsets.only( + right: 15, + left: 15, + top: 10, + bottom: 10), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 10), // button's shape, + ), + ), + onPressed: () => Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => + GameplayPage())), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Next Challenge ", + style: TextStyle( + fontFamily: 'Poppins', + fontSize: 20, + fontWeight: FontWeight.w400, + color: Color(0xFFFFFFFF)), + ), + SvgPicture.asset( + "assets/icons/forwardcarrot.svg") + ]), + ), + ], ), - onPressed: () => Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => BottomNavBar())), - child: Text( - "Leave", - style: TextStyle( - fontFamily: 'Poppins', - fontSize: 20, - fontWeight: FontWeight.w400, - color: Color(0xFFFFFFFF)), - )), - Spacer(), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color.fromARGB(255, 237, 86, 86), - padding: EdgeInsets.only( - right: 15, left: 15, top: 10, bottom: 10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 10), // button's shape, - ), + ) + : Container( + alignment: Alignment.bottomCenter, + margin: EdgeInsets.only( + bottom: MediaQuery.sizeOf(context).height * 0.05), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Color.fromARGB(255, 237, 86, 86), + padding: EdgeInsets.only( + right: 15, left: 15, top: 10, bottom: 10), + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(10), // button's shape, ), - onPressed: () => Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => GameplayPage())), - child: - Row(mainAxisSize: MainAxisSize.min, children: [ - Text( - "Next Challenge ", - style: TextStyle( - fontFamily: 'Poppins', - fontSize: 20, - fontWeight: FontWeight.w400, - color: Color(0xFFFFFFFF)), - ), - SvgPicture.asset("assets/icons/forwardcarrot.svg") - ]), ), - ], + child: Row(mainAxisSize: MainAxisSize.min, children: [ + Text( + ((event?.challenges?.length ?? 0) > 1) + ? "Journey Progress " + : "Return Home ", + style: TextStyle( + fontFamily: 'Poppins', + fontSize: 21, + fontWeight: FontWeight.w400, + color: Color(0xFFFFFFFF)), + ), + SvgPicture.asset("assets/icons/forwardcarrot.svg") + ]), + onPressed: () { + if ((event?.challenges?.length ?? 0) > 1) { + journeyPage = true; + setState(() {}); + } else { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => BottomNavBar())); + } + }, + ), ), - ) - : Container( - alignment: Alignment.bottomCenter, - margin: EdgeInsets.only(bottom: 40), - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: Color.fromARGB(255, 237, 86, 86), - padding: EdgeInsets.only( - right: 15, left: 15, top: 10, bottom: 10), - shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(10), // button's shape, - ), - ), - child: Row(mainAxisSize: MainAxisSize.min, children: [ - Text( - ((event?.challenges?.length ?? 0) > 1) - ? "Journey Progress " - : "Return Home ", - style: TextStyle( - fontFamily: 'Poppins', - fontSize: 21, - fontWeight: FontWeight.w400, - color: Color(0xFFFFFFFF)), - ), - SvgPicture.asset("assets/icons/forwardcarrot.svg") - ]), - onPressed: () { - if ((event?.challenges?.length ?? 0) > 1) { - journeyPage = true; - setState(() {}); - } else { - Navigator.pushReplacement( - context, - MaterialPageRoute( - builder: (context) => BottomNavBar())); - } - }, - ), - ), + ], + )), ])); }); } diff --git a/game/lib/gameplay/gameplay_map.dart b/game/lib/gameplay/gameplay_map.dart index d3e83cf8..3c8a8299 100644 --- a/game/lib/gameplay/gameplay_map.dart +++ b/game/lib/gameplay/gameplay_map.dart @@ -70,10 +70,6 @@ class _GameplayMapState extends State { // whether the picture is expanded over the map bool isExpanded = false; - // name of the challenge, either null or the name of the most recently - // completed challenge in this event - String? challengeName; - @override void initState() { setCustomMarkerIcon(); @@ -103,7 +99,6 @@ class _GameplayMapState extends State { * hints used for this challenge already. */ void setStartingHintCircle() { - print(widget.startingHintsUsed); hintRadius = defaultHintRadius - (defaultHintRadius - widget.awardingRadius) * 0.33 * @@ -111,7 +106,6 @@ class _GameplayMapState extends State { if (hintRadius == null) { hintRadius = defaultHintRadius; } - print(hintRadius); Random _random = Random(); @@ -193,6 +187,10 @@ class _GameplayMapState extends State { void recenterCamera() async { GoogleMapController googleMapController = await mapCompleter.future; + if (currentLocation == null) { + return; + } + // recenters camera to user location googleMapController.animateCamera( CameraUpdate.newCameraPosition( @@ -422,14 +420,9 @@ class _GameplayMapState extends State { } else { apiClient.serverApi ?.completedChallenge(CompletedChallengeDto()); - setState(() { - challengeName = challengeModel - .getChallengeById( - tracker.prevChallenges.last.challengeId) - ?.name; - }); } } + final chalId = widget.challengeId; showDialog( context: context, barrierDismissible: !hasArrived, @@ -441,7 +434,7 @@ class _GameplayMapState extends State { child: ClipRRect( borderRadius: BorderRadius.circular( 10), // Same as the Dialog's shape - child: displayDialogue(hasArrived), + child: displayDialog(context, hasArrived, chalId), ), ), ); @@ -581,11 +574,19 @@ class _GameplayMapState extends State { /** Returns whether the user is at the challenge location */ bool checkArrived() { + if (currentLocation == null) { + return false; + } return currentLocation!.distanceTo(widget.targetLocation) <= widget.awardingRadius; } - Container displayDialogue(bool hasArrived) { + Container displayDialog( + BuildContext context, hasArrived, String challengeId) { + final name = Provider.of(context) + .getChallengeById(challengeId) + ?.name ?? + ""; return hasArrived ? Container( // margin: EdgeInsetsDirectional.only(start: 50, end: 50), @@ -605,7 +606,7 @@ class _GameplayMapState extends State { Container( margin: EdgeInsets.only(bottom: 10), child: Text( - "You've arrived at ${challengeName ?? ""}!", + "You've arrived at ${name}!", style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400), )), @@ -613,7 +614,7 @@ class _GameplayMapState extends State { margin: EdgeInsets.only(bottom: 10), width: MediaQuery.of(context).size.width, decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(5.0))), + borderRadius: BorderRadius.all(Radius.circular(10.0))), child: SvgPicture.asset('assets/images/arrived.svg', fit: BoxFit.cover), ), @@ -663,15 +664,22 @@ class _GameplayMapState extends State { Container( margin: EdgeInsets.only(bottom: 10), child: Text( - "You’re close, but not there yet. Use a hint if needed! Hints use 25 points.", + "You’re close, but not there yet." + + (numHintsLeft > 0 + ? "Use a hint if needed! Hints use 25 points." + : ""), style: TextStyle( fontSize: 14, fontWeight: FontWeight.w400))), - Row(children: [ + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () => Navigator.pop(context, false), style: ButtonStyle( padding: MaterialStateProperty.all( - EdgeInsets.only(left: 15, right: 15)), + EdgeInsets.symmetric( + horizontal: + (MediaQuery.devicePixelRatioOf(context) < 3 + ? 10 + : 20))), shape: MaterialStateProperty.all( RoundedRectangleBorder( @@ -691,25 +699,36 @@ class _GameplayMapState extends State { ), child: Text("Nevermind", style: TextStyle( + fontSize: + MediaQuery.devicePixelRatioOf(context) < 3 + ? 12 + : 14, color: Color.fromARGB(255, 237, 86, 86)))), - Spacer(), - ElevatedButton( - onPressed: () => - {useHint(), Navigator.pop(context, false)}, - style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.only(left: 20, right: 20)), - shape: - MaterialStateProperty.all( - RoundedRectangleBorder( - borderRadius: BorderRadius.circular(7.3), + if (numHintsLeft > 0) Spacer(), + if (numHintsLeft > 0) + ElevatedButton( + onPressed: () => + {useHint(), Navigator.pop(context, false)}, + style: ButtonStyle( + padding: + MaterialStateProperty.all( + EdgeInsets.only(left: 20, right: 20)), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(7.3), + ), ), + backgroundColor: MaterialStateProperty.all( + Color.fromARGB(255, 237, 86, 86)), ), - backgroundColor: MaterialStateProperty.all( - Color.fromARGB(255, 237, 86, 86)), - ), - child: Text("Use Hint (${numHintsLeft} Left)", - style: TextStyle(color: Colors.white))), + child: Text("Use Hint (${numHintsLeft} Left)", + style: TextStyle( + fontSize: + MediaQuery.devicePixelRatioOf(context) < 3 + ? 12 + : 14, + color: Colors.white))), ]) ], ), diff --git a/game/lib/gameplay/gameplay_page.dart b/game/lib/gameplay/gameplay_page.dart index 200719da..cca28ec7 100644 --- a/game/lib/gameplay/gameplay_page.dart +++ b/game/lib/gameplay/gameplay_page.dart @@ -191,7 +191,7 @@ class _GameplayPageState extends State { (abbrevLocation[challenge.location] ?? ""), style: TextStyle( - fontSize: 13, + fontSize: 14, color: Color(0xFF835A7C))) ]), Row(children: [ @@ -207,7 +207,7 @@ class _GameplayPageState extends State { : "?.?") + ' Mi Away', style: TextStyle( - fontSize: 13, + fontSize: 14, color: Color(0xFF58B171))) ]), Row(children: [ @@ -224,7 +224,7 @@ class _GameplayPageState extends State { (challenge.points ?? 0).toString() + " PTS", style: TextStyle( - fontSize: 13, + fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xFFC17E19))) ]), diff --git a/game/lib/global_leaderboard/global_leaderboard_widget.dart b/game/lib/global_leaderboard/global_leaderboard_widget.dart index f2b84fd3..73c1f05e 100644 --- a/game/lib/global_leaderboard/global_leaderboard_widget.dart +++ b/game/lib/global_leaderboard/global_leaderboard_widget.dart @@ -137,8 +137,8 @@ class _GlobalLeaderboardWidgetState extends State { SizedBox(height: MediaQuery.sizeOf(context).height * 0.01), Expanded( child: Container( - width: 360.0, - height: 446.0, + width: MediaQuery.sizeOf(context).width * 0.88, + height: MediaQuery.sizeOf(context).height * 0.5, decoration: BoxDecoration( color: Color.fromRGBO(255, 170, 91, 0.15), borderRadius: BorderRadius.only( diff --git a/game/lib/journeys/journey_cell.dart b/game/lib/journeys/journey_cell.dart index eea479be..9fa7d3d8 100644 --- a/game/lib/journeys/journey_cell.dart +++ b/game/lib/journeys/journey_cell.dart @@ -2,6 +2,95 @@ import 'package:flutter/material.dart'; import 'package:game/preview/preview.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'dart:math'; + +class LoadingBar extends StatelessWidget { + final int totalTasks; + final int tasksFinished; + + const LoadingBar( + this.tasksFinished, + this.totalTasks, + ); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Container( + width: MediaQuery.sizeOf(context).width * 0.7, + height: 22, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Stack(children: [ + Container( + width: constraints.maxWidth, + height: constraints.maxHeight, + alignment: Alignment.centerLeft, + child: Container( + decoration: new BoxDecoration( + color: Color.fromARGB(255, 241, 241, 241), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(16.0)), + ), + ), + ), + Container( + width: (totalTasks > 0 ? tasksFinished / totalTasks : 0) * + constraints.maxWidth, + height: constraints.maxHeight, + alignment: Alignment.centerLeft, + child: Container( + decoration: new BoxDecoration( + color: Color.fromARGB(197, 237, 86, 86), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(16.0)), + ), + ), + ), + Container( + height: 5, + width: max( + (totalTasks > 0 ? tasksFinished / totalTasks : 0) * + constraints.maxWidth - + 16, + 0), + margin: EdgeInsets.only(left: 8, top: 3), + alignment: Alignment.centerLeft, + decoration: new BoxDecoration( + color: Color(0x99F3C6C6), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(5.0)), + ), + ), + ]); + })), + Container( + //width: 50, + child: Row(children: [ + SizedBox( + width: 8, + ), + SvgPicture.asset("assets/icons/pin.svg"), + SizedBox( + width: 5, + ), + Text( + tasksFinished.toString() + "/" + totalTasks.toString(), + style: TextStyle( + color: Color.fromARGB(255, 110, 110, 110), + fontSize: 16, + fontWeight: FontWeight.w600, + fontFamily: 'Poppins', + ), + ), + ]), + ) + ], + ); + } +} + class JourneyCell extends StatefulWidget { final int locationCount; final String location; @@ -251,61 +340,62 @@ class _JourneyCellState extends State { ), Padding( padding: const EdgeInsets.only(top: 15.0, bottom: 5), - child: Row( - children: [ - Stack(children: [ - Container( - width: MediaQuery.sizeOf(context).width * 0.66, - height: 22, - alignment: Alignment.centerLeft, - child: Container( - decoration: new BoxDecoration( - color: Color.fromARGB(255, 241, 241, 241), - shape: BoxShape.rectangle, - borderRadius: - BorderRadius.all(Radius.circular(16.0)), - ), - ), - ), - Container( - width: (locationCount > 0 - ? numberCompleted / locationCount - : 0) * - MediaQuery.sizeOf(context).width * - 0.66, - height: 20, - alignment: Alignment.centerLeft, - child: Container( - decoration: new BoxDecoration( - color: Color.fromARGB(191, 237, 86, 86), - shape: BoxShape.rectangle, - borderRadius: - BorderRadius.all(Radius.circular(16.0)), - ), - ), - ), - ]), - SizedBox(width: 8), - Container( - width: 50, - child: Row(children: [ - SvgPicture.asset("assets/icons/pin.svg"), - Text( - " " + - numberCompleted.toString() + - "/" + - locationCount.toString(), - style: TextStyle( - color: Color.fromARGB(255, 110, 110, 110), - fontSize: 16, - fontWeight: FontWeight.w600, - fontFamily: 'Poppins', - ), - ), - ]), - ) - ], - ), + child: LoadingBar(numberCompleted, locationCount), + // child: Row( + // children: [ + // Stack(children: [ + // Container( + // width: MediaQuery.sizeOf(context).width * 0.66, + // height: 22, + // alignment: Alignment.centerLeft, + // child: Container( + // decoration: new BoxDecoration( + // color: Color.fromARGB(255, 241, 241, 241), + // shape: BoxShape.rectangle, + // borderRadius: + // BorderRadius.all(Radius.circular(16.0)), + // ), + // ), + // ), + // Container( + // width: (locationCount > 0 + // ? numberCompleted / locationCount + // : 0) * + // MediaQuery.sizeOf(context).width * + // 0.66, + // height: 20, + // alignment: Alignment.centerLeft, + // child: Container( + // decoration: new BoxDecoration( + // color: Color.fromARGB(191, 237, 86, 86), + // shape: BoxShape.rectangle, + // borderRadius: + // BorderRadius.all(Radius.circular(16.0)), + // ), + // ), + // ), + // ]), + // SizedBox(width: 8), + // Container( + // width: 50, + // child: Row(children: [ + // SvgPicture.asset("assets/icons/pin.svg"), + // Text( + // " " + + // numberCompleted.toString() + + // "/" + + // locationCount.toString(), + // style: TextStyle( + // color: Color.fromARGB(255, 110, 110, 110), + // fontSize: 16, + // fontWeight: FontWeight.w600, + // fontFamily: 'Poppins', + // ), + // ), + // ]), + // ) + // ], + // ), ), ], ), diff --git a/game/lib/model/user_model.dart b/game/lib/model/user_model.dart index 15d2123d..ffcf6cf5 100644 --- a/game/lib/model/user_model.dart +++ b/game/lib/model/user_model.dart @@ -16,7 +16,6 @@ class UserModel extends ChangeNotifier { */ client.clientApi.updateUserDataStream.listen((event) { if (userData == null) userData = event.user; - userData?.partialUpdate(event.user); notifyListeners(); }); diff --git a/game/lib/navigation_page/home_navbar.dart b/game/lib/navigation_page/home_navbar.dart index 344beabb..daad2628 100644 --- a/game/lib/navigation_page/home_navbar.dart +++ b/game/lib/navigation_page/home_navbar.dart @@ -39,7 +39,8 @@ class _HomeNavbarState extends State with TickerProviderStateMixin { backgroundColor: Color(0xFFED5656), resizeToAvoidBottomInset: false, appBar: PreferredSize( - preferredSize: Size.fromHeight(50.0), + preferredSize: + Size.fromHeight(MediaQuery.sizeOf(context).height * 0.07), child: AppBar( elevation: 0.0, backgroundColor: Color(0xFFED5656), @@ -49,7 +50,7 @@ class _HomeNavbarState extends State with TickerProviderStateMixin { indicator: BoxDecoration( border: Border( bottom: BorderSide( - color: Colors.yellow, + color: Color(0xFFFFAA5B), width: 2.0, ), ), @@ -65,10 +66,11 @@ class _HomeNavbarState extends State with TickerProviderStateMixin { ), labelColor: Colors.white, unselectedLabelColor: Colors.black.withOpacity(0.5), - tabs: const [ + tabs: [ Tab( child: Padding( - padding: EdgeInsets.only(bottom: 20), + padding: EdgeInsets.only( + bottom: MediaQuery.sizeOf(context).height * 0.02), child: Text( 'Challenges', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), @@ -77,7 +79,8 @@ class _HomeNavbarState extends State with TickerProviderStateMixin { ), Tab( child: Padding( - padding: EdgeInsets.only(bottom: 20), + padding: EdgeInsets.only( + bottom: MediaQuery.sizeOf(context).height * 0.02), child: Text( 'Journeys', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), diff --git a/game/lib/navigation_page/search_filter_home.dart b/game/lib/navigation_page/search_filter_home.dart index 7dc199e6..3a44fbd2 100644 --- a/game/lib/navigation_page/search_filter_home.dart +++ b/game/lib/navigation_page/search_filter_home.dart @@ -59,85 +59,88 @@ class _SearchFilterBarState extends State // padding: EdgeInsets.only(top: 50), height: MediaQuery.sizeOf(context).height * 0.175, - child: Column( - children: [ - Spacer(), - Stack( - children: [ - SingleChildScrollView( - physics: NeverScrollableScrollPhysics(), - child: Container( - decoration: BoxDecoration( - color: Color.fromARGB(255, 255, 248, 241), - borderRadius: BorderRadius.circular(30), - ), - child: SizedBox( - width: 345, - height: 45, - child: TextField( - onSubmitted: onSearchTextChanged, - decoration: InputDecoration( - border: InputBorder.none, - prefixIcon: Icon( - Icons.search, - color: Color.fromARGB(76, 0, 0, 0), - size: 20, - ), - labelText: "Search a name, location, etc...", - labelStyle: TextStyle( - // color: Color(0xFFB9B9B9), - fontSize: 12, - fontFamily: 'Poppins', - // backgroundColor: - // Color.fromARGB(255, 255, 248, 241), + child: SafeArea( + child: Column( + children: [ + Spacer(), + Stack( + children: [ + SingleChildScrollView( + physics: NeverScrollableScrollPhysics(), + child: Container( + decoration: BoxDecoration( + color: Color.fromARGB(255, 255, 248, 241), + borderRadius: BorderRadius.circular(30), + ), + child: SizedBox( + width: MediaQuery.sizeOf(context).width * 0.9, + height: 45, + child: TextField( + onSubmitted: onSearchTextChanged, + decoration: InputDecoration( + border: InputBorder.none, + prefixIcon: Icon( + Icons.search, + color: Color.fromARGB(76, 0, 0, 0), + size: 20, + ), + labelText: "Search a name, location, etc...", + labelStyle: TextStyle( + // color: Color(0xFFB9B9B9), + fontSize: 12, + fontFamily: 'Poppins', + // backgroundColor: + // Color.fromARGB(255, 255, 248, 241), + ), ), ), ), ), ), - ), - Positioned( - right: 0, - top: 0, - bottom: 0, - child: Container( - width: 50, - height: 36, - child: IconButton( - icon: SvgPicture.asset( - 'assets/icons/Group 578.svg', + Positioned( + right: 0, + top: 0, + bottom: 0, + child: Container( + width: 50, + height: 36, + child: IconButton( + icon: SvgPicture.asset( + 'assets/icons/Group 578.svg', + ), + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => FilterForm( + onSubmit: handleFilterSubmit, + difficulty: selectedDifficulty, + locations: selectedLocations, + categories: selectedCategories, + ), + ); + }, ), - onPressed: () { - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (context) => FilterForm( - onSubmit: handleFilterSubmit, - difficulty: selectedDifficulty, - locations: selectedLocations, - categories: selectedCategories, - ), - ); - }, ), ), - ), - ], - ), - Column( - children: [ - Container( - height: MediaQuery.sizeOf(context).height * 0.825, - child: HomeNavBar( - difficulty: selectedDifficulty, - locations: selectedLocations, - categories: selectedCategories, - searchText: searchText, + ], + ), + Spacer(), + Column( + children: [ + Container( + height: MediaQuery.sizeOf(context).height * 0.8, + child: HomeNavBar( + difficulty: selectedDifficulty, + locations: selectedLocations, + categories: selectedCategories, + searchText: searchText, + ), ), - ), - ], - ), - ], + ], + ), + ], + ), ), ), ), diff --git a/game/lib/preview/preview.dart b/game/lib/preview/preview.dart index 3fcda2ac..b5d0ea7e 100644 --- a/game/lib/preview/preview.dart +++ b/game/lib/preview/preview.dart @@ -9,6 +9,70 @@ import 'package:game/api/geopoint.dart'; import 'dart:async'; import 'package:flutter_svg/flutter_svg.dart'; +import 'dart:math'; + +class LoadingBar extends StatelessWidget { + final int totalTasks; + final int tasksFinished; + + const LoadingBar( + this.tasksFinished, + this.totalTasks, + ); + + @override + Widget build(BuildContext context) { + return Container( + width: MediaQuery.sizeOf(context).width * 0.9, + height: 24, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Stack(children: [ + Container( + width: constraints.maxWidth, + height: constraints.maxHeight, + alignment: Alignment.centerLeft, + child: Container( + decoration: new BoxDecoration( + color: Color.fromARGB(255, 241, 241, 241), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(16.0)), + ), + ), + ), + Container( + width: (totalTasks > 0 ? tasksFinished / totalTasks : 0) * + constraints.maxWidth, + height: constraints.maxHeight, + alignment: Alignment.centerLeft, + child: Container( + decoration: new BoxDecoration( + color: Color.fromARGB(197, 237, 86, 86), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(16.0)), + ), + ), + ), + Container( + height: 5, + width: max( + (totalTasks > 0 ? tasksFinished / totalTasks : 0) * + constraints.maxWidth - + 16, + 0), + margin: EdgeInsets.only(left: 8, top: 3), + alignment: Alignment.centerLeft, + decoration: new BoxDecoration( + color: Color(0x99F3C6C6), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(5.0)), + ), + ), + ]); + })); + } +} + enum PreviewType { CHALLENGE, JOURNEY } /** Returns a preview of a challenge given the challenge name, description, @@ -323,11 +387,18 @@ class _PreviewState extends State { width: 40, height: 40, ), - Text(points.toString() + " PTS", - style: TextStyle( - fontSize: 23, - fontWeight: FontWeight.w500, - color: Color(0xFFC17E19))) + Padding( + padding: const EdgeInsets.only( + left: 4), + child: Text( + points.toString() + " PTS", + style: TextStyle( + fontSize: 20, + fontWeight: + FontWeight.w500, + color: + Color(0xFFC17E19))), + ) ]), )), ], @@ -342,38 +413,8 @@ class _PreviewState extends State { Padding( padding: const EdgeInsets.symmetric( horizontal: 25, vertical: 5), - child: Stack(children: [ - Container( - width: MediaQuery.sizeOf(context).width * 0.9, - height: 24, - alignment: Alignment.centerLeft, - child: Container( - decoration: new BoxDecoration( - color: Color.fromARGB(255, 241, 241, 241), - shape: BoxShape.rectangle, - borderRadius: BorderRadius.all( - Radius.circular(16.0)), - ), - ), - ), - Container( - width: (locationCount > 0 - ? numberCompleted / locationCount - : 0) * - MediaQuery.sizeOf(context).width * - 0.9, - height: 24, - alignment: Alignment.centerLeft, - child: Container( - decoration: new BoxDecoration( - color: backgroundRedMuted, - shape: BoxShape.rectangle, - borderRadius: BorderRadius.all( - Radius.circular(16.0)), - ), - ), - ), - ])), + child: + LoadingBar(numberCompleted, locationCount)), Padding( padding: const EdgeInsets.only( left: 25, right: 25, bottom: 15, top: 3), diff --git a/game/lib/profile/achievement_cell.dart b/game/lib/profile/achievement_cell.dart deleted file mode 100644 index 3749f720..00000000 --- a/game/lib/profile/achievement_cell.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:flutter/material.dart'; - -@Deprecated('achievements/achievement_cell.dart') -/** - * Widget that represents each individual achievement - * @param name: Name of the achievement - * @param tasksFinished: Number of currently completed tasks towards the achievement - * @param totalTasks: Total number of tasks needed to gain achievement - * @param picture: picture that is associated with the achievement - */ -Widget achievementCell( - String name, int tasksFinished, int totalTasks, String picture) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Container( - width: 345, - height: 88, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10.0), - boxShadow: [ - BoxShadow( - color: Color.fromRGBO(0, 0, 0, 0.25), - offset: Offset(0, 4), - blurRadius: 4, - ), - ]), - child: Row( - children: [ - Padding( - padding: const EdgeInsets.all(10), - child: ClipRRect( - borderRadius: BorderRadius.circular(20), - child: Image( - width: 80, - height: 80, - image: AssetImage(picture), - fit: BoxFit.cover, - )), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Container( - width: 215, - child: Text( - name, - overflow: TextOverflow.ellipsis, - maxLines: 2, - style: TextStyle( - fontWeight: FontWeight.w500, - ), - ), - ), - ], - ), - ), - Row( - children: [ - Stack(children: [ - Container( - width: 170, - height: 13, - alignment: Alignment.centerLeft, - child: Container( - decoration: new BoxDecoration( - color: Color.fromARGB(255, 241, 241, 241), - shape: BoxShape.rectangle, - borderRadius: - BorderRadius.all(Radius.circular(16.0)), - ), - ), - ), - Container( - width: - (totalTasks > 0 ? tasksFinished / totalTasks : 0) * - 170, - height: 13, - alignment: Alignment.centerLeft, - child: Container( - decoration: new BoxDecoration( - color: Color.fromARGB(197, 237, 86, 86), - shape: BoxShape.rectangle, - borderRadius: - BorderRadius.all(Radius.circular(16.0)), - ), - ), - ), - ]), - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Text( - tasksFinished.toString() + "/" + totalTasks.toString(), - ), - ), - ], - ), - ], - ) - ], - )), - ); -} diff --git a/game/lib/profile/completed_cell.dart b/game/lib/profile/completed_cell.dart index 194ec6ed..246f8437 100644 --- a/game/lib/profile/completed_cell.dart +++ b/game/lib/profile/completed_cell.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:velocity_x/velocity_x.dart'; /** * Widget that represents each individual completed journey or challenge @@ -10,11 +11,18 @@ import 'package:flutter_svg/flutter_svg.dart'; * @param points: Points that the completed journey / challenge had */ -Widget completedCell(String name, String picture, String type, String date, - String difficulty, int points) { +Widget completedCell( + BuildContext context, + String name, + String picture, + String type, + String date, + String difficulty, + int totalHintsUsed, + int points) { return Container( - width: 345, - height: 88, + width: MediaQuery.sizeOf(context).width * 0.85, + height: MediaQuery.sizeOf(context).height * 0.11, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10.0), @@ -72,7 +80,12 @@ Widget completedCell(String name, String picture, String type, String date, "assets/icons/bearcoins.svg", width: 20, ), - Text(' ' + points.toString() + " PTS", + Text( + ' ' + + (points - totalHintsUsed * 25).toString() + + "/" + + points.toString() + + " PTS", style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, diff --git a/game/lib/profile/profile_page.dart b/game/lib/profile/profile_page.dart index 767132e9..557321e9 100644 --- a/game/lib/profile/profile_page.dart +++ b/game/lib/profile/profile_page.dart @@ -48,7 +48,7 @@ class _ProfilePageState extends State { var isGuest = userModel.userData?.authType == UserAuthTypeDto.device; var score = userModel.userData?.score; - List> completedEvents = []; + List> completedEvents = []; //Get completed events for (var eventId in userModel.userData!.trackedEvents!) { @@ -70,7 +70,12 @@ class _ProfilePageState extends State { var completedDate = tracker.prevChallenges.last.dateCompleted; DateTime date = DateFormat("E, d MMM y HH:mm:ss").parse(completedDate); - completedEvents.add(Tuple2(date, event)); + int totalHintsUsed = 0; + for (var prev in tracker.prevChallenges) { + totalHintsUsed += prev.hintsUsed; + } + completedEvents.add( + Tuple3(date, event, totalHintsUsed)); } catch (e) { displayToast("Error with completing challenge", Status.error); } @@ -179,11 +184,12 @@ class _ProfilePageState extends State { } var date = completedEvents[index].item1; var event = completedEvents[index].item2; + var hintsUsed = completedEvents[index].item3; String formattedDate = DateFormat("MMMM d, y").format(date); var type = event.challenges!.length > 1 ? "Journeys" : "Challenge"; - //Calculate totalPoints. + // Calculate totalPoints. var totalPoints = 0; var locationImage; for (var challengeId in event.challenges ?? []) { @@ -193,18 +199,19 @@ class _ProfilePageState extends State { if (locationImage == null || locationImage.length == 0) locationImage = "https://upload.wikimedia.org/wikipedia/commons/b/b1/Missing-image-232x150.png"; - if (challenge != null) { totalPoints += challenge.points ?? 0; } } return completedCell( + context, event.name!, locationImage, type, formattedDate, friendlyDifficulty[event.difficulty]!, + hintsUsed, totalPoints); }, physics: BouncingScrollPhysics(), diff --git a/game/lib/profile/settings_page.dart b/game/lib/profile/settings_page.dart index e6da3fa2..b0686bcc 100644 --- a/game/lib/profile/settings_page.dart +++ b/game/lib/profile/settings_page.dart @@ -6,6 +6,10 @@ import 'package:game/profile/edit_profile.dart'; import 'package:game/main.dart'; import 'package:game/utils/utility_functions.dart'; import 'package:provider/provider.dart'; +import 'package:url_launcher/url_launcher.dart'; + +final FEEDBACK_URL = Uri.parse( + "https://docs.google.com/forms/d/e/1FAIpQLSczSG6iJ_yv6zqlqNJWrtvSB7aupTiIvYGmy7nAzKKKPal-5g/viewform"); class SettingsPage extends StatelessWidget { final bool isGuest; @@ -102,7 +106,9 @@ class SettingsPage extends StatelessWidget { ), ), child: TextButton( - onPressed: () {}, + onPressed: () { + launchUrl(FEEDBACK_URL); + }, style: TextButton.styleFrom( padding: EdgeInsets.only(left: 20.0), alignment: Alignment.centerLeft, @@ -143,7 +149,7 @@ class SettingsPage extends StatelessWidget { (text) { client.serverApi?.joinOrganization( JoinOrganizationDto( - accessCode: text.toUpperCase())); + accessCode: text.toLowerCase())); }); }, style: TextButton.styleFrom( diff --git a/game/lib/widget/leaderboard_cell.dart b/game/lib/widget/leaderboard_cell.dart index e0a3976d..fdedca27 100644 --- a/game/lib/widget/leaderboard_cell.dart +++ b/game/lib/widget/leaderboard_cell.dart @@ -76,13 +76,9 @@ Widget leaderBoardCell( ), Padding( padding: const EdgeInsets.only(left: 10, right: 5), - child: SizedBox( - width: 110, - child: FittedBox( - alignment: Alignment.centerLeft, - fit: BoxFit.scaleDown, - child: Text(name, style: nameStyle), - ), + child: Align( + alignment: Alignment.centerLeft, + child: Text(name, style: nameStyle), ), ), ], diff --git a/game/lib/widget/progress_bar.dart b/game/lib/widget/progress_bar.dart new file mode 100644 index 00000000..b357d796 --- /dev/null +++ b/game/lib/widget/progress_bar.dart @@ -0,0 +1,75 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +class LoadingBar extends StatelessWidget { + final int totalTasks; + final int tasksFinished; + + const LoadingBar( + this.tasksFinished, + this.totalTasks, + ); + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Container( + width: MediaQuery.sizeOf(context).width * 0.47, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Stack(children: [ + Container( + width: constraints.maxWidth, + height: 13, + alignment: Alignment.centerLeft, + child: Container( + decoration: new BoxDecoration( + color: Color.fromARGB(255, 241, 241, 241), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(16.0)), + ), + ), + ), + Container( + width: (totalTasks > 0 ? tasksFinished / totalTasks : 0) * + constraints.maxWidth, + height: 13, + alignment: Alignment.centerLeft, + child: Container( + decoration: new BoxDecoration( + color: Color.fromARGB(197, 237, 86, 86), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(16.0)), + ), + ), + ), + Container( + height: 3, + width: max( + (totalTasks > 0 ? tasksFinished / totalTasks : 0) * + constraints.maxWidth - + 16, + 0), + margin: EdgeInsets.only(left: 8, top: 3), + alignment: Alignment.centerLeft, + decoration: new BoxDecoration( + color: Color(0x99F3C6C6), + shape: BoxShape.rectangle, + borderRadius: BorderRadius.all(Radius.circular(5.0)), + ), + ), + ]); + })), + Padding( + padding: const EdgeInsets.only(left: 8.0), + child: Text( + tasksFinished.toString() + "/" + totalTasks.toString(), + ), + ), + ], + ); + } +} diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma index 0aa24ed4..04e1bf55 100644 --- a/server/prisma/schema.prisma +++ b/server/prisma/schema.prisma @@ -186,7 +186,7 @@ model EventTracker { isRankedForEvent Boolean @default(true) event EventBase @relation(fields: [eventId], references: [id], onDelete: Cascade) eventId String - curChallenge Challenge? @relation(fields: [curChallengeId], references: [id]) + curChallenge Challenge? @relation(fields: [curChallengeId], references: [id], onDelete: SetNull) curChallengeId String? completedChallenges PrevChallenge[] } diff --git a/server/src/challenge/challenge.gateway.ts b/server/src/challenge/challenge.gateway.ts index a397cd59..aeb4c64c 100644 --- a/server/src/challenge/challenge.gateway.ts +++ b/server/src/challenge/challenge.gateway.ts @@ -72,8 +72,8 @@ export class ChallengeGateway { ); await this.groupService.emitUpdateGroupData(group, false); - await this.eventService.emitUpdateEventTracker(tracker); - await this.userService.emitUpdateUserData(user, false, true, user); + await this.eventService.emitUpdateEventTracker(tracker, user); + await this.userService.emitUpdateUserData(user, false, false, user); } else { await this.clientService.emitErrorData( user, diff --git a/server/src/event/event.e2e-spec.ts b/server/src/event/event.e2e-spec.ts index dfa95b1c..22594572 100644 --- a/server/src/event/event.e2e-spec.ts +++ b/server/src/event/event.e2e-spec.ts @@ -131,7 +131,7 @@ describe('EventModule E2E', () => { exPlayer, ); expect(tracker2.curChallengeId).toEqual(journey1Chal.id); - expect(tracker2.score).toEqual(1); + expect(tracker2.score).toEqual(100); await groupGateway.setCurrentEvent(exPlayer, { eventId: exJourney2.id }); const tracker3 = await eventService.getCurrentEventTrackerForUser( @@ -145,14 +145,14 @@ describe('EventModule E2E', () => { exPlayer, ); expect(tracker4.curChallengeId).toEqual(journey2Chal.id); - expect(tracker4.score).toEqual(1); + expect(tracker4.score).toEqual(100); expect(await challengeService.completeChallenge(exPlayer)).toBeTruthy(); const tracker5 = await eventService.getCurrentEventTrackerForUser( exPlayer, ); expect(tracker5.curChallengeId).toEqual(null); - expect(tracker5.score).toEqual(2); + expect(tracker5.score).toEqual(200); await groupGateway.setCurrentEvent(exPlayer, { eventId: exChallenge1.id, @@ -170,7 +170,7 @@ describe('EventModule E2E', () => { exPlayer, ); expect(tracker7.curChallengeId).toEqual(null); - expect(tracker7.score).toEqual(1); + expect(tracker7.score).toEqual(100); await groupGateway.setCurrentEvent(exPlayer, { eventId: exJourney1.id }); @@ -179,7 +179,7 @@ describe('EventModule E2E', () => { ); expect(tracker8.eventId).toEqual(exJourney1.id); expect(tracker8.curChallengeId).toEqual(journey1Chal.id); - expect(tracker8.score).toEqual(1); + expect(tracker8.score).toEqual(100); expect(await challengeService.completeChallenge(exPlayer)).toBeTruthy(); const tracker9 = await eventService.getCurrentEventTrackerForUser( @@ -187,11 +187,11 @@ describe('EventModule E2E', () => { ); expect(tracker9.eventId).toEqual(exJourney1.id); expect(tracker9.curChallengeId).toEqual(null); - expect(tracker9.score).toEqual(2); + expect(tracker9.score).toEqual(200); expect(await challengeService.completeChallenge(exPlayer)).toBeFalsy(); const latestUserData = await userService.byId(exPlayer.id); - expect(latestUserData?.score).toEqual(5); + expect(latestUserData?.score).toEqual(500); }); }); diff --git a/server/src/event/event.service.ts b/server/src/event/event.service.ts index 564a3edd..14106f87 100644 --- a/server/src/event/event.service.ts +++ b/server/src/event/event.service.ts @@ -6,6 +6,7 @@ import { TimeLimitationType, EventTracker, User, + OrganizationSpecialUsage, } from '@prisma/client'; import { LeaderDto, UpdateLeaderDataDto } from './event.dto'; import { v4 } from 'uuid'; @@ -567,6 +568,18 @@ export class EventService { }, }) ) { + const defaultOrg = await this.orgService.getDefaultOrganization( + OrganizationSpecialUsage.DEVICE_LOGIN, + ); + + const defaultEv = await this.orgService.getDefaultEvent(defaultOrg); + if (defaultEv.id === eventId) return false; + + await this.prisma.group.updateMany({ + where: { curEventId: eventId }, + data: { curEventId: defaultEv.id }, + }); + await this.prisma.eventBase.delete({ where: { id: eventId, diff --git a/server/src/organization/organization.service.ts b/server/src/organization/organization.service.ts index 250830d8..44dc4ba9 100644 --- a/server/src/organization/organization.service.ts +++ b/server/src/organization/organization.service.ts @@ -37,7 +37,7 @@ export const defaultChallengeData = { name: 'Default Challenge', location: LocationType.ARTS_QUAD, description: 'McGraw Tower', - points: 1, + points: 100, imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/5/5f/CentralAvenueCornell2.jpg', latitude: 42.44755580740012,