diff --git a/frontend/lib/components/memberPartCard.dart b/frontend/lib/components/memberPartCard.dart index 676de659..cf564655 100644 --- a/frontend/lib/components/memberPartCard.dart +++ b/frontend/lib/components/memberPartCard.dart @@ -1,8 +1,4 @@ -import 'dart:html'; - import 'package:flutter/material.dart'; -import 'package:frontend/services/authService.dart'; -import 'package:provider/provider.dart'; final Map roles = { "MEMBER": "Member", diff --git a/frontend/lib/components/router.dart b/frontend/lib/components/router.dart index a4a9ce6b..89861f6f 100644 --- a/frontend/lib/components/router.dart +++ b/frontend/lib/components/router.dart @@ -8,7 +8,6 @@ import 'package:frontend/routes/company/CompanyListWidget.dart'; import 'package:frontend/routes/meeting/AddMeetingForm.dart'; import 'package:frontend/routes/member/AddMemberForm.dart'; import 'package:frontend/routes/member/MemberListWidget.dart'; -import 'package:frontend/routes/speaker/flights/AddFlightInfoForm.dart'; import 'package:frontend/routes/speaker/SpeakerListWidget.dart'; import 'package:frontend/routes/speaker/AddSpeakerForm.dart'; import 'package:frontend/routes/teams/AddTeamMemberForm.dart'; diff --git a/frontend/lib/components/threads/participations/communicationsList.dart b/frontend/lib/components/threads/participations/communicationsList.dart index d012c3d7..2c19bebf 100644 --- a/frontend/lib/components/threads/participations/communicationsList.dart +++ b/frontend/lib/components/threads/participations/communicationsList.dart @@ -19,7 +19,7 @@ class CommunicationsList extends StatefulWidget { } class _CommunicationsListState extends State - with SingleTickerProviderStateMixin { + with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { late final TabController _tabController; @override @@ -46,8 +46,12 @@ class _CommunicationsListState extends State setState(() {}); } + @override + bool get wantKeepAlive => true; + @override Widget build(BuildContext context) { + super.build(context); return LayoutBuilder(builder: (context, constraints) { return Padding( padding: const EdgeInsets.fromLTRB(0, 0, 0, 20), diff --git a/frontend/lib/models/member.dart b/frontend/lib/models/member.dart index 521d1119..b297e15f 100644 --- a/frontend/lib/models/member.dart +++ b/frontend/lib/models/member.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:frontend/models/contact.dart'; -import 'package:frontend/models/team.dart'; class Member { final String id; diff --git a/frontend/lib/routes/HomeScreen.dart b/frontend/lib/routes/HomeScreen.dart index ef20ec83..85d91857 100644 --- a/frontend/lib/routes/HomeScreen.dart +++ b/frontend/lib/routes/HomeScreen.dart @@ -83,19 +83,19 @@ class _HomeScreenState extends State { child: const SpeakerTable(), ), Center( - child: LandingPage(), + child: const LandingPage(), ), Center( - child: CompanyTable(), + child: const CompanyTable(), ), Center( - child: TeamTable(), + child: const TeamTable(), ), Center( - child: MeetingPage(), + child: const MeetingPage(), ), Center( - child: SessionPage(), + child: const SessionPage(), ) ], ), @@ -222,11 +222,22 @@ class _HomeScreenState extends State { } } -class LandingPage extends StatelessWidget { +class LandingPage extends StatefulWidget { const LandingPage({Key? key}) : super(key: key); + @override + _LandingPageState createState() => _LandingPageState(); +} + +class _LandingPageState extends State + with AutomaticKeepAliveClientMixin { + + @override + bool get wantKeepAlive => true; + @override Widget build(BuildContext context) { + super.build(context); return Container( child: Text("Welcome to deck2! In Progress..."), ); diff --git a/frontend/lib/routes/company/CompanyTable.dart b/frontend/lib/routes/company/CompanyTable.dart index 97bc44e4..c6d937df 100644 --- a/frontend/lib/routes/company/CompanyTable.dart +++ b/frontend/lib/routes/company/CompanyTable.dart @@ -25,6 +25,7 @@ class _CompanyTableState extends State final CompanyService _companyService = CompanyService(); late ParticipationStatus _filter; + @override bool get wantKeepAlive => true; @override diff --git a/frontend/lib/routes/meeting/MeetingPage.dart b/frontend/lib/routes/meeting/MeetingPage.dart index 9f6f65f4..c6b1cbea 100644 --- a/frontend/lib/routes/meeting/MeetingPage.dart +++ b/frontend/lib/routes/meeting/MeetingPage.dart @@ -6,25 +6,14 @@ import 'package:frontend/routes/meeting/MeetingsNotifier.dart'; import 'package:frontend/services/meetingService.dart'; import 'package:provider/provider.dart'; -class MeetingPage extends StatelessWidget { +class MeetingPage extends StatefulWidget { const MeetingPage({Key? key}) : super(key: key); @override - Widget build(BuildContext context) { - return Container( - child: MeetingList(), - ); - } -} - -class MeetingList extends StatefulWidget { - const MeetingList({Key? key}) : super(key: key); - - @override - _MeetingListState createState() => _MeetingListState(); + _MeetingPageState createState() => _MeetingPageState(); } -class _MeetingListState extends State +class _MeetingPageState extends State with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { final MeetingService _service = MeetingService(); late final Future> _meetings; @@ -38,7 +27,6 @@ class _MeetingListState extends State } @override - // TODO: implement wantKeepAlive bool get wantKeepAlive => true; @override diff --git a/frontend/lib/routes/meeting/MeetingScreen.dart b/frontend/lib/routes/meeting/MeetingScreen.dart index d24f6e1b..3c68c6b5 100644 --- a/frontend/lib/routes/meeting/MeetingScreen.dart +++ b/frontend/lib/routes/meeting/MeetingScreen.dart @@ -274,7 +274,7 @@ class MeetingParticipants extends StatelessWidget { onPressed: () => _deleteMeetingParticipant(context, membs[index]!.id, type, membs[index]!.name), icon: Icon(Icons.delete), - style: ElevatedButton.styleFrom(primary: Colors.red), + style: ElevatedButton.styleFrom(backgroundColor: Colors.red), label: const Text("Delete participant")), ]); }); @@ -643,7 +643,7 @@ class MeetingBanner extends StatelessWidget { context), icon: Icon(Icons.article), style: ElevatedButton.styleFrom( - primary: meeting + backgroundColor: meeting .minute!.isNotEmpty ? const Color( 0xFF5C7FF2) @@ -666,7 +666,7 @@ class MeetingBanner extends StatelessWidget { icon: Icon(Icons.article), style: ElevatedButton .styleFrom( - primary: const Color( + backgroundColor: const Color( 0xFFF25C5C)), label: const Text( "Delete Minutes"))) diff --git a/frontend/lib/routes/member/AddMemberForm.dart b/frontend/lib/routes/member/AddMemberForm.dart index 895d3d14..726b3243 100644 --- a/frontend/lib/routes/member/AddMemberForm.dart +++ b/frontend/lib/routes/member/AddMemberForm.dart @@ -140,7 +140,7 @@ class _AddMemberFormState extends State { padding: const EdgeInsets.all(8.0), child: ElevatedButton( style: ElevatedButton.styleFrom( - primary: Theme.of(context).colorScheme.secondary, + backgroundColor: Theme.of(context).colorScheme.secondary, padding: EdgeInsets.symmetric(horizontal: 50), elevation: 2, shape: RoundedRectangleBorder( diff --git a/frontend/lib/routes/member/EditContact.dart b/frontend/lib/routes/member/EditContact.dart index c3daa125..27e430cc 100644 --- a/frontend/lib/routes/member/EditContact.dart +++ b/frontend/lib/routes/member/EditContact.dart @@ -195,7 +195,7 @@ class _MyFormState extends State { padding: const EdgeInsets.all(8.0), child: ElevatedButton( style: ElevatedButton.styleFrom( - primary: Theme.of(context).colorScheme.secondary, + backgroundColor: Theme.of(context).colorScheme.secondary, padding: EdgeInsets.symmetric(horizontal: 50), elevation: 2, shape: RoundedRectangleBorder( diff --git a/frontend/lib/routes/session/EditSessionForm.dart b/frontend/lib/routes/session/EditSessionForm.dart index e9e4f117..3ab4ffc4 100644 --- a/frontend/lib/routes/session/EditSessionForm.dart +++ b/frontend/lib/routes/session/EditSessionForm.dart @@ -5,8 +5,6 @@ import 'package:frontend/services/sessionService.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; -import 'package:dropdown_search/dropdown_search.dart'; -import 'package:form_builder_extra_fields/form_builder_extra_fields.dart'; class EditSessionForm extends StatefulWidget { final Session session; diff --git a/frontend/lib/routes/session/SessionCard.dart b/frontend/lib/routes/session/SessionCard.dart index eecaea4a..ccefe139 100644 --- a/frontend/lib/routes/session/SessionCard.dart +++ b/frontend/lib/routes/session/SessionCard.dart @@ -1,4 +1,3 @@ -import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:frontend/components/blurryDialog.dart'; import 'package:frontend/main.dart'; @@ -9,7 +8,6 @@ import 'package:frontend/services/authService.dart'; import 'package:frontend/services/sessionService.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -import 'package:url_launcher/url_launcher.dart'; class SessionCard extends StatelessWidget { final Session session; diff --git a/frontend/lib/routes/session/SessionPage.dart b/frontend/lib/routes/session/SessionPage.dart index ec5a7c72..f27da3cb 100644 --- a/frontend/lib/routes/session/SessionPage.dart +++ b/frontend/lib/routes/session/SessionPage.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:frontend/main.dart'; import 'package:frontend/models/session.dart'; -import 'package:frontend/routes/session/SessionCard.dart'; import 'package:frontend/routes/session/SessionsNotifier.dart'; import 'package:frontend/routes/session/calendar.dart'; import 'package:frontend/services/sessionService.dart'; @@ -40,7 +38,6 @@ class _SessionListState extends State } @override - // TODO: implement wantKeepAlive bool get wantKeepAlive => true; @override diff --git a/frontend/lib/routes/speaker/DetailsScreen.dart b/frontend/lib/routes/speaker/DetailsScreen.dart new file mode 100644 index 00000000..b694e648 --- /dev/null +++ b/frontend/lib/routes/speaker/DetailsScreen.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:frontend/components/EditableCard.dart'; +import 'package:frontend/models/speaker.dart'; + +class DetailsScreen extends StatefulWidget { + final Speaker speaker; + const DetailsScreen({Key? key, required this.speaker}) : super(key: key); + + @override + _DetailsScreenState createState() => _DetailsScreenState(); +} + +class _DetailsScreenState extends State + with AutomaticKeepAliveClientMixin { + + @override + bool get wantKeepAlive => true; + + @override + Widget build(BuildContext context) { + super.build(context); + return Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: EditableCard( + title: 'Bio', + body: widget.speaker.bio ?? "", + bodyEditedCallback: (newBio) { + //speaker.bio = newBio; + //TODO replace bio with service call to change bio + print('replaced bio'); + return Future.delayed(Duration.zero); + }, + isSingleline: false, + textInputType: TextInputType.multiline, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 8.0), + child: EditableCard( + title: 'Notes', + body: widget.speaker.notes ?? "", + bodyEditedCallback: (newNotes) { + //speaker.bio = newBio; + //TODO replace bio with service call to change bio + print('replaced notes'); + return Future.delayed(Duration.zero); + }, + isSingleline: false, + textInputType: TextInputType.multiline, + ), + ), + ], + )), + ); + } +} diff --git a/frontend/lib/routes/speaker/EditSpeakerForm.dart b/frontend/lib/routes/speaker/EditSpeakerForm.dart index 48317969..22a4f07c 100644 --- a/frontend/lib/routes/speaker/EditSpeakerForm.dart +++ b/frontend/lib/routes/speaker/EditSpeakerForm.dart @@ -4,7 +4,6 @@ import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dropzone/flutter_dropzone.dart'; -import 'package:frontend/components/appbar.dart'; import 'package:frontend/models/speaker.dart'; import 'package:frontend/services/speakerService.dart'; import 'package:image_picker/image_picker.dart'; diff --git a/frontend/lib/routes/speaker/ParticipationList.dart b/frontend/lib/routes/speaker/ParticipationList.dart new file mode 100644 index 00000000..afc31add --- /dev/null +++ b/frontend/lib/routes/speaker/ParticipationList.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:frontend/components/participationCard.dart'; +import 'package:frontend/main.dart'; +import 'package:frontend/models/speaker.dart'; + +class ParticipationList extends StatefulWidget { + final Speaker speaker; + final Future Function(Map) onParticipationChanged; + final void Function() onParticipationDeleted; + const ParticipationList({ + Key? key, + required this.speaker, + required this.onParticipationChanged, + required this.onParticipationDeleted, + }) : super(key: key); + + @override + _ParticipationListState createState() => _ParticipationListState(); +} + +class _ParticipationListState extends State + with AutomaticKeepAliveClientMixin { + + @override + bool get wantKeepAlive => true; + + @override + Widget build(BuildContext context) { + super.build(context); + return LayoutBuilder( + builder: (context, constraints) { + bool small = constraints.maxWidth < App.SIZE; + if (widget.speaker.participations != null) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + child: ListView( + controller: ScrollController(), + children: widget.speaker.participations!.reversed + .map((e) => Padding( + padding: const EdgeInsets.all(8.0), + child: ParticipationCard( + participation: e, + small: small, + type: CardType.SPEAKER, + onEdit: widget.onParticipationChanged, + onDelete: widget.onParticipationDeleted, + ), + )) + .toList(), + ), + ), + ); + } else { + return Container(); + } + }, + ); + } +} diff --git a/frontend/lib/routes/speaker/SpeakerScreen.dart b/frontend/lib/routes/speaker/SpeakerScreen.dart index cdd1c114..57532747 100644 --- a/frontend/lib/routes/speaker/SpeakerScreen.dart +++ b/frontend/lib/routes/speaker/SpeakerScreen.dart @@ -1,22 +1,18 @@ import 'package:flutter/material.dart'; -import 'package:frontend/components/EditableCard.dart'; import 'package:frontend/components/threads/addThreadForm.dart'; import 'package:frontend/components/appbar.dart'; -import 'package:frontend/components/deckTheme.dart'; import 'package:frontend/components/eventNotifier.dart'; -import 'package:frontend/components/participationCard.dart'; import 'package:frontend/components/threads/participations/communicationsList.dart'; +import 'package:frontend/routes/speaker/DetailsScreen.dart'; +import 'package:frontend/routes/speaker/ParticipationList.dart'; +import 'package:frontend/routes/speaker/banner/SpeakerBanner.dart'; import 'package:frontend/routes/speaker/flights/AddFlightInfoForm.dart'; import 'package:frontend/routes/speaker/flights/flightInfoScreen.dart'; import 'package:frontend/routes/speaker/speakerNotifier.dart'; -import 'package:frontend/components/status.dart'; import 'package:frontend/main.dart'; import 'package:frontend/models/speaker.dart'; -import 'package:frontend/models/participation.dart'; -import 'package:frontend/routes/speaker/EditSpeakerForm.dart'; import 'package:frontend/services/speakerService.dart'; import 'package:provider/provider.dart'; -import 'package:collection/collection.dart'; class SpeakerScreen extends StatefulWidget { Speaker speaker; @@ -228,314 +224,3 @@ class _SpeakerScreenState extends State } } } - -class ParticipationList extends StatelessWidget { - final Speaker speaker; - final Future Function(Map) onParticipationChanged; - final void Function() onParticipationDeleted; - const ParticipationList({ - Key? key, - required this.speaker, - required this.onParticipationChanged, - required this.onParticipationDeleted, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - bool small = constraints.maxWidth < App.SIZE; - if (speaker.participations != null) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Container( - child: ListView( - controller: ScrollController(), - children: speaker.participations!.reversed - .map((e) => Padding( - padding: const EdgeInsets.all(8.0), - child: ParticipationCard( - participation: e, - small: small, - type: CardType.SPEAKER, - onEdit: onParticipationChanged, - onDelete: onParticipationDeleted, - ), - )) - .toList(), - ), - ), - ); - } else { - return Container(); - } - }, - ); - } -} - -class SpeakerBanner extends StatelessWidget { - final Speaker speaker; - final void Function(int, BuildContext) statusChangeCallback; - final void Function(BuildContext, Speaker?) onEdit; - const SpeakerBanner( - {Key? key, - required this.speaker, - required this.statusChangeCallback, - required this.onEdit}) - : super(key: key); - - void _editSpeakerModal(context) { - showModalBottomSheet( - context: context, - builder: (context) { - return Container( - child: EditSpeakerForm(speaker: speaker, onEdit: this.onEdit), - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - int event = Provider.of(context).event.id; - bool isEditable = Provider.of(context).isLatest; - Participation? part = speaker.participations! - .firstWhereOrNull((element) => element.event == event); - ParticipationStatus speakerStatus = - part != null ? part.status : ParticipationStatus.NO_STATUS; - - double lum = 0.2; - var matrix = [ - 0.2126 * lum, - 0.7152 * lum, - 0.0722 * lum, - 0, - 0, - 0.2126 * lum, - 0.7152 * lum, - 0.0722 * lum, - 0, - 0, - 0.2126 * lum, - 0.7152 * lum, - 0.0722 * lum, - 0, - 0, - 0, - 0, - 0, - 1, - 0, - ]; // Greyscale matrix. Lum represents level of luminosity - return LayoutBuilder( - builder: (context, constraints) { - bool small = constraints.maxWidth < App.SIZE; - return Stack( - alignment: AlignmentDirectional.topEnd, - children: [ - Container( - decoration: BoxDecoration( - image: DecorationImage( - colorFilter: Provider.of(context).isDark - ? ColorFilter.matrix(matrix) - : null, - image: AssetImage('assets/banner_background.png'), - fit: BoxFit.cover, - ), - ), - child: Padding( - padding: EdgeInsets.symmetric( - horizontal: small ? 4 : 20, vertical: small ? 5 : 25), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: - EdgeInsets.fromLTRB(8.0, 8.0, small ? 8 : 20.0, 8.0), - child: SizedBox( - height: small ? 100 : 150, - width: small ? 100 : 150, - child: Hero( - tag: speaker.id + event.toString(), - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - width: small ? 2 : 4, - color: STATUSCOLOR[speakerStatus]!, - )), - child: CircleAvatar( - foregroundImage: NetworkImage( - speaker.imgs!.speaker ?? - (speaker.imgs!.internal ?? - (speaker.imgs!.company ?? "")), - ), - backgroundImage: AssetImage('assets/noImage.png'), - ), - ), - ), - ), - ), - Expanded( - child: Padding( - padding: EdgeInsets.all(small ? 8 : 12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - speaker.name, - style: Theme.of(context).textTheme.headline5, - overflow: TextOverflow.ellipsis, - ), - Text(speaker.title!, - style: Theme.of(context).textTheme.subtitle1, - softWrap: false, - overflow: TextOverflow.ellipsis), - if (isEditable) - SpeakerStatusDropdownButton( - speakerStatus: speakerStatus, - statusChangeCallback: statusChangeCallback, - speakerId: speaker.id, - ), - if (!isEditable) - Padding( - padding: const EdgeInsets.all(8.0), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - color: STATUSCOLOR[speakerStatus]), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text(STATUSSTRING[speakerStatus]!), - ), - ), - ), - //TODO define subscribe behaviour - ElevatedButton( - onPressed: () => print('zona'), - child: Text('+ Subscribe')) - ], - ), - ), - ), - ], - ), - ), - ), - IconButton( - icon: Icon(Icons.edit), - onPressed: () { - _editSpeakerModal(context); - }, - ) - ], - ); - }, - ); - } -} - -class SpeakerStatusDropdownButton extends StatelessWidget { - final void Function(int, BuildContext) statusChangeCallback; - final ParticipationStatus speakerStatus; - final String speakerId; - final SpeakerService _speakerService = SpeakerService(); - - SpeakerStatusDropdownButton({ - Key? key, - required this.statusChangeCallback, - required this.speakerStatus, - required this.speakerId, - }) : super(key: key); - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: _speakerService.getNextParticipationSteps(id: speakerId), - builder: (context, snapshot) { - List steps = [ - ParticipationStep(next: speakerStatus, step: 0) - ]; - if (snapshot.hasData) { - steps.addAll(snapshot.data as List); - } - return Container( - child: DropdownButton( - underline: Container( - height: 3, - decoration: BoxDecoration(color: STATUSCOLOR[speakerStatus]), - ), - value: steps[0], - style: Theme.of(context).textTheme.subtitle2, - selectedItemBuilder: (BuildContext context) { - return steps.map((e) { - return Align( - alignment: AlignmentDirectional.centerStart, - child: Container(child: Text(STATUSSTRING[e.next]!)), - ); - }).toList(); - }, - items: steps - .map((e) => DropdownMenuItem( - value: e, - child: Text(STATUSSTRING[e.next] ?? ''), - )) - .toList(), - onChanged: (next) { - if (next != null && next.step != 0) { - statusChangeCallback(next.step, context); - } - }, - ), - ); - }, - ); - } -} - -class DetailsScreen extends StatelessWidget { - final Speaker speaker; - const DetailsScreen({Key? key, required this.speaker}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Container( - child: ListView( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: EditableCard( - title: 'Bio', - body: speaker.bio ?? "", - bodyEditedCallback: (newBio) { - //speaker.bio = newBio; - //TODO replace bio with service call to change bio - print('replaced bio'); - return Future.delayed(Duration.zero); - }, - isSingleline: false, - textInputType: TextInputType.multiline, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(8.0, 0, 8.0, 8.0), - child: EditableCard( - title: 'Notes', - body: speaker.notes ?? "", - bodyEditedCallback: (newNotes) { - //speaker.bio = newBio; - //TODO replace bio with service call to change bio - print('replaced notes'); - return Future.delayed(Duration.zero); - }, - isSingleline: false, - textInputType: TextInputType.multiline, - ), - ), - ], - )), - ); - } -} diff --git a/frontend/lib/routes/speaker/SpeakerTable.dart b/frontend/lib/routes/speaker/SpeakerTable.dart index 8f1b6d2e..0902f0aa 100644 --- a/frontend/lib/routes/speaker/SpeakerTable.dart +++ b/frontend/lib/routes/speaker/SpeakerTable.dart @@ -25,6 +25,7 @@ class _SpeakerTableState extends State final SpeakerService _speakerService = SpeakerService(); late ParticipationStatus _filter; + @override bool get wantKeepAlive => true; @override diff --git a/frontend/lib/routes/speaker/banner/SpeakerBanner.dart b/frontend/lib/routes/speaker/banner/SpeakerBanner.dart new file mode 100644 index 00000000..84d372e9 --- /dev/null +++ b/frontend/lib/routes/speaker/banner/SpeakerBanner.dart @@ -0,0 +1,173 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:frontend/components/deckTheme.dart'; +import 'package:frontend/components/eventNotifier.dart'; +import 'package:frontend/components/status.dart'; +import 'package:frontend/main.dart'; +import 'package:frontend/models/participation.dart'; +import 'package:frontend/models/speaker.dart'; +import 'package:frontend/routes/speaker/EditSpeakerForm.dart'; +import 'package:frontend/routes/speaker/banner/SpeakerStatusDropdownButton.dart'; +import 'package:provider/provider.dart'; + +class SpeakerBanner extends StatelessWidget { + final Speaker speaker; + final void Function(int, BuildContext) statusChangeCallback; + final void Function(BuildContext, Speaker?) onEdit; + const SpeakerBanner( + {Key? key, + required this.speaker, + required this.statusChangeCallback, + required this.onEdit}) + : super(key: key); + + void _editSpeakerModal(context) { + showModalBottomSheet( + context: context, + builder: (context) { + return Container( + child: EditSpeakerForm(speaker: speaker, onEdit: this.onEdit), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + int event = Provider.of(context).event.id; + bool isEditable = Provider.of(context).isLatest; + Participation? part = speaker.participations! + .firstWhereOrNull((element) => element.event == event); + ParticipationStatus speakerStatus = + part != null ? part.status : ParticipationStatus.NO_STATUS; + + double lum = 0.2; + var matrix = [ + 0.2126 * lum, + 0.7152 * lum, + 0.0722 * lum, + 0, + 0, + 0.2126 * lum, + 0.7152 * lum, + 0.0722 * lum, + 0, + 0, + 0.2126 * lum, + 0.7152 * lum, + 0.0722 * lum, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + ]; // Greyscale matrix. Lum represents level of luminosity + return LayoutBuilder( + builder: (context, constraints) { + bool small = constraints.maxWidth < App.SIZE; + return Stack( + alignment: AlignmentDirectional.topEnd, + children: [ + Container( + decoration: BoxDecoration( + image: DecorationImage( + colorFilter: Provider.of(context).isDark + ? ColorFilter.matrix(matrix) + : null, + image: AssetImage('assets/banner_background.png'), + fit: BoxFit.cover, + ), + ), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: small ? 4 : 20, vertical: small ? 5 : 25), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: + EdgeInsets.fromLTRB(8.0, 8.0, small ? 8 : 20.0, 8.0), + child: SizedBox( + height: small ? 100 : 150, + width: small ? 100 : 150, + child: Hero( + tag: speaker.id + event.toString(), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + width: small ? 2 : 4, + color: STATUSCOLOR[speakerStatus]!, + )), + child: CircleAvatar( + foregroundImage: NetworkImage( + speaker.imgs!.speaker ?? + (speaker.imgs!.internal ?? + (speaker.imgs!.company ?? "")), + ), + backgroundImage: AssetImage('assets/noImage.png'), + ), + ), + ), + ), + ), + Expanded( + child: Padding( + padding: EdgeInsets.all(small ? 8 : 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + speaker.name, + style: Theme.of(context).textTheme.headline5, + overflow: TextOverflow.ellipsis, + ), + Text(speaker.title!, + style: Theme.of(context).textTheme.subtitle1, + softWrap: false, + overflow: TextOverflow.ellipsis), + if (isEditable) + SpeakerStatusDropdownButton( + speakerStatus: speakerStatus, + statusChangeCallback: statusChangeCallback, + speakerId: speaker.id, + ), + if (!isEditable) + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: STATUSCOLOR[speakerStatus]), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text(STATUSSTRING[speakerStatus]!), + ), + ), + ), + //TODO define subscribe behaviour + ElevatedButton( + onPressed: () => print('zona'), + child: Text('+ Subscribe')) + ], + ), + ), + ), + ], + ), + ), + ), + IconButton( + icon: Icon(Icons.edit), + onPressed: () { + _editSpeakerModal(context); + }, + ) + ], + ); + }, + ); + } +} \ No newline at end of file diff --git a/frontend/lib/routes/speaker/banner/SpeakerStatusDropdownButton.dart b/frontend/lib/routes/speaker/banner/SpeakerStatusDropdownButton.dart new file mode 100644 index 00000000..26216450 --- /dev/null +++ b/frontend/lib/routes/speaker/banner/SpeakerStatusDropdownButton.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:frontend/components/status.dart'; +import 'package:frontend/models/participation.dart'; +import 'package:frontend/services/speakerService.dart'; + +class SpeakerStatusDropdownButton extends StatelessWidget { + final void Function(int, BuildContext) statusChangeCallback; + final ParticipationStatus speakerStatus; + final String speakerId; + final SpeakerService _speakerService = SpeakerService(); + + SpeakerStatusDropdownButton({ + Key? key, + required this.statusChangeCallback, + required this.speakerStatus, + required this.speakerId, + }) : super(key: key); + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _speakerService.getNextParticipationSteps(id: speakerId), + builder: (context, snapshot) { + List steps = [ + ParticipationStep(next: speakerStatus, step: 0) + ]; + if (snapshot.hasData) { + steps.addAll(snapshot.data as List); + } + return Container( + child: DropdownButton( + underline: Container( + height: 3, + decoration: BoxDecoration(color: STATUSCOLOR[speakerStatus]), + ), + value: steps[0], + style: Theme.of(context).textTheme.subtitle2, + selectedItemBuilder: (BuildContext context) { + return steps.map((e) { + return Align( + alignment: AlignmentDirectional.centerStart, + child: Container(child: Text(STATUSSTRING[e.next]!)), + ); + }).toList(); + }, + items: steps + .map((e) => DropdownMenuItem( + value: e, + child: Text(STATUSSTRING[e.next] ?? ''), + )) + .toList(), + onChanged: (next) { + if (next != null && next.step != 0) { + statusChangeCallback(next.step, context); + } + }, + ), + ); + }, + ); + } +} diff --git a/frontend/lib/routes/speaker/flights/flightCard.dart b/frontend/lib/routes/speaker/flights/flightCard.dart index 947055ba..fe85d655 100644 --- a/frontend/lib/routes/speaker/flights/flightCard.dart +++ b/frontend/lib/routes/speaker/flights/flightCard.dart @@ -2,12 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:frontend/components/blurryDialog.dart'; import 'package:frontend/models/flightInfo.dart'; -import 'package:frontend/models/speaker.dart'; import 'package:frontend/routes/speaker/flights/editFlightForm.dart'; -import 'package:frontend/routes/speaker/speakerNotifier.dart'; -import 'package:frontend/services/speakerService.dart'; import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; class FlightCard extends StatefulWidget { FlightInfo flight; @@ -28,6 +24,8 @@ class FlightCard extends StatefulWidget { class _FlightCardState extends State with AutomaticKeepAliveClientMixin { + + @override bool get wantKeepAlive => true; Future flightChangedCallback(BuildContext context, @@ -68,36 +66,6 @@ class _FlightCardState extends State ); } - void _deleteFlight(context) async { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Deleting')), - ); - - SpeakerService _speakerService = SpeakerService(); - Speaker? s = await _speakerService.removeFlightInfo( - id: widget.id, flightInfoId: widget.flight.id); - if (s != null) { - SpeakerTableNotifier notifier = - Provider.of(context, listen: false); - notifier.edit(s); - - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Done'), - duration: Duration(seconds: 2), - ), - ); - } else { - ScaffoldMessenger.of(context).hideCurrentSnackBar(); - - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('An error occured.')), - ); - } - } - @override Widget build(BuildContext context) { super.build(context); diff --git a/frontend/lib/routes/speaker/flights/flightInfoScreen.dart b/frontend/lib/routes/speaker/flights/flightInfoScreen.dart index ad4dc08a..3bd09070 100644 --- a/frontend/lib/routes/speaker/flights/flightInfoScreen.dart +++ b/frontend/lib/routes/speaker/flights/flightInfoScreen.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:frontend/models/participation.dart'; import 'package:frontend/routes/speaker/flights/participationFlightsWidget.dart'; -class FlightInfoScreen extends StatelessWidget { +class FlightInfoScreen extends StatefulWidget { final List participations; final String id; final bool small; @@ -15,22 +15,32 @@ class FlightInfoScreen extends StatelessWidget { required this.id}) : super(key: key); + @override + _FlightInfoScreenState createState() => _FlightInfoScreenState(); +} + +class _FlightInfoScreenState extends State + with AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; + @override Widget build(BuildContext context) { + super.build(context); return LayoutBuilder(builder: (context, constraints) { return Padding( padding: const EdgeInsets.fromLTRB(0, 0, 0, 20), child: ListView( controller: ScrollController(), - children: participations.reversed + children: widget.participations.reversed .where((element) => element.flightsId != null && element.flightsId!.length != 0) .map( (participation) => ParticipationFlightsWidget( participation: participation, - id: id, - small: small, - onDelete: onFlightDeleted, + id: widget.id, + small: widget.small, + onDelete: widget.onFlightDeleted, ), ) .toList()), diff --git a/frontend/lib/routes/teams/AddTeamMemberForm.dart b/frontend/lib/routes/teams/AddTeamMemberForm.dart index d0b6a521..ca902e29 100644 --- a/frontend/lib/routes/teams/AddTeamMemberForm.dart +++ b/frontend/lib/routes/teams/AddTeamMemberForm.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:frontend/components/appbar.dart'; import 'package:frontend/components/SearchResultWidget.dart'; import 'package:frontend/models/member.dart'; import 'package:frontend/models/team.dart'; diff --git a/frontend/lib/routes/teams/TeamScreen.dart b/frontend/lib/routes/teams/TeamScreen.dart index fd1047da..bfab154c 100644 --- a/frontend/lib/routes/teams/TeamScreen.dart +++ b/frontend/lib/routes/teams/TeamScreen.dart @@ -503,7 +503,7 @@ class DisplayMeeting extends StatefulWidget { _DisplayMeetingState createState() => _DisplayMeetingState(); } -class _DisplayMeetingState extends State { +class _DisplayMeetingState extends State with AutomaticKeepAliveClientMixin { MeetingService _meetingService = new MeetingService(); @override @@ -516,6 +516,7 @@ class _DisplayMeetingState extends State { @override Widget build(BuildContext context) { + super.build(context); List> _futureMeetings = widget.meetingsIds!.map((m) => _meetingService.getMeeting(m)).toList(); diff --git a/frontend/lib/routes/teams/TeamsTable.dart b/frontend/lib/routes/teams/TeamsTable.dart index cad522f1..8429ef59 100644 --- a/frontend/lib/routes/teams/TeamsTable.dart +++ b/frontend/lib/routes/teams/TeamsTable.dart @@ -16,7 +16,7 @@ import 'package:shimmer/shimmer.dart'; const ALL = "All"; class TeamTable extends StatefulWidget { - TeamTable({Key? key}) : super(key: key); + const TeamTable({Key? key}) : super(key: key); @override _TeamTableState createState() => _TeamTableState(); @@ -30,6 +30,7 @@ class _TeamTableState extends State late Future> members; + @override bool get wantKeepAlive => true; @override diff --git a/frontend/lib/services/meetingService.dart b/frontend/lib/services/meetingService.dart index ccc03aea..a4614f42 100644 --- a/frontend/lib/services/meetingService.dart +++ b/frontend/lib/services/meetingService.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'dart:typed_data'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/foundation.dart'; import 'package:frontend/models/meeting.dart'; @@ -9,8 +8,6 @@ import 'package:dio/dio.dart'; import 'package:frontend/components/deckException.dart'; import 'dart:io'; -import 'package:image_picker/image_picker.dart'; - class MeetingService extends Service { Future> getMeetings( {String? team, String? company, int? event}) async { diff --git a/frontend/lib/services/teamService.dart b/frontend/lib/services/teamService.dart index e2dc29c0..e62b7689 100644 --- a/frontend/lib/services/teamService.dart +++ b/frontend/lib/services/teamService.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:frontend/components/deckException.dart'; import 'package:frontend/models/meeting.dart'; -import 'package:frontend/models/member.dart'; import 'package:frontend/services/service.dart'; import 'package:frontend/models/team.dart';