From 72a928e1d2a329fb042c35de38b3fbe57318ab39 Mon Sep 17 00:00:00 2001 From: Anton Kriese Date: Wed, 30 Aug 2023 19:45:57 +0200 Subject: [PATCH 1/7] feat: add some popup menu action placeholders --- lib/screens/meet_page.dart | 49 +++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/lib/screens/meet_page.dart b/lib/screens/meet_page.dart index 7a0bf20..a34498d 100644 --- a/lib/screens/meet_page.dart +++ b/lib/screens/meet_page.dart @@ -73,7 +73,7 @@ class MeetPage extends StatelessWidget { ), ), ), - actions: const [MeetPopupMenuCard()], + actions: [MeetPopupMenuCard(isHost: isHost)], iconTheme: const IconThemeData( color: DesignColors.naviColor, )), @@ -359,33 +359,34 @@ class CardTime extends StatelessWidget { } } -class MeetPopupMenuCard extends StatefulWidget { - const MeetPopupMenuCard({super.key}); +class MeetPopupMenuCard extends StatelessWidget { + const MeetPopupMenuCard({super.key, required this.isHost}); - @override - State createState() => _MeetPopupMenuCardState(); -} - -class _MeetPopupMenuCardState extends State { - ReviewPopupMenuItem? selectedMenu; + final bool isHost; @override Widget build(BuildContext context) { - return PopupMenuButton( - initialValue: selectedMenu, - // Callback that sets the selected popup menu item. - onSelected: (ReviewPopupMenuItem item) { - setState(() { - selectedMenu = item; - }); - }, - itemBuilder: (BuildContext context) => - >[ - PopupMenuItem( - value: ReviewPopupMenuItem.report, - child: const SystemText(text: 'Report as inappropriate'), - onTap: () => _showDialog(context), - ), + return PopupMenuButton( + itemBuilder: (BuildContext context) => [ + if (!isHost) + PopupMenuItem( + child: const SystemText(text: 'Report as inappropriate'), + onTap: () => _showDialog(context), + ), + if (isHost) ...[ + PopupMenuItem( + child: const SystemText(text: 'Angebot löschen'), + onTap: () => {}, + ), + PopupMenuItem( + child: const SystemText(text: 'Angebot schliessen'), + onTap: () => {}, + ), + PopupMenuItem( + child: const SystemText(text: 'Angebot öffnen'), + onTap: () => {}, + ) + ], ], ); } From e1492178914fecf88372a98f1b6ab0b6ca09e77c Mon Sep 17 00:00:00 2001 From: Anton Kriese Date: Thu, 31 Aug 2023 16:01:47 +0200 Subject: [PATCH 2/7] feat: stateful meet page --- lib/screens/meet_page.dart | 53 +++++++++++++++++++++++++++++++------- lib/widgets/meet_card.dart | 2 +- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/lib/screens/meet_page.dart b/lib/screens/meet_page.dart index a34498d..1f10b45 100644 --- a/lib/screens/meet_page.dart +++ b/lib/screens/meet_page.dart @@ -4,6 +4,7 @@ import 'package:app/constants/design.dart'; import 'package:app/model/functions.dart'; import 'package:app/provider/activity_type.dart'; import 'package:app/model/generated.dart'; +import 'package:app/provider/generated/offers_provider.dart'; import 'package:app/provider/meetup_manager.dart'; import 'package:app/provider/photos.dart'; import 'package:app/provider/user_manager.dart'; @@ -18,13 +19,40 @@ import 'package:app/widgets/meet_map.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -class MeetPage extends StatelessWidget { - const MeetPage({super.key, required this.offer}); +class MeetPage extends StatefulWidget { + const MeetPage({super.key, required this.id}); - final OfferParsed offer; + final String id; + + @override + State createState() => _MeetPageState(); +} + +class _MeetPageState extends State { + late final Future offerFuture; + + @override + void initState() { + super.initState(); + + offerFuture = MeetupManager.instance.updateMeetupInfo(widget.id); + } @override Widget build(BuildContext context) { + return FutureBuilder( + future: offerFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (!snapshot.hasData) { + return const CircularProgressIndicator(); + } + + return buildWhenLoaded(snapshot.data); + }, + ); + } + + Widget buildWhenLoaded(OfferParsed offer) { final state = context.read(); final bool isHost = state.currentUser!.id == offer.userInfo.id; @@ -64,12 +92,19 @@ class MeetPage extends StatelessWidget { decoration: BoxDecoration( borderRadius: BorderRadius.circular(AppStyle.cornerRadius), ), - child: MeetMap( - center: toLatLng(isHost - ? offer.location_.coords - : offer.blurrInfo.center), - radius: isHost ? 0.3 : offer.blurrInfo.radius, - circleScale: isHost ? 50 : 150), + child: Stack( + children: [ + MeetMap( + center: toLatLng(isHost + ? offer.location_.coords + : offer.blurrInfo.center), + radius: isHost ? 0.3 : offer.blurrInfo.radius, + circleScale: isHost ? 50 : 150), + Align( + alignment: Alignment.bottomCenter, + child: Chip(label: Text(offer.status.toString()))) + ], + ), ), ), ), diff --git a/lib/widgets/meet_card.dart b/lib/widgets/meet_card.dart index e491e63..568f6ab 100644 --- a/lib/widgets/meet_card.dart +++ b/lib/widgets/meet_card.dart @@ -36,7 +36,7 @@ class MeetCard extends StatelessWidget { Navigator.push( context, MaterialPageRoute( - builder: (context) => MeetPage(offer: offer), + builder: (context) => MeetPage(id: offer.id), ), ); }, From 1d0b4900c47d4defe0a6c98987674d26607e8841 Mon Sep 17 00:00:00 2001 From: Anton Kriese Date: Thu, 31 Aug 2023 16:02:55 +0200 Subject: [PATCH 3/7] feat: meet actions including info rerender --- lib/screens/meet_page.dart | 77 ++++++++++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/lib/screens/meet_page.dart b/lib/screens/meet_page.dart index 1f10b45..bef040a 100644 --- a/lib/screens/meet_page.dart +++ b/lib/screens/meet_page.dart @@ -108,7 +108,12 @@ class _MeetPageState extends State { ), ), ), - actions: [MeetPopupMenuCard(isHost: isHost)], + actions: [ + MeetPopupMenuCard( + isHost: isHost, + offer: offer, + onChangeCallback: onChangeCallback) + ], iconTheme: const IconThemeData( color: DesignColors.naviColor, )), @@ -245,6 +250,12 @@ class _MeetPageState extends State { ]), )); } + + void onChangeCallback() { + setState(() { + offerFuture = MeetupManager.instance.updateMeetupInfo(widget.id); + }); + } } class ProfileListCard extends StatelessWidget { @@ -395,9 +406,16 @@ class CardTime extends StatelessWidget { } class MeetPopupMenuCard extends StatelessWidget { - const MeetPopupMenuCard({super.key, required this.isHost}); + const MeetPopupMenuCard({ + super.key, + required this.isHost, + required this.offer, + required this.onChangeCallback, + }); final bool isHost; + final OfferParsed offer; + final VoidCallback onChangeCallback; @override Widget build(BuildContext context) { @@ -411,16 +429,53 @@ class MeetPopupMenuCard extends StatelessWidget { if (isHost) ...[ PopupMenuItem( child: const SystemText(text: 'Angebot löschen'), - onTap: () => {}, + onTap: () { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + content: const Text( + "Willst du das Angebot wirklich löschen?"), + actions: [ + CustomTextButton( + onPressed: () { + OffersProvider.deleteOffer(offerId: offer.id) + .then((_) async { + Navigator.pop(context); + }); + }, + text: "Ja"), + CustomTextButton( + onPressed: () => Navigator.pop(context), + text: "Nein") + ]); + }); + }, ), - PopupMenuItem( - child: const SystemText(text: 'Angebot schliessen'), - onTap: () => {}, - ), - PopupMenuItem( - child: const SystemText(text: 'Angebot öffnen'), - onTap: () => {}, - ) + if (offer.status == OfferStatus.open) + PopupMenuItem( + child: const SystemText(text: 'Angebot schliessen'), + onTap: () { + OffersProvider.setOfferStatus( + offerId: offer.id, status: OfferStatus.closed) + .then((_) async { + await MeetupManager.instance.updateMeetupInfo(offer.id); + onChangeCallback(); + }); + }, + ), + if (offer.status == OfferStatus.closed) + PopupMenuItem( + child: const SystemText(text: 'Angebot öffnen'), + onTap: () { + OffersProvider.setOfferStatus( + offerId: offer.id, status: OfferStatus.open) + .then((_) async { + await MeetupManager.instance.updateMeetupInfo(offer.id); + onChangeCallback(); + }); + }, + ) ], ], ); From 9a9f1361533cea08a466010a6c2202dbae2e7f50 Mon Sep 17 00:00:00 2001 From: Anton Kriese Date: Thu, 31 Aug 2023 16:03:26 +0200 Subject: [PATCH 4/7] feat: show outdated meetups with lower opacity --- lib/screens/more_meetups_screen.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/screens/more_meetups_screen.dart b/lib/screens/more_meetups_screen.dart index a053cf4..b5faa7b 100644 --- a/lib/screens/more_meetups_screen.dart +++ b/lib/screens/more_meetups_screen.dart @@ -1,4 +1,5 @@ import 'package:app/constants/design.dart'; +import 'package:app/model/generated.dart'; import 'package:app/provider/meetup_manager.dart'; import 'package:app/widgets/custom/background.dart'; import 'package:app/widgets/custom/card.dart'; @@ -26,6 +27,8 @@ class MeetUpMoreScreen extends StatelessWidget { double width = size.width; + DateTime now = DateTime.now(); + return Scaffold( body: BackgroundSVG( children: CustomScrollView(slivers: [ @@ -63,7 +66,10 @@ class MeetUpMoreScreen extends StatelessWidget { var children; if (snapshot.hasData) { children = snapshot.data! - .map((m) => MeetCard(offer: m)) + .map((m) => Opacity( + opacity: + m.status == OfferStatus.timeout ? 0.6 : 1.0, + child: MeetCard(offer: m))) .toList(); } else if (snapshot.hasError) { children = [ From aec0268f9288019cc8388df9d9bdbbee1d449639 Mon Sep 17 00:00:00 2001 From: Anton Kriese Date: Thu, 31 Aug 2023 16:34:16 +0200 Subject: [PATCH 5/7] fix: on delete pop navigator twice go back --- lib/screens/community.dart | 5 +++-- lib/screens/meet_page.dart | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/screens/community.dart b/lib/screens/community.dart index cdbd942..9112167 100644 --- a/lib/screens/community.dart +++ b/lib/screens/community.dart @@ -22,14 +22,15 @@ class CommunityScreen extends StatelessWidget { var size = MediaQuery.of(context).size; double width = size.width; - Future> userMeetups = - MeetupManager.instance.getUserMeetups(forceFetch: true); final state = context.read(); if (state.userPosition != null) { MeetupManager.instance.currentPosition = state.userPosition!; } + Future> userMeetups = + MeetupManager.instance.getUserMeetups(forceFetch: true); + Future> availableMeetups = MeetupManager.instance.getAvailableMeetups(forceFetch: true); diff --git a/lib/screens/meet_page.dart b/lib/screens/meet_page.dart index bef040a..932f922 100644 --- a/lib/screens/meet_page.dart +++ b/lib/screens/meet_page.dart @@ -442,6 +442,7 @@ class MeetPopupMenuCard extends StatelessWidget { OffersProvider.deleteOffer(offerId: offer.id) .then((_) async { Navigator.pop(context); + Navigator.pop(context); }); }, text: "Ja"), From e2fafb2f4c15212fa6b0e762bae3d0fc4e236168 Mon Sep 17 00:00:00 2001 From: Anton Kriese Date: Thu, 31 Aug 2023 18:02:19 +0200 Subject: [PATCH 6/7] feat: request to join bottomsheet --- lib/screens/meet_page.dart | 40 +++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/screens/meet_page.dart b/lib/screens/meet_page.dart index 932f922..4ea1315 100644 --- a/lib/screens/meet_page.dart +++ b/lib/screens/meet_page.dart @@ -9,6 +9,7 @@ import 'package:app/provider/meetup_manager.dart'; import 'package:app/provider/photos.dart'; import 'package:app/provider/user_manager.dart'; import 'package:app/widgets/activityType_short.dart'; +import 'package:app/widgets/bottomsheet.dart'; import 'package:app/widgets/custom/background.dart'; import 'package:app/widgets/custom/alertdialog.dart'; @@ -235,7 +236,11 @@ class _MeetPageState extends State { ButtonBookMark(), if (!isParticipant) CustomElevatedButton( - onPressed: () {}, text: "Anfrage senden") + onPressed: () { + bottomSheetRequestToJoin( + context, offer, onChangeCallback); + }, + text: "Anfrage senden") ], )), Padding( @@ -526,3 +531,36 @@ class MyHeaderDelegate extends SliverPersistentHeaderDelegate { return false; } } + +void bottomSheetRequestToJoin( + BuildContext context, OfferParsed offer, VoidCallback onSubmit) { + final width = MediaQuery.of(context).size.width; + final textController = TextEditingController(); + + bottomSheetBase( + context: context, + builder: (context) { + return Padding( + padding: + EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + TitleText(text: "Frage die Teilnahme an", width: width), + MediumText( + text: + "Benachrichtige ${offer.userInfo.displayName}, dass du dabei sein willst!", + width: width), + TextField(controller: textController), + CustomTextButton( + onPressed: () { + OffersProvider.requestToJoin( + offerId: offer.id, data: textController.text) + .whenComplete(() { + Navigator.pop(context); + onSubmit(); + }); + }, + text: "Abschicken") + ]), + ); + }); +} From ee2a17e28bcf9e8b6f2bc1f8f8402b8faec6a1b9 Mon Sep 17 00:00:00 2001 From: Anton Kriese Date: Thu, 31 Aug 2023 18:02:51 +0200 Subject: [PATCH 7/7] feat: accept or decline offer join request --- lib/screens/meet_page.dart | 117 +++++++++++++++++++++++++++---------- 1 file changed, 85 insertions(+), 32 deletions(-) diff --git a/lib/screens/meet_page.dart b/lib/screens/meet_page.dart index 4ea1315..748a4e1 100644 --- a/lib/screens/meet_page.dart +++ b/lib/screens/meet_page.dart @@ -30,7 +30,7 @@ class MeetPage extends StatefulWidget { } class _MeetPageState extends State { - late final Future offerFuture; + late Future offerFuture; @override void initState() { @@ -59,6 +59,9 @@ class _MeetPageState extends State { final bool isHost = state.currentUser!.id == offer.userInfo.id; final bool isParticipant = isHost || offer.participants.any((p) => p.id == state.currentUser!.id); + final bool isWaiting = offer.participants.any((p) => + p.id == state.currentUser!.id && + p.status == ParticipantStatus.requested); double collapsedHeight = 90; const double expandedHeight = 160; @@ -79,6 +82,16 @@ class _MeetPageState extends State { ]; } + UserApiOut? hostInfo; + Future photo = + UserInfoManager.instance.getUserInfo(offer.userInfo.id).then((info) { + hostInfo = info; + if (info.avatar != null) { + return PhotoManager.instance.getThumbnail(info.avatar!.url); + } + return null; + }); + return BackgroundSVG( children: Scaffold( backgroundColor: const Color.fromARGB(255, 255, 255, 255), @@ -129,29 +142,32 @@ class _MeetPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column(children: [ - Row(children: [ - const Padding( - padding: EdgeInsets.only(left: 10, top: 60)), - const CircleAvatar( - backgroundImage: AssetImages.avatarEmpty, - radius: 20, - ), - const SizedBox( - width: 10, - ), - MediumText( - text: offer.userInfo.displayName, - width: width, - ), - ]), + FutureBuilder( + future: photo, + builder: (BuildContext context, + AsyncSnapshot snapshot) { + final result = + getNameAndAvatar(snapshot, hostInfo); + + return Row(children: [ + const Padding( + padding: + EdgeInsets.only(left: 10, top: 60)), + CircleAvatar( + backgroundImage: result.image, + radius: 20, + ), + const SizedBox( + width: 10, + ), + MediumText( + text: offer.userInfo.displayName, + width: width, + ), + ]); + }, + ), ]), - if (!isParticipant) - CustomTextButton( - onPressed: () { - Navigator.of(context) - .pop(); // Dialog schließen - }, - text: 'Anfrage senden'), ]), Center( child: TitleText( @@ -214,12 +230,45 @@ class _MeetPageState extends State { child: Column( children: offer.participants .where((p) => - p.id != state.currentUser!.id && + // p.id != state.currentUser!.id && (p.status == ParticipantStatus.accepted || isHost)) .map( (p) => Column(children: [ - ProfileListCard(width: width, userId: p.id), + ProfileListCard( + width: width, + participant: p, + actions: [ + if (!(p.id == state.currentUser!.id)) + IconButton( + onPressed: () {}, + icon: AppIcons.chat), + if (isHost && + !(p.id == + state.currentUser!.id)) ...[ + if (p.status == + ParticipantStatus.requested) + IconButton( + onPressed: () { + OffersProvider.acceptRequest( + offerId: offer.id, + userId: p.id) + .whenComplete( + onChangeCallback); + }, + icon: + const Icon(AppIcons.done)), + IconButton( + onPressed: () { + OffersProvider.declineRequest( + offerId: offer.id, + userId: p.id) + .whenComplete( + onChangeCallback); + }, + icon: const Icon(AppIcons.close)) + ], + ]), const Divider( // Hier wird die Trennlinie hinzugefügt color: Color.fromARGB( @@ -228,6 +277,11 @@ class _MeetPageState extends State { ]), ) .toList())), + if (isWaiting) + MediumText( + width: width, + text: + "Du wirst die anderen Teilnehmer sehen, sobald deine Teilnahme vom Gastgeber bestätigt wird."), Padding( padding: const EdgeInsets.all(9.0), child: Row( @@ -267,17 +321,19 @@ class ProfileListCard extends StatelessWidget { const ProfileListCard({ super.key, required this.width, - required this.userId, + required this.participant, + required this.actions, }); final double width; - final String userId; + final Participant participant; + final List actions; @override Widget build(BuildContext context) { UserApiOut? userInfo; Future photo = - UserInfoManager.instance.getUserInfo(userId).then((info) { + UserInfoManager.instance.getUserInfo(participant.id).then((info) { userInfo = info; if (info.avatar != null) { return PhotoManager.instance.getThumbnail(info.avatar!.url); @@ -306,10 +362,7 @@ class ProfileListCard extends StatelessWidget { }, ), Row( - children: [ - AppIcons.chat, - const Icon(AppIcons.close), - ], + children: actions, ) ]), ]);