From be398320402f4db70419e48d3bd437edefd3a132 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 23 Sep 2023 19:33:38 +0100 Subject: [PATCH 001/334] Download feature --- .../course_units_info_fetcher.dart | 22 +++++++++ .../parsers/parser_course_unit_info.dart | 33 ++++++++++++++ .../controller/parsers/parser_schedule.dart | 2 + .../course_units/course_unit_file.dart | 8 ++++ .../lazy/course_units_info_provider.dart | 21 +++++++++ .../course_unit_info/course_unit_info.dart | 45 ++++++++++++++++++- .../widgets/course_unit_files.dart | 40 +++++++++++++++++ 7 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 uni/lib/model/entities/course_units/course_unit_file.dart create mode 100644 uni/lib/view/course_unit_info/widgets/course_unit_files.dart diff --git a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart index 608a23f6c..0dd3caa3b 100644 --- a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart +++ b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart @@ -5,6 +5,8 @@ import 'package:uni/controller/parsers/parser_course_unit_info.dart'; import 'package:uni/model/entities/course_units/course_unit_class.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/session.dart'; +import 'package:uni/model/entities/course_units/course_unit_file.dart'; +import 'package:http/http.dart'; class CourseUnitsInfoFetcher implements SessionDependantFetcher { @override @@ -26,6 +28,26 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { return parseCourseUnitSheet(response); } + Future> fetchCourseUnitFiles( + Session session, + int occurId, + ) async { + final urls = + getEndpoints(session).map((url) => '${url}mob_ucurr_geral.conteudos'); + final responses = []; + for (final url in urls) { + final response = await NetworkRouter.getWithCookies( + url, + { + 'pv_ocorrencia_id': occurId.toString(), + }, + session, + ); + responses.add(response); + } + return parseFilesMultipleRequests(responses); + } + Future> fetchCourseUnitClasses( Session session, int occurrId, diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 6ef395a2f..5409be874 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -1,7 +1,40 @@ +import 'dart:convert'; + import 'package:html/parser.dart'; import 'package:http/http.dart' as http; import 'package:uni/model/entities/course_units/course_unit_class.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; +import 'package:uni/model/entities/course_units/course_unit_file.dart'; + +Future> parseFilesMultipleRequests( + List responses, +) async { + var files = []; + for (final response in responses) { + files += await parseFiles(response); + } + return files; +} + +Future> parseFiles(http.Response response) async { + final files = []; + + final json = jsonDecode(response.body) as List; + if (json.isEmpty) return []; + for (var item in json) { + item = item as Map; + print(item); + for (final file in item['ficheiros'] as Iterable) { + final fileName = file['nome'] as String; + final fileCode = file['codigo']; + final courseUnitFile = CourseUnitFile(fileName, + "https://sigarra.up.pt/feup/pt/conteudos_service.conteudos_cont?pct_id=$fileCode"); + files.add(courseUnitFile); + } + } + print(files); + return files; +} Future parseCourseUnitSheet(http.Response response) async { final document = parse(response.body); diff --git a/uni/lib/controller/parsers/parser_schedule.dart b/uni/lib/controller/parsers/parser_schedule.dart index 2a98c4853..0f116fb2f 100644 --- a/uni/lib/controller/parsers/parser_schedule.dart +++ b/uni/lib/controller/parsers/parser_schedule.dart @@ -24,6 +24,8 @@ Future> parseSchedule(http.Response response) async { final json = jsonDecode(response.body) as Map; + print(json); + final schedule = json['horario'] as List; for (var lecture in schedule) { lecture = lecture as Map; diff --git a/uni/lib/model/entities/course_units/course_unit_file.dart b/uni/lib/model/entities/course_units/course_unit_file.dart new file mode 100644 index 000000000..c6b64b160 --- /dev/null +++ b/uni/lib/model/entities/course_units/course_unit_file.dart @@ -0,0 +1,8 @@ +class CourseUnitFile { + CourseUnitFile( + this.name, + this.url, + ); + String name; + String url; +} diff --git a/uni/lib/model/providers/lazy/course_units_info_provider.dart b/uni/lib/model/providers/lazy/course_units_info_provider.dart index 1f1e76a0b..67d55e28d 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -4,6 +4,7 @@ import 'package:logger/logger.dart'; import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/entities/course_units/course_unit_class.dart'; +import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; @@ -15,6 +16,7 @@ class CourseUnitsInfoProvider extends StateProviderNotifier { : super(dependsOnSession: true, cacheDuration: null, initialize: false); final Map _courseUnitsSheets = {}; final Map> _courseUnitsClasses = {}; + final Map> _courseUnitsFiles = {}; UnmodifiableMapView get courseUnitsSheets => UnmodifiableMapView(_courseUnitsSheets); @@ -22,6 +24,9 @@ class CourseUnitsInfoProvider extends StateProviderNotifier { UnmodifiableMapView> get courseUnitsClasses => UnmodifiableMapView(_courseUnitsClasses); + UnmodifiableMapView> get courseUnitsFiles => + UnmodifiableMapView(_courseUnitsFiles); + Future fetchCourseUnitSheet( CourseUnit courseUnit, Session session, @@ -55,6 +60,22 @@ class CourseUnitsInfoProvider extends StateProviderNotifier { updateStatus(RequestStatus.successful); } + Future fetchCourseUnitFiles( + CourseUnit courseUnit, + Session session, + ) async { + updateStatus(RequestStatus.busy); + try { + _courseUnitsFiles[courseUnit] = await CourseUnitsInfoFetcher() + .fetchCourseUnitFiles(session, courseUnit.occurrId); + } catch (e) { + updateStatus(RequestStatus.failed); + Logger().e('Failed to get course unit files for ${courseUnit.name}: $e'); + return; + } + updateStatus(RequestStatus.successful); + } + @override Future loadFromRemote(Session session, Profile profile) async { // Course units info is loaded on demand by its detail page diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index 7c0d61596..b142ee4b9 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -7,6 +7,7 @@ import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; import 'package:uni/view/course_unit_info/widgets/course_unit_classes.dart'; +import 'package:uni/view/course_unit_info/widgets/course_unit_files.dart'; import 'package:uni/view/course_unit_info/widgets/course_unit_sheet.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -36,6 +37,15 @@ class CourseUnitDetailPageViewState ); } + final courseUnitFiles = + courseUnitsProvider.courseUnitsFiles[widget.courseUnit]; + if (courseUnitFiles == null || force) { + await courseUnitsProvider.fetchCourseUnitFiles( + widget.courseUnit, + session, + ); + } + final courseUnitClasses = courseUnitsProvider.courseUnitsClasses[widget.courseUnit]; if (courseUnitClasses == null || force) { @@ -59,7 +69,7 @@ class CourseUnitDetailPageViewState @override Widget getBody(BuildContext context) { return DefaultTabController( - length: 2, + length: 3, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -68,7 +78,13 @@ class CourseUnitDetailPageViewState name: widget.courseUnit.name, ), const TabBar( - tabs: [Tab(text: 'Ficha'), Tab(text: 'Turmas')], + tabs: [ + Tab(text: 'Ficha'), + Tab(text: 'Turmas'), + Tab( + text: 'Ficheiros', + ) + ], ), Expanded( child: Padding( @@ -77,6 +93,7 @@ class CourseUnitDetailPageViewState children: [ _courseUnitSheetView(context), _courseUnitClassesView(context), + _courseUnitFilesView(context), ], ), ), @@ -110,6 +127,30 @@ class CourseUnitDetailPageViewState ); } + Widget _courseUnitFilesView(BuildContext context) { + return LazyConsumer( + builder: (context, courseUnitsInfoProvider) { + return RequestDependentWidgetBuilder( + onNullContent: const Center( + child: Text( + 'Não existem informações para apresentar', + textAlign: TextAlign.center, + ), + ), + status: courseUnitsInfoProvider.status, + builder: () => CourseUnitFilesView( + courseUnitsInfoProvider.courseUnitsFiles[widget.courseUnit]!, + ), + hasContentPredicate: + courseUnitsInfoProvider.courseUnitsFiles[widget.courseUnit] != + null && + courseUnitsInfoProvider + .courseUnitsFiles[widget.courseUnit]!.isNotEmpty, + ); + }, + ); + } + Widget _courseUnitClassesView(BuildContext context) { return LazyConsumer( builder: (context, courseUnitsInfoProvider) { diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart new file mode 100644 index 000000000..ed076aecd --- /dev/null +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:uni/model/entities/course_units/course_unit_file.dart'; +import 'package:uni/view/course_unit_info/widgets/course_unit_info_card.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class CourseUnitFilesView extends StatelessWidget { + const CourseUnitFilesView(this.files, {Key? key}) : super(key: key); + final List files; + + @override + Widget build(BuildContext context) { + final cards = files.map((file) => _buildCard(file)).toList(); + + return Container( + padding: const EdgeInsets.only(left: 10, right: 10), + child: ListView(children: cards), + ); + } + + CourseUnitInfoCard _buildCard(CourseUnitFile file) { + return CourseUnitInfoCard( + file.name, + GestureDetector( + onTap: () { + _launchURL(file.url); + }, + child: Text('Download'), + ), + ); + } + + Future _launchURL(String url) async { + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(Uri.parse(url)); + } else { + // Handle the case when the URL cannot be launched + // For example, show an error message + } + } +} From cb4cc3c2f277c2edeaa9e676b88348d23bc8450e Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sun, 24 Sep 2023 02:51:04 +0100 Subject: [PATCH 002/334] Files page redesign --- .../course_units_info_fetcher.dart | 8 ++-- .../parsers/parser_course_unit_info.dart | 33 ++++++++------ .../controller/parsers/parser_schedule.dart | 2 - .../lazy/course_units_info_provider.dart | 7 +-- .../widgets/course_unit_files.dart | 29 +++++-------- .../widgets/course_unit_files_row.dart | 43 +++++++++++++++++++ 6 files changed, 82 insertions(+), 40 deletions(-) create mode 100644 uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart diff --git a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart index 0dd3caa3b..17425f23d 100644 --- a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart +++ b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart @@ -1,12 +1,12 @@ import 'package:html/parser.dart'; +import 'package:http/http.dart'; import 'package:uni/controller/fetchers/session_dependant_fetcher.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/controller/parsers/parser_course_unit_info.dart'; import 'package:uni/model/entities/course_units/course_unit_class.dart'; +import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/session.dart'; -import 'package:uni/model/entities/course_units/course_unit_file.dart'; -import 'package:http/http.dart'; class CourseUnitsInfoFetcher implements SessionDependantFetcher { @override @@ -28,7 +28,7 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { return parseCourseUnitSheet(response); } - Future> fetchCourseUnitFiles( + Future>>> fetchCourseUnitFiles( Session session, int occurId, ) async { @@ -45,7 +45,7 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { ); responses.add(response); } - return parseFilesMultipleRequests(responses); + return parseFilesMultipleRequests(responses, session); } Future> fetchCourseUnitClasses( diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 5409be874..3d83637b3 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -3,37 +3,46 @@ import 'dart:convert'; import 'package:html/parser.dart'; import 'package:http/http.dart' as http; import 'package:uni/model/entities/course_units/course_unit_class.dart'; -import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; +import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; +import 'package:uni/model/entities/session.dart'; -Future> parseFilesMultipleRequests( +Future>>> parseFilesMultipleRequests( List responses, + Session session, ) async { - var files = []; + final files = >>[]; for (final response in responses) { - files += await parseFiles(response); + files.add(await parseFiles(response, session)); } return files; } -Future> parseFiles(http.Response response) async { - final files = []; +Future>> parseFiles( + http.Response response, + Session session, +) async { + final folders = >{}; final json = jsonDecode(response.body) as List; - if (json.isEmpty) return []; + + if (json.isEmpty) return {}; + for (var item in json) { item = item as Map; - print(item); + final files = []; for (final file in item['ficheiros'] as Iterable) { final fileName = file['nome'] as String; final fileCode = file['codigo']; - final courseUnitFile = CourseUnitFile(fileName, - "https://sigarra.up.pt/feup/pt/conteudos_service.conteudos_cont?pct_id=$fileCode"); + final courseUnitFile = CourseUnitFile( + fileName, + 'https://sigarra.up.pt/feup/pt/conteudos_service.conteudos_cont?pct_id=$fileCode', + ); files.add(courseUnitFile); } + folders[item['nome'] as String] = files; } - print(files); - return files; + return folders; } Future parseCourseUnitSheet(http.Response response) async { diff --git a/uni/lib/controller/parsers/parser_schedule.dart b/uni/lib/controller/parsers/parser_schedule.dart index 0f116fb2f..2a98c4853 100644 --- a/uni/lib/controller/parsers/parser_schedule.dart +++ b/uni/lib/controller/parsers/parser_schedule.dart @@ -24,8 +24,6 @@ Future> parseSchedule(http.Response response) async { final json = jsonDecode(response.body) as Map; - print(json); - final schedule = json['horario'] as List; for (var lecture in schedule) { lecture = lecture as Map; diff --git a/uni/lib/model/providers/lazy/course_units_info_provider.dart b/uni/lib/model/providers/lazy/course_units_info_provider.dart index 67d55e28d..9a5bbce57 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -16,7 +16,8 @@ class CourseUnitsInfoProvider extends StateProviderNotifier { : super(dependsOnSession: true, cacheDuration: null, initialize: false); final Map _courseUnitsSheets = {}; final Map> _courseUnitsClasses = {}; - final Map> _courseUnitsFiles = {}; + final Map>>> + _courseUnitsFiles = {}; UnmodifiableMapView get courseUnitsSheets => UnmodifiableMapView(_courseUnitsSheets); @@ -24,8 +25,8 @@ class CourseUnitsInfoProvider extends StateProviderNotifier { UnmodifiableMapView> get courseUnitsClasses => UnmodifiableMapView(_courseUnitsClasses); - UnmodifiableMapView> get courseUnitsFiles => - UnmodifiableMapView(_courseUnitsFiles); + UnmodifiableMapView>>> + get courseUnitsFiles => UnmodifiableMapView(_courseUnitsFiles); Future fetchCourseUnitSheet( CourseUnit courseUnit, diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart index ed076aecd..88ec0e176 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart @@ -1,15 +1,18 @@ import 'package:flutter/material.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/view/course_unit_info/widgets/course_unit_info_card.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:uni/view/course_unit_info/widgets/course_unit_files_row.dart'; class CourseUnitFilesView extends StatelessWidget { const CourseUnitFilesView(this.files, {Key? key}) : super(key: key); - final List files; + final List>> files; @override Widget build(BuildContext context) { - final cards = files.map((file) => _buildCard(file)).toList(); + final cards = files + .expand((file) => + file.entries.map((item) => _buildCard(item.key, item.value))) + .toList(); return Container( padding: const EdgeInsets.only(left: 10, right: 10), @@ -17,24 +20,12 @@ class CourseUnitFilesView extends StatelessWidget { ); } - CourseUnitInfoCard _buildCard(CourseUnitFile file) { + CourseUnitInfoCard _buildCard(String folder, List files) { return CourseUnitInfoCard( - file.name, - GestureDetector( - onTap: () { - _launchURL(file.url); - }, - child: Text('Download'), + folder, + Column( + children: files.map((file) => CourseUnitFilesRow(file)).toList(), ), ); } - - Future _launchURL(String url) async { - if (await canLaunchUrl(Uri.parse(url))) { - await launchUrl(Uri.parse(url)); - } else { - // Handle the case when the URL cannot be launched - // For example, show an error message - } - } } diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart new file mode 100644 index 000000000..d0a41accd --- /dev/null +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:uni/model/entities/course_units/course_unit_file.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class CourseUnitFilesRow extends StatelessWidget { + const CourseUnitFilesRow(this.file, {Key? key}) : super(key: key); + + final CourseUnitFile file; + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.only(bottom: 10), + child: Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.only(left: 10), + child: Text( + file.name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyLarge, + ), + ), + ), + IconButton( + icon: Icon(Icons.download), + onPressed: () => _launchURL(file.url), + ), + ], + ), + ); + } + + Future _launchURL(String url) async { + if (await canLaunchUrl(Uri.parse(url))) { + await launchUrl(Uri.parse(url)); + } else { + // Handle the case when the URL cannot be launched + // For example, show an error message + } + } +} From f1a93e9ee694286adde81a77eb3ecab409edd9d6 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Tue, 26 Sep 2023 16:33:54 +0100 Subject: [PATCH 003/334] Fixing day title translation --- uni/lib/generated/intl/messages_en.dart | 1 + uni/lib/generated/intl/messages_pt_PT.dart | 1 + uni/lib/generated/l10n.dart | 10 ++++++++++ uni/lib/l10n/intl_en.arb | 2 ++ uni/lib/l10n/intl_pt_PT.arb | 2 ++ uni/lib/view/exams/widgets/day_title.dart | 3 ++- 6 files changed, 18 insertions(+), 1 deletion(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 7c9d65e6c..6b46a9a56 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -112,6 +112,7 @@ class MessageLookup extends MessageLookupByLibrary { "edit_on": MessageLookupByLibrary.simpleMessage("Finish editing"), "empty_text": MessageLookupByLibrary.simpleMessage("Please fill in this field"), + "exam_of": MessageLookupByLibrary.simpleMessage("of"), "exams_filter": MessageLookupByLibrary.simpleMessage("Exams Filter Settings"), "exit_confirm": diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index c86c90267..a8a3ab3bf 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -111,6 +111,7 @@ class MessageLookup extends MessageLookupByLibrary { "edit_on": MessageLookupByLibrary.simpleMessage("Concluir edição"), "empty_text": MessageLookupByLibrary.simpleMessage( "Por favor preenche este campo"), + "exam_of": MessageLookupByLibrary.simpleMessage("de"), "exams_filter": MessageLookupByLibrary.simpleMessage("Definições Filtro de Exames"), "exit_confirm": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 804bd1400..ef1a35f26 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1028,6 +1028,16 @@ class S { ); } + /// `of` + String get exam_of { + return Intl.message( + 'of', + name: 'exam_of', + desc: '', + args: [], + ); + } + /// `Other links` String get other_links { return Intl.message( diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index acc5d7b7c..e8fec3e09 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -200,6 +200,8 @@ "@no_selected_exams": {}, "occurrence_type": "Type of occurrence", "@occurrence_type": {}, + "exam_of": "of", + "@exam_of": {}, "other_links": "Other links", "@other_links": {}, "pass_change_request": "For security reasons, passwords must be changed periodically.", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 7552ae5c3..b9a9d0bca 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -200,6 +200,8 @@ "@no_selected_exams": {}, "occurrence_type": "Tipo de ocorrência", "@occurrence_type": {}, + "exam_of": "de", + "@exam_of": {}, "other_links": "Outros links", "@other_links": {}, "pass_change_request": "Por razões de segurança, as palavras-passe têm de ser alteradas periodicamente.", diff --git a/uni/lib/view/exams/widgets/day_title.dart b/uni/lib/view/exams/widgets/day_title.dart index 56710245a..f42528a03 100644 --- a/uni/lib/view/exams/widgets/day_title.dart +++ b/uni/lib/view/exams/widgets/day_title.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; class DayTitle extends StatelessWidget { const DayTitle({ @@ -17,7 +18,7 @@ class DayTitle extends StatelessWidget { padding: const EdgeInsets.only(top: 15, bottom: 3), alignment: Alignment.center, child: Text( - '$weekDay, $day de $month', + '$weekDay, $day ${S.of(context).exam_of} $month', style: Theme.of(context).textTheme.titleLarge, ), ); From 3e057de3b2e25f28e43f37aae65fb2a35006fafc Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 28 Sep 2023 13:06:55 +0100 Subject: [PATCH 004/334] Pdf icon and open file support --- .../course_units_info_fetcher.dart | 15 ++++++++++ .../parsers/parser_course_unit_info.dart | 5 +++- .../course_units/course_unit_file.dart | 4 +-- .../widgets/course_unit_files.dart | 5 ++-- .../widgets/course_unit_files_row.dart | 29 ++++++++++++++----- uni/pubspec.yaml | 1 + 6 files changed, 46 insertions(+), 13 deletions(-) diff --git a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart index 17425f23d..585ea6dc7 100644 --- a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart +++ b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart @@ -48,6 +48,21 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { return parseFilesMultipleRequests(responses, session); } + Future> downloadFile( + Session session, + dynamic fileCode, + ) async { + final url = '${getEndpoints(session)[0]}conteudos_service.conteudos_cont'; + final response = await NetworkRouter.getWithCookies( + url, + { + 'pct_id': fileCode.toString(), + }, + session, + ); + return response.bodyBytes; + } + Future> fetchCourseUnitClasses( Session session, int occurrId, diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 3d83637b3..49b31f2cb 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -6,6 +6,7 @@ import 'package:uni/model/entities/course_units/course_unit_class.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/session.dart'; +import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart'; Future>>> parseFilesMultipleRequests( List responses, @@ -34,9 +35,11 @@ Future>> parseFiles( for (final file in item['ficheiros'] as Iterable) { final fileName = file['nome'] as String; final fileCode = file['codigo']; + final bodyBytes = + await CourseUnitsInfoFetcher().downloadFile(session, fileCode); final courseUnitFile = CourseUnitFile( fileName, - 'https://sigarra.up.pt/feup/pt/conteudos_service.conteudos_cont?pct_id=$fileCode', + bodyBytes, ); files.add(courseUnitFile); } diff --git a/uni/lib/model/entities/course_units/course_unit_file.dart b/uni/lib/model/entities/course_units/course_unit_file.dart index c6b64b160..7375fef1c 100644 --- a/uni/lib/model/entities/course_units/course_unit_file.dart +++ b/uni/lib/model/entities/course_units/course_unit_file.dart @@ -1,8 +1,8 @@ class CourseUnitFile { CourseUnitFile( this.name, - this.url, + this.bodyBytes, ); String name; - String url; + List bodyBytes; } diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart index 88ec0e176..23badd351 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart @@ -10,8 +10,9 @@ class CourseUnitFilesView extends StatelessWidget { @override Widget build(BuildContext context) { final cards = files - .expand((file) => - file.entries.map((item) => _buildCard(item.key, item.value))) + .expand((file) => file.entries + .where((item) => item.value.isNotEmpty) + .map((item) => _buildCard(item.key, item.value))) .toList(); return Container( diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index d0a41accd..19c4e0b78 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -1,6 +1,9 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:open_file_plus/open_file_plus.dart'; class CourseUnitFilesRow extends StatelessWidget { const CourseUnitFilesRow(this.file, {Key? key}) : super(key: key); @@ -13,6 +16,9 @@ class CourseUnitFilesRow extends StatelessWidget { padding: const EdgeInsets.only(bottom: 10), child: Row( children: [ + const SizedBox(width: 8), + const Icon(Icons.picture_as_pdf), + const SizedBox(width: 1), Expanded( child: Container( padding: const EdgeInsets.only(left: 10), @@ -25,19 +31,26 @@ class CourseUnitFilesRow extends StatelessWidget { ), IconButton( icon: Icon(Icons.download), - onPressed: () => _launchURL(file.url), + onPressed: () => openFile(file), ), ], ), ); } - Future _launchURL(String url) async { - if (await canLaunchUrl(Uri.parse(url))) { - await launchUrl(Uri.parse(url)); - } else { - // Handle the case when the URL cannot be launched - // For example, show an error message + Future openFile(CourseUnitFile course_unit_file) async { + final response = course_unit_file.bodyBytes; + + final String fileName = course_unit_file.name + '.pdf'; + + final downloadsDir = await getDownloadsDirectory(); + final downloadPath = '${downloadsDir!.path}/$fileName'; + + final file = File(downloadPath); + if (!await file.exists()) { + await file.create(); } + await file.writeAsBytes(response); + await OpenFile.open(file.path.toString()); } } diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index fdbccd0c1..f3bbb9349 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -42,6 +42,7 @@ dependencies: latlong2: ^0.8.1 logger: ^1.1.0 material_design_icons_flutter: ^7.0.7296 + open_file_plus: ^3.4.1 path: ^1.8.0 path_provider: ^2.0.0 percent_indicator: ^4.2.2 From b3bbc32f92535f3d314779a5d3e2130bc5aa99b2 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sun, 1 Oct 2023 21:01:31 +0100 Subject: [PATCH 005/334] Lint fix --- .../parsers/parser_course_unit_info.dart | 2 +- .../course_unit_info/course_unit_info.dart | 2 +- .../widgets/course_unit_files.dart | 12 +++++----- .../widgets/course_unit_files_row.dart | 22 +++++++++---------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 49b31f2cb..6c39f0cba 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -2,11 +2,11 @@ import 'dart:convert'; import 'package:html/parser.dart'; import 'package:http/http.dart' as http; +import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart'; import 'package:uni/model/entities/course_units/course_unit_class.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/session.dart'; -import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart'; Future>>> parseFilesMultipleRequests( List responses, diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index c2b2d7a84..1aa43b8cc 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -82,7 +82,7 @@ class CourseUnitDetailPageViewState tabs: [ Tab(text: S.of(context).course_info), Tab(text: S.of(context).course_class), - Tab( + const Tab( text: 'Ficheiros', ) ], diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart index 23badd351..8878174fe 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart @@ -4,15 +4,17 @@ import 'package:uni/view/course_unit_info/widgets/course_unit_info_card.dart'; import 'package:uni/view/course_unit_info/widgets/course_unit_files_row.dart'; class CourseUnitFilesView extends StatelessWidget { - const CourseUnitFilesView(this.files, {Key? key}) : super(key: key); + const CourseUnitFilesView(this.files, {super.key}); final List>> files; @override Widget build(BuildContext context) { final cards = files - .expand((file) => file.entries - .where((item) => item.value.isNotEmpty) - .map((item) => _buildCard(item.key, item.value))) + .expand( + (file) => file.entries + .where((item) => item.value.isNotEmpty) + .map((item) => _buildCard(item.key, item.value)), + ) .toList(); return Container( @@ -25,7 +27,7 @@ class CourseUnitFilesView extends StatelessWidget { return CourseUnitInfoCard( folder, Column( - children: files.map((file) => CourseUnitFilesRow(file)).toList(), + children: files.map(CourseUnitFilesRow.new).toList(), ), ); } diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index 19c4e0b78..23036aea1 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -1,12 +1,12 @@ import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:open_file_plus/open_file_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; -import 'package:open_file_plus/open_file_plus.dart'; class CourseUnitFilesRow extends StatelessWidget { - const CourseUnitFilesRow(this.file, {Key? key}) : super(key: key); + const CourseUnitFilesRow(this.file, {super.key}); final CourseUnitFile file; @@ -30,7 +30,7 @@ class CourseUnitFilesRow extends StatelessWidget { ), ), IconButton( - icon: Icon(Icons.download), + icon: const Icon(Icons.download), onPressed: () => openFile(file), ), ], @@ -38,19 +38,19 @@ class CourseUnitFilesRow extends StatelessWidget { ); } - Future openFile(CourseUnitFile course_unit_file) async { - final response = course_unit_file.bodyBytes; + Future openFile(CourseUnitFile unitFile) async { + final response = unitFile.bodyBytes; - final String fileName = course_unit_file.name + '.pdf'; + final fileName = '${unitFile.name}.pdf'; - final downloadsDir = await getDownloadsDirectory(); - final downloadPath = '${downloadsDir!.path}/$fileName'; + final downloadDir = (await getTemporaryDirectory()).path; + final downloadPath = '$downloadDir$fileName'; final file = File(downloadPath); - if (!await file.exists()) { - await file.create(); + if (!file.existsSync()) { + file.createSync(); } await file.writeAsBytes(response); - await OpenFile.open(file.path.toString()); + await OpenFile.open(file.path); } } From 25c47bbacb588f470fbc0843e97d2e2f1cdf10d6 Mon Sep 17 00:00:00 2001 From: coutinho21 Date: Mon, 2 Oct 2023 15:03:55 +0100 Subject: [PATCH 006/334] iOS related files --- uni/.metadata | 30 ++++++++++++++++++ .../LaunchImage.imageset/Contents.json | 23 ++++++++++++++ .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 +++ uni/ios/RunnerTests/RunnerTests.swift | 12 +++++++ uni/test/widget_test.dart | 30 ++++++++++++++++++ 8 files changed, 100 insertions(+) create mode 100644 uni/.metadata create mode 100644 uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 uni/ios/RunnerTests/RunnerTests.swift create mode 100644 uni/test/widget_test.dart diff --git a/uni/.metadata b/uni/.metadata new file mode 100644 index 000000000..b7a08be36 --- /dev/null +++ b/uni/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + channel: unknown + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + - platform: ios + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 000000000..0bedcf2fd --- /dev/null +++ b/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 000000000..89c2725b7 --- /dev/null +++ b/uni/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/uni/ios/RunnerTests/RunnerTests.swift b/uni/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000..86a7c3b1b --- /dev/null +++ b/uni/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/uni/test/widget_test.dart b/uni/test/widget_test.dart new file mode 100644 index 000000000..785fdc88e --- /dev/null +++ b/uni/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:uni/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} From 5085c09d1a9efb07364ff1646d514a2084af432b Mon Sep 17 00:00:00 2001 From: coutinho21 Date: Mon, 2 Oct 2023 15:05:14 +0100 Subject: [PATCH 007/334] Remove github fetch --- .../load_static/terms_and_conditions.dart | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/uni/lib/controller/load_static/terms_and_conditions.dart b/uni/lib/controller/load_static/terms_and_conditions.dart index ba6945175..af0df93fe 100644 --- a/uni/lib/controller/load_static/terms_and_conditions.dart +++ b/uni/lib/controller/load_static/terms_and_conditions.dart @@ -1,9 +1,7 @@ import 'dart:convert'; -import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:crypto/crypto.dart'; import 'package:flutter/services.dart' show rootBundle; -import 'package:http/http.dart' as http; import 'package:logger/logger.dart'; import 'package:uni/controller/local_storage/app_shared_preferences.dart'; @@ -12,19 +10,6 @@ import 'package:uni/controller/local_storage/app_shared_preferences.dart'; /// /// If this operation is unsuccessful, an error message is returned. Future fetchTermsAndConditions() async { - if (await Connectivity().checkConnectivity() != ConnectivityResult.none) { - try { - const url = - 'https://raw.githubusercontent.com/NIAEFEUP/project-schrodinger/develop/uni/assets/text/TermsAndConditions.md'; - final response = await http.get(Uri.parse(url)); - if (response.statusCode == 200) { - return response.body; - } - } catch (e) { - Logger().e('Failed to fetch Terms and Conditions: $e'); - } - } - try { return await rootBundle.loadString('assets/text/TermsAndConditions.md'); } catch (e) { From 43a8106ad76ac13f3f888825b1cb08ee303741de Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 4 Oct 2023 14:10:06 +0100 Subject: [PATCH 008/334] of_month --- uni/lib/generated/intl/messages_pt_PT.dart | 2 +- uni/lib/generated/l10n.dart | 4 ++-- uni/lib/l10n/intl_en.arb | 4 ++-- uni/lib/l10n/intl_pt_PT.arb | 4 ++-- uni/lib/view/exams/widgets/day_title.dart | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index a8a3ab3bf..f023e098b 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -111,7 +111,7 @@ class MessageLookup extends MessageLookupByLibrary { "edit_on": MessageLookupByLibrary.simpleMessage("Concluir edição"), "empty_text": MessageLookupByLibrary.simpleMessage( "Por favor preenche este campo"), - "exam_of": MessageLookupByLibrary.simpleMessage("de"), + "of_month": MessageLookupByLibrary.simpleMessage("de"), "exams_filter": MessageLookupByLibrary.simpleMessage("Definições Filtro de Exames"), "exit_confirm": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index ef1a35f26..c8fc61c27 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1029,10 +1029,10 @@ class S { } /// `of` - String get exam_of { + String get of_month { return Intl.message( 'of', - name: 'exam_of', + name: 'of_month', desc: '', args: [], ); diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index e8fec3e09..d1dcadff2 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -200,8 +200,8 @@ "@no_selected_exams": {}, "occurrence_type": "Type of occurrence", "@occurrence_type": {}, - "exam_of": "of", - "@exam_of": {}, + "of_month": "of", + "@of_month": {}, "other_links": "Other links", "@other_links": {}, "pass_change_request": "For security reasons, passwords must be changed periodically.", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index b9a9d0bca..145d3bbfd 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -200,8 +200,8 @@ "@no_selected_exams": {}, "occurrence_type": "Tipo de ocorrência", "@occurrence_type": {}, - "exam_of": "de", - "@exam_of": {}, + "of_month": "de", + "@of_month": {}, "other_links": "Outros links", "@other_links": {}, "pass_change_request": "Por razões de segurança, as palavras-passe têm de ser alteradas periodicamente.", diff --git a/uni/lib/view/exams/widgets/day_title.dart b/uni/lib/view/exams/widgets/day_title.dart index f42528a03..b0626a86b 100644 --- a/uni/lib/view/exams/widgets/day_title.dart +++ b/uni/lib/view/exams/widgets/day_title.dart @@ -18,7 +18,7 @@ class DayTitle extends StatelessWidget { padding: const EdgeInsets.only(top: 15, bottom: 3), alignment: Alignment.center, child: Text( - '$weekDay, $day ${S.of(context).exam_of} $month', + '$weekDay, $day ${S.of(context).of_month} $month', style: Theme.of(context).textTheme.titleLarge, ), ); From 3fccae6377aa382f684a31b2cb4dfbcb84d494af Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 5 Oct 2023 19:17:30 +0100 Subject: [PATCH 009/334] Opening file fix and dependency update --- .../course_unit_info/widgets/course_unit_files_row.dart | 2 +- uni/pubspec.lock | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index 23036aea1..b39ae76c3 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -44,7 +44,7 @@ class CourseUnitFilesRow extends StatelessWidget { final fileName = '${unitFile.name}.pdf'; final downloadDir = (await getTemporaryDirectory()).path; - final downloadPath = '$downloadDir$fileName'; + final downloadPath = '$downloadDir/$fileName'; final file = File(downloadPath); if (!file.existsSync()) { diff --git a/uni/pubspec.lock b/uni/pubspec.lock index c2e1870fb..2c732cb72 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -686,6 +686,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.2" + open_file_plus: + dependency: "direct main" + description: + name: open_file_plus + sha256: f087e32722ffe4bac71925e7a1a9848a1008fd789e47c6628da3ed7845922227 + url: "https://pub.dev" + source: hosted + version: "3.4.1" package_config: dependency: transitive description: From 8ec07b6c0dbbe1174307698fd04afea331afc762 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 5 Oct 2023 20:23:48 +0100 Subject: [PATCH 010/334] Local storage integration --- .../course_units_info_fetcher.dart | 13 ++------- .../local_storage/file_offline_storage.dart | 4 ++- .../parsers/parser_course_unit_info.dart | 12 ++++---- .../course_units/course_unit_file.dart | 6 ++-- .../widgets/course_unit_files_row.dart | 29 +++++++++---------- 5 files changed, 28 insertions(+), 36 deletions(-) diff --git a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart index 585ea6dc7..bd0278913 100644 --- a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart +++ b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart @@ -48,19 +48,10 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { return parseFilesMultipleRequests(responses, session); } - Future> downloadFile( + Future getDownloadLink( Session session, - dynamic fileCode, ) async { - final url = '${getEndpoints(session)[0]}conteudos_service.conteudos_cont'; - final response = await NetworkRouter.getWithCookies( - url, - { - 'pct_id': fileCode.toString(), - }, - session, - ); - return response.bodyBytes; + return '${getEndpoints(session)[0]}conteudos_service.conteudos_cont'; } Future> fetchCourseUnitClasses( diff --git a/uni/lib/controller/local_storage/file_offline_storage.dart b/uni/lib/controller/local_storage/file_offline_storage.dart index 9580def16..afad71b3a 100644 --- a/uni/lib/controller/local_storage/file_offline_storage.dart +++ b/uni/lib/controller/local_storage/file_offline_storage.dart @@ -67,9 +67,11 @@ Future _downloadAndSaveFile( Session? session, Map? headers, ) async { + final header = headers ?? {}; + final response = session == null ? await http.get(url.toUri(), headers: headers) - : await NetworkRouter.getWithCookies(url, {}, session); + : await NetworkRouter.getWithCookies(url, header, session); if (response.statusCode == 200) { return File(filePath).writeAsBytes(response.bodyBytes); diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 6c39f0cba..7f6892ab0 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -32,14 +32,14 @@ Future>> parseFiles( for (var item in json) { item = item as Map; final files = []; - for (final file in item['ficheiros'] as Iterable) { - final fileName = file['nome'] as String; + for (final file in item['ficheiros'] as List) { + final fileName = file['nome']; final fileCode = file['codigo']; - final bodyBytes = - await CourseUnitsInfoFetcher().downloadFile(session, fileCode); + final url = await CourseUnitsInfoFetcher().getDownloadLink(session); final courseUnitFile = CourseUnitFile( - fileName, - bodyBytes, + fileName.toString(), + url, + fileCode.toString(), ); files.add(courseUnitFile); } diff --git a/uni/lib/model/entities/course_units/course_unit_file.dart b/uni/lib/model/entities/course_units/course_unit_file.dart index 7375fef1c..e7076df1d 100644 --- a/uni/lib/model/entities/course_units/course_unit_file.dart +++ b/uni/lib/model/entities/course_units/course_unit_file.dart @@ -1,8 +1,10 @@ class CourseUnitFile { CourseUnitFile( this.name, - this.bodyBytes, + this.url, + this.fileCode, ); + String fileCode; String name; - List bodyBytes; + String url; } diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index b39ae76c3..8fb8e05b5 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -1,9 +1,9 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; import 'package:open_file_plus/open_file_plus.dart'; -import 'package:path_provider/path_provider.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/file_offline_storage.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; +import 'package:uni/model/providers/startup/session_provider.dart'; class CourseUnitFilesRow extends StatelessWidget { const CourseUnitFilesRow(this.file, {super.key}); @@ -31,26 +31,23 @@ class CourseUnitFilesRow extends StatelessWidget { ), IconButton( icon: const Icon(Icons.download), - onPressed: () => openFile(file), + onPressed: () => openFile(context, file), ), ], ), ); } - Future openFile(CourseUnitFile unitFile) async { - final response = unitFile.bodyBytes; - - final fileName = '${unitFile.name}.pdf'; + Future openFile(BuildContext context, CourseUnitFile unitFile) async { + final session = context.read().session; - final downloadDir = (await getTemporaryDirectory()).path; - final downloadPath = '$downloadDir/$fileName'; + final result = await loadFileFromStorageOrRetrieveNew( + '${unitFile.name}.pdf', + unitFile.url, + session, + headers: {'pct_id': unitFile.fileCode}, + ); - final file = File(downloadPath); - if (!file.existsSync()) { - file.createSync(); - } - await file.writeAsBytes(response); - await OpenFile.open(file.path); + await OpenFile.open(result!.path); } } From 22dba728660b01bfe674827c67d50b1f75e50d99 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 5 Oct 2023 20:39:46 +0100 Subject: [PATCH 011/334] Card redesign --- .../widgets/course_unit_files_row.dart | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index 8fb8e05b5..095c6aff9 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -13,26 +13,25 @@ class CourseUnitFilesRow extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: const EdgeInsets.only(bottom: 10), + padding: const EdgeInsets.only(bottom: 20), child: Row( children: [ const SizedBox(width: 8), const Icon(Icons.picture_as_pdf), const SizedBox(width: 1), Expanded( - child: Container( - padding: const EdgeInsets.only(left: 10), - child: Text( - file.name, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.bodyLarge, + child: InkWell( + onTap: () => openFile(context, file), + child: Container( + padding: const EdgeInsets.only(left: 10), + child: Text( + file.name, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyLarge, + ), ), ), ), - IconButton( - icon: const Icon(Icons.download), - onPressed: () => openFile(context, file), - ), ], ), ); From c470bd80fdbf957587f20c3288ab2ff103f219c4 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 6 Oct 2023 11:56:51 +0100 Subject: [PATCH 012/334] Unused months removal --- .../local_storage/app_exams_database.dart | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/uni/lib/controller/local_storage/app_exams_database.dart b/uni/lib/controller/local_storage/app_exams_database.dart index 36264e347..47dbb4e39 100644 --- a/uni/lib/controller/local_storage/app_exams_database.dart +++ b/uni/lib/controller/local_storage/app_exams_database.dart @@ -11,20 +11,6 @@ import 'package:uni/model/entities/exam.dart'; class AppExamsDatabase extends AppDatabase { AppExamsDatabase() : super('exams.db', [_createScript], onUpgrade: migrate, version: 5); - Map months = { - 'Janeiro': '01', - 'Fevereiro': '02', - 'Março': '03', - 'Abril': '04', - 'Maio': '05', - 'Junho': '06', - 'Julho': '07', - 'Agosto': '08', - 'Setembro': '09', - 'Outubro': '10', - 'Novembro': '11', - 'Dezembro': '12' - }; static const _createScript = ''' CREATE TABLE exams(id TEXT, subject TEXT, begin TEXT, end TEXT, From d436bcce066e5598d4bc0134a13bbcb74d6853bb Mon Sep 17 00:00:00 2001 From: LuisDuarte1 Date: Tue, 10 Oct 2023 09:20:11 +0000 Subject: [PATCH 013/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 5659dbf3d..9d44229de 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.15+205 \ No newline at end of file +1.7.16+206 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index a02b440b0..d4619d650 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: 'none' # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.15+205 +version: 1.7.16+206 environment: sdk: '>=3.0.0 <4.0.0' From 0b5ff94fdf51d63a9860f2309d3bb3ef8479a5e7 Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Fri, 6 Oct 2023 14:58:23 +0100 Subject: [PATCH 014/334] Encapsulate Url Launch with Toast --- .../controller/networking/url_launcher.dart | 18 ++++++++++++++++++ uni/lib/generated/intl/messages_en.dart | 2 ++ uni/lib/generated/intl/messages_pt_PT.dart | 2 ++ uni/lib/generated/l10n.dart | 10 ++++++++++ uni/lib/l10n/intl_en.arb | 2 ++ uni/lib/l10n/intl_pt_PT.arb | 2 ++ .../about/widgets/terms_and_conditions.dart | 12 +++++------- .../view/schedule/widgets/schedule_slot.dart | 8 ++++---- uni/lib/view/terms_and_condition_dialog.dart | 2 +- .../view/useful_info/widgets/link_button.dart | 4 ++-- .../useful_info/widgets/text_components.dart | 4 ++-- 11 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 uni/lib/controller/networking/url_launcher.dart diff --git a/uni/lib/controller/networking/url_launcher.dart b/uni/lib/controller/networking/url_launcher.dart new file mode 100644 index 000000000..f4ff85ae5 --- /dev/null +++ b/uni/lib/controller/networking/url_launcher.dart @@ -0,0 +1,18 @@ +import 'dart:async'; + +import 'package:flutter/cupertino.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/common_widgets/toast_message.dart'; +import 'package:url_launcher/url_launcher.dart'; + +Future launchUrlWithToast(BuildContext context, String url) async { + final validUrl = Uri.parse(url); + if (url != '' && canLaunchUrl(validUrl) as bool) { + await launchUrl(Uri.parse(url)); + } else { + await ToastMessage.error( + context, + S.of(context).no_link, + ); + } +} diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 112516f05..b1570e0e7 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -180,6 +180,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("No favorite restaurants"), "no_info": MessageLookupByLibrary.simpleMessage( "There is no information to display"), + "no_link": + MessageLookupByLibrary.simpleMessage("We couldn\'t open the link"), "no_menu_info": MessageLookupByLibrary.simpleMessage( "There is no information available about meals"), "no_menus": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 68a173ffe..e09dafb26 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -181,6 +181,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Sem restaurantes favoritos"), "no_info": MessageLookupByLibrary.simpleMessage( "Não existem informações para apresentar"), + "no_link": MessageLookupByLibrary.simpleMessage( + "Não conseguimos abrir o link"), "no_menu_info": MessageLookupByLibrary.simpleMessage( "Não há informação disponível sobre refeições"), "no_menus": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 936259b3e..584841336 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1028,6 +1028,16 @@ class S { ); } + /// `We couldn't open the link` + String get no_link { + return Intl.message( + 'We couldn\'t open the link', + name: 'no_link', + desc: '', + args: [], + ); + } + /// `Other links` String get other_links { return Intl.message( diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index b1d6be897..ad759e52a 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -200,6 +200,8 @@ "@no_selected_exams": {}, "occurrence_type": "Type of occurrence", "@occurrence_type": {}, + "no_link": "We couldn't open the link", + "@no_link": {}, "other_links": "Other links", "@other_links": {}, "pass_change_request": "For security reasons, passwords must be changed periodically.", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 4315ac0af..b6cb3b650 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -200,6 +200,8 @@ "@no_selected_exams": {}, "occurrence_type": "Tipo de ocorrência", "@occurrence_type": {}, + "no_link": "Não conseguimos abrir o link", + "@no_link": {}, "other_links": "Outros links", "@other_links": {}, "pass_change_request": "Por razões de segurança, as palavras-passe têm de ser alteradas periodicamente.", diff --git a/uni/lib/view/about/widgets/terms_and_conditions.dart b/uni/lib/view/about/widgets/terms_and_conditions.dart index 1a53d07da..049872e2b 100644 --- a/uni/lib/view/about/widgets/terms_and_conditions.dart +++ b/uni/lib/view/about/widgets/terms_and_conditions.dart @@ -2,15 +2,15 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:uni/controller/load_static/terms_and_conditions.dart'; +import 'package:uni/controller/networking/url_launcher.dart'; import 'package:uni/generated/l10n.dart'; -import 'package:url_launcher/url_launcher.dart'; class TermsAndConditions extends StatelessWidget { const TermsAndConditions({super.key}); @override Widget build(BuildContext context) { - var termsAndConditionsSaved = S.of(context).loading_terms; + String? termsAndConditionsSaved = S.of(context).loading_terms; final termsAndConditionsFuture = fetchTermsAndConditions(); return FutureBuilder( future: termsAndConditionsFuture, @@ -18,16 +18,14 @@ class TermsAndConditions extends StatelessWidget { (BuildContext context, AsyncSnapshot termsAndConditions) { if (termsAndConditions.connectionState == ConnectionState.done && termsAndConditions.hasData) { - termsAndConditionsSaved = termsAndConditions.data!; + termsAndConditionsSaved = termsAndConditions.data; } return MarkdownBody( styleSheet: MarkdownStyleSheet(), shrinkWrap: false, - data: termsAndConditionsSaved, + data: termsAndConditionsSaved!, onTapLink: (text, url, title) async { - if (await canLaunchUrl(Uri.parse(url!))) { - await launchUrl(Uri.parse(url)); - } + await launchUrlWithToast(context, url!); }, ); }, diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index d63957f02..86078a71f 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:uni/controller/networking/network_router.dart'; +import 'package:uni/controller/networking/url_launcher.dart'; import 'package:uni/view/common_widgets/row_container.dart'; -import 'package:url_launcher/url_launcher.dart'; class ScheduleSlot extends StatelessWidget { const ScheduleSlot({ @@ -113,9 +113,9 @@ class SubjectButtonWidget extends StatelessWidget { 'UCURR_GERAL.FICHA_UC_VIEW?pv_ocorrencia_id=$occurrId'; } - Future _launchURL() async { + Future _launchURL(BuildContext context) async { final url = toUcLink(occurrId); - await launchUrl(Uri.parse(url)); + await launchUrlWithToast(context, url); } @override @@ -133,7 +133,7 @@ class SubjectButtonWidget extends StatelessWidget { color: Colors.grey, alignment: Alignment.centerRight, tooltip: 'Abrir página da UC no browser', - onPressed: _launchURL, + onPressed: () => _launchURL(context), ), ], ); diff --git a/uni/lib/view/terms_and_condition_dialog.dart b/uni/lib/view/terms_and_condition_dialog.dart index 245cd0747..5f4027ea0 100644 --- a/uni/lib/view/terms_and_condition_dialog.dart +++ b/uni/lib/view/terms_and_condition_dialog.dart @@ -80,7 +80,7 @@ class TermsAndConditionDialog { ), ), ], - ) + ), ], ), ); diff --git a/uni/lib/view/useful_info/widgets/link_button.dart b/uni/lib/view/useful_info/widgets/link_button.dart index 3dc240d3c..032a90c34 100644 --- a/uni/lib/view/useful_info/widgets/link_button.dart +++ b/uni/lib/view/useful_info/widgets/link_button.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:uni/controller/networking/url_launcher.dart'; class LinkButton extends StatelessWidget { const LinkButton({ @@ -27,7 +27,7 @@ class LinkButton extends StatelessWidget { .headlineSmall! .copyWith(decoration: TextDecoration.underline), ), - onTap: () => launchUrl(Uri.parse(link)), + onTap: () => launchUrlWithToast(context, link), ), ) ], diff --git a/uni/lib/view/useful_info/widgets/text_components.dart b/uni/lib/view/useful_info/widgets/text_components.dart index 345685277..7ef70043e 100644 --- a/uni/lib/view/useful_info/widgets/text_components.dart +++ b/uni/lib/view/useful_info/widgets/text_components.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:uni/controller/networking/url_launcher.dart'; Container h1(String text, BuildContext context, {bool initial = false}) { final marginTop = initial ? 15.0 : 30.0; @@ -44,7 +44,7 @@ Container infoText( .bodyLarge! .apply(color: Theme.of(context).colorScheme.tertiary), ), - onTap: () => link != '' ? launchUrl(Uri.parse(link)) : null, + onTap: () => launchUrlWithToast(context, link), ), ), ); From 1758e7c056a20f0f4e3498b1bfa1d750405ca280 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Wed, 11 Oct 2023 11:08:35 +0000 Subject: [PATCH 015/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 9d44229de..30f41288c 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.16+206 \ No newline at end of file +1.7.17+207 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index d4619d650..8a66599d1 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: 'none' # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.16+206 +version: 1.7.17+207 environment: sdk: '>=3.0.0 <4.0.0' From 0863b86bdc1caa6a26e41a02a23353c618617289 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 11 Oct 2023 14:47:12 +0100 Subject: [PATCH 016/334] format fix --- uni/lib/generated/l10n.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 7165da849..107ad3a6f 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1037,7 +1037,7 @@ class S { args: [], ); } - + /// `We couldn't open the link` String get no_link { return Intl.message( From 4613cebc34cc571c941b284b662022ac307bce4a Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 13 Oct 2023 10:09:00 +0100 Subject: [PATCH 017/334] File name changes --- uni/lib/controller/parsers/parser_course_unit_info.dart | 2 +- .../view/course_unit_info/widgets/course_unit_files_row.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 7f6892ab0..818a457b5 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -33,7 +33,7 @@ Future>> parseFiles( item = item as Map; final files = []; for (final file in item['ficheiros'] as List) { - final fileName = file['nome']; + final fileName = file['nome'] + '_' + file['data_actualizacao']; final fileCode = file['codigo']; final url = await CourseUnitsInfoFetcher().getDownloadLink(session); final courseUnitFile = CourseUnitFile( diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index 095c6aff9..913af7ade 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -25,7 +25,7 @@ class CourseUnitFilesRow extends StatelessWidget { child: Container( padding: const EdgeInsets.only(left: 10), child: Text( - file.name, + file.name.substring(0, file.name.indexOf('_')), overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodyLarge, ), From c4b8d9c26eb4a39f854451986f167f19c8023231 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 20 Oct 2023 14:55:33 +0100 Subject: [PATCH 018/334] Cache clean up flow --- uni/lib/controller/cleanup.dart | 37 ++++++++++++++++ .../local_storage/app_shared_preferences.dart | 13 ++++++ .../local_storage/file_offline_storage.dart | 2 +- uni/lib/main.dart | 3 ++ uni/pubspec.lock | 42 +++++++++++-------- 5 files changed, 79 insertions(+), 18 deletions(-) diff --git a/uni/lib/controller/cleanup.dart b/uni/lib/controller/cleanup.dart index 11bea65d2..079845e30 100644 --- a/uni/lib/controller/cleanup.dart +++ b/uni/lib/controller/cleanup.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:uni/controller/local_storage/app_bus_stop_database.dart'; @@ -41,3 +42,39 @@ Future cleanupStoredData(BuildContext context) async { directory.deleteSync(recursive: true); } } + +Future cleanupCachedFiles() async { + final lastCleanupDate = await AppSharedPreferences.getLastCleanUpDate(); + final daysSinceLastCleanup = + DateTime.now().difference(lastCleanupDate).inDays; + + if (daysSinceLastCleanup < 14) { + return; + } + + final cacheManager = DefaultCacheManager(); + final cacheDirectory = await getApplicationDocumentsDirectory(); + final treshold = DateTime.now().subtract(const Duration(days: 30)); + + final directories = cacheDirectory.listSync(followLinks: false); + for (final directory in directories) { + if (directory is Directory) { + final files = directory.listSync(recursive: true, followLinks: false); + + final oldFiles = files.where((file) { + try { + final fileDate = File(file.path).lastModifiedSync(); + return fileDate.isBefore(treshold); + } catch (e) { + return false; + } + }); + + for (final file in oldFiles) { + await cacheManager.removeFile(file.path); + } + } + } + + await AppSharedPreferences.setLastCleanUpDate(DateTime.now()); +} diff --git a/uni/lib/controller/local_storage/app_shared_preferences.dart b/uni/lib/controller/local_storage/app_shared_preferences.dart index faca04476..748067d84 100644 --- a/uni/lib/controller/local_storage/app_shared_preferences.dart +++ b/uni/lib/controller/local_storage/app_shared_preferences.dart @@ -28,6 +28,7 @@ class AppSharedPreferences { 'tuition_notification_toogle'; static const String themeMode = 'theme_mode'; static const String locale = 'app_locale'; + static const String lastCacheCleanUpDate = 'last_clean'; static const String favoriteCards = 'favorite_cards'; static final List defaultFavoriteCards = [ FavoriteWidgetType.schedule, @@ -136,6 +137,18 @@ class AppSharedPreferences { ); } + static Future setLastCleanUpDate(DateTime date) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString(lastCacheCleanUpDate, date.toString()); + } + + static Future getLastCleanUpDate() async { + final prefs = await SharedPreferences.getInstance(); + final date = + prefs.getString(lastCacheCleanUpDate) ?? DateTime.now().toString(); + return DateTime.parse(date); + } + /// Deletes the user's student number and password. static Future removePersistentUserInfo() async { final prefs = await SharedPreferences.getInstance(); diff --git a/uni/lib/controller/local_storage/file_offline_storage.dart b/uni/lib/controller/local_storage/file_offline_storage.dart index afad71b3a..768661709 100644 --- a/uni/lib/controller/local_storage/file_offline_storage.dart +++ b/uni/lib/controller/local_storage/file_offline_storage.dart @@ -11,7 +11,7 @@ import 'package:uni/model/entities/session.dart'; /// The offline image storage location on the device. Future get _localPath async { - final directory = await getTemporaryDirectory(); + final directory = await getApplicationDocumentsDirectory(); return directory.path; } diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 01fac7edd..0d9d36e45 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -9,6 +9,7 @@ import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:uni/controller/background_workers/background_callback.dart'; +import 'package:uni/controller/cleanup.dart'; import 'package:uni/controller/load_static/terms_and_conditions.dart'; import 'package:uni/controller/local_storage/app_shared_preferences.dart'; import 'package:uni/generated/l10n.dart'; @@ -79,6 +80,8 @@ Future main() async { WidgetsFlutterBinding.ensureInitialized(); + unawaited(cleanupCachedFiles()); + // Initialize WorkManager for background tasks await Workmanager().initialize( workerStartCallback, diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 8da48a9fa..394e8fbb7 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -205,10 +205,10 @@ packages: dependency: "direct main" description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" connectivity_plus: dependency: "direct main" description: @@ -521,10 +521,10 @@ packages: dependency: "direct main" description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" io: dependency: transitive description: @@ -593,18 +593,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" material_design_icons_flutter: dependency: "direct main" description: @@ -1015,10 +1015,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sqflite: dependency: "direct main" description: @@ -1087,26 +1087,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "3dac9aecf2c3991d09b9cdde4f98ded7b30804a88a0d7e4e7e1678e78d6b97f4" + sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" url: "https://pub.dev" source: hosted - version: "1.24.1" + version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" test_core: dependency: transitive description: name: test_core - sha256: "5138dbffb77b2289ecb12b81c11ba46036590b72a64a7a90d6ffb880f1a29e93" + sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.5.3" timelines: dependency: "direct main" description: @@ -1283,6 +1283,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -1348,5 +1356,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.10.6" From 5e975fb3847467092cc69c8ccba04c9c7ba361a9 Mon Sep 17 00:00:00 2001 From: Nishchay Shakya <79781577+NishchayShakya1@users.noreply.github.com> Date: Mon, 23 Oct 2023 21:04:55 +0530 Subject: [PATCH 019/334] Calendar Link Removed --- uni/lib/view/useful_info/widgets/sigarra_links_card.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/uni/lib/view/useful_info/widgets/sigarra_links_card.dart b/uni/lib/view/useful_info/widgets/sigarra_links_card.dart index bd1ec3813..91ed49da0 100644 --- a/uni/lib/view/useful_info/widgets/sigarra_links_card.dart +++ b/uni/lib/view/useful_info/widgets/sigarra_links_card.dart @@ -34,11 +34,6 @@ class SigarraLinksCard extends GenericExpansionCard { link: 'https://sigarra.up.pt/feup/pt/inqueritos_geral.inqueritos_list', ), - LinkButton( - title: S.of(context).school_calendar, - link: - 'https://sigarra.up.pt/feup/pt/web_base.gera_pagina?p_pagina=p%c3%a1gina%20est%c3%a1tica%20gen%c3%a9rica%20106', - ), ], ); } From 6f4a810ab36a30db43a6c28491ed4d4dacea56b8 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Fri, 10 Nov 2023 09:03:37 +0000 Subject: [PATCH 020/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 099adf0f5..76f04fb93 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.18+208 \ No newline at end of file +1.7.19+209 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 4a5dd2f55..6db358086 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.18+208 +version: 1.7.19+209 environment: sdk: ">=3.0.0 <4.0.0" From 87c14368f14bd4e5aa0d945b8ace5e2ff0124e02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 10 Nov 2023 09:06:07 +0000 Subject: [PATCH 021/334] Bump shared_preferences from 2.2.1 to 2.2.2 in /uni Bumps [shared_preferences](https://github.com/flutter/packages/tree/main/packages/shared_preferences) from 2.2.1 to 2.2.2. - [Release notes](https://github.com/flutter/packages/releases) - [Commits](https://github.com/flutter/packages/commits/shared_preferences-v2.2.2/packages/shared_preferences) --- updated-dependencies: - dependency-name: shared_preferences dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 4 ++-- uni/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 1ac709fa0..e69dba9ff 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -882,10 +882,10 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_android: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 6db358086..34d66f7bb 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -47,7 +47,7 @@ dependencies: percent_indicator: ^4.2.2 provider: ^6.0.4 sentry_flutter: ^7.9.0 - shared_preferences: ^2.0.3 + shared_preferences: ^2.2.2 shimmer: ^3.0.0 sqflite: ^2.0.3 synchronized: ^3.0.0 From c8608ad6a144a372a52ef8caa69e6e032c117668 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 1 Dec 2023 13:10:53 +0000 Subject: [PATCH 022/334] Implementing text animation and improving cleaning flow --- uni/lib/controller/cleanup.dart | 8 +-- .../parsers/parser_course_unit_info.dart | 22 +++++--- .../course_unit_info/course_unit_info.dart | 2 +- .../widgets/course_unit_files_row.dart | 56 ++++++++++++++++--- uni/pubspec.lock | 11 ++-- uni/pubspec.yaml | 8 ++- 6 files changed, 79 insertions(+), 28 deletions(-) diff --git a/uni/lib/controller/cleanup.dart b/uni/lib/controller/cleanup.dart index 079845e30..955b88ffa 100644 --- a/uni/lib/controller/cleanup.dart +++ b/uni/lib/controller/cleanup.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:uni/controller/local_storage/app_bus_stop_database.dart'; @@ -52,11 +51,10 @@ Future cleanupCachedFiles() async { return; } - final cacheManager = DefaultCacheManager(); - final cacheDirectory = await getApplicationDocumentsDirectory(); + final toCleanDirectory = await getApplicationDocumentsDirectory(); final treshold = DateTime.now().subtract(const Duration(days: 30)); + final directories = toCleanDirectory.listSync(followLinks: false); - final directories = cacheDirectory.listSync(followLinks: false); for (final directory in directories) { if (directory is Directory) { final files = directory.listSync(recursive: true, followLinks: false); @@ -71,7 +69,7 @@ Future cleanupCachedFiles() async { }); for (final file in oldFiles) { - await cacheManager.removeFile(file.path); + await File(file.path).delete(); } } } diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 818a457b5..98fbda163 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -33,15 +33,19 @@ Future>> parseFiles( item = item as Map; final files = []; for (final file in item['ficheiros'] as List) { - final fileName = file['nome'] + '_' + file['data_actualizacao']; - final fileCode = file['codigo']; - final url = await CourseUnitsInfoFetcher().getDownloadLink(session); - final courseUnitFile = CourseUnitFile( - fileName.toString(), - url, - fileCode.toString(), - ); - files.add(courseUnitFile); + if(file is Map){ + final fileName = file['nome'].toString(); + final fileDate = file['data_actualizacao'].toString(); + final fileCode = file['codigo'].toString(); + final url = await CourseUnitsInfoFetcher().getDownloadLink(session); + final courseUnitFile = CourseUnitFile( + '${fileName}_$fileDate', + url, + fileCode, + ); + files.add(courseUnitFile); + } + } folders[item['nome'] as String] = files; } diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index a77270ff8..0602c6b88 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -83,7 +83,7 @@ class CourseUnitDetailPageViewState Tab(text: S.of(context).course_class), const Tab( text: 'Ficheiros', - ) + ), ], ), Expanded( diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index 913af7ade..5426096fb 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:open_file_plus/open_file_plus.dart'; import 'package:provider/provider.dart'; @@ -5,11 +7,36 @@ import 'package:uni/controller/local_storage/file_offline_storage.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; -class CourseUnitFilesRow extends StatelessWidget { +class CourseUnitFilesRow extends StatefulWidget { const CourseUnitFilesRow(this.file, {super.key}); final CourseUnitFile file; + @override + State createState() { + return CourseUnitFilesRowState(); + } +} + +class CourseUnitFilesRowState extends State + with SingleTickerProviderStateMixin { + late final AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(milliseconds: 1500), + vsync: this, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Container( @@ -20,14 +47,28 @@ class CourseUnitFilesRow extends StatelessWidget { const Icon(Icons.picture_as_pdf), const SizedBox(width: 1), Expanded( - child: InkWell( - onTap: () => openFile(context, file), + child: GestureDetector( + onTap: () { + _controller + ..reset() + ..repeat(reverse: true); + openFile(context, widget.file); + }, child: Container( padding: const EdgeInsets.only(left: 10), - child: Text( - file.name.substring(0, file.name.indexOf('_')), - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.bodyLarge, + child: AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return Opacity( + opacity: 1 - 0.5 * sin(_controller.value * pi), + child: Text( + widget.file.name + .substring(0, widget.file.name.indexOf('_')), + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyLarge, + ), + ); + }, ), ), ), @@ -48,5 +89,6 @@ class CourseUnitFilesRow extends StatelessWidget { ); await OpenFile.open(result!.path); + _controller.reset(); } } diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 3f8fe9ebf..17866fc25 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -351,7 +351,7 @@ packages: source: sdk version: "0.0.0" flutter_cache_manager: - dependency: transitive + dependency: "direct main" description: name: flutter_cache_manager sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba" @@ -681,10 +681,11 @@ packages: open_file_plus: dependency: "direct main" description: - name: open_file_plus - sha256: f087e32722ffe4bac71925e7a1a9848a1008fd789e47c6628da3ed7845922227 - url: "https://pub.dev" - source: hosted + path: "." + ref: "3c32191" + resolved-ref: "3c321911c54388d1316e34d4f999776281398fc2" + url: "https://github.com/joutvhu/open_file_plus.git" + source: git version: "3.4.1" package_config: dependency: transitive diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index d1177cd51..3bf89c0b7 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: expansion_tile_card: ^3.0.0 flutter: sdk: flutter + flutter_cache_manager: ^3.3.1 flutter_dotenv: ^5.0.2 flutter_local_notifications: ^15.1.0+1 flutter_localizations: @@ -42,7 +43,10 @@ dependencies: latlong2: ^0.9.0 logger: ^2.0.2+1 material_design_icons_flutter: ^7.0.7296 - open_file_plus: ^3.4.1 + open_file_plus: + git: + url: https://github.com/joutvhu/open_file_plus.git + ref: "3c32191" path: ^1.8.0 path_provider: ^2.0.0 percent_indicator: ^4.2.2 @@ -68,8 +72,10 @@ dev_dependencies: git: url: https://github.com/dart-lang/mockito.git ref: "e54a006" + test: any very_good_analysis: ^5.1.0 + flutter: generate: true From 86c0cfad014413b0d0aa59c13787f1bafa947d1e Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 1 Dec 2023 13:10:54 +0000 Subject: [PATCH 023/334] Modified getExamRows method to enable the display of multiple exams if they occur on the same day. --- uni/lib/view/home/widgets/exam_card.dart | 63 ++++++++++++++++-------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index 0b3199cc4..f5adcfc46 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -75,18 +75,36 @@ class ExamCard extends GenericCard { ); } + /// Returns true if two exams are in the same day + bool isSameDay(DateTime datetime1, DateTime datetime2) { + return datetime1.year == datetime2.year + && datetime1.month == datetime2.month + && datetime1.day == datetime2.day; + } + /// Returns a list of widgets with the primary and secondary exams to /// be displayed in the exam card. List getExamRows(BuildContext context, List exams) { final rows = []; - for (var i = 0; i < 1 && i < exams.length; i++) { - rows.add(createRowFromExam(context, exams[i])); - } - if (exams.length > 1) { + + rows.add(createRowFromExam(context, exams[0], isFirst: true)); + + var sameDayExamCount = exams.sublist(1).takeWhile( + (exam) => isSameDay(exam.begin, exams[0].begin), + ).toList().fold(0, (count, exam) { + rows.addAll([ + Container( + margin: const EdgeInsets.only(top: 8), + ), + createRowFromExam(context, exam), + ]); + return count + 1; + }); + + if (exams.length > 1 && sameDayExamCount > 0) { rows.add( Container( - margin: - const EdgeInsets.only(right: 80, left: 80, top: 15, bottom: 7), + margin: const EdgeInsets.only(right: 80, left: 80, top: 15, bottom: 7), decoration: BoxDecoration( border: Border( bottom: BorderSide( @@ -98,28 +116,33 @@ class ExamCard extends GenericCard { ), ); } - for (var i = 1; i < 4 && i < exams.length; i++) { - rows.add(createSecondaryRowFromExam(context, exams[i])); - } + + // Processing secondary exams without a loop + exams.sublist(1 + sameDayExamCount) + .take(4 - sameDayExamCount - 1) + .forEach((exam) => rows.add(createSecondaryRowFromExam(context, exam))); + return rows; } /// Creates a row with the closest exam (which appears separated from the /// others in the card). - Widget createRowFromExam(BuildContext context, Exam exam) { + Widget createRowFromExam(BuildContext context, Exam exam, {bool isFirst = false}) { final locale = Provider.of(context).getLocale(); return Column( children: [ - if (locale == AppLocale.pt) ...[ - DateRectangle( - date: '''${exam.weekDay(locale)}, ''' - '''${exam.begin.day} de ${exam.month(locale)}''', - ), - ] else ...[ - DateRectangle( - date: '''${exam.weekDay(locale)}, ''' - '''${exam.begin.day} ${exam.month(locale)}''', - ), + if(isFirst) ...[ + if (locale == AppLocale.pt) ...[ + DateRectangle( + date: '''${exam.weekDay(locale)}, ''' + '''${exam.begin.day} de ${exam.month(locale)}''', + ), + ] else ...[ + DateRectangle( + date: '''${exam.weekDay(locale)}, ''' + '''${exam.begin.day} ${exam.month(locale)}''', + ), + ], ], RowContainer( child: ExamRow( From 32325d031ef49695caf056f75fc67faccf2f8d82 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 1 Dec 2023 13:19:08 +0000 Subject: [PATCH 024/334] Fixing format --- .../parsers/parser_course_unit_info.dart | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 98fbda163..d93ff9e56 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -33,19 +33,18 @@ Future>> parseFiles( item = item as Map; final files = []; for (final file in item['ficheiros'] as List) { - if(file is Map){ - final fileName = file['nome'].toString(); - final fileDate = file['data_actualizacao'].toString(); - final fileCode = file['codigo'].toString(); - final url = await CourseUnitsInfoFetcher().getDownloadLink(session); - final courseUnitFile = CourseUnitFile( - '${fileName}_$fileDate', - url, - fileCode, - ); - files.add(courseUnitFile); + if (file is Map) { + final fileName = file['nome'].toString(); + final fileDate = file['data_actualizacao'].toString(); + final fileCode = file['codigo'].toString(); + final url = await CourseUnitsInfoFetcher().getDownloadLink(session); + final courseUnitFile = CourseUnitFile( + '${fileName}_$fileDate', + url, + fileCode, + ); + files.add(courseUnitFile); } - } folders[item['nome'] as String] = files; } From a84f3157ab389732e27ce8021ea5b1f6ce5f24a5 Mon Sep 17 00:00:00 2001 From: Bruno Mendes <61701401+bdmendes@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:45:56 +0000 Subject: [PATCH 025/334] Delete boilerplate test file --- uni/test/widget_test.dart | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 uni/test/widget_test.dart diff --git a/uni/test/widget_test.dart b/uni/test/widget_test.dart deleted file mode 100644 index 785fdc88e..000000000 --- a/uni/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:uni/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} From 665f97832582e341f1ba9996d1d9f22e9298f881 Mon Sep 17 00:00:00 2001 From: Bruno Mendes <61701401+bdmendes@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:49:30 +0000 Subject: [PATCH 026/334] Remove logger import --- uni/lib/controller/load_static/terms_and_conditions.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/uni/lib/controller/load_static/terms_and_conditions.dart b/uni/lib/controller/load_static/terms_and_conditions.dart index 377c61231..590d3bf51 100644 --- a/uni/lib/controller/load_static/terms_and_conditions.dart +++ b/uni/lib/controller/load_static/terms_and_conditions.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:flutter/services.dart' show rootBundle; -import 'package:logger/logger.dart'; import 'package:uni/controller/local_storage/app_shared_preferences.dart'; /// Returns the content of the Terms and Conditions remote file, From 30173bb6c321819a9f1fd2de2989665dc7dd325d Mon Sep 17 00:00:00 2001 From: bdmendes Date: Tue, 5 Dec 2023 12:52:15 +0000 Subject: [PATCH 027/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 76f04fb93..7f068fcb9 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.19+209 \ No newline at end of file +1.7.20+210 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 34d66f7bb..6eedd9a0a 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.19+209 +version: 1.7.20+210 environment: sdk: ">=3.0.0 <4.0.0" From ae0171363610041c9ff5d5a5f63fbfd8db312ada Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 12:59:28 +0000 Subject: [PATCH 028/334] Bump flutter_widget_from_html_core from 0.10.5 to 0.14.9 in /uni Bumps [flutter_widget_from_html_core](https://github.com/daohoangson/flutter_widget_from_html/tree/master/packages) from 0.10.5 to 0.14.9. - [Release notes](https://github.com/daohoangson/flutter_widget_from_html/releases) - [Commits](https://github.com/daohoangson/flutter_widget_from_html/commits/HEAD/packages) --- updated-dependencies: - dependency-name: flutter_widget_from_html_core dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 4 ++-- uni/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index e69dba9ff..82413a767 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -449,10 +449,10 @@ packages: dependency: "direct main" description: name: flutter_widget_from_html_core - sha256: a7dc9eb4bfdef4ea06d114528bf52a7efcdfc6ba46d933957c25067f17c1c6b4 + sha256: "86d40a9f26d10011664df057c950e9c348ee1a7dbf141f295a07b0075ffd780b" url: "https://pub.dev" source: hosted - version: "0.10.5" + version: "0.14.9" frontend_server_client: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 6eedd9a0a..f8496c4fc 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -34,7 +34,7 @@ dependencies: flutter_map_marker_popup: ^5.0.0 flutter_markdown: ^0.6.0 flutter_svg: ^2.0.0+1 - flutter_widget_from_html_core: ^0.10.3 + flutter_widget_from_html_core: ^0.14.9 html: ^0.15.0 http: ^1.1.0 image: ^4.0.13 From c8c97b4df3cdc6fe41ca594a9a38b906b5747a30 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 8 Dec 2023 14:45:50 +0000 Subject: [PATCH 029/334] Catching a timeout error while opening files --- .../widgets/course_unit_files_row.dart | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index 5426096fb..8e875e109 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -5,6 +5,8 @@ import 'package:open_file_plus/open_file_plus.dart'; import 'package:provider/provider.dart'; import 'package:uni/controller/local_storage/file_offline_storage.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; +import 'package:uni/view/common_widgets/toast_message.dart'; +import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; class CourseUnitFilesRow extends StatefulWidget { @@ -81,14 +83,23 @@ class CourseUnitFilesRowState extends State Future openFile(BuildContext context, CourseUnitFile unitFile) async { final session = context.read().session; - final result = await loadFileFromStorageOrRetrieveNew( - '${unitFile.name}.pdf', - unitFile.url, - session, - headers: {'pct_id': unitFile.fileCode}, - ); + try { + final result = await loadFileFromStorageOrRetrieveNew( + '${unitFile.name}.pdf', + unitFile.url, + session, + headers: {'pct_id': unitFile.fileCode}, + ); - await OpenFile.open(result!.path); + await OpenFile.open(result!.path); + } catch (e) { + if (context.mounted) { + await ToastMessage.error( + context, + S.of(context).load_error, + ); + } + } _controller.reset(); } } From 5a2df5839eaa6bc7dece0627de102824008ccbfe Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 8 Dec 2023 14:51:46 +0000 Subject: [PATCH 030/334] Lint fix --- .../view/course_unit_info/widgets/course_unit_files_row.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index 8e875e109..1f6c2581e 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -4,10 +4,10 @@ import 'package:flutter/material.dart'; import 'package:open_file_plus/open_file_plus.dart'; import 'package:provider/provider.dart'; import 'package:uni/controller/local_storage/file_offline_storage.dart'; -import 'package:uni/model/entities/course_units/course_unit_file.dart'; -import 'package:uni/view/common_widgets/toast_message.dart'; import 'package:uni/generated/l10n.dart'; +import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; +import 'package:uni/view/common_widgets/toast_message.dart'; class CourseUnitFilesRow extends StatefulWidget { const CourseUnitFilesRow(this.file, {super.key}); From b16515d431a772853a922ba97ebab1a07a57f164 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Dec 2023 22:59:04 +0000 Subject: [PATCH 031/334] Bump connectivity_plus from 5.0.0 to 5.0.2 in /uni Bumps [connectivity_plus](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/connectivity_plus) from 5.0.0 to 5.0.2. - [Release notes](https://github.com/fluttercommunity/plus_plugins/releases) - [Commits](https://github.com/fluttercommunity/plus_plugins/commits/connectivity_plus-v5.0.2/packages/connectivity_plus) --- updated-dependencies: - dependency-name: connectivity_plus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 4 ++-- uni/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 82413a767..f7a45f92e 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -213,10 +213,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: "94d51c6f1299133a2baa4c5c3d2c11ec7d7fb4768dee5c52a56f7d7522fcf70e" + sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.0.2" connectivity_plus_platform_interface: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index f8496c4fc..53ae8ec28 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -17,7 +17,7 @@ dependencies: add_2_calendar: ^2.2.5 cached_network_image: ^3.2.3 collection: ^1.16.0 - connectivity_plus: ^5.0.0 + connectivity_plus: ^5.0.2 crypto: ^3.0.1 cupertino_icons: ^1.0.2 currency_text_input_formatter: ^2.1.5 From bb56ef17a74bc62d1396f8ac05a9c536195dfc8f Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Sat, 18 Nov 2023 21:22:50 +0000 Subject: [PATCH 032/334] Implement Restaurant Home Card Dialog --- uni/lib/generated/intl/messages_en.dart | 2 + uni/lib/generated/intl/messages_pt_PT.dart | 2 + uni/lib/generated/l10n.dart | 10 ++++ uni/lib/l10n/intl_en.arb | 2 + uni/lib/l10n/intl_pt_PT.arb | 2 + .../widgets/restaurant_page_card.dart | 47 +++++++++++++++++-- 6 files changed, 62 insertions(+), 3 deletions(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 112516f05..2256f8e76 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -217,6 +217,8 @@ class MessageLookup extends MessageLookupByLibrary { "Reference created successfully!"), "remove": MessageLookupByLibrary.simpleMessage("Delete"), "report_error": MessageLookupByLibrary.simpleMessage("Report error"), + "restaurant_main_page": MessageLookupByLibrary.simpleMessage( + "Do you want to see your favorite restaurants in the main page?"), "room": MessageLookupByLibrary.simpleMessage("Room"), "school_calendar": MessageLookupByLibrary.simpleMessage("School Calendar"), diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 68a173ffe..4e7a23288 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -218,6 +218,8 @@ class MessageLookup extends MessageLookupByLibrary { "Referência criada com sucesso!"), "remove": MessageLookupByLibrary.simpleMessage("Remover"), "report_error": MessageLookupByLibrary.simpleMessage("Reportar erro"), + "restaurant_main_page": MessageLookupByLibrary.simpleMessage( + "Queres ver os teus restaurantes favoritos na página principal?"), "room": MessageLookupByLibrary.simpleMessage("Sala"), "school_calendar": MessageLookupByLibrary.simpleMessage("Calendário Escolar"), diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 936259b3e..c73c45a2d 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1158,6 +1158,16 @@ class S { ); } + /// `Do you want to see your favorite restaurants in the main page?` + String get restaurant_main_page { + return Intl.message( + 'Do you want to see your favorite restaurants in the main page?', + name: 'restaurant_main_page', + desc: '', + args: [], + ); + } + /// `Room` String get room { return Intl.message( diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index b1d6be897..c1e22210e 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -226,6 +226,8 @@ "@remove": {}, "report_error": "Report error", "@report_error": {}, + "restaurant_main_page" : "Do you want to see your favorite restaurants in the main page?", + "@restaurant_main_page": {}, "room": "Room", "@room": {}, "school_calendar": "School Calendar", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 4315ac0af..26cef1c70 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -226,6 +226,8 @@ "@remove": {}, "report_error": "Reportar erro", "@report_error": {}, + "restaurant_main_page" : "Queres ver os teus restaurantes favoritos na página principal?", + "@restaurant_main_page": {}, "room": "Sala", "@room": {}, "school_calendar": "Calendário Escolar", diff --git a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart index d02d616aa..cd6ba0381 100644 --- a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart +++ b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart @@ -1,7 +1,11 @@ import 'package:flutter/material.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/restaurant.dart'; +import 'package:uni/model/providers/lazy/home_page_provider.dart'; import 'package:uni/model/providers/lazy/restaurant_provider.dart'; +import 'package:uni/utils/favorite_widget_type.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -43,13 +47,50 @@ class CardFavoriteButton extends StatelessWidget { builder: (context, restaurantProvider) { final isFavorite = restaurantProvider.favoriteRestaurants.contains(restaurant.name); + final favoriteCardTypes = + Provider.of(context).favoriteCards; return IconButton( icon: isFavorite ? Icon(MdiIcons.heart) : Icon(MdiIcons.heartOutline), - onPressed: () => restaurantProvider.toggleFavoriteRestaurant( - restaurant.name, - ), + onPressed: () => { + restaurantProvider.toggleFavoriteRestaurant( + restaurant.name, + ), + if (!isFavorite && + !favoriteCardTypes.contains(FavoriteWidgetType.restaurant) && + restaurantProvider.favoriteRestaurants.length < 2) + showRestaurantCardHomeDialog(context, favoriteCardTypes), + }, ); }, ); } + + void showRestaurantCardHomeDialog( + BuildContext context, + List favoriteCardTypes, + ) { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(S.of(context).restaurant_main_page), + actions: [ + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: Text(S.of(context).no), + ), + ElevatedButton( + onPressed: () { + favoriteCardTypes.add(FavoriteWidgetType.restaurant); + Provider.of(context, listen: false) + .setFavoriteCards(favoriteCardTypes); + Navigator.of(context).pop(); + }, + child: Text(S.of(context).yes), + ), + ], + ), + ); + } } From a1937a7198c9278b97dff2023d1508c2c316f762 Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Thu, 7 Dec 2023 11:15:32 +0000 Subject: [PATCH 033/334] Simplify some statements --- uni/lib/view/restaurant/widgets/restaurant_page_card.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart index cd6ba0381..7b919d8ec 100644 --- a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart +++ b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart @@ -56,8 +56,7 @@ class CardFavoriteButton extends StatelessWidget { restaurant.name, ), if (!isFavorite && - !favoriteCardTypes.contains(FavoriteWidgetType.restaurant) && - restaurantProvider.favoriteRestaurants.length < 2) + !favoriteCardTypes.contains(FavoriteWidgetType.restaurant)) showRestaurantCardHomeDialog(context, favoriteCardTypes), }, ); @@ -82,9 +81,10 @@ class CardFavoriteButton extends StatelessWidget { ), ElevatedButton( onPressed: () { - favoriteCardTypes.add(FavoriteWidgetType.restaurant); Provider.of(context, listen: false) - .setFavoriteCards(favoriteCardTypes); + .setFavoriteCards( + favoriteCardTypes + [FavoriteWidgetType.restaurant], + ); Navigator.of(context).pop(); }, child: Text(S.of(context).yes), From 0f0c4695ed55067651571f1d85f7201fd5713f96 Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Fri, 8 Dec 2023 18:47:15 +0000 Subject: [PATCH 034/334] Fix implementation and reorganize statements --- .../widgets/restaurant_page_card.dart | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart index 7b919d8ec..7e323e1c8 100644 --- a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart +++ b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart @@ -47,17 +47,22 @@ class CardFavoriteButton extends StatelessWidget { builder: (context, restaurantProvider) { final isFavorite = restaurantProvider.favoriteRestaurants.contains(restaurant.name); - final favoriteCardTypes = - Provider.of(context).favoriteCards; return IconButton( icon: isFavorite ? Icon(MdiIcons.heart) : Icon(MdiIcons.heartOutline), - onPressed: () => { + onPressed: () { restaurantProvider.toggleFavoriteRestaurant( restaurant.name, - ), + ); + final favoriteCardTypes = + context.read().favoriteCards; if (!isFavorite && - !favoriteCardTypes.contains(FavoriteWidgetType.restaurant)) - showRestaurantCardHomeDialog(context, favoriteCardTypes), + !favoriteCardTypes.contains(FavoriteWidgetType.restaurant)) { + showRestaurantCardHomeDialog(context, favoriteCardTypes, + (newFavoriteCards) { + Provider.of(context, listen: false) + .setFavoriteCards(newFavoriteCards); + }); + } }, ); }, @@ -67,6 +72,7 @@ class CardFavoriteButton extends StatelessWidget { void showRestaurantCardHomeDialog( BuildContext context, List favoriteCardTypes, + void Function(List) updateHomePage, ) { showDialog( context: context, @@ -81,8 +87,7 @@ class CardFavoriteButton extends StatelessWidget { ), ElevatedButton( onPressed: () { - Provider.of(context, listen: false) - .setFavoriteCards( + updateHomePage( favoriteCardTypes + [FavoriteWidgetType.restaurant], ); Navigator.of(context).pop(); From 273ef296856be8596c29bd66f85051b4675b2feb Mon Sep 17 00:00:00 2001 From: bdmendes Date: Sat, 9 Dec 2023 09:03:24 +0000 Subject: [PATCH 035/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 7f068fcb9..32cc5a199 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.20+210 \ No newline at end of file +1.7.21+211 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 53ae8ec28..998bf377e 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.20+210 +version: 1.7.21+211 environment: sdk: ">=3.0.0 <4.0.0" From 7b4c4e6494338de06e17c26cbfb1f5b304bec138 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Fri, 8 Dec 2023 19:46:48 +0000 Subject: [PATCH 036/334] Bootstrap settings page and change homepage edit button style --- uni/lib/l10n/intl_en.arb | 2 + uni/lib/l10n/intl_pt_PT.arb | 2 + uni/lib/utils/drawer_items.dart | 1 + .../pages_layouts/general/general.dart | 4 +- .../general/widgets/navigation_drawer.dart | 66 ++++--------------- .../view/home/widgets/main_cards_list.dart | 10 +-- uni/lib/view/settings/settings.dart | 58 ++++++++++++++++ .../widgets/locale_switch_button.dart | 32 +++++++++ .../settings/widgets/theme_switch_button.dart | 23 +++++++ 9 files changed, 136 insertions(+), 62 deletions(-) create mode 100644 uni/lib/view/settings/settings.dart create mode 100644 uni/lib/view/settings/widgets/locale_switch_button.dart create mode 100644 uni/lib/view/settings/widgets/theme_switch_button.dart diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index c1e22210e..5252b6ea9 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -144,6 +144,8 @@ "@loading_terms": {}, "login": "Login", "@login": {}, + "settings": "Settings", + "@settings": {}, "logout": "Log out", "@logout": {}, "menus": "Menus", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 26cef1c70..f77e5d802 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -1,5 +1,7 @@ { "@@locale": "pt_PT", + "settings": "Definições", + "@settings": {}, "exit_confirm": "Tem a certeza de que pretende sair?", "@exit_confirm": {}, "no": "Não", diff --git a/uni/lib/utils/drawer_items.dart b/uni/lib/utils/drawer_items.dart index ca70697f0..b983afeab 100644 --- a/uni/lib/utils/drawer_items.dart +++ b/uni/lib/utils/drawer_items.dart @@ -15,6 +15,7 @@ enum DrawerItem { navLogOut('Terminar sessão'); const DrawerItem(this.title, {this.faculties}); + final String title; final Set? faculties; diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 0314f2eb4..313b729bd 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -64,9 +64,7 @@ abstract class GeneralPageViewState extends State { ); } - Widget getBody(BuildContext context) { - return Container(); - } + Widget getBody(BuildContext context); Future buildProfileDecorationImage( BuildContext context, { diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart index 18572148d..911831298 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart @@ -6,8 +6,11 @@ import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/theme_notifier.dart'; +import '../../../../settings/settings.dart'; + class AppNavigationDrawer extends StatefulWidget { const AppNavigationDrawer({required this.parentContext, super.key}); + final BuildContext parentContext; @override @@ -31,7 +34,6 @@ class AppNavigationDrawerState extends State { } } - // Callback Functions String getCurrentRoute() => ModalRoute.of(widget.parentContext)!.settings.name == null ? drawerItems.keys.toList()[0].title @@ -52,8 +54,6 @@ class AppNavigationDrawerState extends State { .pushNamedAndRemoveUntil('/$key', (Route route) => false); } - // End of Callback Functions - BoxDecoration? _getSelectionDecoration(String name) { return (name == getCurrentRoute()) ? BoxDecoration( @@ -89,52 +89,6 @@ class AppNavigationDrawerState extends State { ); } - Widget createLocaleBtn() { - return Consumer( - builder: (context, localeNotifier, _) { - return TextButton( - onPressed: () => localeNotifier.setNextLocale(), - style: TextButton.styleFrom( - elevation: 0, - padding: const EdgeInsets.symmetric(horizontal: 5), - ), - child: Container( - padding: const EdgeInsets.all(15), - child: Text( - localeNotifier.getLocale().localeCode.languageCode.toUpperCase(), - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: Theme.of(context).primaryColor), - ), - ), - ); - }, - ); - } - - Widget createThemeSwitchBtn() { - Icon getThemeIcon(ThemeMode theme) { - switch (theme) { - case ThemeMode.light: - return const Icon(Icons.wb_sunny); - case ThemeMode.dark: - return const Icon(Icons.nightlight_round); - case ThemeMode.system: - return const Icon(Icons.brightness_6); - } - } - - return Consumer( - builder: (context, themeNotifier, _) { - return IconButton( - icon: getThemeIcon(themeNotifier.getTheme()), - onPressed: themeNotifier.setNextTheme, - ); - }, - ); - } - Widget createDrawerNavigationOption(DrawerItem d) { return DecoratedBox( decoration: _getSelectionDecoration(d.title) ?? const BoxDecoration(), @@ -161,7 +115,7 @@ class AppNavigationDrawerState extends State { @override Widget build(BuildContext context) { final drawerOptions = []; - final userSession = Provider.of(context).session; + final userSession = context.read().session; for (final key in drawerItems.keys) { if (key.isVisible(userSession.faculties)) { @@ -187,11 +141,15 @@ class AppNavigationDrawerState extends State { child: createLogoutBtn(), ), ), - Align( - alignment: Alignment.centerRight, - child: createLocaleBtn(), + IconButton( + icon: const Icon(Icons.settings), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const SettingsPage(), + ), + ), ), - createThemeSwitchBtn(), ], ), ], diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index 241f6a2f1..1d63f27b5 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -150,7 +150,7 @@ class MainCardsList extends StatelessWidget { HomePageProvider editingModeProvider, ) { return Container( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 5), + padding: const EdgeInsets.fromLTRB(20, 10, 20, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -159,16 +159,16 @@ class MainCardsList extends StatelessWidget { center: false, pad: false, ), - GestureDetector( - onTap: () => Provider.of(context, listen: false) - .setHomePageEditingMode( + OutlinedButton( + onPressed: () => + Provider.of(context, listen: false) + .setHomePageEditingMode( editingMode: !editingModeProvider.isEditing, ), child: Text( editingModeProvider.isEditing ? S.of(context).edit_on : S.of(context).edit_off, - style: Theme.of(context).textTheme.bodySmall, ), ), ], diff --git a/uni/lib/view/settings/settings.dart b/uni/lib/view/settings/settings.dart new file mode 100644 index 000000000..f767081dc --- /dev/null +++ b/uni/lib/view/settings/settings.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:uni/view/common_widgets/page_title.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; + +import 'package:uni/generated/l10n.dart'; + +class SettingsPage extends StatefulWidget { + const SettingsPage({super.key}); + + @override + State createState() { + return SettingsPageState(); + } +} + +class SettingsPageState extends GeneralPageViewState { + @override + Future onRefresh(BuildContext context) async {} + + @override + Widget getBody(BuildContext context) { + return Column( + children: [ + PageTitle(name: "defs"), + const Padding(padding: EdgeInsets.only(top: 10)), + Expanded( + child: Container( + padding: EdgeInsets.symmetric(horizontal: 20), + child: ListView( + children: [ + ListTile( + title: const Text('Language'), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + Navigator.pushNamed(context, '/settings/language'); + }, + ), + ListTile( + title: const Text('Theme'), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + Navigator.pushNamed(context, '/settings/theme'); + }, + ), + ListTile( + title: const Text('About'), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + Navigator.pushNamed(context, '/settings/about'); + }, + ), + ], + ), + )), + ], + ); + } +} diff --git a/uni/lib/view/settings/widgets/locale_switch_button.dart b/uni/lib/view/settings/widgets/locale_switch_button.dart new file mode 100644 index 000000000..c6c40b5f8 --- /dev/null +++ b/uni/lib/view/settings/widgets/locale_switch_button.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/view/locale_notifier.dart'; + +class LocaleSwitchButton extends StatelessWidget { + const LocaleSwitchButton({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, localeNotifier, _) { + return TextButton( + onPressed: () => localeNotifier.setNextLocale(), + style: TextButton.styleFrom( + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 5), + ), + child: Container( + padding: const EdgeInsets.all(15), + child: Text( + localeNotifier.getLocale().localeCode.languageCode.toUpperCase(), + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: Theme.of(context).primaryColor), + ), + ), + ); + }, + ); + } +} diff --git a/uni/lib/view/settings/widgets/theme_switch_button.dart b/uni/lib/view/settings/widgets/theme_switch_button.dart new file mode 100644 index 000000000..ddabc3b69 --- /dev/null +++ b/uni/lib/view/settings/widgets/theme_switch_button.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/view/theme_notifier.dart'; + +class ThemeSwitchButton extends StatelessWidget { + const ThemeSwitchButton({super.key}); + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, themeNotifier, _) { + return IconButton( + icon: switch (themeNotifier.getTheme()) { + ThemeMode.light => const Icon(Icons.wb_sunny), + ThemeMode.dark => const Icon(Icons.nightlight_round), + ThemeMode.system => const Icon(Icons.brightness_6), + }, + onPressed: themeNotifier.setNextTheme, + ); + }, + ); + } +} From 230b469bc1e85807a15d5cc430a0b526ea0bbf61 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Fri, 8 Dec 2023 22:08:46 +0000 Subject: [PATCH 037/334] Finish settings page and remove drawer options --- .../local_storage/app_shared_preferences.dart | 13 ++ uni/lib/generated/intl/messages_en.dart | 15 ++- uni/lib/generated/intl/messages_pt_PT.dart | 17 ++- uni/lib/generated/l10n.dart | 88 +++++++++++++- uni/lib/l10n/intl_en.arb | 22 +++- uni/lib/l10n/intl_pt_PT.arb | 22 +++- uni/lib/main.dart | 31 +---- uni/lib/utils/drawer_items.dart | 10 +- uni/lib/view/about/about.dart | 9 +- uni/lib/view/bug_report/bug_report.dart | 9 +- uni/lib/view/bug_report/widgets/form.dart | 106 +++++++--------- .../general/widgets/navigation_drawer.dart | 50 -------- .../pages_layouts/secondary/secondary.dart | 5 + .../request_dependent_widget_builder.dart | 8 +- .../view/home/widgets/main_cards_list.dart | 32 +++-- uni/lib/view/navigation_service.dart | 7 +- uni/lib/view/profile/profile.dart | 15 ++- .../profile/widgets/account_info_card.dart | 24 ---- uni/lib/view/settings/settings.dart | 115 ++++++++++++------ .../widgets/locale_switch_button.dart | 17 +-- .../widgets/logout_confirm_dialog.dart | 28 +++++ .../widgets/notifications_dialog.dart | 24 ++++ .../settings/widgets/theme_switch_button.dart | 13 +- .../widgets/tuition_notification_switch.dart | 0 .../settings/widgets/usage_stats_switch.dart | 39 ++++++ .../src/exams_page_test.mocks.dart | 14 +++ .../src/schedule_page_test.mocks.dart | 43 ++++++- .../providers/exams_provider_test.mocks.dart | 15 +++ .../lecture_provider_test.mocks.dart | 16 +++ 29 files changed, 535 insertions(+), 272 deletions(-) create mode 100644 uni/lib/view/settings/widgets/logout_confirm_dialog.dart create mode 100644 uni/lib/view/settings/widgets/notifications_dialog.dart rename uni/lib/view/{profile => settings}/widgets/tuition_notification_switch.dart (100%) create mode 100644 uni/lib/view/settings/widgets/usage_stats_switch.dart diff --git a/uni/lib/controller/local_storage/app_shared_preferences.dart b/uni/lib/controller/local_storage/app_shared_preferences.dart index 8c458cbd2..e2b60e5d7 100644 --- a/uni/lib/controller/local_storage/app_shared_preferences.dart +++ b/uni/lib/controller/local_storage/app_shared_preferences.dart @@ -26,6 +26,7 @@ class AppSharedPreferences { static const String areTermsAndConditionsAcceptedKey = 'is_t&c_accepted'; static const String tuitionNotificationsToggleKey = 'tuition_notification_toogle'; + static const String usageStatsToggleKey = 'usage_stats_toogle'; static const String themeMode = 'theme_mode'; static const String locale = 'app_locale'; static const String favoriteCards = 'favorite_cards'; @@ -293,4 +294,16 @@ class AppSharedPreferences { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(tuitionNotificationsToggleKey, value); } + + static Future getUsageStatsToggle() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getBool(usageStatsToggleKey) ?? true; + } + + static Future setUsageStatsToggle({ + required bool value, + }) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(usageStatsToggleKey, value); + } } diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 2256f8e76..aec972737 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -36,13 +36,12 @@ class MessageLookup extends MessageLookupByLibrary { 'calendario': 'Calendar', 'biblioteca': 'Library', 'uteis': 'Utils', - 'sobre': 'About', - 'bugs': 'Bugs/Suggestions', 'other': 'Other', })}"; final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { + "about": MessageLookupByLibrary.simpleMessage("About us"), "academic_services": MessageLookupByLibrary.simpleMessage("Academic services"), "account_card_title": @@ -80,6 +79,8 @@ class MessageLookup extends MessageLookupByLibrary { "Check your internet connection"), "class_registration": MessageLookupByLibrary.simpleMessage("Class Registration"), + "collect_usage_stats": + MessageLookupByLibrary.simpleMessage("Collect usage statistics"), "college": MessageLookupByLibrary.simpleMessage("College: "), "college_select": MessageLookupByLibrary.simpleMessage("select your college(s)"), @@ -87,6 +88,8 @@ class MessageLookup extends MessageLookupByLibrary { "configured_buses": MessageLookupByLibrary.simpleMessage("Configured Buses"), "confirm": MessageLookupByLibrary.simpleMessage("Confirm"), + "confirm_logout": MessageLookupByLibrary.simpleMessage( + "Do you really want to log out? Your local data will be deleted and you will have to log in again."), "consent": MessageLookupByLibrary.simpleMessage( "I consent to this information being reviewed by NIAEFEUP and may be deleted at my request."), "contact": MessageLookupByLibrary.simpleMessage("Contact (optional)"), @@ -122,7 +125,7 @@ class MessageLookup extends MessageLookupByLibrary { "fee_date": MessageLookupByLibrary.simpleMessage("Deadline for next fee:"), "fee_notification": - MessageLookupByLibrary.simpleMessage("Notify next deadline:"), + MessageLookupByLibrary.simpleMessage("Fee deadline"), "first_year_registration": MessageLookupByLibrary.simpleMessage( "Year of first registration: "), "floor": MessageLookupByLibrary.simpleMessage("Floor"), @@ -139,6 +142,7 @@ class MessageLookup extends MessageLookupByLibrary { "invalid_credentials": MessageLookupByLibrary.simpleMessage("Invalid credentials"), "keep_login": MessageLookupByLibrary.simpleMessage("Stay signed in"), + "language": MessageLookupByLibrary.simpleMessage("Language"), "last_refresh_time": m0, "last_timestamp": m1, "library_occupation": @@ -195,6 +199,7 @@ class MessageLookup extends MessageLookupByLibrary { "There are no course units to display"), "no_selected_exams": MessageLookupByLibrary.simpleMessage( "There are no exams to present"), + "notifications": MessageLookupByLibrary.simpleMessage("Notifications"), "occurrence_type": MessageLookupByLibrary.simpleMessage("Type of occurrence"), "other_links": MessageLookupByLibrary.simpleMessage("Other links"), @@ -217,6 +222,8 @@ class MessageLookup extends MessageLookupByLibrary { "Reference created successfully!"), "remove": MessageLookupByLibrary.simpleMessage("Delete"), "report_error": MessageLookupByLibrary.simpleMessage("Report error"), + "report_error_suggestion": + MessageLookupByLibrary.simpleMessage("Report error/suggestion"), "restaurant_main_page": MessageLookupByLibrary.simpleMessage( "Do you want to see your favorite restaurants in the main page?"), "room": MessageLookupByLibrary.simpleMessage("Room"), @@ -226,6 +233,7 @@ class MessageLookup extends MessageLookupByLibrary { "send": MessageLookupByLibrary.simpleMessage("Send"), "sent_error": MessageLookupByLibrary.simpleMessage( "An error occurred in sending"), + "settings": MessageLookupByLibrary.simpleMessage("Settings"), "some_error": MessageLookupByLibrary.simpleMessage("Some error!"), "stcp_stops": MessageLookupByLibrary.simpleMessage("STCP - Upcoming Trips"), @@ -238,6 +246,7 @@ class MessageLookup extends MessageLookupByLibrary { "Face-to-face and telephone assistance"), "telephone": MessageLookupByLibrary.simpleMessage("Telephone"), "terms": MessageLookupByLibrary.simpleMessage("Terms and Conditions"), + "theme": MessageLookupByLibrary.simpleMessage("Theme"), "title": MessageLookupByLibrary.simpleMessage("Title"), "unavailable": MessageLookupByLibrary.simpleMessage("Unavailable"), "valid_email": diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 4e7a23288..bb6a83237 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -36,13 +36,12 @@ class MessageLookup extends MessageLookupByLibrary { 'calendario': 'Calendário', 'biblioteca': 'Biblioteca', 'uteis': 'Úteis', - 'sobre': 'Sobre', - 'bugs': 'Bugs e Sugestões', 'other': 'Outros', })}"; final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { + "about": MessageLookupByLibrary.simpleMessage("Sobre nós"), "academic_services": MessageLookupByLibrary.simpleMessage("Serviços académicos"), "account_card_title": @@ -80,6 +79,8 @@ class MessageLookup extends MessageLookupByLibrary { "Verifica a tua ligação à internet"), "class_registration": MessageLookupByLibrary.simpleMessage("Inscrição de Turmas"), + "collect_usage_stats": MessageLookupByLibrary.simpleMessage( + "Partilhar estatísticas de uso"), "college": MessageLookupByLibrary.simpleMessage("Faculdade: "), "college_select": MessageLookupByLibrary.simpleMessage( "seleciona a(s) tua(s) faculdade(s)"), @@ -87,6 +88,8 @@ class MessageLookup extends MessageLookupByLibrary { "configured_buses": MessageLookupByLibrary.simpleMessage("Autocarros Configurados"), "confirm": MessageLookupByLibrary.simpleMessage("Confirmar"), + "confirm_logout": MessageLookupByLibrary.simpleMessage( + "Tens a certeza de que queres terminar sessão? Os teus dados locais serão apagados e terás de iniciar sessão novamente."), "consent": MessageLookupByLibrary.simpleMessage( "Consinto que esta informação seja revista pelo NIAEFEUP, podendo ser eliminada a meu pedido."), "contact": MessageLookupByLibrary.simpleMessage("Contacto (opcional)"), @@ -120,8 +123,8 @@ class MessageLookup extends MessageLookupByLibrary { "failed_login": MessageLookupByLibrary.simpleMessage("O login falhou"), "fee_date": MessageLookupByLibrary.simpleMessage( "Data limite próxima prestação:"), - "fee_notification": MessageLookupByLibrary.simpleMessage( - "Notificar próxima data limite:"), + "fee_notification": + MessageLookupByLibrary.simpleMessage("Data limite de propina"), "first_year_registration": MessageLookupByLibrary.simpleMessage("Ano da primeira inscrição: "), "floor": MessageLookupByLibrary.simpleMessage("Piso"), @@ -139,6 +142,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Credenciais inválidas"), "keep_login": MessageLookupByLibrary.simpleMessage("Manter sessão iniciada"), + "language": MessageLookupByLibrary.simpleMessage("Idioma"), "last_refresh_time": m0, "last_timestamp": m1, "library_occupation": @@ -196,6 +200,7 @@ class MessageLookup extends MessageLookupByLibrary { "Não existem cadeiras para apresentar"), "no_selected_exams": MessageLookupByLibrary.simpleMessage( "Não existem exames para apresentar"), + "notifications": MessageLookupByLibrary.simpleMessage("Notificações"), "occurrence_type": MessageLookupByLibrary.simpleMessage("Tipo de ocorrência"), "other_links": MessageLookupByLibrary.simpleMessage("Outros links"), @@ -218,6 +223,8 @@ class MessageLookup extends MessageLookupByLibrary { "Referência criada com sucesso!"), "remove": MessageLookupByLibrary.simpleMessage("Remover"), "report_error": MessageLookupByLibrary.simpleMessage("Reportar erro"), + "report_error_suggestion": + MessageLookupByLibrary.simpleMessage("Reportar erro/sugestão"), "restaurant_main_page": MessageLookupByLibrary.simpleMessage( "Queres ver os teus restaurantes favoritos na página principal?"), "room": MessageLookupByLibrary.simpleMessage("Sala"), @@ -227,6 +234,7 @@ class MessageLookup extends MessageLookupByLibrary { "send": MessageLookupByLibrary.simpleMessage("Enviar"), "sent_error": MessageLookupByLibrary.simpleMessage("Ocorreu um erro no envio"), + "settings": MessageLookupByLibrary.simpleMessage("Definições"), "some_error": MessageLookupByLibrary.simpleMessage("Algum erro!"), "stcp_stops": MessageLookupByLibrary.simpleMessage("STCP - Próximas Viagens"), @@ -239,6 +247,7 @@ class MessageLookup extends MessageLookupByLibrary { "Atendimento presencial e telefónico"), "telephone": MessageLookupByLibrary.simpleMessage("Telefone"), "terms": MessageLookupByLibrary.simpleMessage("Termos e Condições"), + "theme": MessageLookupByLibrary.simpleMessage("Tema"), "title": MessageLookupByLibrary.simpleMessage("Título"), "unavailable": MessageLookupByLibrary.simpleMessage("Indisponível"), "valid_email": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index c73c45a2d..deaa16010 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -80,6 +80,56 @@ class S { ); } + /// `About us` + String get about { + return Intl.message( + 'About us', + name: 'about', + desc: '', + args: [], + ); + } + + /// `Report error/suggestion` + String get report_error_suggestion { + return Intl.message( + 'Report error/suggestion', + name: 'report_error_suggestion', + desc: '', + args: [], + ); + } + + /// `Language` + String get language { + return Intl.message( + 'Language', + name: 'language', + desc: '', + args: [], + ); + } + + /// `Theme` + String get theme { + return Intl.message( + 'Theme', + name: 'theme', + desc: '', + args: [], + ); + } + + /// `Notifications` + String get notifications { + return Intl.message( + 'Notifications', + name: 'notifications', + desc: '', + args: [], + ); + } + /// `Academic services` String get academic_services { return Intl.message( @@ -560,10 +610,10 @@ class S { ); } - /// `Notify next deadline:` + /// `Fee deadline` String get fee_notification { return Intl.message( - 'Notify next deadline:', + 'Fee deadline', name: 'fee_notification', desc: '', args: [], @@ -733,6 +783,16 @@ class S { ); } + /// `Settings` + String get settings { + return Intl.message( + 'Settings', + name: 'settings', + desc: '', + args: [], + ); + } + /// `Log out` String get logout { return Intl.message( @@ -773,7 +833,7 @@ class S { ); } - /// `{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} uteis{Utils} sobre{About} bugs{Bugs/Suggestions} other{Other}}` + /// `{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} uteis{Utils} other{Other}}` String nav_title(Object title) { return Intl.select( title, @@ -788,8 +848,6 @@ class S { 'calendario': 'Calendar', 'biblioteca': 'Library', 'uteis': 'Utils', - 'sobre': 'About', - 'bugs': 'Bugs/Suggestions', 'other': 'Other', }, name: 'nav_title', @@ -1347,6 +1405,26 @@ class S { args: [], ); } + + /// `Do you really want to log out? Your local data will be deleted and you will have to log in again.` + String get confirm_logout { + return Intl.message( + 'Do you really want to log out? Your local data will be deleted and you will have to log in again.', + name: 'confirm_logout', + desc: '', + args: [], + ); + } + + /// `Collect usage statistics` + String get collect_usage_stats { + return Intl.message( + 'Collect usage statistics', + name: 'collect_usage_stats', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 5252b6ea9..501c6d1d8 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -6,6 +6,16 @@ "@no": {}, "yes": "Yes", "@yes": {}, + "about": "About us", + "@about": {}, + "report_error_suggestion": "Report error/suggestion", + "@report_error_suggestion": {}, + "language": "Language", + "@language": {}, + "theme": "Theme", + "@theme": {}, + "notifications": "Notifications", + "@notifications": {}, "academic_services": "Academic services", "@academic_services": {}, "account_card_title": "Checking account", @@ -102,7 +112,7 @@ "@failed_login": {}, "fee_date": "Deadline for next fee:", "@fee_date": {}, - "fee_notification": "Notify next deadline:", + "fee_notification": "Fee deadline", "@fee_notification": {}, "first_year_registration": "Year of first registration: ", "@first_year_registration": {}, @@ -145,7 +155,7 @@ "login": "Login", "@login": {}, "settings": "Settings", - "@settings": {}, + "@settings": {}, "logout": "Log out", "@logout": {}, "menus": "Menus", @@ -154,7 +164,7 @@ "@min_value_reference": {}, "multimedia_center": "Multimedia center", "@multimedia_center": {}, - "nav_title": "{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} uteis{Utils} sobre{About} bugs{Bugs/Suggestions} other{Other}}", + "nav_title": "{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} uteis{Utils} other{Other}}", "@nav_title": {}, "news": "News", "@news": {}, @@ -265,5 +275,9 @@ "widget_prompt": "Choose a widget to add to your personal area:", "@widget_prompt": {}, "year": "Year", - "@year": {} + "@year": {}, + "confirm_logout": "Do you really want to log out? Your local data will be deleted and you will have to log in again.", + "@confirm_logout": {}, + "collect_usage_stats": "Collect usage statistics", + "@usage_stats": {} } \ No newline at end of file diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index f77e5d802..5e42488f3 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -1,13 +1,23 @@ { "@@locale": "pt_PT", "settings": "Definições", - "@settings": {}, + "@settings": {}, "exit_confirm": "Tem a certeza de que pretende sair?", "@exit_confirm": {}, + "about": "Sobre nós", + "@about": {}, "no": "Não", "@no": {}, "yes": "Sim", "@yes": {}, + "report_error_suggestion": "Reportar erro/sugestão", + "@report_error_suggestion": {}, + "language": "Idioma", + "@language": {}, + "theme": "Tema", + "@theme": {}, + "notifications": "Notificações", + "@notifications": {}, "academic_services": "Serviços académicos", "@academic_services": {}, "account_card_title": "Conta Corrente", @@ -104,7 +114,7 @@ "@failed_login": {}, "fee_date": "Data limite próxima prestação:", "@fee_date": {}, - "fee_notification": "Notificar próxima data limite:", + "fee_notification": "Data limite de propina", "@fee_notification": {}, "first_year_registration": "Ano da primeira inscrição: ", "@first_year_registration": {}, @@ -154,7 +164,7 @@ "@min_value_reference": {}, "multimedia_center": "Centro de multimédia", "@multimedia_center": {}, - "nav_title": "{title, select, horario{Horário} exames{Exames} area{Área Pessoal} cadeiras{Cadeiras} autocarros{Autocarros} locais{Locais} restaurantes{Restaurantes} calendario{Calendário} biblioteca{Biblioteca} uteis{Úteis} sobre{Sobre} bugs{Bugs e Sugestões} other{Outros}}", + "nav_title": "{title, select, horario{Horário} exames{Exames} area{Área Pessoal} cadeiras{Cadeiras} autocarros{Autocarros} locais{Locais} restaurantes{Restaurantes} calendario{Calendário} biblioteca{Biblioteca} uteis{Úteis} other{Outros}}", "@nav_title": {}, "news": "Notícias", "@news": {}, @@ -265,5 +275,9 @@ "widget_prompt": "Escolhe um widget para adicionares à tua área pessoal:", "@widget_prompt": {}, "year": "Ano", - "@year": {} + "@year": {}, + "confirm_logout": "Tens a certeza de que queres terminar sessão? Os teus dados locais serão apagados e terás de iniciar sessão novamente.", + "@confirm_logout": {}, + "collect_usage_stats": "Partilhar estatísticas de uso", + "@collect_usage_stats": {} } \ No newline at end of file diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 01fac7edd..d77c467c3 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -26,8 +26,6 @@ import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/model/providers/state_providers.dart'; import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/about/about.dart'; -import 'package:uni/view/bug_report/bug_report.dart'; import 'package:uni/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart'; import 'package:uni/view/calendar/calendar.dart'; import 'package:uni/view/common_widgets/page_transition.dart'; @@ -38,7 +36,6 @@ import 'package:uni/view/library/library.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/locations/locations.dart'; import 'package:uni/view/login/login.dart'; -import 'package:uni/view/navigation_service.dart'; import 'package:uni/view/restaurant/restaurant_page_view.dart'; import 'package:uni/view/schedule/schedule.dart'; import 'package:uni/view/theme.dart'; @@ -50,15 +47,15 @@ SentryEvent? beforeSend(SentryEvent event) { return event.level == SentryLevel.info ? event : null; } -Future firstRoute() async { +Future firstRoute() async { final userPersistentInfo = await AppSharedPreferences.getPersistentUserInfo(); if (userPersistentInfo != null) { - return '/${DrawerItem.navPersonalArea.title}'; + return const HomePageView(); } await acceptTermsAndConditions(); - return '/${DrawerItem.navLogIn.title}'; + return const LoginPageView(); } Future main() async { @@ -164,9 +161,9 @@ Future main() async { /// This class is necessary to track the app's state for /// the current execution. class Application extends StatefulWidget { - const Application(this.initialRoute, {super.key}); + const Application(this.initialWidget, {super.key}); - final String initialRoute; + final Widget initialWidget; static GlobalKey navigatorKey = GlobalKey(); @@ -196,7 +193,7 @@ class ApplicationState extends State { GlobalCupertinoLocalizations.delegate, ], supportedLocales: S.delegate.supportedLocales, - initialRoute: widget.initialRoute, + home: widget.initialWidget, onGenerateRoute: (RouteSettings settings) { final transitions = { '/${DrawerItem.navPersonalArea.title}': @@ -247,22 +244,6 @@ class ApplicationState extends State { page: const UsefulInfoPageView(), settings: settings, ), - '/${DrawerItem.navAbout.title}': PageTransition.makePageTransition( - page: const AboutPageView(), - settings: settings, - ), - '/${DrawerItem.navBugReport.title}': - PageTransition.makePageTransition( - page: const BugReportPageView(), - settings: settings, - maintainState: false, - ), - '/${DrawerItem.navLogIn.title}': PageTransition.makePageTransition( - page: const LoginPageView(), - settings: settings, - ), - '/${DrawerItem.navLogOut.title}': - NavigationService.buildLogoutRoute(), }; return transitions[settings.name]; }, diff --git a/uni/lib/utils/drawer_items.dart b/uni/lib/utils/drawer_items.dart index b983afeab..09d1e6f7d 100644 --- a/uni/lib/utils/drawer_items.dart +++ b/uni/lib/utils/drawer_items.dart @@ -8,11 +8,7 @@ enum DrawerItem { navRestaurants('restaurantes'), navCalendar('calendario'), navLibrary('biblioteca', faculties: {'feup'}), - navUsefulInfo('uteis', faculties: {'feup'}), - navAbout('sobre'), - navBugReport('bugs'), - navLogIn('Iniciar sessão'), - navLogOut('Terminar sessão'); + navUsefulInfo('uteis', faculties: {'feup'}); const DrawerItem(this.title, {this.faculties}); @@ -20,10 +16,6 @@ enum DrawerItem { final Set? faculties; bool isVisible(List userFaculties) { - if (this == DrawerItem.navLogIn || this == DrawerItem.navLogOut) { - return false; - } - if (faculties == null) { return true; } diff --git a/uni/lib/view/about/about.dart b/uni/lib/view/about/about.dart index af9f90eb7..513ff4a98 100644 --- a/uni/lib/view/about/about.dart +++ b/uni/lib/view/about/about.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:uni/view/about/widgets/terms_and_conditions.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; class AboutPageView extends StatefulWidget { const AboutPageView({super.key}); @@ -11,7 +11,7 @@ class AboutPageView extends StatefulWidget { } /// Manages the 'about' section of the app. -class AboutPageViewState extends GeneralPageViewState { +class AboutPageViewState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { final queryData = MediaQuery.of(context); @@ -37,4 +37,9 @@ class AboutPageViewState extends GeneralPageViewState { @override Future onRefresh(BuildContext context) async {} + + @override + Widget getTopRightButton(BuildContext context) { + return Container(); + } } diff --git a/uni/lib/view/bug_report/bug_report.dart b/uni/lib/view/bug_report/bug_report.dart index f9f7a5c82..b04005058 100644 --- a/uni/lib/view/bug_report/bug_report.dart +++ b/uni/lib/view/bug_report/bug_report.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:uni/view/bug_report/widgets/form.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; class BugReportPageView extends StatefulWidget { const BugReportPageView({super.key}); @@ -11,7 +11,7 @@ class BugReportPageView extends StatefulWidget { } /// Manages the 'Bugs and sugestions' section of the app. -class BugReportPageViewState extends GeneralPageViewState { +class BugReportPageViewState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { return Container( @@ -22,4 +22,9 @@ class BugReportPageViewState extends GeneralPageViewState { @override Future onRefresh(BuildContext context) async {} + + @override + Container getTopRightButton(BuildContext context) { + return Container(); + } } diff --git a/uni/lib/view/bug_report/widgets/form.dart b/uni/lib/view/bug_report/widgets/form.dart index 18ec82a16..672d7d8b3 100644 --- a/uni/lib/view/bug_report/widgets/form.dart +++ b/uni/lib/view/bug_report/widgets/form.dart @@ -8,7 +8,6 @@ import 'package:uni/controller/local_storage/app_shared_preferences.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/app_locale.dart'; import 'package:uni/model/entities/bug_report.dart'; -import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/bug_report/widgets/text_field.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/toast_message.dart'; @@ -81,69 +80,52 @@ class BugReportFormState extends State { Widget build(BuildContext context) { return Form( key: _formKey, - child: ListView(children: getFormWidget(context)), - ); - } - - List getFormWidget(BuildContext context) { - return [ - bugReportTitle(context), - bugReportIntro(context), - dropdownBugSelectWidget(context), - FormTextField( - titleController, - Icons.title, - maxLines: 2, - description: S.of(context).title, - labelText: S.of(context).problem_id, - bottomMargin: 30, - ), - FormTextField( - descriptionController, - Icons.description, - maxLines: 30, - description: S.of(context).description, - labelText: S.of(context).bug_description, - bottomMargin: 30, - ), - FormTextField( - emailController, - Icons.mail, - maxLines: 2, - description: S.of(context).contact, - labelText: S.of(context).desired_email, - bottomMargin: 30, - isOptional: true, - formatValidator: (String? value) { - if (value == null || value.isEmpty) { - return null; - } - - return EmailValidator.validate(value) - ? null - : S.of(context).valid_email; - }, - ), - consentBox(context), - submitButton(context), - ]; - } - - /// Returns a widget for the title of the bug report form - Widget bugReportTitle(BuildContext context) { - return Container( - margin: const EdgeInsets.symmetric(vertical: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const Icon(Icons.bug_report, size: 40), + child: ListView( + children: [ + const Padding(padding: EdgeInsets.only(bottom: 10)), PageTitle( - name: S.of(context).nav_title( - DrawerItem.navBugReport.title, - ), - center: false, + name: S.of(context).report_error_suggestion, + pad: false, + ), + const Padding(padding: EdgeInsets.only(bottom: 10)), + bugReportIntro(context), + dropdownBugSelectWidget(context), + FormTextField( + titleController, + Icons.title, + maxLines: 2, + description: S.of(context).title, + labelText: S.of(context).problem_id, + bottomMargin: 30, + ), + FormTextField( + descriptionController, + Icons.description, + maxLines: 30, + description: S.of(context).description, + labelText: S.of(context).bug_description, + bottomMargin: 30, + ), + FormTextField( + emailController, + Icons.mail, + maxLines: 2, + description: S.of(context).contact, + labelText: S.of(context).desired_email, + bottomMargin: 30, + isOptional: true, + formatValidator: (String? value) { + if (value == null || value.isEmpty) { + return null; + } + + return EmailValidator.validate(value) + ? null + : S.of(context).valid_email; + }, ), - const Icon(Icons.bug_report, size: 40), + consentBox(context), + submitButton(context), ], ), ); diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart index 911831298..ad5fd1d74 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart @@ -3,10 +3,6 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/locale_notifier.dart'; -import 'package:uni/view/theme_notifier.dart'; - -import '../../../../settings/settings.dart'; class AppNavigationDrawer extends StatefulWidget { const AppNavigationDrawer({required this.parentContext, super.key}); @@ -41,19 +37,12 @@ class AppNavigationDrawerState extends State { void _onSelectPage(String key) { final prev = getCurrentRoute(); - Navigator.of(context).pop(); - if (prev != key) { Navigator.pushNamed(context, '/$key'); } } - void _onLogOut(String key) { - Navigator.of(context) - .pushNamedAndRemoveUntil('/$key', (Route route) => false); - } - BoxDecoration? _getSelectionDecoration(String name) { return (name == getCurrentRoute()) ? BoxDecoration( @@ -68,27 +57,6 @@ class AppNavigationDrawerState extends State { : null; } - Widget createLogoutBtn() { - final logOutText = DrawerItem.navLogOut.title; - return TextButton( - onPressed: () => _onLogOut(logOutText), - style: TextButton.styleFrom( - elevation: 0, - padding: const EdgeInsets.symmetric(horizontal: 5), - ), - child: Container( - padding: const EdgeInsets.all(15), - child: Text( - S.of(context).logout, - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: Theme.of(context).primaryColor), - ), - ), - ); - } - Widget createDrawerNavigationOption(DrawerItem d) { return DecoratedBox( decoration: _getSelectionDecoration(d.title) ?? const BoxDecoration(), @@ -134,24 +102,6 @@ class AppNavigationDrawerState extends State { ), ), ), - Row( - children: [ - Expanded( - child: Align( - child: createLogoutBtn(), - ), - ), - IconButton( - icon: const Icon(Icons.settings), - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (_) => const SettingsPage(), - ), - ), - ), - ], - ), ], ), ); diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 473fb3299..9266ffc0c 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -11,4 +11,9 @@ abstract class SecondaryPageViewState body: refreshState(context, body), ); } + + @override + Widget getTopRightButton(BuildContext context) { + return Container(); + } } diff --git a/uni/lib/view/common_widgets/request_dependent_widget_builder.dart b/uni/lib/view/common_widgets/request_dependent_widget_builder.dart index 5efd0dfe6..924320dd1 100644 --- a/uni/lib/view/common_widgets/request_dependent_widget_builder.dart +++ b/uni/lib/view/common_widgets/request_dependent_widget_builder.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/request_status.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/bug_report/bug_report.dart'; /// Wraps content given its fetch data from the redux store, /// hydrating the component, displaying an empty message, @@ -93,9 +93,11 @@ class RequestDependentWidgetBuilder extends StatelessWidget { ), ), OutlinedButton( - onPressed: () => Navigator.pushNamed( + onPressed: () => Navigator.push( context, - '/${DrawerItem.navBugReport.title}', + MaterialPageRoute( + builder: (context) => const BugReportPageView(), + ), ), child: Text(S.of(context).report_error), ), diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index 1d63f27b5..2eede80c4 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -159,18 +159,28 @@ class MainCardsList extends StatelessWidget { center: false, pad: false, ), - OutlinedButton( - onPressed: () => - Provider.of(context, listen: false) - .setHomePageEditingMode( - editingMode: !editingModeProvider.isEditing, - ), - child: Text( - editingModeProvider.isEditing - ? S.of(context).edit_on - : S.of(context).edit_off, + if (editingModeProvider.isEditing) + ElevatedButton( + onPressed: () => + Provider.of(context, listen: false) + .setHomePageEditingMode( + editingMode: false, + ), + child: Text( + S.of(context).edit_on, + ), + ) + else + OutlinedButton( + onPressed: () => + Provider.of(context, listen: false) + .setHomePageEditingMode( + editingMode: true, + ), + child: Text( + S.of(context).edit_off, + ), ), - ), ], ), ); diff --git a/uni/lib/view/navigation_service.dart b/uni/lib/view/navigation_service.dart index 903f5410e..e896d904d 100644 --- a/uni/lib/view/navigation_service.dart +++ b/uni/lib/view/navigation_service.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:uni/controller/cleanup.dart'; import 'package:uni/main.dart'; -import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/login/login.dart'; /// Manages the navigation logic @@ -13,9 +12,11 @@ class NavigationService { unawaited(cleanupStoredData(context)); - Navigator.pushNamedAndRemoveUntil( + Navigator.pushAndRemoveUntil( context, - '/${DrawerItem.navLogIn.title}', + MaterialPageRoute( + builder: (context) => const LoginPageView(), + ), (route) => false, ); } diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 6e23db12a..4ae9bc379 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -7,6 +7,7 @@ import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/profile/widgets/account_info_card.dart'; import 'package:uni/view/profile/widgets/course_info_card.dart'; import 'package:uni/view/profile/widgets/profile_overview.dart'; +import 'package:uni/view/settings/settings.dart'; class ProfilePageView extends StatefulWidget { const ProfilePageView({super.key}); @@ -35,6 +36,7 @@ class ProfilePageViewState extends SecondaryPageViewState { return ListView( children: [ const Padding(padding: EdgeInsets.all(5)), + const Padding(padding: EdgeInsets.all(10)), ProfileOverview( profile: profile, getProfileDecorationImage: getProfileDecorationImage, @@ -52,7 +54,18 @@ class ProfilePageViewState extends SecondaryPageViewState { @override Widget getTopRightButton(BuildContext context) { - return Container(); + return Container( + padding: const EdgeInsets.fromLTRB(0, 10, 20, 10), + child: IconButton( + icon: const Icon(Icons.settings), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (_) => const SettingsPage(), + ), + ), + ), + ); } @override diff --git a/uni/lib/view/profile/widgets/account_info_card.dart b/uni/lib/view/profile/widgets/account_info_card.dart index 9e5162fa3..31fe34b0f 100644 --- a/uni/lib/view/profile/widgets/account_info_card.dart +++ b/uni/lib/view/profile/widgets/account_info_card.dart @@ -8,7 +8,6 @@ import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/profile/widgets/reference_section.dart'; -import 'package:uni/view/profile/widgets/tuition_notification_switch.dart'; /// Manages the 'Current account' section inside the user's page (accessible /// through the top-right widget with the user picture) @@ -95,29 +94,6 @@ class AccountInfoCard extends GenericCard { ), ], ), - TableRow( - children: [ - Container( - margin: const EdgeInsets.only( - top: 8, - bottom: 20, - left: 20, - ), - child: Text( - S.of(context).fee_notification, - style: Theme.of(context).textTheme.titleSmall, - ), - ), - Container( - margin: const EdgeInsets.only( - top: 8, - bottom: 20, - left: 20, - ), - child: const TuitionNotificationSwitch(), - ), - ], - ), ], ), Container( diff --git a/uni/lib/view/settings/settings.dart b/uni/lib/view/settings/settings.dart index f767081dc..8360b80d2 100644 --- a/uni/lib/view/settings/settings.dart +++ b/uni/lib/view/settings/settings.dart @@ -1,8 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; - import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/about/about.dart'; +import 'package:uni/view/bug_report/bug_report.dart'; +import 'package:uni/view/common_widgets/page_title.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; +import 'package:uni/view/settings/widgets/locale_switch_button.dart'; +import 'package:uni/view/settings/widgets/logout_confirm_dialog.dart'; +import 'package:uni/view/settings/widgets/notifications_dialog.dart'; +import 'package:uni/view/settings/widgets/theme_switch_button.dart'; +import 'package:uni/view/settings/widgets/usage_stats_switch.dart'; class SettingsPage extends StatefulWidget { const SettingsPage({super.key}); @@ -13,46 +19,85 @@ class SettingsPage extends StatefulWidget { } } -class SettingsPageState extends GeneralPageViewState { - @override - Future onRefresh(BuildContext context) async {} - +class SettingsPageState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { return Column( children: [ - PageTitle(name: "defs"), + PageTitle(name: S.of(context).settings), const Padding(padding: EdgeInsets.only(top: 10)), Expanded( - child: Container( - padding: EdgeInsets.symmetric(horizontal: 20), - child: ListView( - children: [ - ListTile( - title: const Text('Language'), - trailing: const Icon(Icons.arrow_forward_ios), - onTap: () { - Navigator.pushNamed(context, '/settings/language'); - }, - ), - ListTile( - title: const Text('Theme'), - trailing: const Icon(Icons.arrow_forward_ios), - onTap: () { - Navigator.pushNamed(context, '/settings/theme'); - }, - ), - ListTile( - title: const Text('About'), - trailing: const Icon(Icons.arrow_forward_ios), - onTap: () { - Navigator.pushNamed(context, '/settings/about'); - }, - ), - ], + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: ListView( + children: [ + ListTile( + title: Text(S.of(context).language), + trailing: const LocaleSwitchButton(), + ), + ListTile( + title: Text(S.of(context).theme), + trailing: const ThemeSwitchButton(), + ), + ListTile( + title: Text(S.of(context).collect_usage_stats), + trailing: const UsageStatsSwitch(), + ), + ListTile( + title: Text(S.of(context).notifications), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + showDialog( + context: context, + builder: (context) => const NotificationsDialog(), + ); + }, + ), + ListTile( + title: Text(S.of(context).report_error_suggestion), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const BugReportPageView(), + ), + ); + }, + ), + ListTile( + title: Text(S.of(context).about), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const AboutPageView(), + ), + ); + }, + ), + ListTile( + title: Text(S.of(context).logout), + trailing: const Icon(Icons.arrow_forward_ios), + onTap: () => showDialog( + context: context, + builder: (context) => const LogoutConfirmDialog(), + ), + ), + ], + ), ), - )), + ), ], ); } + + @override + Future onRefresh(BuildContext context) async {} + + @override + Widget getTopRightButton(BuildContext context) { + return Container(); + } } diff --git a/uni/lib/view/settings/widgets/locale_switch_button.dart b/uni/lib/view/settings/widgets/locale_switch_button.dart index c6c40b5f8..5b1963cbb 100644 --- a/uni/lib/view/settings/widgets/locale_switch_button.dart +++ b/uni/lib/view/settings/widgets/locale_switch_button.dart @@ -9,21 +9,10 @@ class LocaleSwitchButton extends StatelessWidget { Widget build(BuildContext context) { return Consumer( builder: (context, localeNotifier, _) { - return TextButton( + return ElevatedButton( onPressed: () => localeNotifier.setNextLocale(), - style: TextButton.styleFrom( - elevation: 0, - padding: const EdgeInsets.symmetric(horizontal: 5), - ), - child: Container( - padding: const EdgeInsets.all(15), - child: Text( - localeNotifier.getLocale().localeCode.languageCode.toUpperCase(), - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: Theme.of(context).primaryColor), - ), + child: Text( + localeNotifier.getLocale().localeCode.languageCode.toUpperCase(), ), ); }, diff --git a/uni/lib/view/settings/widgets/logout_confirm_dialog.dart b/uni/lib/view/settings/widgets/logout_confirm_dialog.dart new file mode 100644 index 000000000..2f632886f --- /dev/null +++ b/uni/lib/view/settings/widgets/logout_confirm_dialog.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/navigation_service.dart'; + +class LogoutConfirmDialog extends StatelessWidget { + const LogoutConfirmDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(S.of(context).logout), + content: Text(S.of(context).confirm_logout), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(S.of(context).no), + ), + ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + NavigationService.logoutAndPopHistory(); + }, + child: Text(S.of(context).yes), + ), + ], + ); + } +} diff --git a/uni/lib/view/settings/widgets/notifications_dialog.dart b/uni/lib/view/settings/widgets/notifications_dialog.dart new file mode 100644 index 000000000..f7bea693d --- /dev/null +++ b/uni/lib/view/settings/widgets/notifications_dialog.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/settings/widgets/tuition_notification_switch.dart'; + +class NotificationsDialog extends StatelessWidget { + const NotificationsDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(S.of(context).notifications), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + title: Text(S.of(context).fee_notification), + trailing: const TuitionNotificationSwitch(), + ), + ], + ), + ); + } +} diff --git a/uni/lib/view/settings/widgets/theme_switch_button.dart b/uni/lib/view/settings/widgets/theme_switch_button.dart index ddabc3b69..21537c75c 100644 --- a/uni/lib/view/settings/widgets/theme_switch_button.dart +++ b/uni/lib/view/settings/widgets/theme_switch_button.dart @@ -9,13 +9,14 @@ class ThemeSwitchButton extends StatelessWidget { Widget build(BuildContext context) { return Consumer( builder: (context, themeNotifier, _) { - return IconButton( - icon: switch (themeNotifier.getTheme()) { - ThemeMode.light => const Icon(Icons.wb_sunny), - ThemeMode.dark => const Icon(Icons.nightlight_round), - ThemeMode.system => const Icon(Icons.brightness_6), - }, + final icon = switch (themeNotifier.getTheme()) { + ThemeMode.light => const Icon(Icons.wb_sunny), + ThemeMode.dark => const Icon(Icons.nightlight_round), + ThemeMode.system => const Icon(Icons.brightness_6), + }; + return ElevatedButton( onPressed: themeNotifier.setNextTheme, + child: icon, ); }, ); diff --git a/uni/lib/view/profile/widgets/tuition_notification_switch.dart b/uni/lib/view/settings/widgets/tuition_notification_switch.dart similarity index 100% rename from uni/lib/view/profile/widgets/tuition_notification_switch.dart rename to uni/lib/view/settings/widgets/tuition_notification_switch.dart diff --git a/uni/lib/view/settings/widgets/usage_stats_switch.dart b/uni/lib/view/settings/widgets/usage_stats_switch.dart new file mode 100644 index 000000000..54d1baa5b --- /dev/null +++ b/uni/lib/view/settings/widgets/usage_stats_switch.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:uni/controller/local_storage/app_shared_preferences.dart'; + +class UsageStatsSwitch extends StatefulWidget { + const UsageStatsSwitch({super.key}); + + @override + State createState() => _UsageStatsSwitchState(); +} + +class _UsageStatsSwitchState extends State { + bool usageStatsToggle = true; + + @override + void initState() { + super.initState(); + getUsageStatsToggle(); + } + + Future getUsageStatsToggle() async { + await AppSharedPreferences.getUsageStatsToggle() + .then((value) => setState(() => usageStatsToggle = value)); + } + + Future saveUsageStatsToggle({required bool value}) async { + await AppSharedPreferences.setUsageStatsToggle(value: value); + setState(() { + usageStatsToggle = value; + }); + } + + @override + Widget build(BuildContext context) { + return Switch.adaptive( + value: usageStatsToggle, + onChanged: (value) => saveUsageStatsToggle(value: value), + ); + } +} diff --git a/uni/test/mocks/integration/src/exams_page_test.mocks.dart b/uni/test/mocks/integration/src/exams_page_test.mocks.dart index e28b49f59..40a40fedc 100644 --- a/uni/test/mocks/integration/src/exams_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/exams_page_test.mocks.dart @@ -77,6 +77,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> get( Uri? url, { @@ -106,6 +107,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> post( Uri? url, { @@ -149,6 +151,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> put( Uri? url, { @@ -192,6 +195,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> patch( Uri? url, { @@ -235,6 +239,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> delete( Uri? url, { @@ -278,6 +283,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future read( Uri? url, { @@ -292,6 +298,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future.value(''), returnValueForMissingStub: _i3.Future.value(''), ) as _i3.Future); + @override _i3.Future<_i5.Uint8List> readBytes( Uri? url, { @@ -307,6 +314,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); + @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -331,6 +339,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -351,30 +360,35 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i5.Uint8List(0), returnValueForMissingStub: _i5.Uint8List(0), ) as _i5.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart index e5e3b4548..e03c840a2 100644 --- a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart @@ -93,6 +93,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> get( Uri? url, { @@ -122,6 +123,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> post( Uri? url, { @@ -165,6 +167,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> put( Uri? url, { @@ -208,6 +211,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> patch( Uri? url, { @@ -251,6 +255,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> delete( Uri? url, { @@ -294,6 +299,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future read( Uri? url, { @@ -308,6 +314,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i4.Future.value(''), returnValueForMissingStub: _i4.Future.value(''), ) as _i4.Future); + @override _i4.Future<_i6.Uint8List> readBytes( Uri? url, { @@ -323,6 +330,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i4.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), ) as _i4.Future<_i6.Uint8List>); + @override _i4.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -347,6 +355,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -367,30 +376,35 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i6.Uint8List(0), returnValueForMissingStub: _i6.Uint8List(0), ) as _i6.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), @@ -415,12 +429,14 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { Invocation.getter(#session), ), ) as _i3.Session); + @override bool get dependsOnSession => (super.noSuchMethod( Invocation.getter(#dependsOnSession), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override set dependsOnSession(bool? _dependsOnSession) => super.noSuchMethod( Invocation.setter( @@ -429,6 +445,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override set cacheDuration(Duration? _cacheDuration) => super.noSuchMethod( Invocation.setter( @@ -437,18 +454,21 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override _i8.RequestStatus get status => (super.noSuchMethod( Invocation.getter(#status), returnValue: _i8.RequestStatus.none, returnValueForMissingStub: _i8.RequestStatus.none, ) as _i8.RequestStatus); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override _i4.Future loadFromStorage() => (super.noSuchMethod( Invocation.method( @@ -458,6 +478,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future loadFromRemote( _i3.Session? session, @@ -474,6 +495,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void restoreSession( String? username, @@ -491,6 +513,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override _i4.Future postAuthentication( _i10.BuildContext? context, @@ -513,22 +536,25 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override - void markAsNotInitialized() => super.noSuchMethod( + void markAsInitialized() => super.noSuchMethod( Invocation.method( - #markAsNotInitialized, + #markAsInitialized, [], ), returnValueForMissingStub: null, ); + @override - void updateStatus(_i8.RequestStatus? status) => super.noSuchMethod( + void markAsNotInitialized() => super.noSuchMethod( Invocation.method( - #updateStatus, - [status], + #markAsNotInitialized, + [], ), returnValueForMissingStub: null, ); + @override _i4.Future forceRefresh(_i10.BuildContext? context) => (super.noSuchMethod( @@ -539,6 +565,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future ensureInitialized(_i10.BuildContext? context) => (super.noSuchMethod( @@ -549,6 +576,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future ensureInitializedFromRemote(_i10.BuildContext? context) => (super.noSuchMethod( @@ -559,6 +587,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future ensureInitializedFromStorage() => (super.noSuchMethod( Invocation.method( @@ -568,6 +597,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -576,6 +606,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -584,6 +615,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -592,6 +624,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart index fd78853db..9f2e5d8ed 100644 --- a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart @@ -80,6 +80,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> get( Uri? url, { @@ -109,6 +110,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> post( Uri? url, { @@ -152,6 +154,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> put( Uri? url, { @@ -195,6 +198,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> patch( Uri? url, { @@ -238,6 +242,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> delete( Uri? url, { @@ -281,6 +286,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future read( Uri? url, { @@ -295,6 +301,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future.value(''), returnValueForMissingStub: _i3.Future.value(''), ) as _i3.Future); + @override _i3.Future<_i5.Uint8List> readBytes( Uri? url, { @@ -310,6 +317,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); + @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -334,6 +342,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -357,6 +366,7 @@ class MockParserExams extends _i1.Mock implements _i6.ParserExams { returnValue: '', returnValueForMissingStub: '', ) as String); + @override _i3.Future> parseExams( _i2.Response? response, @@ -386,30 +396,35 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i5.Uint8List(0), returnValueForMissingStub: _i5.Uint8List(0), ) as _i5.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart index 3fd2208a6..49010fdd7 100644 --- a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart @@ -80,6 +80,7 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { returnValueForMissingStub: _i4.Future>.value(<_i5.Lecture>[]), ) as _i4.Future>); + @override _i2.Dates getDates() => (super.noSuchMethod( Invocation.method( @@ -101,6 +102,7 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { ), ), ) as _i2.Dates); + @override List getEndpoints(_i6.Session? session) => (super.noSuchMethod( Invocation.method( @@ -145,6 +147,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> get( Uri? url, { @@ -174,6 +177,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> post( Uri? url, { @@ -217,6 +221,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> put( Uri? url, { @@ -260,6 +265,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> patch( Uri? url, { @@ -303,6 +309,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> delete( Uri? url, { @@ -346,6 +353,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future read( Uri? url, { @@ -360,6 +368,7 @@ class MockClient extends _i1.Mock implements _i3.Client { returnValue: _i4.Future.value(''), returnValueForMissingStub: _i4.Future.value(''), ) as _i4.Future); + @override _i4.Future<_i9.Uint8List> readBytes( Uri? url, { @@ -375,6 +384,7 @@ class MockClient extends _i1.Mock implements _i3.Client { returnValueForMissingStub: _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), ) as _i4.Future<_i9.Uint8List>); + @override _i4.Future<_i3.StreamedResponse> send(_i3.BaseRequest? request) => (super.noSuchMethod( @@ -399,6 +409,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -419,30 +430,35 @@ class MockResponse extends _i1.Mock implements _i3.Response { returnValue: _i9.Uint8List(0), returnValueForMissingStub: _i9.Uint8List(0), ) as _i9.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), From 3d1073de30405990c566d617dadd708de38e3f8c Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Fri, 8 Dec 2023 22:27:53 +0000 Subject: [PATCH 038/334] Allow opening drawer via left swipe or logo tap --- .../pages_layouts/general/general.dart | 47 ++++++++++++------- .../src/exams_page_test.mocks.dart | 14 ------ .../src/schedule_page_test.mocks.dart | 43 ++--------------- .../providers/exams_provider_test.mocks.dart | 15 ------ .../lecture_provider_test.mocks.dart | 16 ------- 5 files changed, 35 insertions(+), 100 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 313b729bd..bf601badd 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -101,7 +101,16 @@ abstract class GeneralPageViewState extends State { Provider.of(context, listen: false).session, forceRetrieval: true, ).then((value) => onRefresh(context)), - child: child, + child: Builder( + builder: (context) => GestureDetector( + onHorizontalDragEnd: (dragDetails) { + if (dragDetails.primaryVelocity! > 2) { + Scaffold.of(context).openDrawer(); + } + }, + child: child, + ), + ), ); } @@ -136,23 +145,27 @@ abstract class GeneralPageViewState extends State { title: ButtonTheme( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: const RoundedRectangleBorder(), - child: TextButton( - onPressed: () { - final currentRouteName = ModalRoute.of(context)!.settings.name; - if (currentRouteName != DrawerItem.navPersonalArea.title) { - Navigator.pushNamed( - context, - '/${DrawerItem.navPersonalArea.title}', - ); - } - }, - child: SvgPicture.asset( - colorFilter: ColorFilter.mode( - Theme.of(context).primaryColor, - BlendMode.srcIn, + child: Builder( + builder: (context) => TextButton( + onPressed: () { + final currentRouteName = ModalRoute.of(context)!.settings.name; + if (currentRouteName != '/${DrawerItem.navPersonalArea.title}') { + Navigator.pushNamed( + context, + '/${DrawerItem.navPersonalArea.title}', + ); + } else { + Scaffold.of(context).openDrawer(); + } + }, + child: SvgPicture.asset( + colorFilter: ColorFilter.mode( + Theme.of(context).primaryColor, + BlendMode.srcIn, + ), + 'assets/images/logo_dark.svg', + height: queryData.size.height / 25, ), - 'assets/images/logo_dark.svg', - height: queryData.size.height / 25, ), ), ), diff --git a/uni/test/mocks/integration/src/exams_page_test.mocks.dart b/uni/test/mocks/integration/src/exams_page_test.mocks.dart index 40a40fedc..e28b49f59 100644 --- a/uni/test/mocks/integration/src/exams_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/exams_page_test.mocks.dart @@ -77,7 +77,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> get( Uri? url, { @@ -107,7 +106,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> post( Uri? url, { @@ -151,7 +149,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> put( Uri? url, { @@ -195,7 +192,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> patch( Uri? url, { @@ -239,7 +235,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> delete( Uri? url, { @@ -283,7 +278,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future read( Uri? url, { @@ -298,7 +292,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future.value(''), returnValueForMissingStub: _i3.Future.value(''), ) as _i3.Future); - @override _i3.Future<_i5.Uint8List> readBytes( Uri? url, { @@ -314,7 +307,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); - @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -339,7 +331,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.StreamedResponse>); - @override void close() => super.noSuchMethod( Invocation.method( @@ -360,35 +351,30 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i5.Uint8List(0), returnValueForMissingStub: _i5.Uint8List(0), ) as _i5.Uint8List); - @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); - @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); - @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); - @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart index e03c840a2..e5e3b4548 100644 --- a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart @@ -93,7 +93,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future<_i2.Response> get( Uri? url, { @@ -123,7 +122,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future<_i2.Response> post( Uri? url, { @@ -167,7 +165,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future<_i2.Response> put( Uri? url, { @@ -211,7 +208,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future<_i2.Response> patch( Uri? url, { @@ -255,7 +251,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future<_i2.Response> delete( Uri? url, { @@ -299,7 +294,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); - @override _i4.Future read( Uri? url, { @@ -314,7 +308,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i4.Future.value(''), returnValueForMissingStub: _i4.Future.value(''), ) as _i4.Future); - @override _i4.Future<_i6.Uint8List> readBytes( Uri? url, { @@ -330,7 +323,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i4.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), ) as _i4.Future<_i6.Uint8List>); - @override _i4.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -355,7 +347,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.StreamedResponse>); - @override void close() => super.noSuchMethod( Invocation.method( @@ -376,35 +367,30 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i6.Uint8List(0), returnValueForMissingStub: _i6.Uint8List(0), ) as _i6.Uint8List); - @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); - @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); - @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); - @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), @@ -429,14 +415,12 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { Invocation.getter(#session), ), ) as _i3.Session); - @override bool get dependsOnSession => (super.noSuchMethod( Invocation.getter(#dependsOnSession), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override set dependsOnSession(bool? _dependsOnSession) => super.noSuchMethod( Invocation.setter( @@ -445,7 +429,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override set cacheDuration(Duration? _cacheDuration) => super.noSuchMethod( Invocation.setter( @@ -454,21 +437,18 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override _i8.RequestStatus get status => (super.noSuchMethod( Invocation.getter(#status), returnValue: _i8.RequestStatus.none, returnValueForMissingStub: _i8.RequestStatus.none, ) as _i8.RequestStatus); - @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override _i4.Future loadFromStorage() => (super.noSuchMethod( Invocation.method( @@ -478,7 +458,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override _i4.Future loadFromRemote( _i3.Session? session, @@ -495,7 +474,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override void restoreSession( String? username, @@ -513,7 +491,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override _i4.Future postAuthentication( _i10.BuildContext? context, @@ -536,25 +513,22 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override - void markAsInitialized() => super.noSuchMethod( + void markAsNotInitialized() => super.noSuchMethod( Invocation.method( - #markAsInitialized, + #markAsNotInitialized, [], ), returnValueForMissingStub: null, ); - @override - void markAsNotInitialized() => super.noSuchMethod( + void updateStatus(_i8.RequestStatus? status) => super.noSuchMethod( Invocation.method( - #markAsNotInitialized, - [], + #updateStatus, + [status], ), returnValueForMissingStub: null, ); - @override _i4.Future forceRefresh(_i10.BuildContext? context) => (super.noSuchMethod( @@ -565,7 +539,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override _i4.Future ensureInitialized(_i10.BuildContext? context) => (super.noSuchMethod( @@ -576,7 +549,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override _i4.Future ensureInitializedFromRemote(_i10.BuildContext? context) => (super.noSuchMethod( @@ -587,7 +559,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override _i4.Future ensureInitializedFromStorage() => (super.noSuchMethod( Invocation.method( @@ -597,7 +568,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -606,7 +576,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -615,7 +584,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override void dispose() => super.noSuchMethod( Invocation.method( @@ -624,7 +592,6 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); - @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart index 9f2e5d8ed..fd78853db 100644 --- a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart @@ -80,7 +80,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> get( Uri? url, { @@ -110,7 +109,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> post( Uri? url, { @@ -154,7 +152,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> put( Uri? url, { @@ -198,7 +195,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> patch( Uri? url, { @@ -242,7 +238,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future<_i2.Response> delete( Uri? url, { @@ -286,7 +281,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); - @override _i3.Future read( Uri? url, { @@ -301,7 +295,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future.value(''), returnValueForMissingStub: _i3.Future.value(''), ) as _i3.Future); - @override _i3.Future<_i5.Uint8List> readBytes( Uri? url, { @@ -317,7 +310,6 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); - @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -342,7 +334,6 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.StreamedResponse>); - @override void close() => super.noSuchMethod( Invocation.method( @@ -366,7 +357,6 @@ class MockParserExams extends _i1.Mock implements _i6.ParserExams { returnValue: '', returnValueForMissingStub: '', ) as String); - @override _i3.Future> parseExams( _i2.Response? response, @@ -396,35 +386,30 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i5.Uint8List(0), returnValueForMissingStub: _i5.Uint8List(0), ) as _i5.Uint8List); - @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); - @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); - @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); - @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart index 49010fdd7..3fd2208a6 100644 --- a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart @@ -80,7 +80,6 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { returnValueForMissingStub: _i4.Future>.value(<_i5.Lecture>[]), ) as _i4.Future>); - @override _i2.Dates getDates() => (super.noSuchMethod( Invocation.method( @@ -102,7 +101,6 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { ), ), ) as _i2.Dates); - @override List getEndpoints(_i6.Session? session) => (super.noSuchMethod( Invocation.method( @@ -147,7 +145,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future<_i3.Response> get( Uri? url, { @@ -177,7 +174,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future<_i3.Response> post( Uri? url, { @@ -221,7 +217,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future<_i3.Response> put( Uri? url, { @@ -265,7 +260,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future<_i3.Response> patch( Uri? url, { @@ -309,7 +303,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future<_i3.Response> delete( Uri? url, { @@ -353,7 +346,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); - @override _i4.Future read( Uri? url, { @@ -368,7 +360,6 @@ class MockClient extends _i1.Mock implements _i3.Client { returnValue: _i4.Future.value(''), returnValueForMissingStub: _i4.Future.value(''), ) as _i4.Future); - @override _i4.Future<_i9.Uint8List> readBytes( Uri? url, { @@ -384,7 +375,6 @@ class MockClient extends _i1.Mock implements _i3.Client { returnValueForMissingStub: _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), ) as _i4.Future<_i9.Uint8List>); - @override _i4.Future<_i3.StreamedResponse> send(_i3.BaseRequest? request) => (super.noSuchMethod( @@ -409,7 +399,6 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.StreamedResponse>); - @override void close() => super.noSuchMethod( Invocation.method( @@ -430,35 +419,30 @@ class MockResponse extends _i1.Mock implements _i3.Response { returnValue: _i9.Uint8List(0), returnValueForMissingStub: _i9.Uint8List(0), ) as _i9.Uint8List); - @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); - @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); - @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); - @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); - @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), From 1a2ec3903776efe87080b7a26ba732d7cd232f6b Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 18:27:04 +0000 Subject: [PATCH 039/334] Do not show stall profile picture in profile page --- uni/lib/view/profile/widgets/profile_overview.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/profile/widgets/profile_overview.dart b/uni/lib/view/profile/widgets/profile_overview.dart index 2ea9b1f30..fcc49a8fc 100644 --- a/uni/lib/view/profile/widgets/profile_overview.dart +++ b/uni/lib/view/profile/widgets/profile_overview.dart @@ -32,7 +32,9 @@ class ProfileOverview extends StatelessWidget { height: 150, decoration: BoxDecoration( shape: BoxShape.circle, - image: getProfileDecorationImage(profilePic.data), + image: profilePic.data != null + ? getProfileDecorationImage(profilePic.data) + : null, ), ), const Padding(padding: EdgeInsets.all(8)), From 6292d3e621b298a16ed9c6fde859538373f46a9b Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 18:30:46 +0000 Subject: [PATCH 040/334] Reduce library indicator radius --- uni/lib/view/library/widgets/library_occupation_card.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/view/library/widgets/library_occupation_card.dart b/uni/lib/view/library/widgets/library_occupation_card.dart index ee6a4548b..05a256194 100644 --- a/uni/lib/view/library/widgets/library_occupation_card.dart +++ b/uni/lib/view/library/widgets/library_occupation_card.dart @@ -66,7 +66,7 @@ class LibraryOccupationCard extends GenericCard { return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: CircularPercentIndicator( - radius: 60, + radius: 40, lineWidth: 8, percent: occupation.percentage / 100, center: Text( From b6b8103bb307aa70df9af2d83a8c462db235bd5e Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 19:03:26 +0000 Subject: [PATCH 041/334] Tweak restaurants page and card --- .../view/home/widgets/restaurant_card.dart | 34 +++++++++++-- .../view/restaurant/restaurant_page_view.dart | 48 +++++++++++++------ 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/uni/lib/view/home/widgets/restaurant_card.dart b/uni/lib/view/home/widgets/restaurant_card.dart index 010264d9b..cc8274aa0 100644 --- a/uni/lib/view/home/widgets/restaurant_card.dart +++ b/uni/lib/view/home/widgets/restaurant_card.dart @@ -52,7 +52,7 @@ class RestaurantCard extends GenericCard { onNullContent: Column( children: [ Padding( - padding: const EdgeInsets.only(top: 15, bottom: 10), + padding: const EdgeInsets.only(top: 10, bottom: 10), child: Center( child: Text( S.of(context).no_favorite_restaurants, @@ -74,10 +74,32 @@ class RestaurantCard extends GenericCard { ); } - Widget generateRestaurants(List data, BuildContext context) { + Widget generateRestaurants( + List restaurants, + BuildContext context, + ) { final weekDay = DateTime.now().weekday; final offset = (weekDay - 1) % 7; - final restaurants = data; + + if (restaurants + .map((e) => e.meals[DayOfWeek.values[offset]]) + .every((element) => element?.isEmpty ?? true)) { + return Column( + children: [ + const SizedBox( + height: 15, + ), + Text( + S.of(context).no_menus, + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox( + height: 15, + ), + ], + ); + } + return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -108,12 +130,13 @@ class RestaurantCard extends GenericCard { Center( child: Container( alignment: Alignment.centerLeft, - padding: const EdgeInsets.fromLTRB(12, 20, 12, 5), + padding: const EdgeInsets.fromLTRB(10, 15, 5, 10), child: Text( restaurant.name, style: TextStyle( + fontSize: 16, color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w400, ), ), ), @@ -143,6 +166,7 @@ class RestaurantCard extends GenericCard { ), ), ), + const SizedBox(height: 10), ], ); } diff --git a/uni/lib/view/restaurant/restaurant_page_view.dart b/uni/lib/view/restaurant/restaurant_page_view.dart index 80a1c96b5..a31fefe11 100644 --- a/uni/lib/view/restaurant/restaurant_page_view.dart +++ b/uni/lib/view/restaurant/restaurant_page_view.dart @@ -65,12 +65,21 @@ class _RestaurantPageViewState extends GeneralPageViewState ], ), const SizedBox(height: 10), - RequestDependentWidgetBuilder( - status: restaurantProvider.status, - builder: () => - createTabViewBuilder(restaurantProvider.restaurants, context), - hasContentPredicate: restaurantProvider.restaurants.isNotEmpty, - onNullContent: Center(child: Text(S.of(context).no_menus)), + Expanded( + child: RequestDependentWidgetBuilder( + status: restaurantProvider.status, + builder: () => createTabViewBuilder( + restaurantProvider.restaurants, + context, + ), + hasContentPredicate: restaurantProvider.restaurants.isNotEmpty, + onNullContent: Center( + child: Text( + S.of(context).no_menus, + style: const TextStyle(fontSize: 18), + ), + ), + ), ), ], ); @@ -78,15 +87,24 @@ class _RestaurantPageViewState extends GeneralPageViewState ); } - Widget createTabViewBuilder(dynamic restaurants, BuildContext context) { - final List dayContents = DayOfWeek.values.map((dayOfWeek) { - var restaurantsWidgets = []; - if (restaurants is List) { - restaurantsWidgets = restaurants - .map( - (restaurant) => createRestaurant(context, restaurant, dayOfWeek), - ) - .toList(); + Widget createTabViewBuilder( + List restaurants, + BuildContext context, + ) { + final dayContents = DayOfWeek.values.map((dayOfWeek) { + final restaurantsWidgets = restaurants + .where((element) => element.meals[dayOfWeek]?.isNotEmpty ?? false) + .map( + (restaurant) => createRestaurant(context, restaurant, dayOfWeek), + ) + .toList(); + if (restaurantsWidgets.isEmpty) { + return Center( + child: Text( + S.of(context).no_menus, + style: const TextStyle(fontSize: 18), + ), + ); } return ListView(children: restaurantsWidgets); }).toList(); From 2927b583cc5727634a467516fe5f2304f7f10cbb Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 19:06:25 +0000 Subject: [PATCH 042/334] Hide references when not available --- uni/lib/view/common_widgets/generic_card.dart | 23 ------------- .../profile/widgets/account_info_card.dart | 32 +++++++++---------- .../view/profile/widgets/print_info_card.dart | 4 --- 3 files changed, 15 insertions(+), 44 deletions(-) diff --git a/uni/lib/view/common_widgets/generic_card.dart b/uni/lib/view/common_widgets/generic_card.dart index 9bd18de0c..5d56b915a 100644 --- a/uni/lib/view/common_widgets/generic_card.dart +++ b/uni/lib/view/common_widgets/generic_card.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/model/entities/time_utilities.dart'; /// App default card abstract class GenericCard extends StatefulWidget { @@ -51,27 +49,6 @@ abstract class GenericCard extends StatefulWidget { style: Theme.of(context).textTheme.titleLarge, ); } - - StatelessWidget showLastRefreshedTime(String? time, BuildContext context) { - if (time == null) { - return const Text('N/A'); - } - - final parsedTime = DateTime.tryParse(time); - if (parsedTime == null) { - return const Text('N/A'); - } - - return Container( - alignment: Alignment.center, - child: Text( - S.of(context).last_refresh_time( - parsedTime.toTimeHourMinString(), - ), - style: Theme.of(context).textTheme.bodySmall, - ), - ); - } } class GenericCardState extends State { diff --git a/uni/lib/view/profile/widgets/account_info_card.dart b/uni/lib/view/profile/widgets/account_info_card.dart index 31fe34b0f..3e5eca04d 100644 --- a/uni/lib/view/profile/widgets/account_info_card.dart +++ b/uni/lib/view/profile/widgets/account_info_card.dart @@ -96,25 +96,23 @@ class AccountInfoCard extends GenericCard { ), ], ), - Container( - padding: const EdgeInsets.all(10), - child: Row( - children: [ - Text( - S.of(context).pendent_references, - style: Theme.of(context).textTheme.titleLarge?.apply( - color: Theme.of(context).colorScheme.secondary, - ), - ), - ], + if (references.isNotEmpty) + Container( + padding: const EdgeInsets.all(10), + child: Row( + children: [ + Text( + S.of(context).pendent_references, + style: Theme.of(context).textTheme.titleLarge?.apply( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ], + ), ), - ), - ReferenceList(references: references), + if (references.isNotEmpty) + ReferenceList(references: references), const SizedBox(height: 10), - showLastRefreshedTime( - profileStateProvider.lastUpdateTime?.toIso8601String(), - context, - ), ], ); }, diff --git a/uni/lib/view/profile/widgets/print_info_card.dart b/uni/lib/view/profile/widgets/print_info_card.dart index cebb17675..b2acae5a8 100644 --- a/uni/lib/view/profile/widgets/print_info_card.dart +++ b/uni/lib/view/profile/widgets/print_info_card.dart @@ -66,10 +66,6 @@ class PrintInfoCard extends GenericCard { ), ], ), - showLastRefreshedTime( - profileStateProvider.lastUpdateTime?.toIso8601String(), - context, - ), ], ); }, From fa3e21ee27fa61dd9b39fb56a17ef7344b0766da Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 20:10:24 +0000 Subject: [PATCH 043/334] Filter map by keyword --- uni/lib/view/locations/locations.dart | 73 ++++++++++++++----- .../view/locations/widgets/faculty_map.dart | 10 ++- uni/lib/view/locations/widgets/map.dart | 17 ++++- 3 files changed, 80 insertions(+), 20 deletions(-) diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index be9e1d15d..266ae3964 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -42,7 +42,7 @@ class LocationsPageState extends GeneralPageViewState Future onRefresh(BuildContext context) async {} } -class LocationsPageView extends StatelessWidget { +class LocationsPageView extends StatefulWidget { const LocationsPageView({ required this.locations, required this.status, @@ -52,29 +52,66 @@ class LocationsPageView extends StatelessWidget { final List locations; final RequestStatus status; + @override + LocationsPageViewState createState() => LocationsPageViewState(); +} + +class LocationsPageViewState extends State { + static GlobalKey searchFormKey = GlobalKey(); + static String searchTerms = ''; + @override Widget build(BuildContext context) { return Column( children: [ - Container( - width: MediaQuery.of(context).size.width * 0.95, - padding: const EdgeInsets.fromLTRB(0, 0, 0, 4), - child: PageTitle( - name: '${S.of(context).nav_title(DrawerItem.navLocations.title)}:' - ' ${getLocation()}', - ), + Row( + children: [ + PageTitle( + name: '${S.of(context).nav_title(DrawerItem.navLocations.title)}:' + ' ${getLocation()}', + center: false, + ), + Expanded( + child: Container( + margin: const EdgeInsets.fromLTRB(20, 10, 20, 0), + child: TextFormField( + key: searchFormKey, + onChanged: (text) { + setState(() { + searchTerms = text; + }); + }, + decoration: const InputDecoration( + contentPadding: EdgeInsets.all(10), + border: OutlineInputBorder(), + hintText: 'Enter a search term', + ), + ), + ), + ), + ], ), - Container( - padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), - height: MediaQuery.of(context).size.height * 0.75, - alignment: Alignment.center, - child: RequestDependentWidgetBuilder( - status: status, - builder: () => FacultyMap(faculty: 'FEUP', locations: locations), - hasContentPredicate: locations.isNotEmpty, - onNullContent: Center(child: Text(S.of(context).no_places_info)), + const SizedBox(height: 10), + Expanded( + child: Container( + height: 10, + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + alignment: Alignment.center, + child: RequestDependentWidgetBuilder( + status: widget.status, + builder: () => FacultyMap( + faculty: getLocation(), + locations: widget.locations, + searchFilter: searchTerms, + ), + hasContentPredicate: widget.locations.isNotEmpty, + onNullContent: Center(child: Text(S.of(context).no_places_info)), + ), + // TODO(bdmendes): add support for multiple faculties ), - // TODO(bdmendes): add support for multiple faculties + ), + const SizedBox( + height: 20, ), ], ); diff --git a/uni/lib/view/locations/widgets/faculty_map.dart b/uni/lib/view/locations/widgets/faculty_map.dart index 1e2354e54..9aa6a10cf 100644 --- a/uni/lib/view/locations/widgets/faculty_map.dart +++ b/uni/lib/view/locations/widgets/faculty_map.dart @@ -4,9 +4,16 @@ import 'package:uni/model/entities/location_group.dart'; import 'package:uni/view/locations/widgets/map.dart'; class FacultyMap extends StatelessWidget { - const FacultyMap({required this.faculty, required this.locations, super.key}); + const FacultyMap({ + required this.faculty, + required this.locations, + required this.searchFilter, + super.key, + }); + final String faculty; final List locations; + final String searchFilter; @override Widget build(BuildContext context) { @@ -17,6 +24,7 @@ class FacultyMap extends StatelessWidget { southWestBoundary: const LatLng(41.17670, -8.59991), center: const LatLng(41.17731, -8.59522), locations: locations, + searchFilter: searchFilter, ); default: return Container(); // Should not happen diff --git a/uni/lib/view/locations/widgets/map.dart b/uni/lib/view/locations/widgets/map.dart index c03b8b42c..ad1e7a0b4 100644 --- a/uni/lib/view/locations/widgets/map.dart +++ b/uni/lib/view/locations/widgets/map.dart @@ -15,6 +15,7 @@ class LocationsMap extends StatelessWidget { required this.southWestBoundary, required this.center, required this.locations, + this.searchFilter = '', super.key, }); @@ -24,8 +25,22 @@ class LocationsMap extends StatelessWidget { final LatLng southWestBoundary; final LatLng center; + final String searchFilter; + @override Widget build(BuildContext context) { + final filteredLocations = List.from(locations); + if (searchFilter.trim().isNotEmpty) { + filteredLocations.retainWhere((location) { + final allLocations = location.floors.values.expand((x) => x).toList(); + return allLocations.any((location) { + return location.description().toLowerCase().contains( + searchFilter.toLowerCase(), + ); + }); + }); + } + return FlutterMap( options: MapOptions( minZoom: 17, @@ -65,7 +80,7 @@ class LocationsMap extends StatelessWidget { ), PopupMarkerLayer( options: PopupMarkerLayerOptions( - markers: locations.map((location) { + markers: filteredLocations.map((location) { return LocationMarker(location.latlng, location); }).toList(), popupController: _popupLayerController, From a25214728d50b606d588f32a09935c347242ea5c Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 20:23:35 +0000 Subject: [PATCH 044/334] Improve search box design --- uni/lib/generated/intl/messages_en.dart | 1 + uni/lib/generated/intl/messages_pt_PT.dart | 1 + uni/lib/generated/l10n.dart | 10 +++++++ uni/lib/l10n/intl_en.arb | 4 ++- uni/lib/l10n/intl_pt_PT.arb | 4 ++- uni/lib/view/locations/locations.dart | 32 ++++++++++++---------- uni/lib/view/locations/widgets/map.dart | 12 +++++--- uni/pubspec.lock | 8 ++++++ uni/pubspec.yaml | 1 + 9 files changed, 53 insertions(+), 20 deletions(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 112516f05..8dd22f248 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -220,6 +220,7 @@ class MessageLookup extends MessageLookupByLibrary { "room": MessageLookupByLibrary.simpleMessage("Room"), "school_calendar": MessageLookupByLibrary.simpleMessage("School Calendar"), + "search": MessageLookupByLibrary.simpleMessage("Search"), "semester": MessageLookupByLibrary.simpleMessage("Semester"), "send": MessageLookupByLibrary.simpleMessage("Send"), "sent_error": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 68a173ffe..cc5b39984 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -221,6 +221,7 @@ class MessageLookup extends MessageLookupByLibrary { "room": MessageLookupByLibrary.simpleMessage("Sala"), "school_calendar": MessageLookupByLibrary.simpleMessage("Calendário Escolar"), + "search": MessageLookupByLibrary.simpleMessage("Pesquisar"), "semester": MessageLookupByLibrary.simpleMessage("Semestre"), "send": MessageLookupByLibrary.simpleMessage("Enviar"), "sent_error": diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 936259b3e..ed43ea562 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1337,6 +1337,16 @@ class S { args: [], ); } + + /// `Search` + String get search { + return Intl.message( + 'Search', + name: 'search', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index b1d6be897..d15d1cf6f 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -261,5 +261,7 @@ "widget_prompt": "Choose a widget to add to your personal area:", "@widget_prompt": {}, "year": "Year", - "@year": {} + "@year": {}, + "search": "Search", + "@search": {} } \ No newline at end of file diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 4315ac0af..7a2096e02 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -261,5 +261,7 @@ "widget_prompt": "Escolhe um widget para adicionares à tua área pessoal:", "@widget_prompt": {}, "year": "Ano", - "@year": {} + "@year": {}, + "search": "Pesquisar", + "@search": {} } \ No newline at end of file diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index 266ae3964..ba2e4ebfe 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -65,27 +65,31 @@ class LocationsPageViewState extends State { return Column( children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ PageTitle( name: '${S.of(context).nav_title(DrawerItem.navLocations.title)}:' ' ${getLocation()}', center: false, ), - Expanded( - child: Container( - margin: const EdgeInsets.fromLTRB(20, 10, 20, 0), - child: TextFormField( - key: searchFormKey, - onChanged: (text) { - setState(() { - searchTerms = text; - }); - }, - decoration: const InputDecoration( - contentPadding: EdgeInsets.all(10), - border: OutlineInputBorder(), - hintText: 'Enter a search term', + Container( + width: 160, + height: 40, + margin: const EdgeInsets.fromLTRB(20, 10, 20, 0), + child: TextFormField( + key: searchFormKey, + onChanged: (text) { + setState(() { + searchTerms = text; + }); + }, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.search), + contentPadding: const EdgeInsets.all(10), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(50), ), + hintText: '${S.of(context).search}...', ), ), ), diff --git a/uni/lib/view/locations/widgets/map.dart b/uni/lib/view/locations/widgets/map.dart index ad1e7a0b4..244bf3d22 100644 --- a/uni/lib/view/locations/widgets/map.dart +++ b/uni/lib/view/locations/widgets/map.dart @@ -1,4 +1,5 @@ import 'package:cached_network_image/cached_network_image.dart'; +import 'package:diacritic/diacritic.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_marker_popup/flutter_map_marker_popup.dart'; @@ -30,13 +31,16 @@ class LocationsMap extends StatelessWidget { @override Widget build(BuildContext context) { final filteredLocations = List.from(locations); + final cleanSearchFilter = + removeDiacritics(searchFilter.toLowerCase().trim()); if (searchFilter.trim().isNotEmpty) { filteredLocations.retainWhere((location) { - final allLocations = location.floors.values.expand((x) => x).toList(); + final allLocations = location.floors.values.expand((x) => x); return allLocations.any((location) { - return location.description().toLowerCase().contains( - searchFilter.toLowerCase(), - ); + return removeDiacritics(location.description().toLowerCase().trim()) + .contains( + cleanSearchFilter, + ); }); }); } diff --git a/uni/pubspec.lock b/uni/pubspec.lock index e69dba9ff..51a79b3dd 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -289,6 +289,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.8" + diacritic: + dependency: "direct main" + description: + name: diacritic + sha256: a84e03ec2779375fb86430dbe9d8fba62c68376f2499097a5f6e75556babe706 + url: "https://pub.dev" + source: hosted + version: "0.1.4" email_validator: dependency: "direct main" description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 6eedd9a0a..35a6fc991 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -31,6 +31,7 @@ dependencies: flutter_localizations: sdk: flutter flutter_map: ^5.0.0 + diacritic: ^0.1.4 flutter_map_marker_popup: ^5.0.0 flutter_markdown: ^0.6.0 flutter_svg: ^2.0.0+1 From b41efe3ee7d56a68f3795cac3238856c5c35ad59 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 20:38:14 +0000 Subject: [PATCH 045/334] Clip location marker popup --- uni/lib/view/locations/locations.dart | 5 +- uni/lib/view/locations/widgets/map.dart | 4 +- .../view/locations/widgets/marker_popup.dart | 46 +++++++++++-------- uni/pubspec.yaml | 2 +- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index ba2e4ebfe..1616a8b13 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -1,3 +1,4 @@ +import 'package:diacritic/diacritic.dart'; import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/location_group.dart'; @@ -73,14 +74,14 @@ class LocationsPageViewState extends State { center: false, ), Container( - width: 160, + width: 150, height: 40, margin: const EdgeInsets.fromLTRB(20, 10, 20, 0), child: TextFormField( key: searchFormKey, onChanged: (text) { setState(() { - searchTerms = text; + searchTerms = removeDiacritics(text.trim().toLowerCase()); }); }, decoration: InputDecoration( diff --git a/uni/lib/view/locations/widgets/map.dart b/uni/lib/view/locations/widgets/map.dart index 244bf3d22..2ad23bb03 100644 --- a/uni/lib/view/locations/widgets/map.dart +++ b/uni/lib/view/locations/widgets/map.dart @@ -31,15 +31,13 @@ class LocationsMap extends StatelessWidget { @override Widget build(BuildContext context) { final filteredLocations = List.from(locations); - final cleanSearchFilter = - removeDiacritics(searchFilter.toLowerCase().trim()); if (searchFilter.trim().isNotEmpty) { filteredLocations.retainWhere((location) { final allLocations = location.floors.values.expand((x) => x); return allLocations.any((location) { return removeDiacritics(location.description().toLowerCase().trim()) .contains( - cleanSearchFilter, + searchFilter, ); }); }); diff --git a/uni/lib/view/locations/widgets/marker_popup.dart b/uni/lib/view/locations/widgets/marker_popup.dart index 91a7251f6..1c46a3fd9 100644 --- a/uni/lib/view/locations/widgets/marker_popup.dart +++ b/uni/lib/view/locations/widgets/marker_popup.dart @@ -24,6 +24,7 @@ class LocationMarkerPopup extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(12), child: Wrap( + clipBehavior: Clip.antiAlias, direction: Axis.vertical, spacing: 8, children: (showId @@ -59,33 +60,37 @@ class Floor extends StatelessWidget { ? ' $floor' : '$floor'; - final Widget floorCol = Column( - mainAxisSize: MainAxisSize.min, + return Row( children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), + child: Text( + '${S.of(context).floor} $floorString', + style: TextStyle(color: fontColor), + ), + ), + ], + ), Container( padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), - child: Text( - '${S.of(context).floor} $floorString', - style: TextStyle(color: fontColor), + decoration: + BoxDecoration(border: Border(left: BorderSide(color: fontColor))), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: locations + .map( + (location) => + LocationRow(location: location, color: fontColor), + ) + .toList(), ), ), ], ); - final Widget locationsColumn = Container( - padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), - decoration: - BoxDecoration(border: Border(left: BorderSide(color: fontColor))), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: locations - .map( - (location) => LocationRow(location: location, color: fontColor), - ) - .toList(), - ), - ); - return Row(children: [floorCol, locationsColumn]); } } @@ -103,6 +108,7 @@ class LocationRow extends StatelessWidget { Text( location.description(), textAlign: TextAlign.left, + overflow: TextOverflow.ellipsis, style: TextStyle(color: color), ), ], diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 35a6fc991..e3fbc88d8 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: crypto: ^3.0.1 cupertino_icons: ^1.0.2 currency_text_input_formatter: ^2.1.5 + diacritic: ^0.1.4 email_validator: ^2.0.1 encrypt: ^5.0.3 expansion_tile_card: ^3.0.0 @@ -31,7 +32,6 @@ dependencies: flutter_localizations: sdk: flutter flutter_map: ^5.0.0 - diacritic: ^0.1.4 flutter_map_marker_popup: ^5.0.0 flutter_markdown: ^0.6.0 flutter_svg: ^2.0.0+1 From d3e3b22428f3a6b33e3c30e32c91d88c76202064 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 20:10:24 +0000 Subject: [PATCH 046/334] Filter map by keyword --- uni/lib/view/locations/locations.dart | 73 ++++++++++++++----- .../view/locations/widgets/faculty_map.dart | 10 ++- uni/lib/view/locations/widgets/map.dart | 17 ++++- 3 files changed, 80 insertions(+), 20 deletions(-) diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index be9e1d15d..266ae3964 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -42,7 +42,7 @@ class LocationsPageState extends GeneralPageViewState Future onRefresh(BuildContext context) async {} } -class LocationsPageView extends StatelessWidget { +class LocationsPageView extends StatefulWidget { const LocationsPageView({ required this.locations, required this.status, @@ -52,29 +52,66 @@ class LocationsPageView extends StatelessWidget { final List locations; final RequestStatus status; + @override + LocationsPageViewState createState() => LocationsPageViewState(); +} + +class LocationsPageViewState extends State { + static GlobalKey searchFormKey = GlobalKey(); + static String searchTerms = ''; + @override Widget build(BuildContext context) { return Column( children: [ - Container( - width: MediaQuery.of(context).size.width * 0.95, - padding: const EdgeInsets.fromLTRB(0, 0, 0, 4), - child: PageTitle( - name: '${S.of(context).nav_title(DrawerItem.navLocations.title)}:' - ' ${getLocation()}', - ), + Row( + children: [ + PageTitle( + name: '${S.of(context).nav_title(DrawerItem.navLocations.title)}:' + ' ${getLocation()}', + center: false, + ), + Expanded( + child: Container( + margin: const EdgeInsets.fromLTRB(20, 10, 20, 0), + child: TextFormField( + key: searchFormKey, + onChanged: (text) { + setState(() { + searchTerms = text; + }); + }, + decoration: const InputDecoration( + contentPadding: EdgeInsets.all(10), + border: OutlineInputBorder(), + hintText: 'Enter a search term', + ), + ), + ), + ), + ], ), - Container( - padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), - height: MediaQuery.of(context).size.height * 0.75, - alignment: Alignment.center, - child: RequestDependentWidgetBuilder( - status: status, - builder: () => FacultyMap(faculty: 'FEUP', locations: locations), - hasContentPredicate: locations.isNotEmpty, - onNullContent: Center(child: Text(S.of(context).no_places_info)), + const SizedBox(height: 10), + Expanded( + child: Container( + height: 10, + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + alignment: Alignment.center, + child: RequestDependentWidgetBuilder( + status: widget.status, + builder: () => FacultyMap( + faculty: getLocation(), + locations: widget.locations, + searchFilter: searchTerms, + ), + hasContentPredicate: widget.locations.isNotEmpty, + onNullContent: Center(child: Text(S.of(context).no_places_info)), + ), + // TODO(bdmendes): add support for multiple faculties ), - // TODO(bdmendes): add support for multiple faculties + ), + const SizedBox( + height: 20, ), ], ); diff --git a/uni/lib/view/locations/widgets/faculty_map.dart b/uni/lib/view/locations/widgets/faculty_map.dart index 1e2354e54..9aa6a10cf 100644 --- a/uni/lib/view/locations/widgets/faculty_map.dart +++ b/uni/lib/view/locations/widgets/faculty_map.dart @@ -4,9 +4,16 @@ import 'package:uni/model/entities/location_group.dart'; import 'package:uni/view/locations/widgets/map.dart'; class FacultyMap extends StatelessWidget { - const FacultyMap({required this.faculty, required this.locations, super.key}); + const FacultyMap({ + required this.faculty, + required this.locations, + required this.searchFilter, + super.key, + }); + final String faculty; final List locations; + final String searchFilter; @override Widget build(BuildContext context) { @@ -17,6 +24,7 @@ class FacultyMap extends StatelessWidget { southWestBoundary: const LatLng(41.17670, -8.59991), center: const LatLng(41.17731, -8.59522), locations: locations, + searchFilter: searchFilter, ); default: return Container(); // Should not happen diff --git a/uni/lib/view/locations/widgets/map.dart b/uni/lib/view/locations/widgets/map.dart index c03b8b42c..ad1e7a0b4 100644 --- a/uni/lib/view/locations/widgets/map.dart +++ b/uni/lib/view/locations/widgets/map.dart @@ -15,6 +15,7 @@ class LocationsMap extends StatelessWidget { required this.southWestBoundary, required this.center, required this.locations, + this.searchFilter = '', super.key, }); @@ -24,8 +25,22 @@ class LocationsMap extends StatelessWidget { final LatLng southWestBoundary; final LatLng center; + final String searchFilter; + @override Widget build(BuildContext context) { + final filteredLocations = List.from(locations); + if (searchFilter.trim().isNotEmpty) { + filteredLocations.retainWhere((location) { + final allLocations = location.floors.values.expand((x) => x).toList(); + return allLocations.any((location) { + return location.description().toLowerCase().contains( + searchFilter.toLowerCase(), + ); + }); + }); + } + return FlutterMap( options: MapOptions( minZoom: 17, @@ -65,7 +80,7 @@ class LocationsMap extends StatelessWidget { ), PopupMarkerLayer( options: PopupMarkerLayerOptions( - markers: locations.map((location) { + markers: filteredLocations.map((location) { return LocationMarker(location.latlng, location); }).toList(), popupController: _popupLayerController, From 10482559ab697062de8f4a16dfc9d5c3d6d6cd73 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 20:23:35 +0000 Subject: [PATCH 047/334] Improve search box design --- uni/lib/generated/intl/messages_en.dart | 1 + uni/lib/generated/intl/messages_pt_PT.dart | 1 + uni/lib/generated/l10n.dart | 10 +++++++ uni/lib/l10n/intl_en.arb | 4 ++- uni/lib/l10n/intl_pt_PT.arb | 4 ++- uni/lib/view/locations/locations.dart | 32 ++++++++++++---------- uni/lib/view/locations/widgets/map.dart | 12 +++++--- uni/pubspec.lock | 8 ++++++ uni/pubspec.yaml | 1 + 9 files changed, 53 insertions(+), 20 deletions(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 2256f8e76..088b8c09f 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -222,6 +222,7 @@ class MessageLookup extends MessageLookupByLibrary { "room": MessageLookupByLibrary.simpleMessage("Room"), "school_calendar": MessageLookupByLibrary.simpleMessage("School Calendar"), + "search": MessageLookupByLibrary.simpleMessage("Search"), "semester": MessageLookupByLibrary.simpleMessage("Semester"), "send": MessageLookupByLibrary.simpleMessage("Send"), "sent_error": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 4e7a23288..9a99d855a 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -223,6 +223,7 @@ class MessageLookup extends MessageLookupByLibrary { "room": MessageLookupByLibrary.simpleMessage("Sala"), "school_calendar": MessageLookupByLibrary.simpleMessage("Calendário Escolar"), + "search": MessageLookupByLibrary.simpleMessage("Pesquisar"), "semester": MessageLookupByLibrary.simpleMessage("Semestre"), "send": MessageLookupByLibrary.simpleMessage("Enviar"), "sent_error": diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index c73c45a2d..9e7c84554 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1347,6 +1347,16 @@ class S { args: [], ); } + + /// `Search` + String get search { + return Intl.message( + 'Search', + name: 'search', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index c1e22210e..2a8590598 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -263,5 +263,7 @@ "widget_prompt": "Choose a widget to add to your personal area:", "@widget_prompt": {}, "year": "Year", - "@year": {} + "@year": {}, + "search": "Search", + "@search": {} } \ No newline at end of file diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 26cef1c70..fa7e7d798 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -263,5 +263,7 @@ "widget_prompt": "Escolhe um widget para adicionares à tua área pessoal:", "@widget_prompt": {}, "year": "Ano", - "@year": {} + "@year": {}, + "search": "Pesquisar", + "@search": {} } \ No newline at end of file diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index 266ae3964..ba2e4ebfe 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -65,27 +65,31 @@ class LocationsPageViewState extends State { return Column( children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ PageTitle( name: '${S.of(context).nav_title(DrawerItem.navLocations.title)}:' ' ${getLocation()}', center: false, ), - Expanded( - child: Container( - margin: const EdgeInsets.fromLTRB(20, 10, 20, 0), - child: TextFormField( - key: searchFormKey, - onChanged: (text) { - setState(() { - searchTerms = text; - }); - }, - decoration: const InputDecoration( - contentPadding: EdgeInsets.all(10), - border: OutlineInputBorder(), - hintText: 'Enter a search term', + Container( + width: 160, + height: 40, + margin: const EdgeInsets.fromLTRB(20, 10, 20, 0), + child: TextFormField( + key: searchFormKey, + onChanged: (text) { + setState(() { + searchTerms = text; + }); + }, + decoration: InputDecoration( + prefixIcon: const Icon(Icons.search), + contentPadding: const EdgeInsets.all(10), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(50), ), + hintText: '${S.of(context).search}...', ), ), ), diff --git a/uni/lib/view/locations/widgets/map.dart b/uni/lib/view/locations/widgets/map.dart index ad1e7a0b4..244bf3d22 100644 --- a/uni/lib/view/locations/widgets/map.dart +++ b/uni/lib/view/locations/widgets/map.dart @@ -1,4 +1,5 @@ import 'package:cached_network_image/cached_network_image.dart'; +import 'package:diacritic/diacritic.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_marker_popup/flutter_map_marker_popup.dart'; @@ -30,13 +31,16 @@ class LocationsMap extends StatelessWidget { @override Widget build(BuildContext context) { final filteredLocations = List.from(locations); + final cleanSearchFilter = + removeDiacritics(searchFilter.toLowerCase().trim()); if (searchFilter.trim().isNotEmpty) { filteredLocations.retainWhere((location) { - final allLocations = location.floors.values.expand((x) => x).toList(); + final allLocations = location.floors.values.expand((x) => x); return allLocations.any((location) { - return location.description().toLowerCase().contains( - searchFilter.toLowerCase(), - ); + return removeDiacritics(location.description().toLowerCase().trim()) + .contains( + cleanSearchFilter, + ); }); }); } diff --git a/uni/pubspec.lock b/uni/pubspec.lock index f7a45f92e..eb33b71d5 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -289,6 +289,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.8" + diacritic: + dependency: "direct main" + description: + name: diacritic + sha256: a84e03ec2779375fb86430dbe9d8fba62c68376f2499097a5f6e75556babe706 + url: "https://pub.dev" + source: hosted + version: "0.1.4" email_validator: dependency: "direct main" description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 998bf377e..9eb3d8f75 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -31,6 +31,7 @@ dependencies: flutter_localizations: sdk: flutter flutter_map: ^5.0.0 + diacritic: ^0.1.4 flutter_map_marker_popup: ^5.0.0 flutter_markdown: ^0.6.0 flutter_svg: ^2.0.0+1 From a13bb978358aa6f189c4d3c23b1ce5ad93ca7622 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 9 Dec 2023 20:38:14 +0000 Subject: [PATCH 048/334] Clip location marker popup --- uni/lib/view/locations/locations.dart | 5 +- uni/lib/view/locations/widgets/map.dart | 4 +- .../view/locations/widgets/marker_popup.dart | 46 +++++++++++-------- uni/pubspec.yaml | 2 +- 4 files changed, 31 insertions(+), 26 deletions(-) diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index ba2e4ebfe..1616a8b13 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -1,3 +1,4 @@ +import 'package:diacritic/diacritic.dart'; import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/location_group.dart'; @@ -73,14 +74,14 @@ class LocationsPageViewState extends State { center: false, ), Container( - width: 160, + width: 150, height: 40, margin: const EdgeInsets.fromLTRB(20, 10, 20, 0), child: TextFormField( key: searchFormKey, onChanged: (text) { setState(() { - searchTerms = text; + searchTerms = removeDiacritics(text.trim().toLowerCase()); }); }, decoration: InputDecoration( diff --git a/uni/lib/view/locations/widgets/map.dart b/uni/lib/view/locations/widgets/map.dart index 244bf3d22..2ad23bb03 100644 --- a/uni/lib/view/locations/widgets/map.dart +++ b/uni/lib/view/locations/widgets/map.dart @@ -31,15 +31,13 @@ class LocationsMap extends StatelessWidget { @override Widget build(BuildContext context) { final filteredLocations = List.from(locations); - final cleanSearchFilter = - removeDiacritics(searchFilter.toLowerCase().trim()); if (searchFilter.trim().isNotEmpty) { filteredLocations.retainWhere((location) { final allLocations = location.floors.values.expand((x) => x); return allLocations.any((location) { return removeDiacritics(location.description().toLowerCase().trim()) .contains( - cleanSearchFilter, + searchFilter, ); }); }); diff --git a/uni/lib/view/locations/widgets/marker_popup.dart b/uni/lib/view/locations/widgets/marker_popup.dart index 91a7251f6..1c46a3fd9 100644 --- a/uni/lib/view/locations/widgets/marker_popup.dart +++ b/uni/lib/view/locations/widgets/marker_popup.dart @@ -24,6 +24,7 @@ class LocationMarkerPopup extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(12), child: Wrap( + clipBehavior: Clip.antiAlias, direction: Axis.vertical, spacing: 8, children: (showId @@ -59,33 +60,37 @@ class Floor extends StatelessWidget { ? ' $floor' : '$floor'; - final Widget floorCol = Column( - mainAxisSize: MainAxisSize.min, + return Row( children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), + child: Text( + '${S.of(context).floor} $floorString', + style: TextStyle(color: fontColor), + ), + ), + ], + ), Container( padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), - child: Text( - '${S.of(context).floor} $floorString', - style: TextStyle(color: fontColor), + decoration: + BoxDecoration(border: Border(left: BorderSide(color: fontColor))), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: locations + .map( + (location) => + LocationRow(location: location, color: fontColor), + ) + .toList(), ), ), ], ); - final Widget locationsColumn = Container( - padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), - decoration: - BoxDecoration(border: Border(left: BorderSide(color: fontColor))), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: locations - .map( - (location) => LocationRow(location: location, color: fontColor), - ) - .toList(), - ), - ); - return Row(children: [floorCol, locationsColumn]); } } @@ -103,6 +108,7 @@ class LocationRow extends StatelessWidget { Text( location.description(), textAlign: TextAlign.left, + overflow: TextOverflow.ellipsis, style: TextStyle(color: color), ), ], diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 9eb3d8f75..d961cc9b5 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -21,6 +21,7 @@ dependencies: crypto: ^3.0.1 cupertino_icons: ^1.0.2 currency_text_input_formatter: ^2.1.5 + diacritic: ^0.1.4 email_validator: ^2.0.1 encrypt: ^5.0.3 expansion_tile_card: ^3.0.0 @@ -31,7 +32,6 @@ dependencies: flutter_localizations: sdk: flutter flutter_map: ^5.0.0 - diacritic: ^0.1.4 flutter_map_marker_popup: ^5.0.0 flutter_markdown: ^0.6.0 flutter_svg: ^2.0.0+1 From 19f64d8875e56f18533dc06e45baefe1db1da83f Mon Sep 17 00:00:00 2001 From: Bruno Mendes <61701401+bdmendes@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:38:02 +0000 Subject: [PATCH 049/334] Remove top right button override on bug report page --- uni/lib/view/bug_report/bug_report.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/uni/lib/view/bug_report/bug_report.dart b/uni/lib/view/bug_report/bug_report.dart index b04005058..26b9ae931 100644 --- a/uni/lib/view/bug_report/bug_report.dart +++ b/uni/lib/view/bug_report/bug_report.dart @@ -22,9 +22,4 @@ class BugReportPageViewState extends SecondaryPageViewState { @override Future onRefresh(BuildContext context) async {} - - @override - Container getTopRightButton(BuildContext context) { - return Container(); - } } From 4d979ea950e8ce8141a6ca075d854a8f57e53109 Mon Sep 17 00:00:00 2001 From: Bruno Mendes <61701401+bdmendes@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:39:23 +0000 Subject: [PATCH 050/334] Remove top right button override on about page --- uni/lib/view/about/about.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/uni/lib/view/about/about.dart b/uni/lib/view/about/about.dart index 513ff4a98..a0c17a67c 100644 --- a/uni/lib/view/about/about.dart +++ b/uni/lib/view/about/about.dart @@ -37,9 +37,4 @@ class AboutPageViewState extends SecondaryPageViewState { @override Future onRefresh(BuildContext context) async {} - - @override - Widget getTopRightButton(BuildContext context) { - return Container(); - } } From 4c641bf822f0518e3c6956e0d2d24f9473468d0c Mon Sep 17 00:00:00 2001 From: Bruno Mendes <61701401+bdmendes@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:42:30 +0000 Subject: [PATCH 051/334] Remove top right button on settings page --- uni/lib/view/settings/settings.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/uni/lib/view/settings/settings.dart b/uni/lib/view/settings/settings.dart index 8360b80d2..c1f0210a9 100644 --- a/uni/lib/view/settings/settings.dart +++ b/uni/lib/view/settings/settings.dart @@ -95,9 +95,4 @@ class SettingsPageState extends SecondaryPageViewState { @override Future onRefresh(BuildContext context) async {} - - @override - Widget getTopRightButton(BuildContext context) { - return Container(); - } } From 801b481c83670eff7da8c71a58eb649a88b52610 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Mon, 11 Dec 2023 12:25:24 +0000 Subject: [PATCH 052/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 32cc5a199..9c84debe6 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.21+211 \ No newline at end of file +1.7.22+212 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 998bf377e..045b77b6e 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.21+211 +version: 1.7.22+212 environment: sdk: ">=3.0.0 <4.0.0" From fbe2129f1a6ad5e200dddbe0f641d3644c1be88b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 12:27:35 +0000 Subject: [PATCH 053/334] Bump build_runner from 2.4.6 to 2.4.7 in /uni Bumps [build_runner](https://github.com/dart-lang/build) from 2.4.6 to 2.4.7. - [Release notes](https://github.com/dart-lang/build/releases) - [Commits](https://github.com/dart-lang/build/compare/build_runner-v2.4.6...build_runner-v2.4.7) --- updated-dependencies: - dependency-name: build_runner dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 4 ++-- uni/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index f7a45f92e..3e3464b01 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -109,10 +109,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" url: "https://pub.dev" source: hosted - version: "2.4.6" + version: "2.4.7" build_runner_core: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 045b77b6e..d21888921 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -57,7 +57,7 @@ dependencies: workmanager: ^0.5.2 dev_dependencies: - build_runner: ^2.4.6 + build_runner: ^2.4.7 flutter_launcher_icons: ^0.13.1 flutter_test: sdk: flutter From 5afe78494854dd7b51a6c3f6e23db2733eda43b5 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Wed, 13 Dec 2023 17:26:29 +0000 Subject: [PATCH 054/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 9c84debe6..69dbf75d4 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.22+212 \ No newline at end of file +1.7.23+213 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index d21888921..9b2e408d0 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.22+212 +version: 1.7.23+213 environment: sdk: ">=3.0.0 <4.0.0" From 6edba2d09e823054a391a7dbcbeec9f225c36d0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:29:51 +0000 Subject: [PATCH 055/334] Bump sentry_flutter from 7.10.1 to 7.13.2 in /uni Bumps [sentry_flutter](https://github.com/getsentry/sentry-dart) from 7.10.1 to 7.13.2. - [Release notes](https://github.com/getsentry/sentry-dart/releases) - [Changelog](https://github.com/getsentry/sentry-dart/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dart/compare/7.10.1...7.13.2) --- updated-dependencies: - dependency-name: sentry_flutter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 8 ++++---- uni/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 3e3464b01..3c82202ea 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -866,18 +866,18 @@ packages: dependency: transitive description: name: sentry - sha256: "830667eadc0398fea3a3424ed1b74568e2db603a42758d0922e2f2974ce55a60" + sha256: e7ded42974bac5f69e4ca4ddc57d30499dd79381838f24b7e8fd9aa4139e7b79 url: "https://pub.dev" source: hosted - version: "7.10.1" + version: "7.13.2" sentry_flutter: dependency: "direct main" description: name: sentry_flutter - sha256: "6730f41b304c6fb0fa590dacccaf73ba11082fc64b274cfe8a79776f2b95309c" + sha256: d6f55ec7a1f681784165021f749007712a72ff57eadf91e963331b6ae326f089 url: "https://pub.dev" source: hosted - version: "7.10.1" + version: "7.13.2" shared_preferences: dependency: "direct main" description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 9b2e408d0..7bfa9d45e 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -46,7 +46,7 @@ dependencies: path_provider: ^2.0.0 percent_indicator: ^4.2.2 provider: ^6.0.4 - sentry_flutter: ^7.9.0 + sentry_flutter: ^7.13.2 shared_preferences: ^2.2.2 shimmer: ^3.0.0 sqflite: ^2.0.3 From 36e66a78d0f0e70a101a36c773c6d59b2709ebca Mon Sep 17 00:00:00 2001 From: LuisDuarte1 Date: Sat, 16 Dec 2023 19:29:02 +0000 Subject: [PATCH 056/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 69dbf75d4..1d4f9ebc6 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.23+213 \ No newline at end of file +1.7.24+214 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 7bfa9d45e..c4722baeb 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.23+213 +version: 1.7.24+214 environment: sdk: ">=3.0.0 <4.0.0" From b05d502f50b4d05e753490c9e4f149d377f953ad Mon Sep 17 00:00:00 2001 From: vitor Date: Sun, 17 Dec 2023 18:09:00 +0000 Subject: [PATCH 057/334] Adjusted display logic for NextExamsWidget and ReaminingExamsWidget --- uni/lib/view/home/widgets/exam_card.dart | 306 +++++++----------- .../view/home/widgets/next_exams_card.dart | 48 +++ .../home/widgets/remaining_exams_card.dart | 45 +++ 3 files changed, 213 insertions(+), 186 deletions(-) create mode 100644 uni/lib/view/home/widgets/next_exams_card.dart create mode 100644 uni/lib/view/home/widgets/remaining_exams_card.dart diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index f5adcfc46..069320ba8 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -1,186 +1,120 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/model/entities/app_locale.dart'; -import 'package:uni/model/entities/exam.dart'; -import 'package:uni/model/providers/lazy/exam_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/common_widgets/date_rectangle.dart'; -import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; -import 'package:uni/view/common_widgets/row_container.dart'; -import 'package:uni/view/exams/widgets/exam_row.dart'; -import 'package:uni/view/exams/widgets/exam_title.dart'; -import 'package:uni/view/home/widgets/exam_card_shimmer.dart'; -import 'package:uni/view/lazy_consumer.dart'; -import 'package:uni/view/locale_notifier.dart'; - -/// Manages the exam card section inside the personal area. -class ExamCard extends GenericCard { - ExamCard({super.key}); - - const ExamCard.fromEditingInformation( - super.key, { - required super.editingMode, - super.onDelete, - }) : super.fromEditingInformation(); - - @override - String getTitle(BuildContext context) => - S.of(context).nav_title(DrawerItem.navExams.title); - - @override - Future onClick(BuildContext context) => - Navigator.pushNamed(context, '/${DrawerItem.navExams.title}'); - - @override - void onRefresh(BuildContext context) { - Provider.of(context, listen: false).forceRefresh(context); - } - - /// Returns a widget with all the exams card content. - /// - /// If there are no exams, a message telling the user - /// that no exams exist is displayed. - @override - Widget buildCardContent(BuildContext context) { - return LazyConsumer( - builder: (context, examProvider) { - final filteredExams = examProvider.getFilteredExams(); - final hiddenExams = examProvider.hiddenExams; - final exams = filteredExams - .where((exam) => !hiddenExams.contains(exam.id)) - .toList(); - return RequestDependentWidgetBuilder( - status: examProvider.status, - builder: () => generateExams(exams, context), - hasContentPredicate: exams.isNotEmpty, - onNullContent: Center( - child: Text( - S.of(context).no_selected_exams, - style: Theme.of(context).textTheme.titleLarge, - ), - ), - contentLoadingWidget: const ExamCardShimmer().build(context), - ); - }, - ); - } - - /// Returns a widget with all the exams. - Widget generateExams(List exams, BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: getExamRows(context, exams), - ); - } - - /// Returns true if two exams are in the same day - bool isSameDay(DateTime datetime1, DateTime datetime2) { - return datetime1.year == datetime2.year - && datetime1.month == datetime2.month - && datetime1.day == datetime2.day; - } - - /// Returns a list of widgets with the primary and secondary exams to - /// be displayed in the exam card. - List getExamRows(BuildContext context, List exams) { - final rows = []; - - rows.add(createRowFromExam(context, exams[0], isFirst: true)); - - var sameDayExamCount = exams.sublist(1).takeWhile( - (exam) => isSameDay(exam.begin, exams[0].begin), - ).toList().fold(0, (count, exam) { - rows.addAll([ - Container( - margin: const EdgeInsets.only(top: 8), - ), - createRowFromExam(context, exam), - ]); - return count + 1; - }); - - if (exams.length > 1 && sameDayExamCount > 0) { - rows.add( - Container( - margin: const EdgeInsets.only(right: 80, left: 80, top: 15, bottom: 7), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - width: 1.5, - color: Theme.of(context).dividerColor, - ), - ), - ), - ), - ); - } - - // Processing secondary exams without a loop - exams.sublist(1 + sameDayExamCount) - .take(4 - sameDayExamCount - 1) - .forEach((exam) => rows.add(createSecondaryRowFromExam(context, exam))); - - return rows; - } - - /// Creates a row with the closest exam (which appears separated from the - /// others in the card). - Widget createRowFromExam(BuildContext context, Exam exam, {bool isFirst = false}) { - final locale = Provider.of(context).getLocale(); - return Column( - children: [ - if(isFirst) ...[ - if (locale == AppLocale.pt) ...[ - DateRectangle( - date: '''${exam.weekDay(locale)}, ''' - '''${exam.begin.day} de ${exam.month(locale)}''', - ), - ] else ...[ - DateRectangle( - date: '''${exam.weekDay(locale)}, ''' - '''${exam.begin.day} ${exam.month(locale)}''', - ), - ], - ], - RowContainer( - child: ExamRow( - exam: exam, - teacher: '', - mainPage: true, - ), - ), - ], - ); - } - - /// Creates a row for the exams which will be displayed under the closest - /// date exam with a separator between them. - Widget createSecondaryRowFromExam(BuildContext context, Exam exam) { - final locale = Provider.of(context).getLocale(); - return Container( - margin: const EdgeInsets.only(top: 8), - child: RowContainer( - color: Theme.of(context).colorScheme.background, - child: Container( - padding: const EdgeInsets.all(11), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${exam.begin.day} de ${exam.month(locale)}', - style: Theme.of(context).textTheme.bodyLarge, - ), - ExamTitle( - subject: exam.subject, - type: exam.type, - reverseOrder: true, - ), - ], - ), - ), - ), - ); - } -} +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/model/entities/exam.dart'; +import 'package:uni/model/providers/lazy/exam_provider.dart'; +import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/common_widgets/generic_card.dart'; +import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; +import 'package:uni/view/home/widgets/exam_card_shimmer.dart'; +import 'package:uni/view/home/widgets/next_exams_card.dart'; +import 'package:uni/view/home/widgets/remaining_exams_card.dart'; +import 'package:uni/view/lazy_consumer.dart'; + + +class ExamCard extends GenericCard { + ExamCard({super.key}); + + const ExamCard.fromEditingInformation( + super.key, { + required super.editingMode, + super.onDelete, + }) : super.fromEditingInformation(); + + @override + String getTitle(BuildContext context) => + S.of(context).nav_title(DrawerItem.navExams.title); + + @override + Future onClick(BuildContext context) => + Navigator.pushNamed(context, '/${DrawerItem.navExams.title}'); + + @override + void onRefresh(BuildContext context) { + Provider.of(context, listen: false).forceRefresh(context); + } + + @override + Widget buildCardContent(BuildContext context) { + return LazyConsumer( + builder: (context, examProvider) { + final filteredExams = examProvider.getFilteredExams(); + final hiddenExams = examProvider.hiddenExams; + final allExams = filteredExams + .where((exam) => !hiddenExams.contains(exam.id)) + .toList(); + return RequestDependentWidgetBuilder( + status: examProvider.status, + builder: () => generateExams(allExams, context), + hasContentPredicate: allExams.isNotEmpty, + onNullContent: Center( + child: Text( + S.of(context).no_selected_exams, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + contentLoadingWidget: const ExamCardShimmer().build(context), + ); + }, + ); + } + + Widget generateExams(List allExams, BuildContext context) { + final nextExams = getPrimaryExams(allExams, + allExams.isNotEmpty ? allExams.first : null, ); + final primaryExams = NextExamsWidget(exams: nextExams); + + + final remainingExamsCount = 4 - nextExams.length; + final List remainingExams; + if (remainingExamsCount > 0) { + remainingExams = allExams + .where((exam) => !nextExams.contains(exam)) + .take(remainingExamsCount) + .toList(); + } else { + remainingExams = []; + } + final secondaryExams = RemainingExamsWidget(exams: remainingExams); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + primaryExams, + if (remainingExamsCount > 0) + Column( + children: [ + Container( + margin: + const EdgeInsets.only(right: 80, left: 80, top: 7, bottom: 7), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor, + ), + ), + ), + ), + secondaryExams, + ], + ), + ], + ); + } + + List getPrimaryExams(List allExams, Exam? nextExam) { + if (nextExam == null) { + return []; + } + + final sameDayExams = allExams.where((exam) { + final nextExamDate = + DateTime(nextExam.begin.year, nextExam.begin.month, nextExam.begin.day); + final examDate = + DateTime(exam.begin.year, exam.begin.month, exam.begin.day); + return nextExamDate.isAtSameMomentAs(examDate); + }).toList(); + + return sameDayExams; + } +} diff --git a/uni/lib/view/home/widgets/next_exams_card.dart b/uni/lib/view/home/widgets/next_exams_card.dart new file mode 100644 index 000000000..829087587 --- /dev/null +++ b/uni/lib/view/home/widgets/next_exams_card.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/model/entities/app_locale.dart'; +import 'package:uni/model/entities/exam.dart'; +import 'package:uni/view/common_widgets/date_rectangle.dart'; +import 'package:uni/view/common_widgets/row_container.dart'; +import 'package:uni/view/exams/widgets/exam_row.dart'; +import 'package:uni/view/locale_notifier.dart'; + +class NextExamsWidget extends StatelessWidget { + const NextExamsWidget({required this.exams, super.key}); + + final List exams; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + DateRectangle( + date: exams.isNotEmpty ? getFormattedDate(exams.first, context) : '', + ), + const SizedBox(height: 8), + Column( + children: exams.map((exam) { + return Padding( + padding: const EdgeInsets.only(bottom: 8), + child: RowContainer( + child: ExamRow( + exam: exam, + teacher: '', + mainPage: true, + ), + ), + ); + }).toList(), + ), + ], + ); + } + + String getFormattedDate(Exam exam, BuildContext context) { + final locale = Provider.of(context).getLocale(); + return locale == AppLocale.pt + ? '${exam.weekDay(locale)}, ${exam.begin.day} de ${exam.month(locale)}' + : '${exam.weekDay(locale)}, ${exam.begin.day} ${exam.month(locale)}'; + } +} diff --git a/uni/lib/view/home/widgets/remaining_exams_card.dart b/uni/lib/view/home/widgets/remaining_exams_card.dart new file mode 100644 index 000000000..610f342db --- /dev/null +++ b/uni/lib/view/home/widgets/remaining_exams_card.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/model/entities/exam.dart'; +import 'package:uni/view/common_widgets/row_container.dart'; +import 'package:uni/view/exams/widgets/exam_title.dart'; +import 'package:uni/view/locale_notifier.dart'; + +class RemainingExamsWidget extends StatelessWidget { + + const RemainingExamsWidget({required this.exams, super.key}); + final List exams; + + @override + Widget build(BuildContext context) { + + return Column( + children: exams.map((exam) { + final locale = Provider.of(context).getLocale(); + return Container( + margin: const EdgeInsets.only(top: 8), + child: RowContainer( + color: Theme.of(context).colorScheme.background, + child: Container( + padding: const EdgeInsets.all(11), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '${exam.begin.day} de ${exam.month(locale)}', + style: Theme.of(context).textTheme.bodyLarge, + ), + ExamTitle( + subject: exam.subject, + type: exam.type, + reverseOrder: true, + ), + ], + ), + ), + ), + ); + }).toList(), + ); + } +} From 724549a5640460fdbb7e3364f16fffa5165e2648 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Mon, 18 Dec 2023 16:44:12 +0000 Subject: [PATCH 058/334] Tracking banner --- .../local_storage/app_shared_preferences.dart | 14 +++++++ uni/lib/view/home/home.dart | 28 ++++++++++++- .../view/home/widgets/tracking_banner.dart | 41 +++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 uni/lib/view/home/widgets/tracking_banner.dart diff --git a/uni/lib/controller/local_storage/app_shared_preferences.dart b/uni/lib/controller/local_storage/app_shared_preferences.dart index e2b60e5d7..5881f95b2 100644 --- a/uni/lib/controller/local_storage/app_shared_preferences.dart +++ b/uni/lib/controller/local_storage/app_shared_preferences.dart @@ -27,6 +27,8 @@ class AppSharedPreferences { static const String tuitionNotificationsToggleKey = 'tuition_notification_toogle'; static const String usageStatsToggleKey = 'usage_stats_toogle'; + static const String isDataCollectionBannerViewedKey = + 'data_collection_banner'; static const String themeMode = 'theme_mode'; static const String locale = 'app_locale'; static const String favoriteCards = 'favorite_cards'; @@ -88,6 +90,18 @@ class AppSharedPreferences { return prefs.getBool(areTermsAndConditionsAcceptedKey) ?? false; } + static Future setDataCollectionBannerViewed({ + required bool isViewed, + }) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setBool(isDataCollectionBannerViewedKey, true); + } + + static Future isDataCollectionBannerViewed() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getBool(isDataCollectionBannerViewedKey) ?? false; + } + /// Returns the hash of the last Terms and Conditions that have /// been accepted by the user. static Future getTermsAndConditionHash() async { diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index b5c5d9d99..3e735876c 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/app_shared_preferences.dart'; import 'package:uni/model/providers/lazy/home_page_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/home/widgets/main_cards_list.dart'; +import 'package:uni/view/home/widgets/tracking_banner.dart'; class HomePageView extends StatefulWidget { const HomePageView({super.key}); @@ -13,9 +15,33 @@ class HomePageView extends StatefulWidget { /// Tracks the state of Home page. class HomePageViewState extends GeneralPageViewState { + bool? isBannerViewed; + + @override + void initState() { + super.initState(); + checkBannerViewed(); + } + + Future checkBannerViewed() async { + isBannerViewed = await AppSharedPreferences.isDataCollectionBannerViewed(); + await AppSharedPreferences.setDataCollectionBannerViewed(isViewed: true); + setState(() {}); + } + @override Widget getBody(BuildContext context) { - return const MainCardsList(); + return (isBannerViewed ?? false) + ? const MainCardsList() + : const Column( + children: [ + BannerWidget(), + Expanded( + // Add this + child: MainCardsList(), + ), + ], + ); } @override diff --git a/uni/lib/view/home/widgets/tracking_banner.dart b/uni/lib/view/home/widgets/tracking_banner.dart new file mode 100644 index 000000000..b4c1063c7 --- /dev/null +++ b/uni/lib/view/home/widgets/tracking_banner.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:uni/view/settings/settings.dart'; + +class BannerWidget extends StatelessWidget { + const BannerWidget({super.key}); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const SettingsPage(), + ), + ); + }, + child: Container( + margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 20), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: Colors.white, + border: Border.all( + color: Theme.of(context).primaryColor, + width: 0.5, + ), + borderRadius: BorderRadius.circular(10), + ), + child: Center( + child: Text( + 'We do now collect anonymous usage statistics in order to improve ' + 'your experience. You can change it by clicking here.', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Theme.of(context).primaryColor, + ), + ), + ), + ), + ); + } +} From 29c0fde51aa4ec9c87c7024124ccc96c3ebf054c Mon Sep 17 00:00:00 2001 From: thePeras Date: Mon, 18 Dec 2023 18:52:46 +0000 Subject: [PATCH 059/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 1d4f9ebc6..76f0b73fc 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.24+214 \ No newline at end of file +1.7.25+215 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index c4722baeb..9e2d230f6 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.24+214 +version: 1.7.25+215 environment: sdk: ">=3.0.0 <4.0.0" From aecbf18020cd24f22dc667f1571c8480cce2a9c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 18:58:26 +0000 Subject: [PATCH 060/334] Bump url_launcher from 6.1.14 to 6.2.2 in /uni Bumps [url_launcher](https://github.com/flutter/packages/tree/main/packages/url_launcher) from 6.1.14 to 6.2.2. - [Release notes](https://github.com/flutter/packages/releases) - [Commits](https://github.com/flutter/packages/commits/url_launcher-v6.2.2/packages/url_launcher) --- updated-dependencies: - dependency-name: url_launcher dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 32 ++++++++++++++++---------------- uni/pubspec.yaml | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 3c82202ea..4a8b7828e 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -1151,66 +1151,66 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 url: "https://pub.dev" source: hosted - version: "6.1.14" + version: "6.2.2" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: b04af59516ab45762b2ca6da40fa830d72d0f6045cd97744450b73493fa76330 + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.2.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: "7c65021d5dee51813d652357bc65b8dd4a6177082a9966bc8ba6ee477baa795f" + sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.2.1" url_launcher_linux: dependency: transitive description: name: url_launcher_linux - sha256: b651aad005e0cb06a01dbd84b428a301916dc75f0e7ea6165f80057fee2d8e8e + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.1.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos - sha256: b55486791f666e62e0e8ff825e58a023fd6b1f71c49926483f1128d3bbd8fe88 + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "3.1.0" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface - sha256: "95465b39f83bfe95fcb9d174829d6476216f2d548b79c38ab2506e0458787618" + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" url: "https://pub.dev" source: hosted - version: "2.1.5" + version: "2.2.0" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: ba140138558fcc3eead51a1c42e92a9fb074a1b1149ed3c73e66035b2ccd94f2 + sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" url: "https://pub.dev" source: hosted - version: "2.0.19" + version: "2.2.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "95fef3129dc7cfaba2bc3d5ba2e16063bb561fc6d78e63eee16162bc70029069" + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.1.1" uuid: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 9e2d230f6..1a41d90ab 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -53,7 +53,7 @@ dependencies: synchronized: ^3.0.0 timelines: ^0.1.0 tuple: ^2.0.0 - url_launcher: ^6.0.2 + url_launcher: ^6.2.2 workmanager: ^0.5.2 dev_dependencies: From b488d6fe1221f8f58fbe51ad4baf654d68c6a7d5 Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 22 Dec 2023 16:50:01 +0000 Subject: [PATCH 061/334] Resolved Dart formatting issue --- uni/lib/view/home/widgets/exam_card.dart | 26 +++++++++---------- .../home/widgets/remaining_exams_card.dart | 2 -- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index 069320ba8..d1a93700d 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -11,15 +11,14 @@ import 'package:uni/view/home/widgets/next_exams_card.dart'; import 'package:uni/view/home/widgets/remaining_exams_card.dart'; import 'package:uni/view/lazy_consumer.dart'; - class ExamCard extends GenericCard { ExamCard({super.key}); const ExamCard.fromEditingInformation( - super.key, { - required super.editingMode, - super.onDelete, - }) : super.fromEditingInformation(); + super.key, { + required super.editingMode, + super.onDelete, + }) : super.fromEditingInformation(); @override String getTitle(BuildContext context) => @@ -60,11 +59,12 @@ class ExamCard extends GenericCard { } Widget generateExams(List allExams, BuildContext context) { - final nextExams = getPrimaryExams(allExams, - allExams.isNotEmpty ? allExams.first : null, ); + final nextExams = getPrimaryExams( + allExams, + allExams.isNotEmpty ? allExams.first : null, + ); final primaryExams = NextExamsWidget(exams: nextExams); - final remainingExamsCount = 4 - nextExams.length; final List remainingExams; if (remainingExamsCount > 0) { @@ -85,8 +85,8 @@ class ExamCard extends GenericCard { Column( children: [ Container( - margin: - const EdgeInsets.only(right: 80, left: 80, top: 7, bottom: 7), + margin: const EdgeInsets.only( + right: 80, left: 80, top: 7, bottom: 7), decoration: BoxDecoration( border: Border( bottom: BorderSide( @@ -108,10 +108,10 @@ class ExamCard extends GenericCard { } final sameDayExams = allExams.where((exam) { - final nextExamDate = - DateTime(nextExam.begin.year, nextExam.begin.month, nextExam.begin.day); + final nextExamDate = DateTime( + nextExam.begin.year, nextExam.begin.month, nextExam.begin.day); final examDate = - DateTime(exam.begin.year, exam.begin.month, exam.begin.day); + DateTime(exam.begin.year, exam.begin.month, exam.begin.day); return nextExamDate.isAtSameMomentAs(examDate); }).toList(); diff --git a/uni/lib/view/home/widgets/remaining_exams_card.dart b/uni/lib/view/home/widgets/remaining_exams_card.dart index 610f342db..8ae7230de 100644 --- a/uni/lib/view/home/widgets/remaining_exams_card.dart +++ b/uni/lib/view/home/widgets/remaining_exams_card.dart @@ -6,13 +6,11 @@ import 'package:uni/view/exams/widgets/exam_title.dart'; import 'package:uni/view/locale_notifier.dart'; class RemainingExamsWidget extends StatelessWidget { - const RemainingExamsWidget({required this.exams, super.key}); final List exams; @override Widget build(BuildContext context) { - return Column( children: exams.map((exam) { final locale = Provider.of(context).getLocale(); From 896e9ef8947ca5937f5268d3d78e4e5ca587300c Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 22 Dec 2023 16:56:52 +0000 Subject: [PATCH 062/334] Fixed missing required trailing commas --- uni/lib/view/home/widgets/exam_card.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index d1a93700d..aed4346e8 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -86,7 +86,11 @@ class ExamCard extends GenericCard { children: [ Container( margin: const EdgeInsets.only( - right: 80, left: 80, top: 7, bottom: 7), + right: 80, + left: 80, + top: 7, + bottom: 7, + ), decoration: BoxDecoration( border: Border( bottom: BorderSide( @@ -109,7 +113,10 @@ class ExamCard extends GenericCard { final sameDayExams = allExams.where((exam) { final nextExamDate = DateTime( - nextExam.begin.year, nextExam.begin.month, nextExam.begin.day); + nextExam.begin.year, + nextExam.begin.month, + nextExam.begin.day, + ); final examDate = DateTime(exam.begin.year, exam.begin.month, exam.begin.day); return nextExamDate.isAtSameMomentAs(examDate); From 6916f9bd808c0b1d3ceb369370310cd69210d511 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 23 Dec 2023 23:12:47 +0000 Subject: [PATCH 063/334] New banner design and translation --- uni/lib/generated/intl/messages_en.dart | 2 + uni/lib/generated/intl/messages_pt_PT.dart | 4 +- uni/lib/generated/l10n.dart | 10 ++++ uni/lib/l10n/intl_en.arb | 2 + uni/lib/l10n/intl_pt_PT.arb | 4 +- uni/lib/view/home/home.dart | 22 ++++---- .../view/home/widgets/tracking_banner.dart | 50 ++++++++----------- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- 8 files changed, 55 insertions(+), 41 deletions(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index aec972737..98393affa 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -59,6 +59,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Available amount"), "average": MessageLookupByLibrary.simpleMessage("Average: "), "balance": MessageLookupByLibrary.simpleMessage("Balance:"), + "banner_info": MessageLookupByLibrary.simpleMessage( + "We do now collect anonymous usage statistics in order to improve your experience. You can change it in settings."), "bs_description": MessageLookupByLibrary.simpleMessage( "Did you find any bugs in the application?\nDo you have any suggestions for the app?\nTell us so we can improve!"), "bug_description": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index bb6a83237..8ac0ffcc4 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -59,6 +59,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Valor disponível"), "average": MessageLookupByLibrary.simpleMessage("Média: "), "balance": MessageLookupByLibrary.simpleMessage("Saldo:"), + "banner_info": MessageLookupByLibrary.simpleMessage( + "Agora recolhemos estatísticas de uso anónimas para melhorar a tua experiência. Podes alterá-lo nas definições."), "bs_description": MessageLookupByLibrary.simpleMessage( "Encontraste algum bug na aplicação?\nTens alguma sugestão para a app?\nConta-nos para que possamos melhorar!"), "bug_description": MessageLookupByLibrary.simpleMessage( @@ -110,7 +112,7 @@ class MessageLookup extends MessageLookupByLibrary { "dona_bia_building": MessageLookupByLibrary.simpleMessage( "Piso -1 do edifício B (B-142)"), "ects": MessageLookupByLibrary.simpleMessage("ECTS realizados: "), - "edit_off": MessageLookupByLibrary.simpleMessage("Editar\n"), + "edit_off": MessageLookupByLibrary.simpleMessage("Editar"), "edit_on": MessageLookupByLibrary.simpleMessage("Concluir edição"), "empty_text": MessageLookupByLibrary.simpleMessage( "Por favor preenche este campo"), diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index deaa16010..68940ff06 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -230,6 +230,16 @@ class S { ); } + /// `We do now collect anonymous usage statistics in order to improve your experience. You can change it in settings.` + String get banner_info { + return Intl.message( + 'We do now collect anonymous usage statistics in order to improve your experience. You can change it in settings.', + name: 'banner_info', + desc: '', + args: [], + ); + } + /// `Balance:` String get balance { return Intl.message( diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 501c6d1d8..2edaa1d93 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -36,6 +36,8 @@ "@available_amount": {}, "average": "Average: ", "@average": {}, + "banner_info": "We do now collect anonymous usage statistics in order to improve your experience. You can change it in settings.", + "@banner_info": {}, "balance": "Balance:", "@balance": {}, "bs_description": "Did you find any bugs in the application?\nDo you have any suggestions for the app?\nTell us so we can improve!", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 5e42488f3..5d9e9a2ec 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -38,6 +38,8 @@ "@available_amount": {}, "average": "Média: ", "@average": {}, + "banner_info": "Agora recolhemos estatísticas de uso anónimas para melhorar a tua experiência. Podes alterá-lo nas definições.", + "@banner_info": {}, "balance": "Saldo:", "@balance": {}, "bs_description": "Encontraste algum bug na aplicação?\nTens alguma sugestão para a app?\nConta-nos para que possamos melhorar!", @@ -100,7 +102,7 @@ "@dona_bia_building": {}, "ects": "ECTS realizados: ", "@ects": {}, - "edit_off": "Editar\n", + "edit_off": "Editar", "@edit_off": {}, "edit_on": "Concluir edição", "@edit_on": {}, diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 3e735876c..25cba7a57 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -13,9 +13,8 @@ class HomePageView extends StatefulWidget { State createState() => HomePageViewState(); } -/// Tracks the state of Home page. class HomePageViewState extends GeneralPageViewState { - bool? isBannerViewed; + bool isBannerViewed = true; @override void initState() { @@ -24,20 +23,25 @@ class HomePageViewState extends GeneralPageViewState { } Future checkBannerViewed() async { - isBannerViewed = await AppSharedPreferences.isDataCollectionBannerViewed(); + final pref = await AppSharedPreferences.isDataCollectionBannerViewed(); + setState(() { + isBannerViewed = pref; + }); + } + + Future setBannerViewed() async { await AppSharedPreferences.setDataCollectionBannerViewed(isViewed: true); - setState(() {}); + await checkBannerViewed(); } @override Widget getBody(BuildContext context) { - return (isBannerViewed ?? false) + return isBannerViewed ? const MainCardsList() - : const Column( + : Column( children: [ - BannerWidget(), - Expanded( - // Add this + BannerWidget(setBannerViewed), + const Expanded( child: MainCardsList(), ), ], diff --git a/uni/lib/view/home/widgets/tracking_banner.dart b/uni/lib/view/home/widgets/tracking_banner.dart index b4c1063c7..c66674311 100644 --- a/uni/lib/view/home/widgets/tracking_banner.dart +++ b/uni/lib/view/home/widgets/tracking_banner.dart @@ -1,40 +1,32 @@ import 'package:flutter/material.dart'; -import 'package:uni/view/settings/settings.dart'; +import 'package:uni/generated/l10n.dart'; class BannerWidget extends StatelessWidget { - const BannerWidget({super.key}); + const BannerWidget(this.onDismiss, {super.key}); + + final void Function() onDismiss; @override Widget build(BuildContext context) { - return GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SettingsPage(), - ), - ); - }, - child: Container( - margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 20), - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), - decoration: BoxDecoration( - color: Colors.white, - border: Border.all( - color: Theme.of(context).primaryColor, - width: 0.5, - ), - borderRadius: BorderRadius.circular(10), + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Theme.of(context).primaryColor, + ), + margin: const EdgeInsets.all(10), + child: MaterialBanner( + padding: const EdgeInsets.all(15), + content: Text(S.of(context).banner_info, + style: const TextStyle(color: Colors.white), ), - child: Center( - child: Text( - 'We do now collect anonymous usage statistics in order to improve ' - 'your experience. You can change it by clicking here.', - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: Theme.of(context).primaryColor, - ), + backgroundColor: + Colors.transparent, + actions: [ + TextButton( + onPressed: onDismiss, + child: const Text('OK', style: TextStyle(color: Colors.white)), ), - ), + ], ), ); } diff --git a/uni/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/uni/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 4b4bb754d..00a12043d 100644 --- a/uni/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/uni/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Date: Sat, 23 Dec 2023 23:15:40 +0000 Subject: [PATCH 064/334] Formatting --- uni/lib/view/home/home.dart | 2 +- uni/lib/view/home/widgets/tracking_banner.dart | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 25cba7a57..13fc9ea33 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -41,7 +41,7 @@ class HomePageViewState extends GeneralPageViewState { : Column( children: [ BannerWidget(setBannerViewed), - const Expanded( + const Expanded( child: MainCardsList(), ), ], diff --git a/uni/lib/view/home/widgets/tracking_banner.dart b/uni/lib/view/home/widgets/tracking_banner.dart index c66674311..52204ae7e 100644 --- a/uni/lib/view/home/widgets/tracking_banner.dart +++ b/uni/lib/view/home/widgets/tracking_banner.dart @@ -16,11 +16,11 @@ class BannerWidget extends StatelessWidget { margin: const EdgeInsets.all(10), child: MaterialBanner( padding: const EdgeInsets.all(15), - content: Text(S.of(context).banner_info, + content: Text( + S.of(context).banner_info, style: const TextStyle(color: Colors.white), ), - backgroundColor: - Colors.transparent, + backgroundColor: Colors.transparent, actions: [ TextButton( onPressed: onDismiss, From 54c3cc9f44f06a9dc9d53de7f214ae7dbe3127c7 Mon Sep 17 00:00:00 2001 From: Diogo Goiana <81827192+DGoiana@users.noreply.github.com> Date: Sat, 23 Dec 2023 23:16:31 +0000 Subject: [PATCH 065/334] Deleting useless file --- .../xcshareddata/xcschemes/Runner.xcscheme | 87 ------------------- 1 file changed, 87 deletions(-) delete mode 100644 uni/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/uni/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/uni/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 00a12043d..000000000 --- a/uni/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 26116bfd280d5468c0ab23ce391e2ca837a42448 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Mon, 25 Dec 2023 23:50:42 +0000 Subject: [PATCH 066/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 76f0b73fc..93343b291 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.25+215 \ No newline at end of file +1.7.26+216 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 1a41d90ab..7fedecbef 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.25+215 +version: 1.7.26+216 environment: sdk: ">=3.0.0 <4.0.0" From 97a30abea0f90efb1531038645ec6b15cca4925a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Dec 2023 18:55:19 +0000 Subject: [PATCH 067/334] Bump provider from 6.0.5 to 6.1.1 in /uni Bumps [provider](https://github.com/rrousselGit/provider) from 6.0.5 to 6.1.1. - [Commits](https://github.com/rrousselGit/provider/compare/provider-v6.0.5...provider-v6.1.1) --- updated-dependencies: - dependency-name: provider dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 4 ++-- uni/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 4a8b7828e..862d99468 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -834,10 +834,10 @@ packages: dependency: "direct main" description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.1" pub_semver: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 7fedecbef..93b1566a0 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -45,7 +45,7 @@ dependencies: path: ^1.8.0 path_provider: ^2.0.0 percent_indicator: ^4.2.2 - provider: ^6.0.4 + provider: ^6.1.1 sentry_flutter: ^7.13.2 shared_preferences: ^2.2.2 shimmer: ^3.0.0 From 8c0903540721a8816f2c21d620237d425850c0f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 23:52:47 +0000 Subject: [PATCH 068/334] Bump add_2_calendar from 2.2.5 to 3.0.1 in /uni Bumps [add_2_calendar](https://github.com/ja2375/add_2_calendar) from 2.2.5 to 3.0.1. - [Release notes](https://github.com/ja2375/add_2_calendar/releases) - [Changelog](https://github.com/ja2375/add_2_calendar/blob/master/CHANGELOG.md) - [Commits](https://github.com/ja2375/add_2_calendar/commits) --- updated-dependencies: - dependency-name: add_2_calendar dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 4 ++-- uni/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 862d99468..4d23b1bce 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: "direct main" description: name: add_2_calendar - sha256: dbcd0bf296fbbe00861a6f101af8cdb3c163a8c3ff5d3c99a4b081c2f37c724f + sha256: "8d7a82aba607d35f2a5bc913419e12f865a96a350a8ad2509a59322bc161f200" url: "https://pub.dev" source: hosted - version: "2.2.5" + version: "3.0.1" analyzer: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 93b1566a0..0d468489e 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -14,7 +14,7 @@ environment: flutter: 3.13.7 dependencies: - add_2_calendar: ^2.2.5 + add_2_calendar: ^3.0.1 cached_network_image: ^3.2.3 collection: ^1.16.0 connectivity_plus: ^5.0.2 From 6d4f860a09b01778dc22cbfc49b8babe638170fa Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Thu, 14 Dec 2023 16:43:05 +0000 Subject: [PATCH 069/334] Bind providers to a model state --- .../background_workers/notifications.dart | 6 +- .../notifications/tuition_notification.dart | 4 +- uni/lib/controller/cleanup.dart | 20 +-- .../terms_and_conditions_fetcher.dart} | 22 ++- .../{ => database}/app_bus_stop_database.dart | 2 +- .../{ => database}/app_calendar_database.dart | 2 +- .../app_course_units_database.dart | 2 +- .../{ => database}/app_courses_database.dart | 2 +- .../{ => database}/app_database.dart | 0 .../{ => database}/app_exams_database.dart | 2 +- .../app_last_user_info_update_database.dart | 2 +- .../{ => database}/app_lectures_database.dart | 2 +- .../app_library_occupation_database.dart | 2 +- .../app_references_database.dart | 2 +- .../app_restaurant_database.dart | 2 +- .../{ => database}/app_user_database.dart | 2 +- ...ences.dart => preferences_controller.dart} | 70 +++----- .../controller/networking/network_router.dart | 4 +- uni/lib/main.dart | 26 +-- .../providers/lazy/bus_stop_provider.dart | 63 ++++--- .../providers/lazy/calendar_provider.dart | 28 ++-- .../lazy/course_units_info_provider.dart | 32 ++-- .../model/providers/lazy/exam_provider.dart | 48 ++---- .../lazy/faculty_locations_provider.dart | 23 ++- .../providers/lazy/home_page_provider.dart | 38 ----- .../providers/lazy/lecture_provider.dart | 28 ++-- .../lazy/library_occupation_provider.dart | 31 ++-- .../providers/lazy/reference_provider.dart | 31 ++-- .../providers/lazy/restaurant_provider.dart | 51 ++---- .../providers/startup/profile_provider.dart | 126 +++++++------- .../providers/startup/session_provider.dart | 58 ++----- .../providers/state_provider_notifier.dart | 154 +++++++++--------- uni/lib/model/providers/state_providers.dart | 31 ++-- .../about/widgets/terms_and_conditions.dart | 2 +- uni/lib/view/bug_report/widgets/form.dart | 4 +- .../bus_stop_next_arrivals.dart | 3 +- .../widgets/estimated_arrival_timestamp.dart | 3 +- .../bus_stop_selection.dart | 4 +- .../widgets/bus_stop_search.dart | 2 +- .../view/bus_stop_selection/widgets/form.dart | 5 +- uni/lib/view/calendar/calendar.dart | 6 +- .../common_widgets/last_update_timestamp.dart | 4 +- .../pages_layouts/general/general.dart | 6 +- .../general/widgets/navigation_drawer.dart | 2 +- .../course_unit_info/course_unit_info.dart | 2 +- .../widgets/course_unit_classes.dart | 3 +- uni/lib/view/course_units/course_units.dart | 4 +- uni/lib/view/exams/exams.dart | 26 ++- .../view/exams/widgets/exam_filter_form.dart | 6 +- .../view/exams/widgets/exam_filter_menu.dart | 3 +- uni/lib/view/exams/widgets/exam_row.dart | 29 ++-- uni/lib/view/home/home.dart | 17 +- uni/lib/view/home/widgets/bus_stop_card.dart | 4 +- uni/lib/view/home/widgets/exam_card.dart | 15 +- .../view/home/widgets/main_cards_list.dart | 149 ++++++++--------- .../view/home/widgets/restaurant_card.dart | 7 +- uni/lib/view/home/widgets/schedule_card.dart | 6 +- uni/lib/view/lazy_consumer.dart | 23 +-- uni/lib/view/library/library.dart | 6 +- .../widgets/library_occupation_card.dart | 6 +- uni/lib/view/locale_notifier.dart | 4 +- uni/lib/view/locations/locations.dart | 4 +- uni/lib/view/profile/profile.dart | 2 +- .../profile/widgets/account_info_card.dart | 4 +- .../widgets/create_print_mb_dialog.dart | 2 +- .../view/profile/widgets/print_info_card.dart | 2 +- .../profile/widgets/profile_overview.dart | 78 +++++---- .../view/restaurant/restaurant_page_view.dart | 6 +- .../widgets/restaurant_page_card.dart | 37 +++-- uni/lib/view/schedule/schedule.dart | 4 +- .../widgets/tuition_notification_switch.dart | 18 +- .../settings/widgets/usage_stats_switch.dart | 17 +- uni/lib/view/terms_and_condition_dialog.dart | 8 +- uni/lib/view/theme_notifier.dart | 4 +- uni/test/integration/src/exams_page_test.dart | 15 +- .../integration/src/schedule_page_test.dart | 2 +- .../unit/providers/exams_provider_test.dart | 16 +- .../unit/providers/lecture_provider_test.dart | 4 +- .../unit/view/Pages/exams_page_view_test.dart | 20 +-- 79 files changed, 671 insertions(+), 839 deletions(-) rename uni/lib/controller/{load_static/terms_and_conditions.dart => fetchers/terms_and_conditions_fetcher.dart} (67%) rename uni/lib/controller/local_storage/{ => database}/app_bus_stop_database.dart (97%) rename uni/lib/controller/local_storage/{ => database}/app_calendar_database.dart (93%) rename uni/lib/controller/local_storage/{ => database}/app_course_units_database.dart (96%) rename uni/lib/controller/local_storage/{ => database}/app_courses_database.dart (97%) rename uni/lib/controller/local_storage/{ => database}/app_database.dart (100%) rename uni/lib/controller/local_storage/{ => database}/app_exams_database.dart (97%) rename uni/lib/controller/local_storage/{ => database}/app_last_user_info_update_database.dart (94%) rename uni/lib/controller/local_storage/{ => database}/app_lectures_database.dart (97%) rename uni/lib/controller/local_storage/{ => database}/app_library_occupation_database.dart (94%) rename uni/lib/controller/local_storage/{ => database}/app_references_database.dart (96%) rename uni/lib/controller/local_storage/{ => database}/app_restaurant_database.dart (98%) rename uni/lib/controller/local_storage/{ => database}/app_user_database.dart (97%) rename uni/lib/controller/local_storage/{app_shared_preferences.dart => preferences_controller.dart} (75%) delete mode 100644 uni/lib/model/providers/lazy/home_page_provider.dart diff --git a/uni/lib/controller/background_workers/notifications.dart b/uni/lib/controller/background_workers/notifications.dart index b09b0365a..ae5934de4 100644 --- a/uni/lib/controller/background_workers/notifications.dart +++ b/uni/lib/controller/background_workers/notifications.dart @@ -7,8 +7,8 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:logger/logger.dart'; import 'package:tuple/tuple.dart'; import 'package:uni/controller/background_workers/notifications/tuition_notification.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; import 'package:uni/controller/local_storage/notification_timeout_storage.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/model/entities/session.dart'; import 'package:workmanager/workmanager.dart'; @@ -68,8 +68,8 @@ class NotificationManager { static const Duration _notificationWorkerPeriod = Duration(hours: 1); static Future updateAndTriggerNotifications() async { - final userInfo = await AppSharedPreferences.getPersistentUserInfo(); - final faculties = await AppSharedPreferences.getUserFaculties(); + final userInfo = PreferencesController.getPersistentUserInfo(); + final faculties = PreferencesController.getUserFaculties(); if (userInfo == null || faculties.isEmpty) { return; diff --git a/uni/lib/controller/background_workers/notifications/tuition_notification.dart b/uni/lib/controller/background_workers/notifications/tuition_notification.dart index 738b5ce24..44411dabb 100644 --- a/uni/lib/controller/background_workers/notifications/tuition_notification.dart +++ b/uni/lib/controller/background_workers/notifications/tuition_notification.dart @@ -2,7 +2,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:tuple/tuple.dart'; import 'package:uni/controller/background_workers/notifications.dart'; import 'package:uni/controller/fetchers/fees_fetcher.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/controller/parsers/parser_fees.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/utils/duration_string_formatter.dart'; @@ -53,7 +53,7 @@ class TuitionNotification extends Notification { @override Future shouldDisplay(Session session) async { final notificationsAreDisabled = - !(await AppSharedPreferences.getTuitionNotificationToggle()); + !PreferencesController.getTuitionNotificationToggle(); if (notificationsAreDisabled) return false; final feesFetcher = FeesFetcher(); final dueDate = parseFeesNextLimit( diff --git a/uni/lib/controller/cleanup.dart b/uni/lib/controller/cleanup.dart index 11bea65d2..71c975533 100644 --- a/uni/lib/controller/cleanup.dart +++ b/uni/lib/controller/cleanup.dart @@ -4,22 +4,22 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:uni/controller/local_storage/app_bus_stop_database.dart'; -import 'package:uni/controller/local_storage/app_course_units_database.dart'; -import 'package:uni/controller/local_storage/app_courses_database.dart'; -import 'package:uni/controller/local_storage/app_exams_database.dart'; -import 'package:uni/controller/local_storage/app_last_user_info_update_database.dart'; -import 'package:uni/controller/local_storage/app_lectures_database.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; -import 'package:uni/controller/local_storage/app_user_database.dart'; +import 'package:uni/controller/local_storage/database/app_bus_stop_database.dart'; +import 'package:uni/controller/local_storage/database/app_course_units_database.dart'; +import 'package:uni/controller/local_storage/database/app_courses_database.dart'; +import 'package:uni/controller/local_storage/database/app_exams_database.dart'; +import 'package:uni/controller/local_storage/database/app_last_user_info_update_database.dart'; +import 'package:uni/controller/local_storage/database/app_lectures_database.dart'; +import 'package:uni/controller/local_storage/database/app_user_database.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/model/providers/state_providers.dart'; Future cleanupStoredData(BuildContext context) async { - StateProviders.fromContext(context).markAsNotInitialized(); + StateProviders.fromContext(context).invalidate(); final prefs = await SharedPreferences.getInstance(); - final faculties = await AppSharedPreferences.getUserFaculties(); + final faculties = PreferencesController.getUserFaculties(); await prefs.clear(); unawaited( diff --git a/uni/lib/controller/load_static/terms_and_conditions.dart b/uni/lib/controller/fetchers/terms_and_conditions_fetcher.dart similarity index 67% rename from uni/lib/controller/load_static/terms_and_conditions.dart rename to uni/lib/controller/fetchers/terms_and_conditions_fetcher.dart index 590d3bf51..151327da4 100644 --- a/uni/lib/controller/load_static/terms_and_conditions.dart +++ b/uni/lib/controller/fetchers/terms_and_conditions_fetcher.dart @@ -2,7 +2,7 @@ import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:flutter/services.dart' show rootBundle; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; /// Returns the content of the Terms and Conditions remote file, /// or the local one if the remote file is not available. @@ -19,27 +19,29 @@ Future fetchTermsAndConditions() async { /// or true if they haven't. /// Returns the updated value. Future updateTermsAndConditionsAcceptancePreference() async { - final hash = await AppSharedPreferences.getTermsAndConditionHash(); + final hash = PreferencesController.getTermsAndConditionHash(); final termsAndConditions = await fetchTermsAndConditions(); final currentHash = md5.convert(utf8.encode(termsAndConditions)).toString(); if (hash == null) { - await AppSharedPreferences.setTermsAndConditionsAcceptance( + await PreferencesController.setTermsAndConditionsAcceptance( areAccepted: true, ); - await AppSharedPreferences.setTermsAndConditionHash(currentHash); + await PreferencesController.setTermsAndConditionHash(currentHash); return true; } if (currentHash != hash) { - await AppSharedPreferences.setTermsAndConditionsAcceptance( + await PreferencesController.setTermsAndConditionsAcceptance( areAccepted: false, ); - await AppSharedPreferences.setTermsAndConditionHash(currentHash); + await PreferencesController.setTermsAndConditionHash(currentHash); return false; } - await AppSharedPreferences.setTermsAndConditionsAcceptance(areAccepted: true); + await PreferencesController.setTermsAndConditionsAcceptance( + areAccepted: true, + ); return true; } @@ -47,6 +49,8 @@ Future updateTermsAndConditionsAcceptancePreference() async { Future acceptTermsAndConditions() async { final termsAndConditions = await fetchTermsAndConditions(); final currentHash = md5.convert(utf8.encode(termsAndConditions)).toString(); - await AppSharedPreferences.setTermsAndConditionHash(currentHash); - await AppSharedPreferences.setTermsAndConditionsAcceptance(areAccepted: true); + await PreferencesController.setTermsAndConditionHash(currentHash); + await PreferencesController.setTermsAndConditionsAcceptance( + areAccepted: true, + ); } diff --git a/uni/lib/controller/local_storage/app_bus_stop_database.dart b/uni/lib/controller/local_storage/database/app_bus_stop_database.dart similarity index 97% rename from uni/lib/controller/local_storage/app_bus_stop_database.dart rename to uni/lib/controller/local_storage/database/app_bus_stop_database.dart index 7d53884d4..357aefd43 100644 --- a/uni/lib/controller/local_storage/app_bus_stop_database.dart +++ b/uni/lib/controller/local_storage/database/app_bus_stop_database.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:sqflite/sqflite.dart'; -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; import 'package:uni/model/entities/bus_stop.dart'; /// Manages the app's Bus Stops database. diff --git a/uni/lib/controller/local_storage/app_calendar_database.dart b/uni/lib/controller/local_storage/database/app_calendar_database.dart similarity index 93% rename from uni/lib/controller/local_storage/app_calendar_database.dart rename to uni/lib/controller/local_storage/database/app_calendar_database.dart index b8674e2b9..6e05891b6 100644 --- a/uni/lib/controller/local_storage/app_calendar_database.dart +++ b/uni/lib/controller/local_storage/database/app_calendar_database.dart @@ -1,4 +1,4 @@ -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; import 'package:uni/model/entities/calendar_event.dart'; class CalendarDatabase extends AppDatabase { diff --git a/uni/lib/controller/local_storage/app_course_units_database.dart b/uni/lib/controller/local_storage/database/app_course_units_database.dart similarity index 96% rename from uni/lib/controller/local_storage/app_course_units_database.dart rename to uni/lib/controller/local_storage/database/app_course_units_database.dart index c9968845a..db82545f2 100644 --- a/uni/lib/controller/local_storage/app_course_units_database.dart +++ b/uni/lib/controller/local_storage/database/app_course_units_database.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:sqflite/sqflite.dart'; -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; class AppCourseUnitsDatabase extends AppDatabase { diff --git a/uni/lib/controller/local_storage/app_courses_database.dart b/uni/lib/controller/local_storage/database/app_courses_database.dart similarity index 97% rename from uni/lib/controller/local_storage/app_courses_database.dart rename to uni/lib/controller/local_storage/database/app_courses_database.dart index db7d9ac6a..b459918e7 100644 --- a/uni/lib/controller/local_storage/app_courses_database.dart +++ b/uni/lib/controller/local_storage/database/app_courses_database.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:sqflite/sqflite.dart'; -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; import 'package:uni/model/entities/course.dart'; /// Manages the app's Courses database. diff --git a/uni/lib/controller/local_storage/app_database.dart b/uni/lib/controller/local_storage/database/app_database.dart similarity index 100% rename from uni/lib/controller/local_storage/app_database.dart rename to uni/lib/controller/local_storage/database/app_database.dart diff --git a/uni/lib/controller/local_storage/app_exams_database.dart b/uni/lib/controller/local_storage/database/app_exams_database.dart similarity index 97% rename from uni/lib/controller/local_storage/app_exams_database.dart rename to uni/lib/controller/local_storage/database/app_exams_database.dart index 6d86a01c7..49f6bcc0c 100644 --- a/uni/lib/controller/local_storage/app_exams_database.dart +++ b/uni/lib/controller/local_storage/database/app_exams_database.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:sqflite/sqflite.dart'; -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; import 'package:uni/model/entities/exam.dart'; /// Manages the app's Exams database. diff --git a/uni/lib/controller/local_storage/app_last_user_info_update_database.dart b/uni/lib/controller/local_storage/database/app_last_user_info_update_database.dart similarity index 94% rename from uni/lib/controller/local_storage/app_last_user_info_update_database.dart rename to uni/lib/controller/local_storage/database/app_last_user_info_update_database.dart index 93af19039..e2d4656ac 100644 --- a/uni/lib/controller/local_storage/app_last_user_info_update_database.dart +++ b/uni/lib/controller/local_storage/database/app_last_user_info_update_database.dart @@ -1,4 +1,4 @@ -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; /// Manages the app's Last User Info Update database. /// diff --git a/uni/lib/controller/local_storage/app_lectures_database.dart b/uni/lib/controller/local_storage/database/app_lectures_database.dart similarity index 97% rename from uni/lib/controller/local_storage/app_lectures_database.dart rename to uni/lib/controller/local_storage/database/app_lectures_database.dart index 7430020e5..6327cec4c 100644 --- a/uni/lib/controller/local_storage/app_lectures_database.dart +++ b/uni/lib/controller/local_storage/database/app_lectures_database.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:sqflite/sqflite.dart'; -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; import 'package:uni/model/entities/lecture.dart'; /// Manages the app's Lectures database. diff --git a/uni/lib/controller/local_storage/app_library_occupation_database.dart b/uni/lib/controller/local_storage/database/app_library_occupation_database.dart similarity index 94% rename from uni/lib/controller/local_storage/app_library_occupation_database.dart rename to uni/lib/controller/local_storage/database/app_library_occupation_database.dart index a4ab4b35a..6f39d77c7 100644 --- a/uni/lib/controller/local_storage/app_library_occupation_database.dart +++ b/uni/lib/controller/local_storage/database/app_library_occupation_database.dart @@ -1,4 +1,4 @@ -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; import 'package:uni/model/entities/library_occupation.dart'; class LibraryOccupationDatabase extends AppDatabase { diff --git a/uni/lib/controller/local_storage/app_references_database.dart b/uni/lib/controller/local_storage/database/app_references_database.dart similarity index 96% rename from uni/lib/controller/local_storage/app_references_database.dart rename to uni/lib/controller/local_storage/database/app_references_database.dart index 2693357a7..feff82385 100644 --- a/uni/lib/controller/local_storage/app_references_database.dart +++ b/uni/lib/controller/local_storage/database/app_references_database.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:sqflite/sqflite.dart'; -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; import 'package:uni/model/entities/reference.dart'; /// Manages the app's References database. diff --git a/uni/lib/controller/local_storage/app_restaurant_database.dart b/uni/lib/controller/local_storage/database/app_restaurant_database.dart similarity index 98% rename from uni/lib/controller/local_storage/app_restaurant_database.dart rename to uni/lib/controller/local_storage/database/app_restaurant_database.dart index 56663b513..de96b51d6 100644 --- a/uni/lib/controller/local_storage/app_restaurant_database.dart +++ b/uni/lib/controller/local_storage/database/app_restaurant_database.dart @@ -1,6 +1,6 @@ import 'package:intl/intl.dart'; import 'package:sqflite/sqflite.dart'; -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; import 'package:uni/model/entities/meal.dart'; import 'package:uni/model/entities/restaurant.dart'; import 'package:uni/model/utils/day_of_week.dart'; diff --git a/uni/lib/controller/local_storage/app_user_database.dart b/uni/lib/controller/local_storage/database/app_user_database.dart similarity index 97% rename from uni/lib/controller/local_storage/app_user_database.dart rename to uni/lib/controller/local_storage/database/app_user_database.dart index 6bbd86d16..6fd14cf29 100644 --- a/uni/lib/controller/local_storage/app_user_database.dart +++ b/uni/lib/controller/local_storage/database/app_user_database.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:uni/controller/local_storage/app_database.dart'; +import 'package:uni/controller/local_storage/database/app_database.dart'; import 'package:uni/model/entities/course.dart'; import 'package:uni/model/entities/profile.dart'; diff --git a/uni/lib/controller/local_storage/app_shared_preferences.dart b/uni/lib/controller/local_storage/preferences_controller.dart similarity index 75% rename from uni/lib/controller/local_storage/app_shared_preferences.dart rename to uni/lib/controller/local_storage/preferences_controller.dart index e2b60e5d7..1f6cff50b 100644 --- a/uni/lib/controller/local_storage/app_shared_preferences.dart +++ b/uni/lib/controller/local_storage/preferences_controller.dart @@ -5,6 +5,7 @@ import 'package:encrypt/encrypt.dart' as encrypt; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tuple/tuple.dart'; +import 'package:uni/main.dart'; import 'package:uni/model/entities/app_locale.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/utils/favorite_widget_type.dart'; @@ -13,7 +14,9 @@ import 'package:uni/utils/favorite_widget_type.dart'; /// /// This database stores the user's student number, password and favorite /// widgets. -class AppSharedPreferences { +class PreferencesController { + static SharedPreferences get prefs => Application.sharedPreferences; + static final iv = encrypt.IV.fromBase64('jF9jjdSEPgsKnf0jCl1GAQ=='); static final key = encrypt.Key.fromBase64('DT3/GTNYldhwOD3ZbpVLoAwA/mncsN7U7sJxfFn3y0A='); @@ -41,8 +44,7 @@ class AppSharedPreferences { static final List defaultFilteredExamTypes = Exam.displayedTypes; /// Returns the last time the data with given key was updated. - static Future getLastDataClassUpdateTime(String dataKey) async { - final prefs = await SharedPreferences.getInstance(); + static DateTime? getLastDataClassUpdateTime(String dataKey) { final lastUpdateTime = prefs.getString(dataKey + lastUpdateTimeKeySuffix); return lastUpdateTime != null ? DateTime.parse(lastUpdateTime) : null; } @@ -52,7 +54,6 @@ class AppSharedPreferences { String dataKey, DateTime dateTime, ) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setString( dataKey + lastUpdateTimeKeySuffix, dateTime.toString(), @@ -65,7 +66,6 @@ class AppSharedPreferences { String pass, List faculties, ) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setString(userNumber, user); await prefs.setString(userPw, encode(pass)); await prefs.setStringList( @@ -78,56 +78,47 @@ class AppSharedPreferences { static Future setTermsAndConditionsAcceptance({ required bool areAccepted, }) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setBool(areTermsAndConditionsAcceptedKey, areAccepted); } /// Returns whether or not the Terms and Conditions have been accepted. - static Future areTermsAndConditionsAccepted() async { - final prefs = await SharedPreferences.getInstance(); + static bool areTermsAndConditionsAccepted() { return prefs.getBool(areTermsAndConditionsAcceptedKey) ?? false; } /// Returns the hash of the last Terms and Conditions that have /// been accepted by the user. - static Future getTermsAndConditionHash() async { - final prefs = await SharedPreferences.getInstance(); + static String? getTermsAndConditionHash() { return prefs.getString(termsAndConditions); } /// Sets the hash of the Terms and Conditions that have been accepted /// by the user. static Future setTermsAndConditionHash(String hashed) async { - final prefs = await SharedPreferences.getInstance(); return prefs.setString(termsAndConditions, hashed); } /// Gets current used theme mode. - static Future getThemeMode() async { - final prefs = await SharedPreferences.getInstance(); + static ThemeMode getThemeMode() { return ThemeMode.values[prefs.getInt(themeMode) ?? ThemeMode.system.index]; } /// Set new app theme mode. static Future setThemeMode(ThemeMode thmMode) async { - final prefs = await SharedPreferences.getInstance(); return prefs.setInt(themeMode, thmMode.index); } /// Set app next theme mode. static Future setNextThemeMode() async { - final prefs = await SharedPreferences.getInstance(); - final themeIndex = (await getThemeMode()).index; + final themeIndex = getThemeMode().index; return prefs.setInt(themeMode, (themeIndex + 1) % 3); } static Future setLocale(AppLocale appLocale) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setString(locale, appLocale.name); } - static Future getLocale() async { - final prefs = await SharedPreferences.getInstance(); + static AppLocale getLocale() { final appLocale = prefs.getString(locale) ?? Platform.localeName.substring(0, 2); @@ -139,7 +130,6 @@ class AppSharedPreferences { /// Deletes the user's student number and password. static Future removePersistentUserInfo() async { - final prefs = await SharedPreferences.getInstance(); await prefs.remove(userNumber); await prefs.remove(userPw); } @@ -150,9 +140,9 @@ class AppSharedPreferences { /// * the first element in the tuple is the user's student number. /// * the second element in the tuple is the user's password, in plain text /// format. - static Future?> getPersistentUserInfo() async { - final userNum = await getUserNumber(); - final userPass = await getUserPassword(); + static Tuple2? getPersistentUserInfo() { + final userNum = getUserNumber(); + final userPass = getUserPassword(); if (userNum == null || userPass == null) { return null; } @@ -160,22 +150,19 @@ class AppSharedPreferences { } /// Returns the user's faculties - static Future> getUserFaculties() async { - final prefs = await SharedPreferences.getInstance(); + static List getUserFaculties() { final storedFaculties = prefs.getStringList(userFaculties); return storedFaculties ?? ['feup']; // TODO(bdmendes): Store dropdown choices in the db for later storage; } /// Returns the user's student number. - static Future getUserNumber() async { - final prefs = await SharedPreferences.getInstance(); + static String? getUserNumber() { return prefs.getString(userNumber); } /// Returns the user's password, in plain text format. - static Future getUserPassword() async { - final prefs = await SharedPreferences.getInstance(); + static String? getUserPassword() { final password = prefs.getString(userPw); return password != null ? decode(password) : null; } @@ -184,7 +171,6 @@ class AppSharedPreferences { static Future saveFavoriteCards( List newFavorites, ) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setStringList( favoriteCards, newFavorites.map((a) => a.index.toString()).toList(), @@ -192,8 +178,7 @@ class AppSharedPreferences { } /// Returns a list containing the user's favorite widgets. - static Future> getFavoriteCards() async { - final prefs = await SharedPreferences.getInstance(); + static List getFavoriteCards() { final storedFavorites = prefs .getStringList(favoriteCards) ?.where( @@ -213,24 +198,20 @@ class AppSharedPreferences { static Future saveFavoriteRestaurants( List newFavoriteRestaurants, ) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setStringList(favoriteRestaurants, newFavoriteRestaurants); } - static Future> getFavoriteRestaurants() async { - final prefs = await SharedPreferences.getInstance(); + static List getFavoriteRestaurants() { final storedFavoriteRestaurants = prefs.getStringList(favoriteRestaurants) ?? []; return storedFavoriteRestaurants; } static Future saveHiddenExams(List newHiddenExams) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setStringList(hiddenExams, newHiddenExams); } - static Future> getHiddenExams() async { - final prefs = await SharedPreferences.getInstance(); + static List getHiddenExams() { final storedHiddenExam = prefs.getStringList(hiddenExams) ?? []; return storedHiddenExam; } @@ -239,8 +220,6 @@ class AppSharedPreferences { static Future saveFilteredExams( Map newFilteredExamTypes, ) async { - final prefs = await SharedPreferences.getInstance(); - final newTypes = newFilteredExamTypes.keys .where((type) => newFilteredExamTypes[type] ?? false) .toList(); @@ -248,8 +227,7 @@ class AppSharedPreferences { } /// Returns the user's exam filter settings. - static Future> getFilteredExams() async { - final prefs = await SharedPreferences.getInstance(); + static Map getFilteredExams() { final storedFilteredExamTypes = prefs.getStringList(filteredExamsTypes); if (storedFilteredExamTypes == null) { @@ -283,27 +261,23 @@ class AppSharedPreferences { return encrypt.Encrypter(encrypt.AES(key)); } - static Future getTuitionNotificationToggle() async { - final prefs = await SharedPreferences.getInstance(); + static bool getTuitionNotificationToggle() { return prefs.getBool(tuitionNotificationsToggleKey) ?? true; } static Future setTuitionNotificationToggle({ required bool value, }) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setBool(tuitionNotificationsToggleKey, value); } - static Future getUsageStatsToggle() async { - final prefs = await SharedPreferences.getInstance(); + static bool getUsageStatsToggle() { return prefs.getBool(usageStatsToggleKey) ?? true; } static Future setUsageStatsToggle({ required bool value, }) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setBool(usageStatsToggleKey, value); } } diff --git a/uni/lib/controller/networking/network_router.dart b/uni/lib/controller/networking/network_router.dart index 6b2ee56d3..3f6594777 100644 --- a/uni/lib/controller/networking/network_router.dart +++ b/uni/lib/controller/networking/network_router.dart @@ -5,7 +5,7 @@ import 'package:http/http.dart' as http; import 'package:http/http.dart'; import 'package:logger/logger.dart'; import 'package:synchronized/synchronized.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/view/navigation_service.dart'; @@ -92,7 +92,7 @@ class NetworkRouter { /// returning an updated Session if successful. static Future reLoginFromSession(Session session) async { final username = session.username; - final password = await AppSharedPreferences.getUserPassword(); + final password = PreferencesController.getUserPassword(); if (password == null) { Logger().e('Re-login failed: password not found'); diff --git a/uni/lib/main.dart b/uni/lib/main.dart index d77c467c3..fa4dfa199 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -8,16 +8,16 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:uni/controller/background_workers/background_callback.dart'; -import 'package:uni/controller/load_static/terms_and_conditions.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/fetchers/terms_and_conditions_fetcher.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; import 'package:uni/model/providers/lazy/calendar_provider.dart'; import 'package:uni/model/providers/lazy/course_units_info_provider.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; -import 'package:uni/model/providers/lazy/home_page_provider.dart'; import 'package:uni/model/providers/lazy/lecture_provider.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; import 'package:uni/model/providers/lazy/reference_provider.dart'; @@ -48,7 +48,7 @@ SentryEvent? beforeSend(SentryEvent event) { } Future firstRoute() async { - final userPersistentInfo = await AppSharedPreferences.getPersistentUserInfo(); + final userPersistentInfo = PreferencesController.getPersistentUserInfo(); if (userPersistentInfo != null) { return const HomePageView(); @@ -59,6 +59,10 @@ Future firstRoute() async { } Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + + Application.sharedPreferences = await SharedPreferences.getInstance(); + final stateProviders = StateProviders( LectureProvider(), ExamProvider(), @@ -70,12 +74,9 @@ Future main() async { CalendarProvider(), LibraryOccupationProvider(), FacultyLocationsProvider(), - HomePageProvider(), ReferenceProvider(), ); - WidgetsFlutterBinding.ensureInitialized(); - // Initialize WorkManager for background tasks await Workmanager().initialize( workerStartCallback, @@ -94,8 +95,8 @@ Future main() async { ); }); - final savedTheme = await AppSharedPreferences.getThemeMode(); - final savedLocale = await AppSharedPreferences.getLocale(); + final savedTheme = PreferencesController.getThemeMode(); + final savedLocale = PreferencesController.getLocale(); final route = await firstRoute(); await SentryFlutter.init( @@ -137,9 +138,6 @@ Future main() async { ChangeNotifierProvider( create: (context) => stateProviders.facultyLocationsProvider, ), - ChangeNotifierProvider( - create: (context) => stateProviders.homePageProvider, - ), ChangeNotifierProvider( create: (context) => stateProviders.referenceProvider, ), @@ -167,6 +165,8 @@ class Application extends StatefulWidget { static GlobalKey navigatorKey = GlobalKey(); + static late SharedPreferences sharedPreferences; + @override State createState() => ApplicationState(); } @@ -207,7 +207,7 @@ class ApplicationState extends State { settings: settings, ), '/${DrawerItem.navExams.title}': PageTransition.makePageTransition( - page: const ExamsPageView(), + page: ExamsPageView(), settings: settings, ), '/${DrawerItem.navStops.title}': PageTransition.makePageTransition( diff --git a/uni/lib/model/providers/lazy/bus_stop_provider.dart b/uni/lib/model/providers/lazy/bus_stop_provider.dart index 2b9f7c36b..3e3769041 100644 --- a/uni/lib/model/providers/lazy/bus_stop_provider.dart +++ b/uni/lib/model/providers/lazy/bus_stop_provider.dart @@ -1,86 +1,79 @@ import 'dart:async'; -import 'dart:collection'; import 'package:uni/controller/fetchers/departures_fetcher.dart'; -import 'package:uni/controller/local_storage/app_bus_stop_database.dart'; +import 'package:uni/controller/local_storage/database/app_bus_stop_database.dart'; import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -class BusStopProvider extends StateProviderNotifier { - BusStopProvider() : super(dependsOnSession: false, cacheDuration: null); - Map _configuredBusStops = Map.identity(); - DateTime _timeStamp = DateTime.now(); - - UnmodifiableMapView get configuredBusStops => - UnmodifiableMapView(_configuredBusStops); - - DateTime get timeStamp => _timeStamp; +class BusStopProvider extends StateProviderNotifier> { + BusStopProvider() : super(cacheDuration: null); @override - Future loadFromStorage() async { + Future> loadFromStorage() { final busStopsDb = AppBusStopDatabase(); - final stops = await busStopsDb.busStops(); - _configuredBusStops = stops; + return busStopsDb.busStops(); } @override - Future loadFromRemote(Session session, Profile profile) async { - await fetchUserBusTrips(); + Future> loadFromRemote( + Session session, + Profile profile, + ) async { + return fetchUserBusTrips(state!); } - Future fetchUserBusTrips() async { - for (final stopCode in configuredBusStops.keys) { + Future> fetchUserBusTrips( + Map currentStops, + ) async { + for (final stopCode in currentStops.keys) { final stopTrips = await DeparturesFetcher.getNextArrivalsStop( stopCode, - configuredBusStops[stopCode]!, + currentStops[stopCode]!, ); - _configuredBusStops[stopCode]?.trips = stopTrips; + currentStops[stopCode]?.trips = stopTrips; } - _timeStamp = DateTime.now(); + return currentStops; } Future addUserBusStop(String stopCode, BusStopData stopData) async { - if (_configuredBusStops.containsKey(stopCode)) { - _configuredBusStops[stopCode]!.configuredBuses.clear(); - _configuredBusStops[stopCode]! - .configuredBuses - .addAll(stopData.configuredBuses); + if (state!.containsKey(stopCode)) { + state![stopCode]!.configuredBuses.clear(); + state![stopCode]!.configuredBuses.addAll(stopData.configuredBuses); } else { - _configuredBusStops[stopCode] = stopData; + state![stopCode] = stopData; } notifyListeners(); - await fetchUserBusTrips(); + await fetchUserBusTrips(state!); notifyListeners(); final db = AppBusStopDatabase(); - await db.setBusStops(configuredBusStops); + await db.setBusStops(state!); } Future removeUserBusStop( String stopCode, ) async { - _configuredBusStops.remove(stopCode); + state!.remove(stopCode); notifyListeners(); - await fetchUserBusTrips(); + await fetchUserBusTrips(state!); notifyListeners(); final db = AppBusStopDatabase(); - await db.setBusStops(_configuredBusStops); + await db.setBusStops(state!); } Future toggleFavoriteUserBusStop( String stopCode, BusStopData stopData, ) async { - _configuredBusStops[stopCode]!.favorited = - !_configuredBusStops[stopCode]!.favorited; + state![stopCode]!.favorited = !state![stopCode]!.favorited; notifyListeners(); - await fetchUserBusTrips(); + await fetchUserBusTrips(state!); notifyListeners(); final db = AppBusStopDatabase(); diff --git a/uni/lib/model/providers/lazy/calendar_provider.dart b/uni/lib/model/providers/lazy/calendar_provider.dart index 3e532b718..b660e55d0 100644 --- a/uni/lib/model/providers/lazy/calendar_provider.dart +++ b/uni/lib/model/providers/lazy/calendar_provider.dart @@ -1,35 +1,29 @@ import 'dart:async'; -import 'dart:collection'; import 'package:uni/controller/fetchers/calendar_fetcher_html.dart'; -import 'package:uni/controller/local_storage/app_calendar_database.dart'; +import 'package:uni/controller/local_storage/database/app_calendar_database.dart'; import 'package:uni/model/entities/calendar_event.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -class CalendarProvider extends StateProviderNotifier { - CalendarProvider() - : super(dependsOnSession: true, cacheDuration: const Duration(days: 30)); - List _calendar = []; - - UnmodifiableListView get calendar => - UnmodifiableListView(_calendar); +class CalendarProvider extends StateProviderNotifier> { + CalendarProvider() : super(cacheDuration: const Duration(days: 30)); @override - Future loadFromRemote(Session session, Profile profile) async { - await fetchCalendar(session); - } - - Future fetchCalendar(Session session) async { - _calendar = await CalendarFetcherHtml().getCalendar(session); + Future> loadFromRemote( + Session session, + Profile profile, + ) async { + final calendar = await CalendarFetcherHtml().getCalendar(session); final db = CalendarDatabase(); unawaited(db.saveCalendar(calendar)); + return calendar; } @override - Future loadFromStorage() async { + Future> loadFromStorage() async { final db = CalendarDatabase(); - _calendar = await db.calendar(); + return db.calendar(); } } diff --git a/uni/lib/model/providers/lazy/course_units_info_provider.dart b/uni/lib/model/providers/lazy/course_units_info_provider.dart index 2a9901870..83b5ae7d0 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -1,5 +1,6 @@ import 'dart:collection'; +import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/entities/course_units/course_unit_class.dart'; @@ -8,39 +9,46 @@ import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -class CourseUnitsInfoProvider extends StateProviderNotifier { - CourseUnitsInfoProvider() - : super(dependsOnSession: true, cacheDuration: null, initialize: false); - final Map _courseUnitsSheets = {}; - final Map> _courseUnitsClasses = {}; +typedef SheetsMap = Map; +typedef ClassesMap = Map>; + +class CourseUnitsInfoProvider + extends StateProviderNotifier> { + CourseUnitsInfoProvider() : super(cacheDuration: null); UnmodifiableMapView get courseUnitsSheets => - UnmodifiableMapView(_courseUnitsSheets); + UnmodifiableMapView(state!.item1); UnmodifiableMapView> - get courseUnitsClasses => UnmodifiableMapView(_courseUnitsClasses); + get courseUnitsClasses => UnmodifiableMapView(state!.item2); Future fetchCourseUnitSheet( CourseUnit courseUnit, Session session, ) async { - _courseUnitsSheets[courseUnit] = await CourseUnitsInfoFetcher() + state!.item1[courseUnit] = await CourseUnitsInfoFetcher() .fetchCourseUnitSheet(session, courseUnit.occurrId); + notifyListeners(); } Future fetchCourseUnitClasses( CourseUnit courseUnit, Session session, ) async { - _courseUnitsClasses[courseUnit] = await CourseUnitsInfoFetcher() + state!.item2[courseUnit] = await CourseUnitsInfoFetcher() .fetchCourseUnitClasses(session, courseUnit.occurrId); } @override - Future loadFromRemote(Session session, Profile profile) async { - // Course units info is loaded on demand by its detail page + Future> loadFromRemote( + Session session, + Profile profile, + ) async { + return const Tuple2({}, {}); } @override - Future loadFromStorage() async {} + Future> loadFromStorage() async { + return const Tuple2({}, {}); + } } diff --git a/uni/lib/model/providers/lazy/exam_provider.dart b/uni/lib/model/providers/lazy/exam_provider.dart index 5f93c548a..37c9271f9 100644 --- a/uni/lib/model/providers/lazy/exam_provider.dart +++ b/uni/lib/model/providers/lazy/exam_provider.dart @@ -1,9 +1,8 @@ import 'dart:async'; -import 'dart:collection'; import 'package:uni/controller/fetchers/exam_fetcher.dart'; -import 'package:uni/controller/local_storage/app_exams_database.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/database/app_exams_database.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/controller/parsers/parser_exams.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/entities/exam.dart'; @@ -11,44 +10,28 @@ import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -class ExamProvider extends StateProviderNotifier { - ExamProvider() - : super(dependsOnSession: true, cacheDuration: const Duration(days: 1)); - List _exams = []; - List _hiddenExams = []; - Map _filteredExamsTypes = {}; - - UnmodifiableListView get exams => UnmodifiableListView(_exams); - - UnmodifiableListView get hiddenExams => - UnmodifiableListView(_hiddenExams); - - UnmodifiableMapView get filteredExamsTypes => - UnmodifiableMapView(_filteredExamsTypes); +class ExamProvider extends StateProviderNotifier> { + ExamProvider() : super(cacheDuration: const Duration(days: 1)); @override - Future loadFromStorage() async { - await setFilteredExams(await AppSharedPreferences.getFilteredExams()); - await setHiddenExams(await AppSharedPreferences.getHiddenExams()); - + Future> loadFromStorage() async { final db = AppExamsDatabase(); - final exams = await db.exams(); - _exams = exams; + return db.exams(); } @override - Future loadFromRemote(Session session, Profile profile) async { - await fetchUserExams( + Future> loadFromRemote(Session session, Profile profile) async { + return fetchUserExams( ParserExams(), profile, session, profile.courseUnits, persistentSession: - (await AppSharedPreferences.getPersistentUserInfo()) != null, + (PreferencesController.getPersistentUserInfo()) != null, ); } - Future fetchUserExams( + Future> fetchUserExams( ParserExams parserExams, Profile profile, Session session, @@ -64,10 +47,10 @@ class ExamProvider extends StateProviderNotifier { await AppExamsDatabase().saveNewExams(exams); } - _exams = exams; + return exams; } - Future updateFilteredExams() async { +/*Future updateFilteredExams() async { final exams = await AppSharedPreferences.getFilteredExams(); _filteredExamsTypes = exams; notifyListeners(); @@ -99,10 +82,5 @@ class ExamProvider extends StateProviderNotifier { : _hiddenExams.add(newExamId); await AppSharedPreferences.saveHiddenExams(hiddenExams); notifyListeners(); - } - - set exams(List newExams) { - _exams = newExams; - notifyListeners(); - } + }*/ } diff --git a/uni/lib/model/providers/lazy/faculty_locations_provider.dart b/uni/lib/model/providers/lazy/faculty_locations_provider.dart index f43da6743..9f6cd29a9 100644 --- a/uni/lib/model/providers/lazy/faculty_locations_provider.dart +++ b/uni/lib/model/providers/lazy/faculty_locations_provider.dart @@ -1,24 +1,23 @@ -import 'dart:collection'; - import 'package:uni/controller/fetchers/location_fetcher/location_fetcher_asset.dart'; import 'package:uni/model/entities/location_group.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -class FacultyLocationsProvider extends StateProviderNotifier { - FacultyLocationsProvider() - : super(dependsOnSession: false, cacheDuration: const Duration(days: 30)); - List _locations = []; - - UnmodifiableListView get locations => - UnmodifiableListView(_locations); +class FacultyLocationsProvider + extends StateProviderNotifier> { + FacultyLocationsProvider() : super(cacheDuration: const Duration(days: 30)); @override - Future loadFromStorage() async { - _locations = await LocationFetcherAsset().getLocations(); + Future> loadFromStorage() { + return LocationFetcherAsset().getLocations(); } @override - Future loadFromRemote(Session session, Profile profile) async {} + Future> loadFromRemote( + Session session, + Profile profile, + ) async { + return state!; + } } diff --git a/uni/lib/model/providers/lazy/home_page_provider.dart b/uni/lib/model/providers/lazy/home_page_provider.dart deleted file mode 100644 index 48d162e33..000000000 --- a/uni/lib/model/providers/lazy/home_page_provider.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; -import 'package:uni/model/entities/profile.dart'; -import 'package:uni/model/entities/session.dart'; -import 'package:uni/model/providers/state_provider_notifier.dart'; -import 'package:uni/utils/favorite_widget_type.dart'; - -class HomePageProvider extends StateProviderNotifier { - HomePageProvider() : super(dependsOnSession: false, cacheDuration: null); - List _favoriteCards = []; - bool _isEditing = false; - - List get favoriteCards => _favoriteCards.toList(); - - bool get isEditing => _isEditing; - - @override - Future loadFromStorage() async { - setFavoriteCards(await AppSharedPreferences.getFavoriteCards()); - } - - @override - Future loadFromRemote(Session session, Profile profile) async {} - - void setHomePageEditingMode({required bool editingMode}) { - _isEditing = editingMode; - notifyListeners(); - } - - void toggleHomePageEditingMode() { - _isEditing = !_isEditing; - notifyListeners(); - } - - void setFavoriteCards(List favoriteCards) { - _favoriteCards = favoriteCards; - notifyListeners(); - } -} diff --git a/uni/lib/model/providers/lazy/lecture_provider.dart b/uni/lib/model/providers/lazy/lecture_provider.dart index 93201bdbd..82cce468b 100644 --- a/uni/lib/model/providers/lazy/lecture_provider.dart +++ b/uni/lib/model/providers/lazy/lecture_provider.dart @@ -1,41 +1,35 @@ import 'dart:async'; -import 'dart:collection'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher.dart'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher_html.dart'; -import 'package:uni/controller/local_storage/app_lectures_database.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/database/app_lectures_database.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -class LectureProvider extends StateProviderNotifier { - LectureProvider() - : super(dependsOnSession: true, cacheDuration: const Duration(hours: 6)); - List _lectures = []; - - UnmodifiableListView get lectures => UnmodifiableListView(_lectures); +class LectureProvider extends StateProviderNotifier> { + LectureProvider() : super(cacheDuration: const Duration(hours: 6)); @override - Future loadFromStorage() async { + Future> loadFromStorage() async { final db = AppLecturesDatabase(); - final lectures = await db.lectures(); - _lectures = lectures; + return db.lectures(); } @override - Future loadFromRemote(Session session, Profile profile) async { - await fetchUserLectures( + Future> loadFromRemote(Session session, Profile profile) async { + return fetchUserLectures( session, profile, persistentSession: - (await AppSharedPreferences.getPersistentUserInfo()) != null, + (PreferencesController.getPersistentUserInfo()) != null, ); } - Future fetchUserLectures( + Future> fetchUserLectures( Session session, Profile profile, { required bool persistentSession, @@ -49,7 +43,7 @@ class LectureProvider extends StateProviderNotifier { await db.saveNewLectures(lectures); } - _lectures = lectures; + return lectures; } Future> getLecturesFromFetcherOrElse( diff --git a/uni/lib/model/providers/lazy/library_occupation_provider.dart b/uni/lib/model/providers/lazy/library_occupation_provider.dart index c1fb18199..ac4ba0781 100644 --- a/uni/lib/model/providers/lazy/library_occupation_provider.dart +++ b/uni/lib/model/providers/lazy/library_occupation_provider.dart @@ -1,36 +1,33 @@ import 'dart:async'; import 'package:uni/controller/fetchers/library_occupation_fetcher.dart'; -import 'package:uni/controller/local_storage/app_library_occupation_database.dart'; +import 'package:uni/controller/local_storage/database/app_library_occupation_database.dart'; import 'package:uni/model/entities/library_occupation.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -class LibraryOccupationProvider extends StateProviderNotifier { - LibraryOccupationProvider() - : super(dependsOnSession: true, cacheDuration: const Duration(hours: 1)); - LibraryOccupation? _occupation; - - LibraryOccupation? get occupation => _occupation; +class LibraryOccupationProvider + extends StateProviderNotifier { + LibraryOccupationProvider() : super(cacheDuration: const Duration(hours: 1)); @override - Future loadFromStorage() async { + Future loadFromStorage() async { final db = LibraryOccupationDatabase(); - final occupation = await db.occupation(); - _occupation = occupation; + return db.occupation(); } @override - Future loadFromRemote(Session session, Profile profile) async { - await fetchLibraryOccupation(session); - } - - Future fetchLibraryOccupation(Session session) async { - _occupation = await LibraryOccupationFetcherSheets() + Future loadFromRemote( + Session session, + Profile profile, + ) async { + final occupation = await LibraryOccupationFetcherSheets() .getLibraryOccupationFromSheets(session); final db = LibraryOccupationDatabase(); - unawaited(db.saveOccupation(_occupation!)); + unawaited(db.saveOccupation(occupation)); + + return occupation; } } diff --git a/uni/lib/model/providers/lazy/reference_provider.dart b/uni/lib/model/providers/lazy/reference_provider.dart index 619090669..0f747376a 100644 --- a/uni/lib/model/providers/lazy/reference_provider.dart +++ b/uni/lib/model/providers/lazy/reference_provider.dart @@ -1,40 +1,33 @@ import 'dart:async'; -import 'dart:collection'; import 'package:uni/controller/fetchers/reference_fetcher.dart'; -import 'package:uni/controller/local_storage/app_references_database.dart'; +import 'package:uni/controller/local_storage/database/app_references_database.dart'; import 'package:uni/controller/parsers/parser_references.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/reference.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -class ReferenceProvider extends StateProviderNotifier { - ReferenceProvider() - : super(dependsOnSession: true, cacheDuration: const Duration(hours: 1)); - List _references = []; - - UnmodifiableListView get references => - UnmodifiableListView(_references); +class ReferenceProvider extends StateProviderNotifier> { + ReferenceProvider() : super(cacheDuration: const Duration(hours: 1)); @override - Future loadFromStorage() async { + Future> loadFromStorage() { final referencesDb = AppReferencesDatabase(); - final references = await referencesDb.references(); - _references = references; + return referencesDb.references(); } @override - Future loadFromRemote(Session session, Profile profile) async { - await fetchUserReferences(session); - } - - Future fetchUserReferences(Session session) async { + Future> loadFromRemote( + Session session, + Profile profile, + ) async { final response = await ReferenceFetcher().getUserReferenceResponse(session); - - _references = await parseReferences(response); + final references = await parseReferences(response); final referencesDb = AppReferencesDatabase(); unawaited(referencesDb.saveNewReferences(references)); + + return references; } } diff --git a/uni/lib/model/providers/lazy/restaurant_provider.dart b/uni/lib/model/providers/lazy/restaurant_provider.dart index d0db39f40..434b6a952 100644 --- a/uni/lib/model/providers/lazy/restaurant_provider.dart +++ b/uni/lib/model/providers/lazy/restaurant_provider.dart @@ -1,63 +1,32 @@ import 'dart:async'; -import 'dart:collection'; import 'package:uni/controller/fetchers/restaurant_fetcher.dart'; -import 'package:uni/controller/local_storage/app_restaurant_database.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/database/app_restaurant_database.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/restaurant.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -class RestaurantProvider extends StateProviderNotifier { - RestaurantProvider() - : super(dependsOnSession: false, cacheDuration: const Duration(days: 1)); - - List _restaurants = []; - List _favoriteRestaurants = []; - - UnmodifiableListView get restaurants => - UnmodifiableListView(_restaurants); - - UnmodifiableListView get favoriteRestaurants => - UnmodifiableListView(_favoriteRestaurants); +class RestaurantProvider extends StateProviderNotifier> { + RestaurantProvider() : super(cacheDuration: const Duration(days: 1)); @override - Future loadFromStorage() async { + Future> loadFromStorage() async { final restaurantDb = RestaurantDatabase(); final restaurants = await restaurantDb.getRestaurants(); - _restaurants = restaurants; - _favoriteRestaurants = await AppSharedPreferences.getFavoriteRestaurants(); + return restaurants; } @override - Future loadFromRemote(Session session, Profile profile) async { - await fetchRestaurants(session); - } - - Future fetchRestaurants(Session session) async { + Future> loadFromRemote( + Session session, + Profile profile, + ) async { final restaurants = await RestaurantFetcher().getRestaurants(session); final db = RestaurantDatabase(); unawaited(db.saveRestaurants(restaurants)); - _restaurants = filterPastMeals(restaurants); - } - - Future toggleFavoriteRestaurant( - String restaurantName, - ) async { - _favoriteRestaurants.contains(restaurantName) - ? _favoriteRestaurants.remove(restaurantName) - : _favoriteRestaurants.add(restaurantName); - notifyListeners(); - await AppSharedPreferences.saveFavoriteRestaurants(favoriteRestaurants); - } - - Future updateStateBasedOnLocalRestaurants() async { - final restaurantDb = RestaurantDatabase(); - final restaurants = await restaurantDb.getRestaurants(); - _restaurants = restaurants; - notifyListeners(); + return filterPastMeals(restaurants); } } diff --git a/uni/lib/model/providers/startup/profile_provider.dart b/uni/lib/model/providers/startup/profile_provider.dart index 349c3d926..236124b74 100644 --- a/uni/lib/model/providers/startup/profile_provider.dart +++ b/uni/lib/model/providers/startup/profile_provider.dart @@ -1,140 +1,148 @@ import 'dart:async'; import 'dart:io'; +import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/course_units_fetcher/all_course_units_fetcher.dart'; import 'package:uni/controller/fetchers/course_units_fetcher/current_course_units_fetcher.dart'; import 'package:uni/controller/fetchers/fees_fetcher.dart'; import 'package:uni/controller/fetchers/print_fetcher.dart'; import 'package:uni/controller/fetchers/profile_fetcher.dart'; -import 'package:uni/controller/local_storage/app_course_units_database.dart'; -import 'package:uni/controller/local_storage/app_courses_database.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; -import 'package:uni/controller/local_storage/app_user_database.dart'; +import 'package:uni/controller/local_storage/database/app_course_units_database.dart'; +import 'package:uni/controller/local_storage/database/app_courses_database.dart'; +import 'package:uni/controller/local_storage/database/app_user_database.dart'; import 'package:uni/controller/local_storage/file_offline_storage.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/controller/parsers/parser_fees.dart'; import 'package:uni/controller/parsers/parser_print_balance.dart'; +import 'package:uni/model/entities/course.dart'; +import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -class ProfileProvider extends StateProviderNotifier { +class ProfileProvider extends StateProviderNotifier { ProfileProvider() - : _profile = Profile(), - super(dependsOnSession: true, cacheDuration: const Duration(days: 1)); - Profile _profile; - - Profile get profile => _profile; + : super(cacheDuration: const Duration(days: 1), dependsOnSession: false); @override - Future loadFromStorage() async { - await loadProfile(); - await Future.wait( - [loadCourses(), loadCourseUnits()], - ); + Future loadFromStorage() async { + final profile = await loadProfile(); + profile + ..courses = await loadCourses() + ..courseUnits = await loadCourseUnits(); + return profile; } @override - Future loadFromRemote(Session session, Profile profile) async { - await fetchUserInfo(session); - - await Future.wait([ - fetchUserFees(session), - fetchUserPrintBalance(session), - fetchCourseUnitsAndCourseAverages(session), - ]); + Future loadFromRemote(Session session, Profile profile) async { + final profile = await fetchUserInfo(session); + + final userBalanceAndFeesLimit = await fetchUserFeesBalanceAndLimit(session); + + profile! + ..feesBalance = userBalanceAndFeesLimit.item1 + ..feesLimit = userBalanceAndFeesLimit.item2 + ..printBalance = await fetchUserPrintBalance(session); + + final courseUnits = await fetchCourseUnitsAndCourseAverages(session); + if (courseUnits != null) { + profile.courseUnits = courseUnits; + } + + return profile; } - Future loadProfile() async { + Future loadProfile() { final profileDb = AppUserDataDatabase(); - _profile = await profileDb.getUserData(); + return profileDb.getUserData(); } - Future loadCourses() async { + Future> loadCourses() { final coursesDb = AppCoursesDatabase(); - final courses = await coursesDb.courses(); - _profile.courses = courses; + return coursesDb.courses(); } - Future loadCourseUnits() async { + Future> loadCourseUnits() { final db = AppCourseUnitsDatabase(); - profile.courseUnits = await db.courseUnits(); + return db.courseUnits(); } - Future fetchUserFees(Session session) async { + Future> fetchUserFeesBalanceAndLimit( + Session session, + ) async { final response = await FeesFetcher().getUserFeesResponse(session); final feesBalance = parseFeesBalance(response); final feesLimit = parseFeesNextLimit(response); - final userPersistentInfo = - await AppSharedPreferences.getPersistentUserInfo(); + final userPersistentInfo = PreferencesController.getPersistentUserInfo(); if (userPersistentInfo != null) { final profileDb = AppUserDataDatabase(); await profileDb.saveUserFees(feesBalance, feesLimit); } - _profile - ..feesBalance = feesBalance - ..feesLimit = feesLimit; + return Tuple2(feesBalance, feesLimit); } - Future fetchUserPrintBalance(Session session) async { + Future fetchUserPrintBalance(Session session) async { final response = await PrintFetcher().getUserPrintsResponse(session); final printBalance = await getPrintsBalance(response); - final userPersistentInfo = - await AppSharedPreferences.getPersistentUserInfo(); + final userPersistentInfo = PreferencesController.getPersistentUserInfo(); if (userPersistentInfo != null) { final profileDb = AppUserDataDatabase(); await profileDb.saveUserPrintBalance(printBalance); } - _profile.printBalance = printBalance; + return printBalance; } - Future fetchUserInfo(Session session) async { + Future fetchUserInfo(Session session) async { final profile = await ProfileFetcher.fetchProfile(session); + if (profile == null) { + return null; + } + final currentCourseUnits = await CurrentCourseUnitsFetcher().getCurrentCourseUnits(session); - _profile = profile ?? Profile(); - _profile.courseUnits = currentCourseUnits; + profile.courseUnits = currentCourseUnits; - final userPersistentInfo = - await AppSharedPreferences.getPersistentUserInfo(); + final userPersistentInfo = PreferencesController.getPersistentUserInfo(); if (userPersistentInfo != null) { // Course units are saved later, so we don't it here final profileDb = AppUserDataDatabase(); - await profileDb.insertUserData(_profile); + await profileDb.insertUserData(profile); } + + return profile; } - Future fetchCourseUnitsAndCourseAverages(Session session) async { - final courses = profile.courses; + Future?> fetchCourseUnitsAndCourseAverages( + Session session, + ) async { final allCourseUnits = await AllCourseUnitsFetcher().getAllCourseUnitsAndCourseAverages( - profile.courses, + state!.courses, session, - currentCourseUnits: profile.courseUnits, + currentCourseUnits: state!.courseUnits, ); - if (allCourseUnits != null) { - _profile.courseUnits = allCourseUnits; - } else { - // Current course units should already have been fetched, - // so this is not a fatal error + if (allCourseUnits == null) { + return allCourseUnits; } - final userPersistentInfo = - await AppSharedPreferences.getPersistentUserInfo(); + final userPersistentInfo = PreferencesController.getPersistentUserInfo(); if (userPersistentInfo != null) { final coursesDb = AppCoursesDatabase(); - await coursesDb.saveNewCourses(courses); + await coursesDb.saveNewCourses(state!.courses); final courseUnitsDatabase = AppCourseUnitsDatabase(); - await courseUnitsDatabase.saveNewCourseUnits(_profile.courseUnits); + await courseUnitsDatabase.saveNewCourseUnits(allCourseUnits); } + + return allCourseUnits; } static Future fetchOrGetCachedProfilePicture( diff --git a/uni/lib/model/providers/startup/session_provider.dart b/uni/lib/model/providers/startup/session_provider.dart index 84cbca043..2f42057b1 100644 --- a/uni/lib/model/providers/startup/session_provider.dart +++ b/uni/lib/model/providers/startup/session_provider.dart @@ -3,67 +3,45 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/controller/background_workers/notifications.dart'; -import 'package:uni/controller/load_static/terms_and_conditions.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/fetchers/terms_and_conditions_fetcher.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/controller/parsers/parser_session.dart'; import 'package:uni/model/entities/login_exceptions.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -import 'package:uni/model/request_status.dart'; import 'package:uni/view/locale_notifier.dart'; -class SessionProvider extends StateProviderNotifier { +class SessionProvider extends StateProviderNotifier { SessionProvider() - : _session = Session( - faculties: ['feup'], - username: '', - cookies: '', - ), - super( - dependsOnSession: false, + : super( cacheDuration: null, - initialStatus: RequestStatus.none, + dependsOnSession: false, ); - Session _session; - - Session get session => _session; - @override - Future loadFromStorage() async { - final userPersistentInfo = - await AppSharedPreferences.getPersistentUserInfo(); + Future loadFromStorage() async { + final userPersistentInfo = PreferencesController.getPersistentUserInfo(); + final faculties = PreferencesController.getUserFaculties(); if (userPersistentInfo == null) { - return; + return Session(username: '', cookies: '', faculties: faculties); } - final userName = userPersistentInfo.item1; - final password = userPersistentInfo.item2; - - final faculties = await AppSharedPreferences.getUserFaculties(); - - restoreSession(userName, password, faculties); - } - - @override - Future loadFromRemote(Session session, Profile profile) async {} - - void restoreSession( - String username, - String password, - List faculties, - ) { - _session = Session( + return Session( faculties: faculties, - username: username, + username: userPersistentInfo.item1, cookies: '', persistentSession: true, ); } + @override + Future loadFromRemote(Session session, Profile profile) async { + return state!; + } + Future postAuthentication( BuildContext context, String username, @@ -98,10 +76,10 @@ class SessionProvider extends StateProviderNotifier { } } - _session = session; + updateState(session); if (persistentSession) { - await AppSharedPreferences.savePersistentUserInfo( + await PreferencesController.savePersistentUserInfo( session.username, password, faculties, diff --git a/uni/lib/model/providers/state_provider_notifier.dart b/uni/lib/model/providers/state_provider_notifier.dart index cc248712e..7f68449ba 100644 --- a/uni/lib/model/providers/state_provider_notifier.dart +++ b/uni/lib/model/providers/state_provider_notifier.dart @@ -1,75 +1,93 @@ +import 'dart:async'; + import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:synchronized/synchronized.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/model/request_status.dart'; -abstract class StateProviderNotifier extends ChangeNotifier { +abstract class StateProviderNotifier extends ChangeNotifier { StateProviderNotifier({ required this.cacheDuration, this.dependsOnSession = true, - RequestStatus initialStatus = RequestStatus.busy, - bool initialize = true, - }) : _initialStatus = initialStatus, - _status = initialStatus, - _initializedFromStorage = !initialize, - _initializedFromRemote = !initialize; - - static const lockTimeout = Duration(seconds: 30); + }); + + /// The model that this notifier provides. + /// This future will throw if the data loading fails. + T? _state; + + /// Whether this provider depends on Session and Profile to fetch data. + bool dependsOnSession; + + /// The data loading request status. + RequestStatus _requestStatus = RequestStatus.none; + + /// The timeout for concurrent state change operations. + static const _lockTimeout = Duration(seconds: 30); + + /// The lock for concurrent state change operations. final Lock _lock = Lock(); - final RequestStatus _initialStatus; - RequestStatus _status; - bool _initializedFromStorage; - bool _initializedFromRemote; + + /// The last time the model was fetched from the remote. DateTime? _lastUpdateTime; - bool dependsOnSession; + + /// The maximum time after the last update from the remote + /// to retrieve cached data. Duration? cacheDuration; - RequestStatus get status => _status; + RequestStatus get requestStatus => _requestStatus; + + T? get state => _state; DateTime? get lastUpdateTime => _lastUpdateTime; - void markAsInitialized() { - _initializedFromStorage = true; - _initializedFromRemote = true; - _status = RequestStatus.successful; - _lastUpdateTime = DateTime.now(); - notifyListeners(); - } + /// Gets the model from the local database. + /// This method such not catch data loading errors. + Future loadFromStorage(); - void markAsNotInitialized() { - _initializedFromStorage = false; - _initializedFromRemote = false; - _status = _initialStatus; - _lastUpdateTime = null; + /// Gets the model from the remote server. + /// This will run once when the provider is first initialized. + /// This method must not catch data loading errors. + /// This method should save data in the database, if appropriate. + Future loadFromRemote(Session session, Profile profile); + + /// Update the current model state, notifying the listeners. + /// This should be called only to modify the model after + /// it has been loaded, for example as a UI callback side effect. + void updateState(T newState) { + _state = newState; + notifyListeners(); } - void _updateStatus(RequestStatus status) { - _status = status; - notifyListeners(); + /// Makes the state null, as if the model has never been loaded, + /// so that consumers may trigger the loading again. + void invalidate() { + _state = null; } Future _loadFromStorage() async { Logger().d('Loading $runtimeType info from storage'); - _lastUpdateTime = await AppSharedPreferences.getLastDataClassUpdateTime( + _updateStatus(RequestStatus.busy); + + _lastUpdateTime = PreferencesController.getLastDataClassUpdateTime( runtimeType.toString(), ); try { - await loadFromStorage(); - notifyListeners(); + updateState(await loadFromStorage()); } catch (e, stackTrace) { await Sentry.captureException(e, stackTrace: stackTrace); Logger() .e('Failed to load $runtimeType info from storage: $e\n$stackTrace'); + _updateStatus(RequestStatus.failed); } Logger().i('Loaded $runtimeType info from storage'); @@ -82,6 +100,8 @@ abstract class StateProviderNotifier extends ChangeNotifier { }) async { Logger().d('Loading $runtimeType info from remote'); + _updateStatus(RequestStatus.busy); + final shouldReload = force || _lastUpdateTime == null || cacheDuration == null || @@ -103,19 +123,18 @@ abstract class StateProviderNotifier extends ChangeNotifier { return; } - _updateStatus(RequestStatus.busy); - try { - await loadFromRemote(session, profile); + updateState(await loadFromRemote(session, profile)); Logger().i('Loaded $runtimeType info from remote'); _lastUpdateTime = DateTime.now(); - _updateStatus(RequestStatus.successful); - await AppSharedPreferences.setLastDataClassUpdateTime( + await PreferencesController.setLastDataClassUpdateTime( runtimeType.toString(), _lastUpdateTime!, ); + + _updateStatus(RequestStatus.successful); } catch (e, stackTrace) { await Sentry.captureException(e, stackTrace: stackTrace); Logger() @@ -124,68 +143,41 @@ abstract class StateProviderNotifier extends ChangeNotifier { } } + void _updateStatus(RequestStatus newStatus) { + _requestStatus = newStatus; + notifyListeners(); + } + Future forceRefresh(BuildContext context) async { await _lock.synchronized( () async { if (!context.mounted) { return; } - final session = context.read().session; - final profile = context.read().profile; - _updateStatus(RequestStatus.busy); + final session = context.read().state!; + final profile = context.read().state!; await _loadFromRemote(session, profile, force: true); }, - timeout: lockTimeout, + timeout: _lockTimeout, ); } Future ensureInitialized(BuildContext context) async { - await ensureInitializedFromStorage(); - - if (context.mounted) { - await ensureInitializedFromRemote(context); - } - } - - Future ensureInitializedFromRemote(BuildContext context) async { await _lock.synchronized( () async { - if (_initializedFromRemote || !context.mounted) { + if (!context.mounted || _state != null) { return; } - final session = context.read().session; - final profile = context.read().profile; - - _initializedFromRemote = true; - - await _loadFromRemote(session, profile); - }, - timeout: lockTimeout, - ); - } + await _loadFromStorage(); - /// Loads data from storage into the provider. - /// This will run once when the provider is first initialized. - /// If the data is not available in storage, this method should do nothing. - Future ensureInitializedFromStorage() async { - await _lock.synchronized( - () async { - if (_initializedFromStorage) { - return; + if (context.mounted) { + final session = context.read().state!; + final profile = context.read().state!; + await _loadFromRemote(session, profile); } - - _initializedFromStorage = true; - await _loadFromStorage(); }, - timeout: lockTimeout, + timeout: _lockTimeout, ); } - - Future loadFromStorage(); - - /// Loads data from the remote server into the provider. - /// This will run once when the provider is first initialized. - /// This method must not catch data loading errors. - Future loadFromRemote(Session session, Profile profile); } diff --git a/uni/lib/model/providers/state_providers.dart b/uni/lib/model/providers/state_providers.dart index d0d6dc5cb..b95a37f5f 100644 --- a/uni/lib/model/providers/state_providers.dart +++ b/uni/lib/model/providers/state_providers.dart @@ -5,7 +5,6 @@ import 'package:uni/model/providers/lazy/calendar_provider.dart'; import 'package:uni/model/providers/lazy/course_units_info_provider.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; -import 'package:uni/model/providers/lazy/home_page_provider.dart'; import 'package:uni/model/providers/lazy/lecture_provider.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; import 'package:uni/model/providers/lazy/reference_provider.dart'; @@ -25,7 +24,6 @@ class StateProviders { this.calendarProvider, this.libraryOccupationProvider, this.facultyLocationsProvider, - this.homePageProvider, this.referenceProvider, ); @@ -49,8 +47,6 @@ class StateProviders { Provider.of(context, listen: false); final facultyLocationsProvider = Provider.of(context, listen: false); - final homePageProvider = - Provider.of(context, listen: false); final referenceProvider = Provider.of(context, listen: false); @@ -65,7 +61,6 @@ class StateProviders { calendarProvider, libraryOccupationProvider, facultyLocationsProvider, - homePageProvider, referenceProvider, ); } @@ -80,21 +75,19 @@ class StateProviders { final CalendarProvider calendarProvider; final LibraryOccupationProvider libraryOccupationProvider; final FacultyLocationsProvider facultyLocationsProvider; - final HomePageProvider homePageProvider; final ReferenceProvider referenceProvider; - void markAsNotInitialized() { - lectureProvider.markAsNotInitialized(); - examProvider.markAsNotInitialized(); - busStopProvider.markAsNotInitialized(); - restaurantProvider.markAsNotInitialized(); - courseUnitsInfoProvider.markAsNotInitialized(); - profileProvider.markAsNotInitialized(); - sessionProvider.markAsNotInitialized(); - calendarProvider.markAsNotInitialized(); - libraryOccupationProvider.markAsNotInitialized(); - facultyLocationsProvider.markAsNotInitialized(); - homePageProvider.markAsNotInitialized(); - referenceProvider.markAsNotInitialized(); + void invalidate() { + lectureProvider.invalidate(); + examProvider.invalidate(); + busStopProvider.invalidate(); + restaurantProvider.invalidate(); + courseUnitsInfoProvider.invalidate(); + profileProvider.invalidate(); + sessionProvider.invalidate(); + calendarProvider.invalidate(); + libraryOccupationProvider.invalidate(); + facultyLocationsProvider.invalidate(); + referenceProvider.invalidate(); } } diff --git a/uni/lib/view/about/widgets/terms_and_conditions.dart b/uni/lib/view/about/widgets/terms_and_conditions.dart index 1a53d07da..10e046352 100644 --- a/uni/lib/view/about/widgets/terms_and_conditions.dart +++ b/uni/lib/view/about/widgets/terms_and_conditions.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:uni/controller/load_static/terms_and_conditions.dart'; +import 'package:uni/controller/fetchers/terms_and_conditions_fetcher.dart'; import 'package:uni/generated/l10n.dart'; import 'package:url_launcher/url_launcher.dart'; diff --git a/uni/lib/view/bug_report/widgets/form.dart b/uni/lib/view/bug_report/widgets/form.dart index 672d7d8b3..8d957ef94 100644 --- a/uni/lib/view/bug_report/widgets/form.dart +++ b/uni/lib/view/bug_report/widgets/form.dart @@ -4,7 +4,7 @@ import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:tuple/tuple.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/app_locale.dart'; import 'package:uni/model/entities/bug_report.dart'; @@ -243,7 +243,7 @@ class BugReportFormState extends State { setState(() { _isButtonTapped = true; }); - final faculties = await AppSharedPreferences.getUserFaculties(); + final faculties = PreferencesController.getUserFaculties(); final bugReport = BugReport( titleController.text, descriptionController.text, diff --git a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart index f48205b66..c22ed9487 100644 --- a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart +++ b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart @@ -28,7 +28,7 @@ class BusStopNextArrivalsPageState return LazyConsumer( builder: (context, busProvider) => ListView( children: [ - NextArrivals(busProvider.configuredBusStops, busProvider.status), + NextArrivals(busProvider.state!, busProvider.requestStatus), ], ), ); @@ -43,6 +43,7 @@ class BusStopNextArrivalsPageState class NextArrivals extends StatefulWidget { const NextArrivals(this.buses, this.busStopStatus, {super.key}); + //final Map> trips; final Map buses; final RequestStatus busStopStatus; diff --git a/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart b/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart index 37c6ca6c8..df60b019b 100644 --- a/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart +++ b/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart @@ -8,13 +8,14 @@ class EstimatedArrivalTimeStamp extends StatelessWidget { required this.timeRemaining, super.key, }); + final String timeRemaining; @override Widget build(BuildContext context) { return LazyConsumer( builder: (context, busProvider) => - getContent(context, busProvider.timeStamp), + getContent(context, busProvider.lastUpdateTime ?? DateTime.now()), ); } diff --git a/uni/lib/view/bus_stop_selection/bus_stop_selection.dart b/uni/lib/view/bus_stop_selection/bus_stop_selection.dart index ecdbfee06..16d1b9373 100644 --- a/uni/lib/view/bus_stop_selection/bus_stop_selection.dart +++ b/uni/lib/view/bus_stop_selection/bus_stop_selection.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:uni/controller/local_storage/app_bus_stop_database.dart'; +import 'package:uni/controller/local_storage/database/app_bus_stop_database.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; @@ -40,7 +40,7 @@ class BusStopSelectionPageState return LazyConsumer( builder: (context, busProvider) { final rows = []; - busProvider.configuredBusStops.forEach( + busProvider.state!.forEach( (stopCode, stopData) => rows.add(BusStopSelectionRow(stopCode, stopData)), ); diff --git a/uni/lib/view/bus_stop_selection/widgets/bus_stop_search.dart b/uni/lib/view/bus_stop_selection/widgets/bus_stop_search.dart index 554c4d8d1..2ac19e288 100644 --- a/uni/lib/view/bus_stop_selection/widgets/bus_stop_search.dart +++ b/uni/lib/view/bus_stop_selection/widgets/bus_stop_search.dart @@ -4,7 +4,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/controller/fetchers/departures_fetcher.dart'; -import 'package:uni/controller/local_storage/app_bus_stop_database.dart'; +import 'package:uni/controller/local_storage/database/app_bus_stop_database.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; diff --git a/uni/lib/view/bus_stop_selection/widgets/form.dart b/uni/lib/view/bus_stop_selection/widgets/form.dart index b7ecb6ba7..c6da8923b 100644 --- a/uni/lib/view/bus_stop_selection/widgets/form.dart +++ b/uni/lib/view/bus_stop_selection/widgets/form.dart @@ -7,6 +7,7 @@ import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; class BusesForm extends StatefulWidget { const BusesForm(this.stopCode, this.updateStopCallback, {super.key}); + final String stopCode; final void Function(String, BusStopData) updateStopCallback; @@ -34,7 +35,7 @@ class BusesFormState extends State { }); if (!mounted) return; final currentConfig = Provider.of(context, listen: false) - .configuredBusStops[widget.stopCode]; + .state![widget.stopCode]; if (currentConfig == null) { return; } @@ -70,7 +71,7 @@ class BusesFormState extends State { void updateBusStop() { final currentConfig = Provider.of(context, listen: false) - .configuredBusStops[widget.stopCode]; + .state![widget.stopCode]; final newBuses = {}; for (var i = 0; i < buses.length; i++) { if (busesToAdd[i]) { diff --git a/uni/lib/view/calendar/calendar.dart b/uni/lib/view/calendar/calendar.dart index 1736da7d7..f54146572 100644 --- a/uni/lib/view/calendar/calendar.dart +++ b/uni/lib/view/calendar/calendar.dart @@ -31,9 +31,9 @@ class CalendarPageViewState extends GeneralPageViewState { ), ), RequestDependentWidgetBuilder( - status: calendarProvider.status, - builder: () => getTimeline(context, calendarProvider.calendar), - hasContentPredicate: calendarProvider.calendar.isNotEmpty, + status: calendarProvider.requestStatus, + builder: () => getTimeline(context, calendarProvider.state!), + hasContentPredicate: calendarProvider.state!.isNotEmpty, onNullContent: const Center( child: Text( 'Nenhum evento encontrado', diff --git a/uni/lib/view/common_widgets/last_update_timestamp.dart b/uni/lib/view/common_widgets/last_update_timestamp.dart index 1e9e31e97..6220fb362 100644 --- a/uni/lib/view/common_widgets/last_update_timestamp.dart +++ b/uni/lib/view/common_widgets/last_update_timestamp.dart @@ -5,7 +5,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; import 'package:uni/view/lazy_consumer.dart'; -class LastUpdateTimeStamp +class LastUpdateTimeStamp> extends StatefulWidget { const LastUpdateTimeStamp({super.key}); @@ -15,7 +15,7 @@ class LastUpdateTimeStamp } } -class _LastUpdateTimeStampState +class _LastUpdateTimeStampState> extends State { DateTime currentTime = DateTime.now(); diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index bf601badd..53b55aa76 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -72,10 +72,10 @@ abstract class GeneralPageViewState extends State { }) async { final sessionProvider = Provider.of(context, listen: false); - await sessionProvider.ensureInitializedFromStorage(); + await sessionProvider.ensureInitialized(context); final profilePictureFile = await ProfileProvider.fetchOrGetCachedProfilePicture( - sessionProvider.session, + sessionProvider.state!, forceRetrieval: forceRetrieval, ); return getProfileDecorationImage(profilePictureFile); @@ -98,7 +98,7 @@ abstract class GeneralPageViewState extends State { return RefreshIndicator( key: GlobalKey(), onRefresh: () => ProfileProvider.fetchOrGetCachedProfilePicture( - Provider.of(context, listen: false).session, + Provider.of(context, listen: false).state!, forceRetrieval: true, ).then((value) => onRefresh(context)), child: Builder( diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart index ad5fd1d74..48b38157a 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart @@ -83,7 +83,7 @@ class AppNavigationDrawerState extends State { @override Widget build(BuildContext context) { final drawerOptions = []; - final userSession = context.read().session; + final userSession = context.read().state!; for (final key in drawerItems.keys) { if (key.isVisible(userSession.faculties)) { diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index aa38ec088..5b1a6c49d 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -25,7 +25,7 @@ class CourseUnitDetailPageViewState Future loadInfo({required bool force}) async { final courseUnitsProvider = Provider.of(context, listen: false); - final session = context.read().session; + final session = context.read().state!; final courseUnitSheet = courseUnitsProvider.courseUnitsSheets[widget.courseUnit]; diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_classes.dart b/uni/lib/view/course_unit_info/widgets/course_unit_classes.dart index afd997326..5c5822c06 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_classes.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_classes.dart @@ -7,11 +7,12 @@ import 'package:uni/view/course_unit_info/widgets/course_unit_student_row.dart'; class CourseUnitClassesView extends StatelessWidget { const CourseUnitClassesView(this.classes, {super.key}); + final List classes; @override Widget build(BuildContext context) { - final session = context.read().session; + final session = context.read().state!; final cards = []; for (final courseUnitClass in classes) { final isMyClass = courseUnitClass.students diff --git a/uni/lib/view/course_units/course_units.dart b/uni/lib/view/course_units/course_units.dart index 26c1b1715..6cd50bfe3 100644 --- a/uni/lib/view/course_units/course_units.dart +++ b/uni/lib/view/course_units/course_units.dart @@ -32,7 +32,7 @@ class CourseUnitsPageViewState Widget getBody(BuildContext context) { return LazyConsumer( builder: (context, profileProvider) { - final courseUnits = profileProvider.profile.courseUnits; + final courseUnits = profileProvider.state!.courseUnits; var availableYears = []; var availableSemesters = []; @@ -54,7 +54,7 @@ class CourseUnitsPageViewState return _getPageView( courseUnits, - profileProvider.status, + profileProvider.requestStatus, availableYears, availableSemesters, ); diff --git a/uni/lib/view/exams/exams.dart b/uni/lib/view/exams/exams.dart index 036b254ad..83a057f6a 100644 --- a/uni/lib/view/exams/exams.dart +++ b/uni/lib/view/exams/exams.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; @@ -13,16 +14,17 @@ import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; class ExamsPageView extends StatefulWidget { - const ExamsPageView({super.key}); + ExamsPageView({super.key}); + + final List hiddenExams = PreferencesController.getHiddenExams(); + final Map filteredExamTypes = + PreferencesController.getFilteredExams(); @override State createState() => ExamsPageViewState(); } -/// Tracks the state of `ExamsLists`. class ExamsPageViewState extends GeneralPageViewState { - final double borderRadius = 10; - @override Widget getBody(BuildContext context) { return LazyConsumer( @@ -30,8 +32,17 @@ class ExamsPageViewState extends GeneralPageViewState { return ListView( children: [ Column( - children: - createExamsColumn(context, examProvider.getFilteredExams()), + children: createExamsColumn( + context, + examProvider.state! + .where( + (exam) => + widget.filteredExamTypes[ + Exam.getExamTypeLong(exam.type)] ?? + true, + ) + .toList(), + ), ), ], ); @@ -124,8 +135,7 @@ class ExamsPageViewState extends GeneralPageViewState { } Widget createExamContext(BuildContext context, Exam exam) { - final isHidden = - Provider.of(context).hiddenExams.contains(exam.id); + final isHidden = widget.hiddenExams.contains(exam.id); return Container( key: Key('$exam-exam'), margin: const EdgeInsets.fromLTRB(12, 4, 12, 0), diff --git a/uni/lib/view/exams/widgets/exam_filter_form.dart b/uni/lib/view/exams/widgets/exam_filter_form.dart index 7f8396e01..7f208ba24 100644 --- a/uni/lib/view/exams/widgets/exam_filter_form.dart +++ b/uni/lib/view/exams/widgets/exam_filter_form.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/exam.dart'; -import 'package:uni/model/providers/lazy/exam_provider.dart'; class ExamFilterForm extends StatefulWidget { const ExamFilterForm(this.filteredExamsTypes, {super.key}); @@ -32,8 +31,7 @@ class ExamFilterFormState extends State { ElevatedButton( child: Text(S.of(context).confirm), onPressed: () { - Provider.of(context, listen: false) - .setFilteredExams(widget.filteredExamsTypes); + PreferencesController.saveFilteredExams(widget.filteredExamsTypes); Navigator.pop(context); }, ), diff --git a/uni/lib/view/exams/widgets/exam_filter_menu.dart b/uni/lib/view/exams/widgets/exam_filter_menu.dart index 1ea516678..0aa22e334 100644 --- a/uni/lib/view/exams/widgets/exam_filter_menu.dart +++ b/uni/lib/view/exams/widgets/exam_filter_menu.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/view/exams/widgets/exam_filter_form.dart'; @@ -21,7 +22,7 @@ class ExamFilterMenuState extends State { builder: (_) { final examProvider = Provider.of(context, listen: false); - final filteredExamsTypes = examProvider.filteredExamsTypes; + final filteredExamsTypes = PreferencesController.getFilteredExams(); return ChangeNotifierProvider.value( value: examProvider, child: ExamFilterForm( diff --git a/uni/lib/view/exams/widgets/exam_row.dart b/uni/lib/view/exams/widgets/exam_row.dart index 83457ad84..bddb5b560 100644 --- a/uni/lib/view/exams/widgets/exam_row.dart +++ b/uni/lib/view/exams/widgets/exam_row.dart @@ -1,9 +1,8 @@ import 'package:add_2_calendar/add_2_calendar.dart'; import 'package:flutter/material.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; -import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/model/entities/exam.dart'; -import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/view/exams/widgets/exam_time.dart'; import 'package:uni/view/exams/widgets/exam_title.dart'; @@ -14,6 +13,7 @@ class ExamRow extends StatefulWidget { required this.mainPage, super.key, }); + final Exam exam; final String teacher; final bool mainPage; @@ -27,8 +27,7 @@ class ExamRow extends StatefulWidget { class _ExamRowState extends State { @override Widget build(BuildContext context) { - final isHidden = - Provider.of(context).hiddenExams.contains(widget.exam.id); + final isHidden = PreferencesController.hiddenExams.contains(widget.exam.id); final roomsKey = '${widget.exam.subject}-${widget.exam.rooms}-${widget.exam.beginTime}-' '${widget.exam.endTime}'; @@ -69,12 +68,22 @@ class _ExamRowState extends State { tooltip: isHidden ? 'Mostrar na Área Pessoal' : 'Ocultar da Área Pessoal', - onPressed: () => setState(() { - Provider.of( - context, - listen: false, - ).toggleHiddenExam(widget.exam.id); - }), + onPressed: () async { + final hiddenExams = + PreferencesController.getHiddenExams(); + + if (hiddenExams.contains(widget.exam.id)) { + hiddenExams.remove(widget.exam.id); + } else { + hiddenExams.remove(widget.exam.id); + } + + setState(() { + PreferencesController.saveHiddenExams( + hiddenExams, + ); + }); + }, ), IconButton( icon: Icon(MdiIcons.calendarPlus, size: 30), diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index b5c5d9d99..4cfcdc016 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:uni/model/providers/lazy/home_page_provider.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; +import 'package:uni/utils/favorite_widget_type.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/home/widgets/main_cards_list.dart'; @@ -13,14 +13,23 @@ class HomePageView extends StatefulWidget { /// Tracks the state of Home page. class HomePageViewState extends GeneralPageViewState { + List favoriteCardTypes = + PreferencesController.getFavoriteCards(); + + void setFavoriteCards(List favorites) { + setState(() { + favoriteCardTypes = favorites; + }); + PreferencesController.saveFavoriteCards(favorites); + } + @override Widget getBody(BuildContext context) { - return const MainCardsList(); + return MainCardsList(favoriteCardTypes, setFavoriteCards); } @override Future onRefresh(BuildContext context) async { - final favoriteCardTypes = context.read().favoriteCards; final cards = favoriteCardTypes .map( (e) => diff --git a/uni/lib/view/home/widgets/bus_stop_card.dart b/uni/lib/view/home/widgets/bus_stop_card.dart index 2345458cc..132201dc0 100644 --- a/uni/lib/view/home/widgets/bus_stop_card.dart +++ b/uni/lib/view/home/widgets/bus_stop_card.dart @@ -33,8 +33,8 @@ class BusStopCard extends GenericCard { builder: (context, busProvider) { return getCardContent( context, - busProvider.configuredBusStops, - busProvider.status, + busProvider.state!, + busProvider.requestStatus, ); }, ); diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index 0b3199cc4..b5608669e 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/app_locale.dart'; import 'package:uni/model/entities/exam.dart'; @@ -46,13 +47,17 @@ class ExamCard extends GenericCard { Widget buildCardContent(BuildContext context) { return LazyConsumer( builder: (context, examProvider) { - final filteredExams = examProvider.getFilteredExams(); - final hiddenExams = examProvider.hiddenExams; - final exams = filteredExams - .where((exam) => !hiddenExams.contains(exam.id)) + final filteredExams = PreferencesController.getFilteredExams(); + final hiddenExams = PreferencesController.getHiddenExams(); + final exams = examProvider.state! + .where( + (exam) => + !hiddenExams.contains(exam.id) && + (filteredExams[Exam.getExamTypeLong(exam.type)] ?? true), + ) .toList(); return RequestDependentWidgetBuilder( - status: examProvider.status, + status: examProvider.requestStatus, builder: () => generateExams(exams, context), hasContentPredicate: exams.isNotEmpty, onNullContent: Center( diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index 2eede80c4..e1f06199c 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -1,8 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; import 'package:uni/generated/l10n.dart'; -import 'package:uni/model/providers/lazy/home_page_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/utils/favorite_widget_type.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; @@ -12,71 +10,74 @@ import 'package:uni/view/home/widgets/exam_card.dart'; import 'package:uni/view/home/widgets/exit_app_dialog.dart'; import 'package:uni/view/home/widgets/restaurant_card.dart'; import 'package:uni/view/home/widgets/schedule_card.dart'; -import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/library/widgets/library_occupation_card.dart'; import 'package:uni/view/profile/widgets/account_info_card.dart'; +import 'package:uni/view/profile/widgets/print_info_card.dart'; + typedef CardCreator = GenericCard Function( Key key, { required bool editingMode, void Function()? onDelete, }); -class MainCardsList extends StatelessWidget { - const MainCardsList({super.key}); +class MainCardsList extends StatefulWidget { + const MainCardsList( + this.favoriteCardTypes, + this.saveFavoriteCards, { + super.key, + }); + + final List favoriteCardTypes; + final void Function(List) saveFavoriteCards; static Map cardCreators = { FavoriteWidgetType.schedule: ScheduleCard.fromEditingInformation, FavoriteWidgetType.exams: ExamCard.fromEditingInformation, FavoriteWidgetType.account: AccountInfoCard.fromEditingInformation, - - // TODO(bdmendes): Bring print card back when it is ready - /*FavoriteWidgetType.printBalance: (k, em, od) => - PrintInfoCard.fromEditingInformation(k, em, od),*/ - + FavoriteWidgetType.printBalance: PrintInfoCard.fromEditingInformation, FavoriteWidgetType.busStops: BusStopCard.fromEditingInformation, FavoriteWidgetType.restaurant: RestaurantCard.fromEditingInformation, FavoriteWidgetType.libraryOccupation: LibraryOccupationCard.fromEditingInformation, }; + @override + State createState() { + return MainCardsListState(); + } +} + +class MainCardsListState extends State { + bool isEditing = false; + @override Widget build(BuildContext context) { - return LazyConsumer( - builder: (context, homePageProvider) => Scaffold( - body: BackButtonExitWrapper( - child: SizedBox( - height: MediaQuery.of(context).size.height, - child: homePageProvider.isEditing - ? ReorderableListView( - onReorder: (oldIndex, newIndex) => reorderCard( - oldIndex, - newIndex, - homePageProvider.favoriteCards, - context, - ), - header: createTopBar(context, homePageProvider), - children: favoriteCardsFromTypes( - homePageProvider.favoriteCards, + return Scaffold( + body: BackButtonExitWrapper( + child: SizedBox( + height: MediaQuery.of(context).size.height, + child: isEditing + ? ReorderableListView( + onReorder: reorderCard, + header: createTopBar(context), + children: favoriteCardsFromTypes( + widget.favoriteCardTypes, + context, + ), + ) + : ListView( + children: [ + createTopBar(context), + ...favoriteCardsFromTypes( + widget.favoriteCardTypes, context, - homePageProvider, ), - ) - : ListView( - children: [ - createTopBar(context, homePageProvider), - ...favoriteCardsFromTypes( - homePageProvider.favoriteCards, - context, - homePageProvider, - ), - ], - ), - ), + ], + ), ), - floatingActionButton: - homePageProvider.isEditing ? createActionButton(context) : null, ), + floatingActionButton: isEditing ? createActionButton(context) : null, ); } @@ -113,14 +114,11 @@ class MainCardsList extends StatelessWidget { } List getCardAdders(BuildContext context) { - final session = - Provider.of(context, listen: false).session; - final favorites = - Provider.of(context, listen: false).favoriteCards; + final session = Provider.of(context, listen: false).state!; - final possibleCardAdditions = cardCreators.entries + final possibleCardAdditions = MainCardsList.cardCreators.entries .where((e) => e.key.isVisible(session.faculties)) - .where((e) => !favorites.contains(e.key)) + .where((e) => !widget.favoriteCardTypes.contains(e.key)) .map( (e) => DecoratedBox( decoration: const BoxDecoration(), @@ -147,7 +145,6 @@ class MainCardsList extends StatelessWidget { Widget createTopBar( BuildContext context, - HomePageProvider editingModeProvider, ) { return Container( padding: const EdgeInsets.fromLTRB(20, 10, 20, 0), @@ -159,24 +156,20 @@ class MainCardsList extends StatelessWidget { center: false, pad: false, ), - if (editingModeProvider.isEditing) + if (isEditing) ElevatedButton( - onPressed: () => - Provider.of(context, listen: false) - .setHomePageEditingMode( - editingMode: false, - ), + onPressed: () => setState(() { + isEditing = false; + }), child: Text( S.of(context).edit_on, ), ) else OutlinedButton( - onPressed: () => - Provider.of(context, listen: false) - .setHomePageEditingMode( - editingMode: true, - ), + onPressed: () => setState(() { + isEditing = true; + }), child: Text( S.of(context).edit_off, ), @@ -189,18 +182,17 @@ class MainCardsList extends StatelessWidget { List favoriteCardsFromTypes( List cardTypes, BuildContext context, - HomePageProvider homePageProvider, ) { final userSession = - Provider.of(context, listen: false).session; + Provider.of(context, listen: false).state!; return cardTypes .where((type) => type.isVisible(userSession.faculties)) - .where((type) => cardCreators.containsKey(type)) + .where((type) => MainCardsList.cardCreators.containsKey(type)) .map((type) { final i = cardTypes.indexOf(type); - return cardCreators[type]!( + return MainCardsList.cardCreators[type]!( Key(i.toString()), - editingMode: homePageProvider.isEditing, + editingMode: isEditing, onDelete: () => removeCardIndexFromFavorites(i, context), ); }).toList(); @@ -209,38 +201,27 @@ class MainCardsList extends StatelessWidget { void reorderCard( int oldIndex, int newIndex, - List favorites, - BuildContext context, ) { - final tmp = favorites[oldIndex]; - favorites + final newFavorites = + List.from(widget.favoriteCardTypes); + final tmp = newFavorites[oldIndex]; + newFavorites ..removeAt(oldIndex) ..insert(oldIndex < newIndex ? newIndex - 1 : newIndex, tmp); - saveFavoriteCards(context, favorites); + widget.saveFavoriteCards(newFavorites); } void removeCardIndexFromFavorites(int i, BuildContext context) { - final favorites = Provider.of(context, listen: false) - .favoriteCards + final favorites = List.from(widget.favoriteCardTypes) ..removeAt(i); - saveFavoriteCards(context, favorites); + widget.saveFavoriteCards(favorites); } void addCardToFavorites(FavoriteWidgetType type, BuildContext context) { - final favorites = - Provider.of(context, listen: false).favoriteCards; + final favorites = List.from(widget.favoriteCardTypes); if (!favorites.contains(type)) { favorites.add(type); } - saveFavoriteCards(context, favorites); - } - - void saveFavoriteCards( - BuildContext context, - List favorites, - ) { - Provider.of(context, listen: false) - .setFavoriteCards(favorites); - AppSharedPreferences.saveFavoriteCards(favorites); + widget.saveFavoriteCards(favorites); } } diff --git a/uni/lib/view/home/widgets/restaurant_card.dart b/uni/lib/view/home/widgets/restaurant_card.dart index cc8274aa0..c7868aaf1 100644 --- a/uni/lib/view/home/widgets/restaurant_card.dart +++ b/uni/lib/view/home/widgets/restaurant_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/meal.dart'; import 'package:uni/model/entities/restaurant.dart'; @@ -39,14 +40,14 @@ class RestaurantCard extends GenericCard { Widget buildCardContent(BuildContext context) { return LazyConsumer( builder: (context, restaurantProvider) { - final favoriteRestaurants = restaurantProvider.restaurants + final favoriteRestaurants = restaurantProvider.state! .where( - (restaurant) => restaurantProvider.favoriteRestaurants + (restaurant) => PreferencesController.getFavoriteRestaurants() .contains(restaurant.name), ) .toList(); return RequestDependentWidgetBuilder( - status: restaurantProvider.status, + status: restaurantProvider.requestStatus, builder: () => generateRestaurants(favoriteRestaurants, context), hasContentPredicate: favoriteRestaurants.isNotEmpty, onNullContent: Column( diff --git a/uni/lib/view/home/widgets/schedule_card.dart b/uni/lib/view/home/widgets/schedule_card.dart index f70d40b61..fd8daf7a4 100644 --- a/uni/lib/view/home/widgets/schedule_card.dart +++ b/uni/lib/view/home/widgets/schedule_card.dart @@ -37,12 +37,12 @@ class ScheduleCard extends GenericCard { Widget buildCardContent(BuildContext context) { return LazyConsumer( builder: (context, lectureProvider) => RequestDependentWidgetBuilder( - status: lectureProvider.status, + status: lectureProvider.requestStatus, builder: () => Column( mainAxisSize: MainAxisSize.min, - children: getScheduleRows(context, lectureProvider.lectures), + children: getScheduleRows(context, lectureProvider.state!), ), - hasContentPredicate: lectureProvider.lectures.isNotEmpty, + hasContentPredicate: lectureProvider.state!.isNotEmpty, onNullContent: Center( child: Text( S.of(context).no_classes, diff --git a/uni/lib/view/lazy_consumer.dart b/uni/lib/view/lazy_consumer.dart index ccb2b0305..bb8295cf2 100644 --- a/uni/lib/view/lazy_consumer.dart +++ b/uni/lib/view/lazy_consumer.dart @@ -14,7 +14,8 @@ import 'package:uni/model/providers/state_provider_notifier.dart'; /// The provider will not reload its data if it has already been loaded before. /// If the provider depends on the session, it will ensure that SessionProvider /// and ProfileProvider are initialized before initializing itself. -class LazyConsumer extends StatelessWidget { +class LazyConsumer> + extends StatelessWidget { const LazyConsumer({ required this.builder, super.key, @@ -25,7 +26,7 @@ class LazyConsumer extends StatelessWidget { @override Widget build(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((_) async { - StateProviderNotifier? provider; + StateProviderNotifier? provider; try { provider = Provider.of(context, listen: false); } catch (e) { @@ -57,21 +58,11 @@ class LazyConsumer extends StatelessWidget { } } - // Load data stored in the database immediately - try { - await provider.ensureInitializedFromStorage(); - } catch (exception, stackTrace) { - Logger().e( - 'Failed to initialize ${T.runtimeType} from storage: $exception', - ); - await Sentry.captureException(exception, stackTrace: stackTrace); - } - - // Finally, complete provider initialization if (context.mounted) { - await sessionFuture?.then((_) async { - await provider!.ensureInitializedFromRemote(context); - }); + await sessionFuture; + if (context.mounted) { + await provider.ensureInitialized(context); + } } }); diff --git a/uni/lib/view/library/library.dart b/uni/lib/view/library/library.dart index c5e48db7e..d3ad494b7 100644 --- a/uni/lib/view/library/library.dart +++ b/uni/lib/view/library/library.dart @@ -21,8 +21,9 @@ class LibraryPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { return LazyConsumer( - builder: (context, libraryOccupationProvider) => - LibraryPage(libraryOccupationProvider.occupation), + builder: (context, libraryOccupationProvider) => LibraryPage( + libraryOccupationProvider.state, + ), ); } @@ -35,6 +36,7 @@ class LibraryPageViewState extends GeneralPageViewState { class LibraryPage extends StatelessWidget { const LibraryPage(this.occupation, {super.key}); + final LibraryOccupation? occupation; @override diff --git a/uni/lib/view/library/widgets/library_occupation_card.dart b/uni/lib/view/library/widgets/library_occupation_card.dart index 05a256194..d112c450d 100644 --- a/uni/lib/view/library/widgets/library_occupation_card.dart +++ b/uni/lib/view/library/widgets/library_occupation_card.dart @@ -38,13 +38,13 @@ class LibraryOccupationCard extends GenericCard { return LazyConsumer( builder: (context, libraryOccupationProvider) => RequestDependentWidgetBuilder( - status: libraryOccupationProvider.status, + status: libraryOccupationProvider.requestStatus, builder: () => generateOccupation( - libraryOccupationProvider.occupation, + libraryOccupationProvider.state, context, ), hasContentPredicate: - libraryOccupationProvider.status != RequestStatus.busy, + libraryOccupationProvider.requestStatus != RequestStatus.busy, onNullContent: const CircularProgressIndicator(), ), ); diff --git a/uni/lib/view/locale_notifier.dart b/uni/lib/view/locale_notifier.dart index 5ecbccda6..008508b49 100644 --- a/uni/lib/view/locale_notifier.dart +++ b/uni/lib/view/locale_notifier.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/model/entities/app_locale.dart'; class LocaleNotifier with ChangeNotifier { @@ -20,7 +20,7 @@ class LocaleNotifier with ChangeNotifier { void setLocale(AppLocale locale) { _locale = locale; - AppSharedPreferences.setLocale(locale); + PreferencesController.setLocale(locale); notifyListeners(); } diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index be9e1d15d..43591f5e6 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -31,8 +31,8 @@ class LocationsPageState extends GeneralPageViewState return LazyConsumer( builder: (context, locationsProvider) { return LocationsPageView( - locations: locationsProvider.locations, - status: locationsProvider.status, + locations: locationsProvider.state!, + status: locationsProvider.requestStatus, ); }, ); diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 4ae9bc379..111af8b3c 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -22,7 +22,7 @@ class ProfilePageViewState extends SecondaryPageViewState { Widget getBody(BuildContext context) { return LazyConsumer( builder: (context, profileStateProvider) { - final profile = profileStateProvider.profile; + final profile = profileStateProvider.state!; final courseWidgets = profile.courses .map( (e) => [ diff --git a/uni/lib/view/profile/widgets/account_info_card.dart b/uni/lib/view/profile/widgets/account_info_card.dart index 3e5eca04d..ab221c051 100644 --- a/uni/lib/view/profile/widgets/account_info_card.dart +++ b/uni/lib/view/profile/widgets/account_info_card.dart @@ -33,8 +33,8 @@ class AccountInfoCard extends GenericCard { builder: (context, profileStateProvider) { return LazyConsumer( builder: (context, referenceProvider) { - final profile = profileStateProvider.profile; - final List references = referenceProvider.references; + final profile = profileStateProvider.state!; + final references = referenceProvider.state!; return Column( children: [ diff --git a/uni/lib/view/profile/widgets/create_print_mb_dialog.dart b/uni/lib/view/profile/widgets/create_print_mb_dialog.dart index 1e6eee537..7198b32a8 100644 --- a/uni/lib/view/profile/widgets/create_print_mb_dialog.dart +++ b/uni/lib/view/profile/widgets/create_print_mb_dialog.dart @@ -135,7 +135,7 @@ Future generateReference(BuildContext context, double amount) async { return; } - final session = Provider.of(context, listen: false).session; + final session = Provider.of(context, listen: false).state!; final response = await PrintFetcher.generatePrintMoneyReference(amount, session); diff --git a/uni/lib/view/profile/widgets/print_info_card.dart b/uni/lib/view/profile/widgets/print_info_card.dart index b2acae5a8..f61808b53 100644 --- a/uni/lib/view/profile/widgets/print_info_card.dart +++ b/uni/lib/view/profile/widgets/print_info_card.dart @@ -19,7 +19,7 @@ class PrintInfoCard extends GenericCard { Widget buildCardContent(BuildContext context) { return LazyConsumer( builder: (context, profileStateProvider) { - final profile = profileStateProvider.profile; + final profile = profileStateProvider.state!; return Column( mainAxisSize: MainAxisSize.min, children: [ diff --git a/uni/lib/view/profile/widgets/profile_overview.dart b/uni/lib/view/profile/widgets/profile_overview.dart index fcc49a8fc..b0ad87195 100644 --- a/uni/lib/view/profile/widgets/profile_overview.dart +++ b/uni/lib/view/profile/widgets/profile_overview.dart @@ -12,53 +12,51 @@ class ProfileOverview extends StatelessWidget { required this.getProfileDecorationImage, super.key, }); + final Profile profile; final DecorationImage Function(File?) getProfileDecorationImage; @override Widget build(BuildContext context) { - return Consumer( - builder: (context, sessionProvider, _) { - return FutureBuilder( - future: ProfileProvider.fetchOrGetCachedProfilePicture( - sessionProvider.session, + final session = context.read().state!; + return FutureBuilder( + future: ProfileProvider.fetchOrGetCachedProfilePicture( + session, + ), + builder: (BuildContext context, AsyncSnapshot profilePic) => + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 150, + height: 150, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: profilePic.data != null + ? getProfileDecorationImage(profilePic.data) + : null, + ), + ), + const Padding(padding: EdgeInsets.all(8)), + Text( + profile.name, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.w400, + ), ), - builder: (BuildContext context, AsyncSnapshot profilePic) => - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: 150, - height: 150, - decoration: BoxDecoration( - shape: BoxShape.circle, - image: profilePic.data != null - ? getProfileDecorationImage(profilePic.data) - : null, - ), - ), - const Padding(padding: EdgeInsets.all(8)), - Text( - profile.name, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.w400, - ), - ), - const Padding(padding: EdgeInsets.all(5)), - Text( - profile.email, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w300, - ), - ), - ], + const Padding(padding: EdgeInsets.all(5)), + Text( + profile.email, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w300, + ), ), - ); - }, + ], + ), ); } } diff --git a/uni/lib/view/restaurant/restaurant_page_view.dart b/uni/lib/view/restaurant/restaurant_page_view.dart index a31fefe11..ce108b7c6 100644 --- a/uni/lib/view/restaurant/restaurant_page_view.dart +++ b/uni/lib/view/restaurant/restaurant_page_view.dart @@ -67,12 +67,12 @@ class _RestaurantPageViewState extends GeneralPageViewState const SizedBox(height: 10), Expanded( child: RequestDependentWidgetBuilder( - status: restaurantProvider.status, + status: restaurantProvider.requestStatus, builder: () => createTabViewBuilder( - restaurantProvider.restaurants, + restaurantProvider.state!, context, ), - hasContentPredicate: restaurantProvider.restaurants.isNotEmpty, + hasContentPredicate: restaurantProvider.state!.isNotEmpty, onNullContent: Center( child: Text( S.of(context).no_menus, diff --git a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart index 7e323e1c8..908531997 100644 --- a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart +++ b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; -import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/restaurant.dart'; -import 'package:uni/model/providers/lazy/home_page_provider.dart'; import 'package:uni/model/providers/lazy/restaurant_provider.dart'; import 'package:uni/utils/favorite_widget_type.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; @@ -39,29 +38,39 @@ class RestaurantPageCard extends GenericCard { class CardFavoriteButton extends StatelessWidget { const CardFavoriteButton(this.restaurant, {super.key}); + final Restaurant restaurant; @override Widget build(BuildContext context) { return LazyConsumer( builder: (context, restaurantProvider) { - final isFavorite = - restaurantProvider.favoriteRestaurants.contains(restaurant.name); + final isFavorite = PreferencesController.getFavoriteRestaurants() + .contains(restaurant.name); return IconButton( icon: isFavorite ? Icon(MdiIcons.heart) : Icon(MdiIcons.heartOutline), - onPressed: () { - restaurantProvider.toggleFavoriteRestaurant( - restaurant.name, + onPressed: () async { + final favoriteRestaurants = + PreferencesController.getFavoriteRestaurants(); + if (favoriteRestaurants.contains(restaurant.name)) { + favoriteRestaurants.remove(restaurant.name); + } else { + favoriteRestaurants.add(restaurant.name); + } + await PreferencesController.saveFavoriteRestaurants( + favoriteRestaurants, ); + final favoriteCardTypes = - context.read().favoriteCards; - if (!isFavorite && + PreferencesController.getFavoriteCards(); + if (context.mounted && + !isFavorite && !favoriteCardTypes.contains(FavoriteWidgetType.restaurant)) { - showRestaurantCardHomeDialog(context, favoriteCardTypes, - (newFavoriteCards) { - Provider.of(context, listen: false) - .setFavoriteCards(newFavoriteCards); - }); + showRestaurantCardHomeDialog( + context, + favoriteCardTypes, + PreferencesController.saveFavoriteCards, + ); } }, ); diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index 8ebe3664f..9fe05b05b 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -26,8 +26,8 @@ class SchedulePageState extends State { return LazyConsumer( builder: (context, lectureProvider) { return SchedulePageView( - lectures: lectureProvider.lectures, - scheduleStatus: lectureProvider.status, + lectures: lectureProvider.state!, + scheduleStatus: lectureProvider.requestStatus, ); }, ); diff --git a/uni/lib/view/settings/widgets/tuition_notification_switch.dart b/uni/lib/view/settings/widgets/tuition_notification_switch.dart index 9abd7766f..71eb04dc9 100644 --- a/uni/lib/view/settings/widgets/tuition_notification_switch.dart +++ b/uni/lib/view/settings/widgets/tuition_notification_switch.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; class TuitionNotificationSwitch extends StatefulWidget { const TuitionNotificationSwitch({super.key}); @@ -9,21 +9,11 @@ class TuitionNotificationSwitch extends StatefulWidget { } class _TuitionNotificationSwitchState extends State { - bool tuitionNotificationToggle = true; - - @override - void initState() { - super.initState(); - getTuitionNotificationToggle(); - } - - Future getTuitionNotificationToggle() async { - await AppSharedPreferences.getTuitionNotificationToggle() - .then((value) => setState(() => tuitionNotificationToggle = value)); - } + bool tuitionNotificationToggle = PreferencesController + .getTuitionNotificationToggle(); Future saveTuitionNotificationToggle({required bool value}) async { - await AppSharedPreferences.setTuitionNotificationToggle(value: value); + await PreferencesController.setTuitionNotificationToggle(value: value); setState(() { tuitionNotificationToggle = value; }); diff --git a/uni/lib/view/settings/widgets/usage_stats_switch.dart b/uni/lib/view/settings/widgets/usage_stats_switch.dart index 54d1baa5b..049f02cde 100644 --- a/uni/lib/view/settings/widgets/usage_stats_switch.dart +++ b/uni/lib/view/settings/widgets/usage_stats_switch.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; class UsageStatsSwitch extends StatefulWidget { const UsageStatsSwitch({super.key}); @@ -9,21 +9,10 @@ class UsageStatsSwitch extends StatefulWidget { } class _UsageStatsSwitchState extends State { - bool usageStatsToggle = true; - - @override - void initState() { - super.initState(); - getUsageStatsToggle(); - } - - Future getUsageStatsToggle() async { - await AppSharedPreferences.getUsageStatsToggle() - .then((value) => setState(() => usageStatsToggle = value)); - } + bool usageStatsToggle = PreferencesController.getUsageStatsToggle(); Future saveUsageStatsToggle({required bool value}) async { - await AppSharedPreferences.setUsageStatsToggle(value: value); + await PreferencesController.setUsageStatsToggle(value: value); setState(() { usageStatsToggle = value; }); diff --git a/uni/lib/view/terms_and_condition_dialog.dart b/uni/lib/view/terms_and_condition_dialog.dart index 5f4027ea0..50f72ccd6 100644 --- a/uni/lib/view/terms_and_condition_dialog.dart +++ b/uni/lib/view/terms_and_condition_dialog.dart @@ -2,8 +2,8 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:uni/controller/load_static/terms_and_conditions.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/fetchers/terms_and_conditions_fetcher.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/view/about/widgets/terms_and_conditions.dart'; enum TermsAndConditionsState { accepted, rejected } @@ -57,7 +57,7 @@ class TermsAndConditionDialog { Navigator.of(context).pop(); userTermsDecision .complete(TermsAndConditionsState.accepted); - await AppSharedPreferences + await PreferencesController .setTermsAndConditionsAcceptance(areAccepted: true); }, child: const Text( @@ -72,7 +72,7 @@ class TermsAndConditionDialog { Navigator.of(context).pop(); userTermsDecision .complete(TermsAndConditionsState.rejected); - await AppSharedPreferences + await PreferencesController .setTermsAndConditionsAcceptance(areAccepted: false); }, child: const Text( diff --git a/uni/lib/view/theme_notifier.dart b/uni/lib/view/theme_notifier.dart index 0ce1ffd5a..65374093a 100644 --- a/uni/lib/view/theme_notifier.dart +++ b/uni/lib/view/theme_notifier.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; class ThemeNotifier with ChangeNotifier { ThemeNotifier(this._themeMode); @@ -15,7 +15,7 @@ class ThemeNotifier with ChangeNotifier { void setTheme(ThemeMode themeMode) { _themeMode = themeMode; - AppSharedPreferences.setThemeMode(themeMode); + PreferencesController.setThemeMode(themeMode); notifyListeners(); } } diff --git a/uni/test/integration/src/exams_page_test.dart b/uni/test/integration/src/exams_page_test.dart index 4e37ebd3f..956b365a2 100644 --- a/uni/test/integration/src/exams_page_test.dart +++ b/uni/test/integration/src/exams_page_test.dart @@ -68,7 +68,7 @@ void main() { final examProvider = ExamProvider(); - const widget = ExamsPageView(); + final widget = ExamsPageView(); final providers = [ ChangeNotifierProvider(create: (_) => examProvider), @@ -87,7 +87,7 @@ void main() { persistentSession: false, ); - examProvider.markAsInitialized(); + examProvider.invalidate(); await tester.pumpAndSettle(); expect(find.byKey(Key('$sdisExam-exam')), findsOneWidget); @@ -95,7 +95,8 @@ void main() { expect(find.byKey(Key('$mdisExam-exam')), findsNothing); }); - testWidgets('Filtered Exams', (WidgetTester tester) async { + // TODO(bdmendes): Bring back filtered exams tests + /*testWidgets('Filtered Exams', (WidgetTester tester) async { NetworkRouter.httpClient = mockClient; final mockHtml = File('test/integration/resources/exam_example.html') .readAsStringSync(); @@ -106,7 +107,7 @@ void main() { final examProvider = ExamProvider(); - const widget = ExamsPageView(); + final widget = ExamsPageView(); final providers = [ ChangeNotifierProvider(create: (_) => examProvider), @@ -125,7 +126,7 @@ void main() { persistentSession: false, ); - examProvider.markAsInitialized(); + examProvider.invalidate(); await tester.pumpAndSettle(); expect(find.byKey(Key('$sdisExam-exam')), findsOneWidget); @@ -134,8 +135,6 @@ void main() { filteredExams['ExamDoesNotExist'] = true; - await examProvider.setFilteredExams(filteredExams); - await tester.pumpAndSettle(); final filterButton = find.widgetWithIcon(IconButton, Icons.filter_alt); @@ -166,6 +165,6 @@ void main() { expect(find.byKey(Key('$sdisExam-exam')), findsNothing); expect(find.byKey(Key('$sopeExam-exam')), findsNothing); - }); + });*/ }); } diff --git a/uni/test/integration/src/schedule_page_test.dart b/uni/test/integration/src/schedule_page_test.dart index bfa07b2a2..687a61abb 100644 --- a/uni/test/integration/src/schedule_page_test.dart +++ b/uni/test/integration/src/schedule_page_test.dart @@ -75,7 +75,7 @@ void main() { persistentSession: false, ); - scheduleProvider.markAsInitialized(); + scheduleProvider.invalidate(); await tester.tap(find.byKey(const Key('schedule-page-tab-2'))); await tester.pumpAndSettle(); diff --git a/uni/test/unit/providers/exams_provider_test.dart b/uni/test/unit/providers/exams_provider_test.dart index d424a2a48..7d82c1831 100644 --- a/uni/test/unit/providers/exams_provider_test.dart +++ b/uni/test/unit/providers/exams_provider_test.dart @@ -73,7 +73,7 @@ void main() { setUp(() { provider = ExamProvider(); - expect(provider.status, RequestStatus.busy); + expect(provider.requestStatus, RequestStatus.busy); }); test('When given one exam', () async { @@ -88,8 +88,8 @@ void main() { persistentSession: false, ); - expect(provider.exams.isNotEmpty, true); - expect(provider.exams, [sopeExam]); + expect(provider.state!.isNotEmpty, true); + expect(provider.state, [sopeExam]); }); test('When given two exams', () async { @@ -104,7 +104,7 @@ void main() { persistentSession: false, ); - expect(provider.exams, [sopeExam, sdisExam]); + expect(provider.state, [sopeExam, sdisExam]); }); test(''' @@ -133,7 +133,7 @@ When given three exams but one is to be parsed out, persistentSession: false, ); - expect(provider.exams, [sopeExam, sdisExam]); + expect(provider.state, [sopeExam, sdisExam]); }); test('When an error occurs while trying to obtain the exams', () async { @@ -175,7 +175,7 @@ When given three exams but one is to be parsed out, persistentSession: false, ); - expect(provider.exams, [todayExam]); + expect(provider.state, [todayExam]); }); test('When Exam was one hour ago', () async { @@ -202,7 +202,7 @@ When given three exams but one is to be parsed out, persistentSession: false, ); - expect(provider.exams, []); + expect(provider.state, []); }); test('When Exam is ocurring', () async { @@ -229,7 +229,7 @@ When given three exams but one is to be parsed out, persistentSession: false, ); - expect(provider.exams, [todayExam]); + expect(provider.state, [todayExam]); }); }); } diff --git a/uni/test/unit/providers/lecture_provider_test.dart b/uni/test/unit/providers/lecture_provider_test.dart index 78a1bbb3b..53302ad94 100644 --- a/uni/test/unit/providers/lecture_provider_test.dart +++ b/uni/test/unit/providers/lecture_provider_test.dart @@ -56,7 +56,7 @@ void main() { late LectureProvider provider; setUp(() { provider = LectureProvider(); - expect(provider.status, RequestStatus.busy); + expect(provider.requestStatus, RequestStatus.busy); }); test('When given a single schedule', () async { @@ -70,7 +70,7 @@ void main() { persistentSession: false, ); - expect(provider.lectures, [lecture1, lecture2]); + expect(provider.state, [lecture1, lecture2]); }); test('When an error occurs while trying to obtain the schedule', () async { diff --git a/uni/test/unit/view/Pages/exams_page_view_test.dart b/uni/test/unit/view/Pages/exams_page_view_test.dart index d4e3a227f..f8b164904 100644 --- a/uni/test/unit/view/Pages/exams_page_view_test.dart +++ b/uni/test/unit/view/Pages/exams_page_view_test.dart @@ -18,8 +18,8 @@ void main() { const secondExamDate = '2019-09-12'; testWidgets('When given an empty list', (WidgetTester tester) async { - const widget = ExamsPageView(); - final examProvider = ExamProvider()..exams = []; + final widget = ExamsPageView(); + final examProvider = ExamProvider()..updateState([]); final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; @@ -41,9 +41,9 @@ void main() { 'feup', ); - const widget = ExamsPageView(); + final widget = ExamsPageView(); - final examProvider = ExamProvider()..exams = [firstExam]; + final examProvider = ExamProvider()..updateState([firstExam]); final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; @@ -84,9 +84,9 @@ void main() { secondExam, ]; - const widget = ExamsPageView(); + final widget = ExamsPageView(); - final examProvider = ExamProvider()..exams = examList; + final examProvider = ExamProvider()..updateState(examList); final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; @@ -130,9 +130,9 @@ void main() { secondExam, ]; - const widget = ExamsPageView(); + final widget = ExamsPageView(); - final examProvider = ExamProvider()..exams = examList; + final examProvider = ExamProvider()..updateState(examList); final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; @@ -194,9 +194,9 @@ void main() { ); final examList = [firstExam, secondExam, thirdExam, fourthExam]; - const widget = ExamsPageView(); + final widget = ExamsPageView(); - final examProvider = ExamProvider()..exams = examList; + final examProvider = ExamProvider()..updateState(examList); final firstDayKey = [firstExam, secondExam].map((ex) => ex.toString()).join(); From 2e5c2965848b15b4d0e8ca946722cce023b7597e Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Thu, 14 Dec 2023 22:02:15 +0000 Subject: [PATCH 070/334] Remove RequestDependentWidgetBuilder --- .../local_storage/preferences_controller.dart | 3 +- uni/lib/main.dart | 4 +- .../providers/lazy/bus_stop_provider.dart | 10 +- .../providers/lazy/calendar_provider.dart | 19 +- .../lazy/course_units_info_provider.dart | 12 +- .../model/providers/lazy/exam_provider.dart | 23 ++- .../lazy/faculty_locations_provider.dart | 9 +- .../providers/lazy/lecture_provider.dart | 9 +- .../lazy/library_occupation_provider.dart | 10 +- .../providers/lazy/reference_provider.dart | 11 +- .../providers/lazy/restaurant_provider.dart | 13 +- .../providers/startup/profile_provider.dart | 7 +- .../providers/startup/session_provider.dart | 8 +- .../providers/state_provider_notifier.dart | 39 ++-- .../bus_stop_next_arrivals.dart | 14 +- .../widgets/estimated_arrival_timestamp.dart | 5 +- .../bus_stop_selection.dart | 5 +- uni/lib/view/calendar/calendar.dart | 36 ++-- .../common_widgets/last_update_timestamp.dart | 8 +- .../pages_layouts/general/general.dart | 125 ++++++------ .../request_dependent_widget_builder.dart | 109 ----------- .../course_unit_info/course_unit_info.dart | 26 ++- uni/lib/view/course_units/course_units.dart | 52 +++-- uni/lib/view/exams/exams.dart | 49 +++-- uni/lib/view/home/widgets/bus_stop_card.dart | 106 ++++------ uni/lib/view/home/widgets/exam_card.dart | 29 ++- .../view/home/widgets/main_cards_list.dart | 181 ++++++++++-------- .../view/home/widgets/restaurant_card.dart | 53 +++-- uni/lib/view/home/widgets/schedule_card.dart | 28 ++- uni/lib/view/lazy_consumer.dart | 112 ++++++++++- uni/lib/view/library/library.dart | 12 +- .../widgets/library_occupation_card.dart | 19 +- uni/lib/view/locations/locations.dart | 58 ++---- uni/lib/view/profile/profile.dart | 8 +- .../profile/widgets/account_info_card.dart | 164 ++++++++-------- .../view/profile/widgets/print_info_card.dart | 29 ++- .../view/restaurant/restaurant_page_view.dart | 77 ++++---- .../widgets/restaurant_page_card.dart | 57 +++--- uni/lib/view/schedule/schedule.dart | 63 +++--- .../unit/providers/exams_provider_test.dart | 18 +- .../unit/providers/lecture_provider_test.dart | 16 +- 41 files changed, 803 insertions(+), 833 deletions(-) delete mode 100644 uni/lib/view/common_widgets/request_dependent_widget_builder.dart diff --git a/uni/lib/controller/local_storage/preferences_controller.dart b/uni/lib/controller/local_storage/preferences_controller.dart index 1f6cff50b..0aee5a5eb 100644 --- a/uni/lib/controller/local_storage/preferences_controller.dart +++ b/uni/lib/controller/local_storage/preferences_controller.dart @@ -15,7 +15,8 @@ import 'package:uni/utils/favorite_widget_type.dart'; /// This database stores the user's student number, password and favorite /// widgets. class PreferencesController { - static SharedPreferences get prefs => Application.sharedPreferences; + // TODO: Initilizate this also on workmanager + static late SharedPreferences prefs; static final iv = encrypt.IV.fromBase64('jF9jjdSEPgsKnf0jCl1GAQ=='); static final key = diff --git a/uni/lib/main.dart b/uni/lib/main.dart index fa4dfa199..8d3426922 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -61,7 +61,7 @@ Future firstRoute() async { Future main() async { WidgetsFlutterBinding.ensureInitialized(); - Application.sharedPreferences = await SharedPreferences.getInstance(); + PreferencesController.prefs = await SharedPreferences.getInstance(); final stateProviders = StateProviders( LectureProvider(), @@ -165,8 +165,6 @@ class Application extends StatefulWidget { static GlobalKey navigatorKey = GlobalKey(); - static late SharedPreferences sharedPreferences; - @override State createState() => ApplicationState(); } diff --git a/uni/lib/model/providers/lazy/bus_stop_provider.dart b/uni/lib/model/providers/lazy/bus_stop_provider.dart index 3e3769041..e7031094e 100644 --- a/uni/lib/model/providers/lazy/bus_stop_provider.dart +++ b/uni/lib/model/providers/lazy/bus_stop_provider.dart @@ -3,23 +3,23 @@ import 'dart:async'; import 'package:uni/controller/fetchers/departures_fetcher.dart'; import 'package:uni/controller/local_storage/database/app_bus_stop_database.dart'; import 'package:uni/model/entities/bus_stop.dart'; -import 'package:uni/model/entities/profile.dart'; -import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; class BusStopProvider extends StateProviderNotifier> { BusStopProvider() : super(cacheDuration: null); @override - Future> loadFromStorage() { + Future> loadFromStorage( + StateProviders stateProviders, + ) { final busStopsDb = AppBusStopDatabase(); return busStopsDb.busStops(); } @override Future> loadFromRemote( - Session session, - Profile profile, + StateProviders stateProviders, ) async { return fetchUserBusTrips(state!); } diff --git a/uni/lib/model/providers/lazy/calendar_provider.dart b/uni/lib/model/providers/lazy/calendar_provider.dart index b660e55d0..51de622ac 100644 --- a/uni/lib/model/providers/lazy/calendar_provider.dart +++ b/uni/lib/model/providers/lazy/calendar_provider.dart @@ -6,24 +6,27 @@ import 'package:uni/model/entities/calendar_event.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; class CalendarProvider extends StateProviderNotifier> { CalendarProvider() : super(cacheDuration: const Duration(days: 30)); @override - Future> loadFromRemote( - Session session, - Profile profile, + Future> loadFromStorage( + StateProviders stateProviders, ) async { - final calendar = await CalendarFetcherHtml().getCalendar(session); final db = CalendarDatabase(); - unawaited(db.saveCalendar(calendar)); - return calendar; + return db.calendar(); } @override - Future> loadFromStorage() async { + Future> loadFromRemote( + StateProviders stateProviders, + ) async { + final session = stateProviders.sessionProvider.state!; + final calendar = await CalendarFetcherHtml().getCalendar(session); final db = CalendarDatabase(); - return db.calendar(); + unawaited(db.saveCalendar(calendar)); + return calendar; } } diff --git a/uni/lib/model/providers/lazy/course_units_info_provider.dart b/uni/lib/model/providers/lazy/course_units_info_provider.dart index 83b5ae7d0..817bb9754 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -8,13 +8,16 @@ import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; typedef SheetsMap = Map; typedef ClassesMap = Map>; class CourseUnitsInfoProvider extends StateProviderNotifier> { - CourseUnitsInfoProvider() : super(cacheDuration: null); + CourseUnitsInfoProvider() : super(cacheDuration: null) { + updateState(const Tuple2({}, {})); + } UnmodifiableMapView get courseUnitsSheets => UnmodifiableMapView(state!.item1); @@ -41,14 +44,15 @@ class CourseUnitsInfoProvider @override Future> loadFromRemote( - Session session, - Profile profile, + StateProviders stateProviders, ) async { return const Tuple2({}, {}); } @override - Future> loadFromStorage() async { + Future> loadFromStorage( + StateProviders stateProviders, + ) async { return const Tuple2({}, {}); } } diff --git a/uni/lib/model/providers/lazy/exam_provider.dart b/uni/lib/model/providers/lazy/exam_provider.dart index 37c9271f9..bb53b1f92 100644 --- a/uni/lib/model/providers/lazy/exam_provider.dart +++ b/uni/lib/model/providers/lazy/exam_provider.dart @@ -9,35 +9,38 @@ import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; class ExamProvider extends StateProviderNotifier> { ExamProvider() : super(cacheDuration: const Duration(days: 1)); @override - Future> loadFromStorage() async { + Future> loadFromStorage(StateProviders stateProviders) async { final db = AppExamsDatabase(); return db.exams(); } @override - Future> loadFromRemote(Session session, Profile profile) async { + Future> loadFromRemote(StateProviders stateProviders) async { + final session = stateProviders.sessionProvider.state!; + final profile = stateProviders.profileProvider.state!; + return fetchUserExams( ParserExams(), profile, session, profile.courseUnits, persistentSession: - (PreferencesController.getPersistentUserInfo()) != null, + (PreferencesController.getPersistentUserInfo()) != null, ); } - Future> fetchUserExams( - ParserExams parserExams, - Profile profile, - Session session, - List userUcs, { - required bool persistentSession, - }) async { + Future> fetchUserExams(ParserExams parserExams, + Profile profile, + Session session, + List userUcs, { + required bool persistentSession, + }) async { final exams = await ExamFetcher(profile.courses, userUcs) .extractExams(session, parserExams); diff --git a/uni/lib/model/providers/lazy/faculty_locations_provider.dart b/uni/lib/model/providers/lazy/faculty_locations_provider.dart index 9f6cd29a9..7547a2abd 100644 --- a/uni/lib/model/providers/lazy/faculty_locations_provider.dart +++ b/uni/lib/model/providers/lazy/faculty_locations_provider.dart @@ -1,23 +1,20 @@ import 'package:uni/controller/fetchers/location_fetcher/location_fetcher_asset.dart'; import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/model/entities/profile.dart'; -import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; class FacultyLocationsProvider extends StateProviderNotifier> { FacultyLocationsProvider() : super(cacheDuration: const Duration(days: 30)); @override - Future> loadFromStorage() { + Future> loadFromStorage(StateProviders stateProviders) { return LocationFetcherAsset().getLocations(); } @override Future> loadFromRemote( - Session session, - Profile profile, - ) async { + StateProviders stateProviders) async { return state!; } } diff --git a/uni/lib/model/providers/lazy/lecture_provider.dart b/uni/lib/model/providers/lazy/lecture_provider.dart index 82cce468b..3082bd66e 100644 --- a/uni/lib/model/providers/lazy/lecture_provider.dart +++ b/uni/lib/model/providers/lazy/lecture_provider.dart @@ -9,21 +9,22 @@ import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; class LectureProvider extends StateProviderNotifier> { LectureProvider() : super(cacheDuration: const Duration(hours: 6)); @override - Future> loadFromStorage() async { + Future> loadFromStorage(StateProviders stateProviders) async { final db = AppLecturesDatabase(); return db.lectures(); } @override - Future> loadFromRemote(Session session, Profile profile) async { + Future> loadFromRemote(StateProviders stateProviders) async { return fetchUserLectures( - session, - profile, + stateProviders.sessionProvider.state!, + stateProviders.profileProvider.state!, persistentSession: (PreferencesController.getPersistentUserInfo()) != null, ); diff --git a/uni/lib/model/providers/lazy/library_occupation_provider.dart b/uni/lib/model/providers/lazy/library_occupation_provider.dart index ac4ba0781..8ec915390 100644 --- a/uni/lib/model/providers/lazy/library_occupation_provider.dart +++ b/uni/lib/model/providers/lazy/library_occupation_provider.dart @@ -3,25 +3,25 @@ import 'dart:async'; import 'package:uni/controller/fetchers/library_occupation_fetcher.dart'; import 'package:uni/controller/local_storage/database/app_library_occupation_database.dart'; import 'package:uni/model/entities/library_occupation.dart'; -import 'package:uni/model/entities/profile.dart'; -import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; class LibraryOccupationProvider extends StateProviderNotifier { LibraryOccupationProvider() : super(cacheDuration: const Duration(hours: 1)); @override - Future loadFromStorage() async { + Future loadFromStorage( + StateProviders stateProviders) async { final db = LibraryOccupationDatabase(); return db.occupation(); } @override Future loadFromRemote( - Session session, - Profile profile, + StateProviders stateProviders, ) async { + final session = stateProviders.sessionProvider.state!; final occupation = await LibraryOccupationFetcherSheets() .getLibraryOccupationFromSheets(session); diff --git a/uni/lib/model/providers/lazy/reference_provider.dart b/uni/lib/model/providers/lazy/reference_provider.dart index 0f747376a..f2e9effba 100644 --- a/uni/lib/model/providers/lazy/reference_provider.dart +++ b/uni/lib/model/providers/lazy/reference_provider.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:flutter/cupertino.dart'; import 'package:uni/controller/fetchers/reference_fetcher.dart'; import 'package:uni/controller/local_storage/database/app_references_database.dart'; import 'package:uni/controller/parsers/parser_references.dart'; @@ -7,21 +8,21 @@ import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/reference.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; class ReferenceProvider extends StateProviderNotifier> { ReferenceProvider() : super(cacheDuration: const Duration(hours: 1)); @override - Future> loadFromStorage() { + Future> loadFromStorage(StateProviders stateProviders) { final referencesDb = AppReferencesDatabase(); return referencesDb.references(); } @override - Future> loadFromRemote( - Session session, - Profile profile, - ) async { + Future> loadFromRemote(StateProviders stateProviders) async { + final session = stateProviders.sessionProvider.state!; + final response = await ReferenceFetcher().getUserReferenceResponse(session); final references = await parseReferences(response); diff --git a/uni/lib/model/providers/lazy/restaurant_provider.dart b/uni/lib/model/providers/lazy/restaurant_provider.dart index 434b6a952..0721d3754 100644 --- a/uni/lib/model/providers/lazy/restaurant_provider.dart +++ b/uni/lib/model/providers/lazy/restaurant_provider.dart @@ -2,26 +2,25 @@ import 'dart:async'; import 'package:uni/controller/fetchers/restaurant_fetcher.dart'; import 'package:uni/controller/local_storage/database/app_restaurant_database.dart'; -import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/restaurant.dart'; -import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; class RestaurantProvider extends StateProviderNotifier> { RestaurantProvider() : super(cacheDuration: const Duration(days: 1)); @override - Future> loadFromStorage() async { + Future> loadFromStorage( + StateProviders stateProviders, + ) async { final restaurantDb = RestaurantDatabase(); final restaurants = await restaurantDb.getRestaurants(); return restaurants; } @override - Future> loadFromRemote( - Session session, - Profile profile, - ) async { + Future> loadFromRemote(StateProviders stateProviders) async { + final session = stateProviders.sessionProvider.state!; final restaurants = await RestaurantFetcher().getRestaurants(session); final db = RestaurantDatabase(); diff --git a/uni/lib/model/providers/startup/profile_provider.dart b/uni/lib/model/providers/startup/profile_provider.dart index 236124b74..015d3753e 100644 --- a/uni/lib/model/providers/startup/profile_provider.dart +++ b/uni/lib/model/providers/startup/profile_provider.dart @@ -19,13 +19,14 @@ import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; class ProfileProvider extends StateProviderNotifier { ProfileProvider() : super(cacheDuration: const Duration(days: 1), dependsOnSession: false); @override - Future loadFromStorage() async { + Future loadFromStorage(StateProviders stateProviders) async { final profile = await loadProfile(); profile ..courses = await loadCourses() @@ -34,7 +35,9 @@ class ProfileProvider extends StateProviderNotifier { } @override - Future loadFromRemote(Session session, Profile profile) async { + Future loadFromRemote(StateProviders stateProviders) async { + final session = stateProviders.sessionProvider.state!; + final profile = await fetchUserInfo(session); final userBalanceAndFeesLimit = await fetchUserFeesBalanceAndLimit(session); diff --git a/uni/lib/model/providers/startup/session_provider.dart b/uni/lib/model/providers/startup/session_provider.dart index 2f42057b1..2d9567519 100644 --- a/uni/lib/model/providers/startup/session_provider.dart +++ b/uni/lib/model/providers/startup/session_provider.dart @@ -8,20 +8,22 @@ import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/controller/parsers/parser_session.dart'; import 'package:uni/model/entities/login_exceptions.dart'; -import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/providers/state_providers.dart'; +import 'package:uni/model/request_status.dart'; import 'package:uni/view/locale_notifier.dart'; class SessionProvider extends StateProviderNotifier { SessionProvider() : super( cacheDuration: null, + initialStatus: RequestStatus.none, dependsOnSession: false, ); @override - Future loadFromStorage() async { + Future loadFromStorage(StateProviders stateProviders) async { final userPersistentInfo = PreferencesController.getPersistentUserInfo(); final faculties = PreferencesController.getUserFaculties(); @@ -38,7 +40,7 @@ class SessionProvider extends StateProviderNotifier { } @override - Future loadFromRemote(Session session, Profile profile) async { + Future loadFromRemote(StateProviders stateProviders) async { return state!; } diff --git a/uni/lib/model/providers/state_provider_notifier.dart b/uni/lib/model/providers/state_provider_notifier.dart index 7f68449ba..ada5001f8 100644 --- a/uni/lib/model/providers/state_provider_notifier.dart +++ b/uni/lib/model/providers/state_provider_notifier.dart @@ -3,21 +3,18 @@ import 'dart:async'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; -import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:synchronized/synchronized.dart'; import 'package:uni/controller/local_storage/preferences_controller.dart'; -import 'package:uni/model/entities/profile.dart'; -import 'package:uni/model/entities/session.dart'; -import 'package:uni/model/providers/startup/profile_provider.dart'; -import 'package:uni/model/providers/startup/session_provider.dart'; +import 'package:uni/model/providers/state_providers.dart'; import 'package:uni/model/request_status.dart'; abstract class StateProviderNotifier extends ChangeNotifier { StateProviderNotifier({ required this.cacheDuration, this.dependsOnSession = true, - }); + RequestStatus initialStatus = RequestStatus.busy, + }) : _requestStatus = initialStatus; /// The model that this notifier provides. /// This future will throw if the data loading fails. @@ -27,7 +24,7 @@ abstract class StateProviderNotifier extends ChangeNotifier { bool dependsOnSession; /// The data loading request status. - RequestStatus _requestStatus = RequestStatus.none; + RequestStatus _requestStatus; /// The timeout for concurrent state change operations. static const _lockTimeout = Duration(seconds: 30); @@ -50,13 +47,13 @@ abstract class StateProviderNotifier extends ChangeNotifier { /// Gets the model from the local database. /// This method such not catch data loading errors. - Future loadFromStorage(); + Future loadFromStorage(StateProviders stateProviders); /// Gets the model from the remote server. /// This will run once when the provider is first initialized. /// This method must not catch data loading errors. /// This method should save data in the database, if appropriate. - Future loadFromRemote(Session session, Profile profile); + Future loadFromRemote(StateProviders stateProviders); /// Update the current model state, notifying the listeners. /// This should be called only to modify the model after @@ -72,7 +69,7 @@ abstract class StateProviderNotifier extends ChangeNotifier { _state = null; } - Future _loadFromStorage() async { + Future _loadFromStorage(BuildContext context) async { Logger().d('Loading $runtimeType info from storage'); _updateStatus(RequestStatus.busy); @@ -82,7 +79,7 @@ abstract class StateProviderNotifier extends ChangeNotifier { ); try { - updateState(await loadFromStorage()); + updateState(await loadFromStorage(StateProviders.fromContext(context))); } catch (e, stackTrace) { await Sentry.captureException(e, stackTrace: stackTrace); Logger() @@ -93,9 +90,8 @@ abstract class StateProviderNotifier extends ChangeNotifier { Logger().i('Loaded $runtimeType info from storage'); } - Future _loadFromRemote( - Session session, - Profile profile, { + Future _loadFromRemoteFromContext( + BuildContext context, { bool force = false, }) async { Logger().d('Loading $runtimeType info from remote'); @@ -124,7 +120,10 @@ abstract class StateProviderNotifier extends ChangeNotifier { } try { - updateState(await loadFromRemote(session, profile)); + if (!context.mounted) { + return; + } + updateState(await loadFromRemote(StateProviders.fromContext(context))); Logger().i('Loaded $runtimeType info from remote'); _lastUpdateTime = DateTime.now(); @@ -154,9 +153,7 @@ abstract class StateProviderNotifier extends ChangeNotifier { if (!context.mounted) { return; } - final session = context.read().state!; - final profile = context.read().state!; - await _loadFromRemote(session, profile, force: true); + await _loadFromRemoteFromContext(context, force: true); }, timeout: _lockTimeout, ); @@ -169,12 +166,10 @@ abstract class StateProviderNotifier extends ChangeNotifier { return; } - await _loadFromStorage(); + await _loadFromStorage(context); if (context.mounted) { - final session = context.read().state!; - final profile = context.read().state!; - await _loadFromRemote(session, profile); + await _loadFromRemoteFromContext(context); } }, timeout: _lockTimeout, diff --git a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart index c22ed9487..5fe7423c3 100644 --- a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart +++ b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; @@ -11,7 +13,6 @@ import 'package:uni/view/common_widgets/expanded_image_label.dart'; import 'package:uni/view/common_widgets/last_update_timestamp.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; -import 'package:uni/view/lazy_consumer.dart'; class BusStopNextArrivalsPage extends StatefulWidget { const BusStopNextArrivalsPage({super.key}); @@ -25,13 +26,15 @@ class BusStopNextArrivalsPageState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return LazyConsumer( - builder: (context, busProvider) => ListView( + return Consumer(builder: (context, busProvider, _) { + // TODO: Refactor to LazyConsumer and automatic status checks + unawaited(busProvider.ensureInitialized(context)); + return ListView( children: [ NextArrivals(busProvider.state!, busProvider.requestStatus), ], - ), - ); + ); + }); } @override @@ -44,7 +47,6 @@ class BusStopNextArrivalsPageState class NextArrivals extends StatefulWidget { const NextArrivals(this.buses, this.busStopStatus, {super.key}); - //final Map> trips; final Map buses; final RequestStatus busStopStatus; diff --git a/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart b/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart index df60b019b..0cab9559c 100644 --- a/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart +++ b/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -13,8 +14,8 @@ class EstimatedArrivalTimeStamp extends StatelessWidget { @override Widget build(BuildContext context) { - return LazyConsumer( - builder: (context, busProvider) => + return Consumer( + builder: (context, busProvider, _) => getContent(context, busProvider.lastUpdateTime ?? DateTime.now()), ); } diff --git a/uni/lib/view/bus_stop_selection/bus_stop_selection.dart b/uni/lib/view/bus_stop_selection/bus_stop_selection.dart index 16d1b9373..af5f946fc 100644 --- a/uni/lib/view/bus_stop_selection/bus_stop_selection.dart +++ b/uni/lib/view/bus_stop_selection/bus_stop_selection.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:uni/controller/local_storage/database/app_bus_stop_database.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/bus_stop.dart'; @@ -37,8 +38,8 @@ class BusStopSelectionPageState @override Widget getBody(BuildContext context) { final width = MediaQuery.of(context).size.width; - return LazyConsumer( - builder: (context, busProvider) { + return Consumer( + builder: (context, busProvider, _) { final rows = []; busProvider.state!.forEach( (stopCode, stopData) => diff --git a/uni/lib/view/calendar/calendar.dart b/uni/lib/view/calendar/calendar.dart index f54146572..39526d3fa 100644 --- a/uni/lib/view/calendar/calendar.dart +++ b/uni/lib/view/calendar/calendar.dart @@ -8,7 +8,6 @@ import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/calendar/widgets/calendar_tile.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; import 'package:uni/view/lazy_consumer.dart'; class CalendarPageView extends StatefulWidget { @@ -21,28 +20,25 @@ class CalendarPageView extends StatefulWidget { class CalendarPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return LazyConsumer( - builder: (context, calendarProvider) => ListView( - children: [ - Container( - padding: const EdgeInsets.only(bottom: 6), - child: PageTitle( - name: S.of(context).nav_title(DrawerItem.navCalendar.title), - ), + return ListView( + children: [ + Container( + padding: const EdgeInsets.only(bottom: 6), + child: PageTitle( + name: S.of(context).nav_title(DrawerItem.navCalendar.title), ), - RequestDependentWidgetBuilder( - status: calendarProvider.requestStatus, - builder: () => getTimeline(context, calendarProvider.state!), - hasContentPredicate: calendarProvider.state!.isNotEmpty, - onNullContent: const Center( - child: Text( - 'Nenhum evento encontrado', - style: TextStyle(fontSize: 18), - ), + ), + LazyConsumer>( + builder: getTimeline, + hasContent: (calendar) => calendar.isNotEmpty, + onNullContent: const Center( + child: Text( + 'Nenhum evento encontrado', + style: TextStyle(fontSize: 18), ), ), - ], - ), + ), + ], ); } diff --git a/uni/lib/view/common_widgets/last_update_timestamp.dart b/uni/lib/view/common_widgets/last_update_timestamp.dart index 6220fb362..4e70543fe 100644 --- a/uni/lib/view/common_widgets/last_update_timestamp.dart +++ b/uni/lib/view/common_widgets/last_update_timestamp.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; -import 'package:uni/view/lazy_consumer.dart'; class LastUpdateTimeStamp> extends StatefulWidget { @@ -16,7 +16,7 @@ class LastUpdateTimeStamp> } class _LastUpdateTimeStampState> - extends State { + extends State { DateTime currentTime = DateTime.now(); @override @@ -37,8 +37,8 @@ class _LastUpdateTimeStampState> @override Widget build(BuildContext context) { - return LazyConsumer( - builder: (context, provider) => Container( + return Consumer( + builder: (context, provider, _) => Container( padding: const EdgeInsets.only(top: 8, bottom: 10), child: provider.lastUpdateTime != null ? _getContent(context, provider.lastUpdateTime!) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 53b55aa76..27232f4c5 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -51,30 +51,29 @@ abstract class GeneralPageViewState extends State { context, _loading ? const Flex( - direction: Axis.vertical, - children: [ - Expanded( - child: Center( - child: CircularProgressIndicator(), - ), - ), - ], - ) + direction: Axis.vertical, + children: [ + Expanded( + child: Center( + child: CircularProgressIndicator(), + ), + ), + ], + ) : getBody(context), ); } Widget getBody(BuildContext context); - Future buildProfileDecorationImage( - BuildContext context, { + Future buildProfileDecorationImage(BuildContext context, { bool forceRetrieval = false, }) async { final sessionProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); await sessionProvider.ensureInitialized(context); final profilePictureFile = - await ProfileProvider.fetchOrGetCachedProfilePicture( + await ProfileProvider.fetchOrGetCachedProfilePicture( sessionProvider.state!, forceRetrieval: forceRetrieval, ); @@ -87,29 +86,33 @@ abstract class GeneralPageViewState extends State { DecorationImage getProfileDecorationImage(File? profilePicture) { const fallbackPicture = AssetImage('assets/images/profile_placeholder.png'); final image = - profilePicture == null ? fallbackPicture : FileImage(profilePicture); + profilePicture == null ? fallbackPicture : FileImage(profilePicture); final result = - DecorationImage(fit: BoxFit.cover, image: image as ImageProvider); + DecorationImage(fit: BoxFit.cover, image: image as ImageProvider); return result; } Widget refreshState(BuildContext context, Widget child) { return RefreshIndicator( key: GlobalKey(), - onRefresh: () => ProfileProvider.fetchOrGetCachedProfilePicture( - Provider.of(context, listen: false).state!, - forceRetrieval: true, - ).then((value) => onRefresh(context)), + onRefresh: () => + ProfileProvider.fetchOrGetCachedProfilePicture( + Provider + .of(context, listen: false) + .state!, + forceRetrieval: true, + ).then((value) => onRefresh(context)), child: Builder( - builder: (context) => GestureDetector( - onHorizontalDragEnd: (dragDetails) { - if (dragDetails.primaryVelocity! > 2) { - Scaffold.of(context).openDrawer(); - } - }, - child: child, - ), + builder: (context) => + GestureDetector( + onHorizontalDragEnd: (dragDetails) { + if (dragDetails.primaryVelocity! > 2) { + Scaffold.of(context).openDrawer(); + } + }, + child: child, + ), ), ); } @@ -133,40 +136,53 @@ abstract class GeneralPageViewState extends State { bottom: PreferredSize( preferredSize: Size.zero, child: Container( - color: Theme.of(context).dividerColor, + color: Theme + .of(context) + .dividerColor, margin: EdgeInsets.only(left: borderMargin, right: borderMargin), height: 1.5, ), ), elevation: 0, - iconTheme: Theme.of(context).iconTheme, - backgroundColor: Theme.of(context).scaffoldBackgroundColor, + iconTheme: Theme + .of(context) + .iconTheme, + backgroundColor: Theme + .of(context) + .scaffoldBackgroundColor, titleSpacing: 0, title: ButtonTheme( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: const RoundedRectangleBorder(), child: Builder( - builder: (context) => TextButton( - onPressed: () { - final currentRouteName = ModalRoute.of(context)!.settings.name; - if (currentRouteName != '/${DrawerItem.navPersonalArea.title}') { - Navigator.pushNamed( - context, - '/${DrawerItem.navPersonalArea.title}', - ); - } else { - Scaffold.of(context).openDrawer(); - } - }, - child: SvgPicture.asset( - colorFilter: ColorFilter.mode( - Theme.of(context).primaryColor, - BlendMode.srcIn, + builder: (context) => + TextButton( + onPressed: () { + final currentRouteName = ModalRoute + .of(context)! + .settings + .name; + if (currentRouteName != + '/${DrawerItem.navPersonalArea.title}') { + Navigator.pushNamed( + context, + '/${DrawerItem.navPersonalArea.title}', + ); + } else { + Scaffold.of(context).openDrawer(); + } + }, + child: SvgPicture.asset( + colorFilter: ColorFilter.mode( + Theme + .of(context) + .primaryColor, + BlendMode.srcIn, + ), + 'assets/images/logo_dark.svg', + height: queryData.size.height / 25, + ), ), - 'assets/images/logo_dark.svg', - height: queryData.size.height / 25, - ), - ), ), ), actions: [ @@ -179,12 +195,11 @@ abstract class GeneralPageViewState extends State { Widget getTopRightButton(BuildContext context) { return FutureBuilder( future: buildProfileDecorationImage(context), - builder: ( - BuildContext context, - AsyncSnapshot decorationImage, - ) { + builder: (BuildContext context, + AsyncSnapshot decorationImage,) { return TextButton( - onPressed: () => { + onPressed: () => + { Navigator.push( context, MaterialPageRoute( diff --git a/uni/lib/view/common_widgets/request_dependent_widget_builder.dart b/uni/lib/view/common_widgets/request_dependent_widget_builder.dart deleted file mode 100644 index 924320dd1..000000000 --- a/uni/lib/view/common_widgets/request_dependent_widget_builder.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:flutter/material.dart'; -import 'package:shimmer/shimmer.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/model/request_status.dart'; -import 'package:uni/view/bug_report/bug_report.dart'; - -/// Wraps content given its fetch data from the redux store, -/// hydrating the component, displaying an empty message, -/// a connection error or a loading circular effect as appropriate -class RequestDependentWidgetBuilder extends StatelessWidget { - const RequestDependentWidgetBuilder({ - required this.status, - required this.builder, - required this.hasContentPredicate, - required this.onNullContent, - super.key, - this.contentLoadingWidget, - }); - - final RequestStatus status; - final Widget Function() builder; - final Widget? contentLoadingWidget; - final bool hasContentPredicate; - final Widget onNullContent; - - @override - Widget build(BuildContext context) { - if (status == RequestStatus.busy && !hasContentPredicate) { - return loadingWidget(context); - } else if (status == RequestStatus.failed) { - return requestFailedMessage(); - } - - return hasContentPredicate - ? builder() - : Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: onNullContent, - ); - } - - Widget loadingWidget(BuildContext context) { - return contentLoadingWidget == null - ? const Center( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 20), - child: CircularProgressIndicator(), - ), - ) - : Center( - child: Shimmer.fromColors( - baseColor: Theme.of(context).highlightColor, - highlightColor: Theme.of(context).colorScheme.onPrimary, - child: contentLoadingWidget!, - ), - ); - } - - Widget requestFailedMessage() { - return FutureBuilder( - future: Connectivity().checkConnectivity(), - builder: ( - BuildContext context, - AsyncSnapshot connectivitySnapshot, - ) { - if (!connectivitySnapshot.hasData) { - return const Center( - heightFactor: 3, - child: CircularProgressIndicator(), - ); - } - - if (connectivitySnapshot.data == ConnectivityResult.none) { - return Center( - heightFactor: 3, - child: Text( - S.of(context).check_internet, - style: Theme.of(context).textTheme.titleMedium, - ), - ); - } - - return Column( - children: [ - Padding( - padding: const EdgeInsets.only(top: 15, bottom: 10), - child: Center( - child: Text( - S.of(context).load_error, - style: Theme.of(context).textTheme.titleMedium, - ), - ), - ), - OutlinedButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const BugReportPageView(), - ), - ), - child: Text(S.of(context).report_error), - ), - ], - ); - }, - ); - } -} diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index 5b1a6c49d..3da06627c 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -24,11 +24,13 @@ class CourseUnitDetailPageViewState extends SecondaryPageViewState { Future loadInfo({required bool force}) async { final courseUnitsProvider = - Provider.of(context, listen: false); - final session = context.read().state!; + Provider.of(context, listen: false); + final session = context + .read() + .state!; final courseUnitSheet = - courseUnitsProvider.courseUnitsSheets[widget.courseUnit]; + courseUnitsProvider.courseUnitsSheets[widget.courseUnit]; if (courseUnitSheet == null || force) { await courseUnitsProvider.fetchCourseUnitSheet( widget.courseUnit, @@ -37,7 +39,7 @@ class CourseUnitDetailPageViewState } final courseUnitClasses = - courseUnitsProvider.courseUnitsClasses[widget.courseUnit]; + courseUnitsProvider.courseUnitsClasses[widget.courseUnit]; if (courseUnitClasses == null || force) { await courseUnitsProvider.fetchCourseUnitClasses( widget.courseUnit, @@ -69,8 +71,12 @@ class CourseUnitDetailPageViewState ), TabBar( tabs: [ - Tab(text: S.of(context).course_info), - Tab(text: S.of(context).course_class), + Tab(text: S + .of(context) + .course_info), + Tab(text: S + .of(context) + .course_class), ], ), Expanded( @@ -97,7 +103,9 @@ class CourseUnitDetailPageViewState if (sheet == null || sheet.sections.isEmpty) { return Center( child: Text( - S.of(context).no_info, + S + .of(context) + .no_info, textAlign: TextAlign.center, ), ); @@ -114,7 +122,9 @@ class CourseUnitDetailPageViewState if (classes == null || classes.isEmpty) { return Center( child: Text( - S.of(context).no_class, + S + .of(context) + .no_class, textAlign: TextAlign.center, ), ); diff --git a/uni/lib/view/course_units/course_units.dart b/uni/lib/view/course_units/course_units.dart index 6cd50bfe3..9e7afc046 100644 --- a/uni/lib/view/course_units/course_units.dart +++ b/uni/lib/view/course_units/course_units.dart @@ -3,12 +3,11 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; +import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; -import 'package:uni/model/request_status.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; import 'package:uni/view/course_units/widgets/course_unit_card.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -30,9 +29,9 @@ class CourseUnitsPageViewState @override Widget getBody(BuildContext context) { - return LazyConsumer( - builder: (context, profileProvider) { - final courseUnits = profileProvider.state!.courseUnits; + return LazyConsumer( + builder: (context, profile) { + final courseUnits = profile.courseUnits; var availableYears = []; var availableSemesters = []; @@ -52,19 +51,29 @@ class CourseUnitsPageViewState } } - return _getPageView( - courseUnits, - profileProvider.requestStatus, - availableYears, - availableSemesters, - ); + return Column(children: [ + _getPageTitleAndFilters(availableYears, availableSemesters), + _getPageView(courseUnits, availableYears, availableSemesters), + ]); }, + hasContent: (Profile profile) => profile.courseUnits.isNotEmpty, + onNullContent: Column( + children: [ + _getPageTitleAndFilters([], []), + Center( + heightFactor: 10, + child: Text( + S.of(context).no_selected_courses, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + ], + ), ); } Widget _getPageView( List courseUnits, - RequestStatus requestStatus, List availableYears, List availableSemesters, ) { @@ -80,24 +89,7 @@ class CourseUnitsPageViewState element.semesterCode == selectedSemester, ) .toList(); - return Column( - children: [ - _getPageTitleAndFilters(availableYears, availableSemesters), - RequestDependentWidgetBuilder( - status: requestStatus, - builder: () => - _generateCourseUnitsCards(filteredCourseUnits, context), - hasContentPredicate: courseUnits.isNotEmpty, - onNullContent: Center( - heightFactor: 10, - child: Text( - S.of(context).no_selected_courses, - style: Theme.of(context).textTheme.titleLarge, - ), - ), - ), - ], - ); + return _generateCourseUnitsCards(filteredCourseUnits, context); } Widget _getPageTitleAndFilters( diff --git a/uni/lib/view/exams/exams.dart b/uni/lib/view/exams/exams.dart index 83a057f6a..06068d9a6 100644 --- a/uni/lib/view/exams/exams.dart +++ b/uni/lib/view/exams/exams.dart @@ -27,14 +27,14 @@ class ExamsPageView extends StatefulWidget { class ExamsPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return LazyConsumer( - builder: (context, examProvider) { + return LazyConsumer>( + builder: (context, exams) { return ListView( children: [ Column( children: createExamsColumn( context, - examProvider.state! + exams .where( (exam) => widget.filteredExamTypes[ @@ -47,38 +47,33 @@ class ExamsPageViewState extends GeneralPageViewState { ], ); }, + hasContent: (List exams) => exams.isNotEmpty, + onNullContent: Center( + heightFactor: 1.2, + child: ImageLabel( + imagePath: 'assets/images/vacation.png', + label: S.of(context).no_exams_label, + labelTextStyle: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + color: Theme.of(context).colorScheme.primary, + ), + sublabel: S.of(context).no_exams, + sublabelTextStyle: const TextStyle(fontSize: 15), + ), + ), ); } /// Creates a column with all the user's exams. List createExamsColumn(BuildContext context, List exams) { - final columns = [const ExamPageTitle()]; - - if (exams.isEmpty) { - columns.add( - Center( - heightFactor: 1.2, - child: ImageLabel( - imagePath: 'assets/images/vacation.png', - label: S.of(context).no_exams_label, - labelTextStyle: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18, - color: Theme.of(context).colorScheme.primary, - ), - sublabel: S.of(context).no_exams, - sublabelTextStyle: const TextStyle(fontSize: 15), - ), - ), - ); - return columns; - } - if (exams.length == 1) { - columns.add(createExamCard(context, [exams[0]])); - return columns; + return [ + createExamCard(context, [exams[0]]), + ]; } + final columns = [const ExamPageTitle()]; final currentDayExams = []; for (var i = 0; i < exams.length; i++) { diff --git a/uni/lib/view/home/widgets/bus_stop_card.dart b/uni/lib/view/home/widgets/bus_stop_card.dart index 132201dc0..025c4490e 100644 --- a/uni/lib/view/home/widgets/bus_stop_card.dart +++ b/uni/lib/view/home/widgets/bus_stop_card.dart @@ -3,7 +3,6 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; -import 'package:uni/model/request_status.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/bus_stop_next_arrivals/widgets/bus_stop_row.dart'; import 'package:uni/view/bus_stop_selection/bus_stop_selection.dart'; @@ -29,14 +28,19 @@ class BusStopCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { - return LazyConsumer( - builder: (context, busProvider) { - return getCardContent( - context, - busProvider.state!, - busProvider.requestStatus, - ); - }, + return LazyConsumer>( + contentLoadingWidget: Column( + children: [ + getCardTitle(context), + Container( + padding: const EdgeInsets.all(22), + child: const Center(child: CircularProgressIndicator()), + ), + ], + ), + builder: getCardContent, + hasContent: (Map busStops) => false, + onNullContent: const CircularProgressIndicator(), ); } @@ -50,66 +54,38 @@ class BusStopCard extends GenericCard { Widget getCardContent( BuildContext context, Map stopData, - RequestStatus busStopStatus, ) { - switch (busStopStatus) { - case RequestStatus.successful: - if (stopData.isNotEmpty) { - return Column( - children: [ - getCardTitle(context), - getBusStopsInfo(context, stopData), - ], - ); - } else { - return Container( - padding: const EdgeInsets.all(8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - S.of(context).buses_personalize, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.titleSmall!.apply(), - ), - IconButton( - icon: const Icon(Icons.settings), - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const BusStopSelectionPage(), - ), - ), - ), - ], - ), - ); - } - case RequestStatus.busy: - return Column( + if (stopData.isNotEmpty) { + return Column( + children: [ + getCardTitle(context), + getBusStopsInfo(context, stopData), + ], + ); + } else { + return Container( + padding: const EdgeInsets.all(8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - getCardTitle(context), - Container( - padding: const EdgeInsets.all(22), - child: const Center(child: CircularProgressIndicator()), + Text( + S.of(context).buses_personalize, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.titleSmall!.apply(), ), - ], - ); - case RequestStatus.failed: - case RequestStatus.none: - return Column( - children: [ - getCardTitle(context), - Container( - padding: const EdgeInsets.all(8), - child: Text( - S.of(context).bus_error, - style: Theme.of(context).textTheme.titleMedium, + IconButton( + icon: const Icon(Icons.settings), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const BusStopSelectionPage(), + ), ), ), ], - ); + ), + ); } } @@ -154,7 +130,9 @@ List getEachBusStopInfo( BuildContext context, Map stopData, ) { - final rows = [const LastUpdateTimeStamp()]; + final rows = [ + const LastUpdateTimeStamp(), + ]; stopData.forEach((stopCode, stopInfo) { if (stopInfo.trips.isNotEmpty && stopInfo.favorited) { diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index b5608669e..d0f95445a 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -8,7 +8,6 @@ import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/date_rectangle.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; import 'package:uni/view/common_widgets/row_container.dart'; import 'package:uni/view/exams/widgets/exam_row.dart'; import 'package:uni/view/exams/widgets/exam_title.dart'; @@ -45,30 +44,28 @@ class ExamCard extends GenericCard { /// that no exams exist is displayed. @override Widget buildCardContent(BuildContext context) { - return LazyConsumer( - builder: (context, examProvider) { + return LazyConsumer>( + builder: (context, exams) { final filteredExams = PreferencesController.getFilteredExams(); final hiddenExams = PreferencesController.getHiddenExams(); - final exams = examProvider.state! + final shownExams = exams .where( (exam) => !hiddenExams.contains(exam.id) && (filteredExams[Exam.getExamTypeLong(exam.type)] ?? true), ) .toList(); - return RequestDependentWidgetBuilder( - status: examProvider.requestStatus, - builder: () => generateExams(exams, context), - hasContentPredicate: exams.isNotEmpty, - onNullContent: Center( - child: Text( - S.of(context).no_selected_exams, - style: Theme.of(context).textTheme.titleLarge, - ), - ), - contentLoadingWidget: const ExamCardShimmer().build(context), - ); + + return generateExams(shownExams, context); }, + hasContent: (exams) => exams.isNotEmpty, + onNullContent: Center( + child: Text( + S.of(context).no_selected_exams, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + contentLoadingWidget: const ExamCardShimmer().build(context), ); } diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index e1f06199c..ca61fea38 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -16,17 +16,16 @@ import 'package:uni/view/profile/widgets/account_info_card.dart'; import 'package:uni/view/profile/widgets/print_info_card.dart'; typedef CardCreator = GenericCard Function( - Key key, { - required bool editingMode, - void Function()? onDelete, -}); + Key key, { + required bool editingMode, + void Function()? onDelete, + }); class MainCardsList extends StatefulWidget { - const MainCardsList( - this.favoriteCardTypes, - this.saveFavoriteCards, { - super.key, - }); + const MainCardsList(this.favoriteCardTypes, + this.saveFavoriteCards, { + super.key, + }); final List favoriteCardTypes; final void Function(List) saveFavoriteCards; @@ -39,7 +38,7 @@ class MainCardsList extends StatefulWidget { FavoriteWidgetType.busStops: BusStopCard.fromEditingInformation, FavoriteWidgetType.restaurant: RestaurantCard.fromEditingInformation, FavoriteWidgetType.libraryOccupation: - LibraryOccupationCard.fromEditingInformation, + LibraryOccupationCard.fromEditingInformation, }; @override @@ -56,25 +55,28 @@ class MainCardsListState extends State { return Scaffold( body: BackButtonExitWrapper( child: SizedBox( - height: MediaQuery.of(context).size.height, + height: MediaQuery + .of(context) + .size + .height, child: isEditing ? ReorderableListView( - onReorder: reorderCard, - header: createTopBar(context), - children: favoriteCardsFromTypes( - widget.favoriteCardTypes, - context, - ), - ) + onReorder: reorderCard, + header: createTopBar(context), + children: favoriteCardsFromTypes( + widget.favoriteCardTypes, + context, + ), + ) : ListView( - children: [ - createTopBar(context), - ...favoriteCardsFromTypes( - widget.favoriteCardTypes, - context, - ), - ], - ), + children: [ + createTopBar(context), + ...favoriteCardsFromTypes( + widget.favoriteCardTypes, + context, + ), + ], + ), ), ), floatingActionButton: isEditing ? createActionButton(context) : null, @@ -83,44 +85,64 @@ class MainCardsListState extends State { Widget createActionButton(BuildContext context) { return FloatingActionButton( - onPressed: () => showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text( - S.of(context).widget_prompt, - style: Theme.of(context).textTheme.headlineSmall, - ), - content: SizedBox( - height: 200, - width: 100, - child: ListView(children: getCardAdders(context)), - ), - actions: [ - TextButton( - child: Text( - S.of(context).cancel, - style: Theme.of(context).textTheme.bodyMedium, + onPressed: () => + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + S + .of(context) + .widget_prompt, + style: Theme + .of(context) + .textTheme + .headlineSmall, ), - onPressed: () => Navigator.pop(context), - ), - ], - ); - }, - ), //Add FAB functionality here - tooltip: S.of(context).add_widget, - child: Icon(Icons.add, color: Theme.of(context).colorScheme.onPrimary), + content: SizedBox( + height: 200, + width: 100, + child: ListView(children: getCardAdders(context)), + ), + actions: [ + TextButton( + child: Text( + S + .of(context) + .cancel, + style: Theme + .of(context) + .textTheme + .bodyMedium, + ), + onPressed: () => Navigator.pop(context), + ), + ], + ); + }, + ), //Add FAB functionality here + tooltip: S + .of(context) + .add_widget, + child: Icon(Icons.add, color: Theme + .of(context) + .colorScheme + .onPrimary), ); } List getCardAdders(BuildContext context) { - final session = Provider.of(context, listen: false).state!; + final session = + Provider + .of(context, listen: false) + .state!; final possibleCardAdditions = MainCardsList.cardCreators.entries .where((e) => e.key.isVisible(session.faculties)) .where((e) => !widget.favoriteCardTypes.contains(e.key)) .map( - (e) => DecoratedBox( + (e) => + DecoratedBox( decoration: const BoxDecoration(), child: ListTile( title: Text( @@ -135,17 +157,18 @@ class MainCardsListState extends State { }, ), ), - ) + ) .toList(); return possibleCardAdditions.isEmpty - ? [Text(S.of(context).all_widgets_added)] + ? [Text(S + .of(context) + .all_widgets_added) + ] : possibleCardAdditions; } - Widget createTopBar( - BuildContext context, - ) { + Widget createTopBar(BuildContext context,) { return Container( padding: const EdgeInsets.fromLTRB(20, 10, 20, 0), child: Row( @@ -158,20 +181,26 @@ class MainCardsListState extends State { ), if (isEditing) ElevatedButton( - onPressed: () => setState(() { - isEditing = false; - }), + onPressed: () => + setState(() { + isEditing = false; + }), child: Text( - S.of(context).edit_on, + S + .of(context) + .edit_on, ), ) else OutlinedButton( - onPressed: () => setState(() { - isEditing = true; - }), + onPressed: () => + setState(() { + isEditing = true; + }), child: Text( - S.of(context).edit_off, + S + .of(context) + .edit_off, ), ), ], @@ -179,12 +208,12 @@ class MainCardsListState extends State { ); } - List favoriteCardsFromTypes( - List cardTypes, - BuildContext context, - ) { + List favoriteCardsFromTypes(List cardTypes, + BuildContext context,) { final userSession = - Provider.of(context, listen: false).state!; + Provider + .of(context, listen: false) + .state!; return cardTypes .where((type) => type.isVisible(userSession.faculties)) .where((type) => MainCardsList.cardCreators.containsKey(type)) @@ -198,12 +227,10 @@ class MainCardsListState extends State { }).toList(); } - void reorderCard( - int oldIndex, - int newIndex, - ) { + void reorderCard(int oldIndex, + int newIndex,) { final newFavorites = - List.from(widget.favoriteCardTypes); + List.from(widget.favoriteCardTypes); final tmp = newFavorites[oldIndex]; newFavorites ..removeAt(oldIndex) diff --git a/uni/lib/view/home/widgets/restaurant_card.dart b/uni/lib/view/home/widgets/restaurant_card.dart index c7868aaf1..17a0ae005 100644 --- a/uni/lib/view/home/widgets/restaurant_card.dart +++ b/uni/lib/view/home/widgets/restaurant_card.dart @@ -8,7 +8,6 @@ import 'package:uni/model/providers/lazy/restaurant_provider.dart'; import 'package:uni/model/utils/day_of_week.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; import 'package:uni/view/common_widgets/row_container.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/restaurant/widgets/restaurant_slot.dart'; @@ -38,40 +37,38 @@ class RestaurantCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { - return LazyConsumer( - builder: (context, restaurantProvider) { - final favoriteRestaurants = restaurantProvider.state! + return LazyConsumer>( + builder: (context, restaurants) { + final favoriteRestaurants = restaurants .where( (restaurant) => PreferencesController.getFavoriteRestaurants() .contains(restaurant.name), ) .toList(); - return RequestDependentWidgetBuilder( - status: restaurantProvider.requestStatus, - builder: () => generateRestaurants(favoriteRestaurants, context), - hasContentPredicate: favoriteRestaurants.isNotEmpty, - onNullContent: Column( - children: [ - Padding( - padding: const EdgeInsets.only(top: 10, bottom: 10), - child: Center( - child: Text( - S.of(context).no_favorite_restaurants, - style: Theme.of(context).textTheme.titleMedium, - ), - ), - ), - OutlinedButton( - onPressed: () => Navigator.pushNamed( - context, - '/${DrawerItem.navRestaurants.title}', - ), - child: Text(S.of(context).add), + return generateRestaurants(favoriteRestaurants, context); + }, + hasContent: (restaurants) => + PreferencesController.getFavoriteRestaurants().isNotEmpty, + onNullContent: Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 10, bottom: 10), + child: Center( + child: Text( + S.of(context).no_favorite_restaurants, + style: Theme.of(context).textTheme.titleMedium, ), - ], + ), ), - ); - }, + OutlinedButton( + onPressed: () => Navigator.pushNamed( + context, + '/${DrawerItem.navRestaurants.title}', + ), + child: Text(S.of(context).add), + ), + ], + ), ); } diff --git a/uni/lib/view/home/widgets/schedule_card.dart b/uni/lib/view/home/widgets/schedule_card.dart index fd8daf7a4..3f0ecd86d 100644 --- a/uni/lib/view/home/widgets/schedule_card.dart +++ b/uni/lib/view/home/widgets/schedule_card.dart @@ -9,7 +9,6 @@ import 'package:uni/model/providers/lazy/lecture_provider.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/date_rectangle.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; import 'package:uni/view/home/widgets/schedule_card_shimmer.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; @@ -35,23 +34,20 @@ class ScheduleCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { - return LazyConsumer( - builder: (context, lectureProvider) => RequestDependentWidgetBuilder( - status: lectureProvider.requestStatus, - builder: () => Column( - mainAxisSize: MainAxisSize.min, - children: getScheduleRows(context, lectureProvider.state!), - ), - hasContentPredicate: lectureProvider.state!.isNotEmpty, - onNullContent: Center( - child: Text( - S.of(context).no_classes, - style: Theme.of(context).textTheme.titleLarge, - textAlign: TextAlign.center, - ), + return LazyConsumer>( + builder: (context, lectures) => Column( + mainAxisSize: MainAxisSize.min, + children: getScheduleRows(context, lectures), + ), + hasContent: (lectures) => lectures.isNotEmpty, + onNullContent: Center( + child: Text( + S.of(context).no_classes, + style: Theme.of(context).textTheme.titleLarge, + textAlign: TextAlign.center, ), - contentLoadingWidget: const ScheduleCardShimmer().build(context), ), + contentLoadingWidget: const ScheduleCardShimmer().build(context), ); } diff --git a/uni/lib/view/lazy_consumer.dart b/uni/lib/view/lazy_consumer.dart index bb8295cf2..ea20dd42d 100644 --- a/uni/lib/view/lazy_consumer.dart +++ b/uni/lib/view/lazy_consumer.dart @@ -1,37 +1,50 @@ import 'dart:async'; import 'dart:io'; -import 'package:flutter/cupertino.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:shimmer/shimmer.dart'; +import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; +import 'package:uni/model/request_status.dart'; +import 'package:uni/view/bug_report/bug_report.dart'; /// Wrapper around Consumer that ensures that the provider is initialized, /// meaning that it has loaded its data from storage and/or remote. /// The provider will not reload its data if it has already been loaded before. /// If the provider depends on the session, it will ensure that SessionProvider /// and ProfileProvider are initialized before initializing itself. -class LazyConsumer> +/// This widget also falls back to loading or error widgets if +/// the provider data is not ready or has thrown an error, respectively. +class LazyConsumer, T2> extends StatelessWidget { const LazyConsumer({ required this.builder, + required this.hasContent, + required this.onNullContent, + this.contentLoadingWidget, super.key, }); - final Widget Function(BuildContext, T) builder; + final Widget Function(BuildContext, T2) builder; + final bool Function(T2) hasContent; + final Widget onNullContent; + final Widget? contentLoadingWidget; @override Widget build(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((_) async { StateProviderNotifier? provider; try { - provider = Provider.of(context, listen: false); + provider = Provider.of(context, listen: false); } catch (e) { // The provider was not found. This should only happen in tests. - Logger().e('LazyConsumer: ${T.runtimeType} not found'); + Logger().e('LazyConsumer: ${T1.runtimeType} not found'); return; } @@ -66,9 +79,94 @@ class LazyConsumer> } }); - return Consumer( + return Consumer( builder: (context, provider, _) { - return builder(context, provider); + return requestDependantWidget(context, provider); + }, + ); + } + + Widget requestDependantWidget(BuildContext context, T1 provider) { + final showContent = + provider.state != null && hasContent(provider.state as T2); + + if (provider.requestStatus == RequestStatus.busy && !showContent) { + return loadingWidget(context); + } else if (provider.requestStatus == RequestStatus.failed) { + return requestFailedMessage(); + } + + return showContent + ? builder(context, provider.state as T2) + : Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: onNullContent, + ); + } + + Widget loadingWidget(BuildContext context) { + return contentLoadingWidget == null + ? const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 20), + child: CircularProgressIndicator(), + ), + ) + : Center( + child: Shimmer.fromColors( + baseColor: Theme.of(context).highlightColor, + highlightColor: Theme.of(context).colorScheme.onPrimary, + child: contentLoadingWidget!, + ), + ); + } + + Widget requestFailedMessage() { + return FutureBuilder( + future: Connectivity().checkConnectivity(), + builder: ( + BuildContext context, + AsyncSnapshot connectivitySnapshot, + ) { + if (!connectivitySnapshot.hasData) { + return const Center( + heightFactor: 3, + child: CircularProgressIndicator(), + ); + } + + if (connectivitySnapshot.data == ConnectivityResult.none) { + return Center( + heightFactor: 3, + child: Text( + S.of(context).check_internet, + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 15, bottom: 10), + child: Center( + child: Text( + S.of(context).load_error, + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + OutlinedButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const BugReportPageView(), + ), + ), + child: Text(S.of(context).report_error), + ), + ], + ); }, ); } diff --git a/uni/lib/view/library/library.dart b/uni/lib/view/library/library.dart index d3ad494b7..a5173127d 100644 --- a/uni/lib/view/library/library.dart +++ b/uni/lib/view/library/library.dart @@ -20,10 +20,14 @@ class LibraryPageView extends StatefulWidget { class LibraryPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return LazyConsumer( - builder: (context, libraryOccupationProvider) => LibraryPage( - libraryOccupationProvider.state, - ), + return Consumer( + builder: (context, libraryOccupationProvider, _) { + // TODO: Refactor to LazyConsumer + libraryOccupationProvider.ensureInitialized(context); + return LibraryPage( + libraryOccupationProvider.state, + ); + }, ); } diff --git a/uni/lib/view/library/widgets/library_occupation_card.dart b/uni/lib/view/library/widgets/library_occupation_card.dart index d112c450d..49fedb3a3 100644 --- a/uni/lib/view/library/widgets/library_occupation_card.dart +++ b/uni/lib/view/library/widgets/library_occupation_card.dart @@ -4,10 +4,8 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/library_occupation.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; -import 'package:uni/model/request_status.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; import 'package:uni/view/lazy_consumer.dart'; /// Manages the library card section inside the personal area. @@ -35,18 +33,13 @@ class LibraryOccupationCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { - return LazyConsumer( - builder: (context, libraryOccupationProvider) => - RequestDependentWidgetBuilder( - status: libraryOccupationProvider.requestStatus, - builder: () => generateOccupation( - libraryOccupationProvider.state, - context, - ), - hasContentPredicate: - libraryOccupationProvider.requestStatus != RequestStatus.busy, - onNullContent: const CircularProgressIndicator(), + return LazyConsumer( + builder: (context, libraryOccupation) => generateOccupation( + libraryOccupation, + context, ), + hasContent: (libraryOccupation) => true, + onNullContent: const CircularProgressIndicator(), ); } diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index 43591f5e6..4907b1500 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -2,11 +2,9 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/location_group.dart'; import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; -import 'package:uni/model/request_status.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locations/widgets/faculty_map.dart'; @@ -21,39 +19,8 @@ class LocationsPageState extends GeneralPageViewState with SingleTickerProviderStateMixin { ScrollController? scrollViewController; - @override - void initState() { - super.initState(); - } - @override Widget getBody(BuildContext context) { - return LazyConsumer( - builder: (context, locationsProvider) { - return LocationsPageView( - locations: locationsProvider.state!, - status: locationsProvider.requestStatus, - ); - }, - ); - } - - @override - Future onRefresh(BuildContext context) async {} -} - -class LocationsPageView extends StatelessWidget { - const LocationsPageView({ - required this.locations, - required this.status, - super.key, - }); - - final List locations; - final RequestStatus status; - - @override - Widget build(BuildContext context) { return Column( children: [ Container( @@ -61,26 +28,23 @@ class LocationsPageView extends StatelessWidget { padding: const EdgeInsets.fromLTRB(0, 0, 0, 4), child: PageTitle( name: '${S.of(context).nav_title(DrawerItem.navLocations.title)}:' - ' ${getLocation()}', + ' FEUP', // TODO(bdmendes): Add locations for all faculties ), ), - Container( - padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), - height: MediaQuery.of(context).size.height * 0.75, - alignment: Alignment.center, - child: RequestDependentWidgetBuilder( - status: status, - builder: () => FacultyMap(faculty: 'FEUP', locations: locations), - hasContentPredicate: locations.isNotEmpty, - onNullContent: Center(child: Text(S.of(context).no_places_info)), + LazyConsumer>( + builder: (context, locations) => Container( + padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), + height: MediaQuery.of(context).size.height * 0.75, + alignment: Alignment.center, + child: FacultyMap(faculty: 'FEUP', locations: locations), ), - // TODO(bdmendes): add support for multiple faculties + hasContent: (locations) => locations.isNotEmpty, + onNullContent: Center(child: Text(S.of(context).no_places_info)), ), ], ); } - String getLocation() { - return 'FEUP'; - } + @override + Future onRefresh(BuildContext context) async {} } diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 111af8b3c..31d880881 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -1,6 +1,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -20,9 +21,8 @@ class ProfilePageView extends StatefulWidget { class ProfilePageViewState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { - return LazyConsumer( - builder: (context, profileStateProvider) { - final profile = profileStateProvider.state!; + return LazyConsumer( + builder: (context, profile) { final courseWidgets = profile.courses .map( (e) => [ @@ -49,6 +49,8 @@ class ProfilePageViewState extends SecondaryPageViewState { ], ); }, + hasContent: (Profile profile) => profile.courses.isNotEmpty, + onNullContent: Container(), ); } diff --git a/uni/lib/view/profile/widgets/account_info_card.dart b/uni/lib/view/profile/widgets/account_info_card.dart index ab221c051..3a168e1e8 100644 --- a/uni/lib/view/profile/widgets/account_info_card.dart +++ b/uni/lib/view/profile/widgets/account_info_card.dart @@ -9,6 +9,8 @@ import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/profile/widgets/reference_section.dart'; +import '../../../model/entities/profile.dart'; + /// Manages the 'Current account' section inside the user's page (accessible /// through the top-right widget with the user picture) class AccountInfoCard extends GenericCard { @@ -29,95 +31,95 @@ class AccountInfoCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { - return LazyConsumer( - builder: (context, profileStateProvider) { - return LazyConsumer( - builder: (context, referenceProvider) { - final profile = profileStateProvider.state!; - final references = referenceProvider.state!; - - return Column( - children: [ - Table( - columnWidths: const {1: FractionColumnWidth(.4)}, - defaultVerticalAlignment: TableCellVerticalAlignment.middle, - children: [ - TableRow( - children: [ - Container( - margin: const EdgeInsets.only( - top: 20, - bottom: 8, - left: 20, - ), - child: Text( - S.of(context).balance, - style: Theme.of(context).textTheme.titleSmall, - ), - ), - Container( - margin: const EdgeInsets.only( - top: 20, - bottom: 8, - right: 30, - ), - child: getInfoText(profile.feesBalance, context), - ), - ], + return Column( + children: [ + LazyConsumer( + builder: (BuildContext context, profile) => Table( + columnWidths: const {1: FractionColumnWidth(.4)}, + defaultVerticalAlignment: TableCellVerticalAlignment.middle, + children: [ + TableRow( + children: [ + Container( + margin: const EdgeInsets.only( + top: 20, + bottom: 8, + left: 20, ), - TableRow( - children: [ - Container( - margin: const EdgeInsets.only( - top: 8, - bottom: 20, - left: 20, - ), - child: Text( - S.of(context).fee_date, - style: Theme.of(context).textTheme.titleSmall, - ), - ), - Container( - margin: const EdgeInsets.only( - top: 8, - bottom: 20, - right: 30, - ), - child: getInfoText( - profile.feesLimit != null - ? DateFormat('yyyy-MM-dd') - .format(profile.feesLimit!) - : S.of(context).no_date, - context, - ), - ), - ], + child: Text( + S.of(context).balance, + style: Theme.of(context).textTheme.titleSmall, ), - ], - ), - if (references.isNotEmpty) + ), + Container( + margin: const EdgeInsets.only( + top: 20, + bottom: 8, + right: 30, + ), + child: getInfoText(profile.feesBalance, context), + ), + ], + ), + TableRow( + children: [ Container( - padding: const EdgeInsets.all(10), - child: Row( - children: [ - Text( - S.of(context).pendent_references, - style: Theme.of(context).textTheme.titleLarge?.apply( - color: Theme.of(context).colorScheme.secondary, - ), - ), - ], + margin: const EdgeInsets.only( + top: 8, + bottom: 20, + left: 20, ), + child: Text( + S.of(context).fee_date, + style: Theme.of(context).textTheme.titleSmall, + ), + ), + Container( + margin: const EdgeInsets.only( + top: 8, + bottom: 20, + right: 30, + ), + child: getInfoText( + profile.feesLimit != null + ? DateFormat('yyyy-MM-dd').format(profile.feesLimit!) + : S.of(context).no_date, + context, + ), + ), + ], + ), + ], + ), + hasContent: (Profile profile) => true, + onNullContent: Container(), + ), + LazyConsumer>( + builder: (BuildContext context, references) { + return Column( + children: [ + Container( + padding: const EdgeInsets.all(10), + child: Row( + children: [ + Text( + S.of(context).pendent_references, + style: Theme.of(context).textTheme.titleLarge?.apply( + color: Theme.of(context).colorScheme.secondary, + ), + ), + ], ), - if (references.isNotEmpty) - ReferenceList(references: references), - const SizedBox(height: 10), + ), + ReferenceList(references: references), ], ); }, - ); - }, + hasContent: (references) => references.isNotEmpty, + onNullContent: Container(), + ), + const SizedBox(height: 10), + ], ); } diff --git a/uni/lib/view/profile/widgets/print_info_card.dart b/uni/lib/view/profile/widgets/print_info_card.dart index f61808b53..bfaa52fa2 100644 --- a/uni/lib/view/profile/widgets/print_info_card.dart +++ b/uni/lib/view/profile/widgets/print_info_card.dart @@ -9,16 +9,18 @@ import 'package:uni/view/profile/widgets/create_print_mb_dialog.dart'; class PrintInfoCard extends GenericCard { PrintInfoCard({super.key}); - const PrintInfoCard.fromEditingInformation( - super.key, { + const PrintInfoCard.fromEditingInformation(super.key, { required super.editingMode, super.onDelete, }) : super.fromEditingInformation(); @override Widget buildCardContent(BuildContext context) { - return LazyConsumer( - builder: (context, profileStateProvider) { + return Consumer( + builder: (context, profileStateProvider, _) { + // TODO: Refactor to LazyConsumer + profileStateProvider.ensureInitialized(context); + final profile = profileStateProvider.state!; return Column( mainAxisSize: MainAxisSize.min, @@ -39,8 +41,13 @@ class PrintInfoCard extends GenericCard { left: 20, ), child: Text( - S.of(context).available_amount, - style: Theme.of(context).textTheme.titleSmall, + S + .of(context) + .available_amount, + style: Theme + .of(context) + .textTheme + .titleSmall, ), ), Container( @@ -48,7 +55,10 @@ class PrintInfoCard extends GenericCard { child: Text( profile.printBalance, textAlign: TextAlign.end, - style: Theme.of(context).textTheme.titleLarge, + style: Theme + .of(context) + .textTheme + .titleLarge, ), ), Container( @@ -73,7 +83,10 @@ class PrintInfoCard extends GenericCard { } @override - String getTitle(BuildContext context) => S.of(context).prints; + String getTitle(BuildContext context) => + S + .of(context) + .prints; @override void onClick(BuildContext context) {} diff --git a/uni/lib/view/restaurant/restaurant_page_view.dart b/uni/lib/view/restaurant/restaurant_page_view.dart index ce108b7c6..2a894a5da 100644 --- a/uni/lib/view/restaurant/restaurant_page_view.dart +++ b/uni/lib/view/restaurant/restaurant_page_view.dart @@ -8,7 +8,6 @@ import 'package:uni/model/utils/day_of_week.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/restaurant/widgets/restaurant_page_card.dart'; @@ -39,51 +38,45 @@ class _RestaurantPageViewState extends GeneralPageViewState @override Widget getBody(BuildContext context) { - return LazyConsumer( - builder: (context, restaurantProvider) { - return Column( + return Column( + children: [ + ListView( + shrinkWrap: true, children: [ - ListView( - shrinkWrap: true, - children: [ - Container( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 10), - alignment: Alignment.center, - child: PageTitle( - name: S - .of(context) - .nav_title(DrawerItem.navRestaurants.title), - center: false, - pad: false, - ), - ), - TabBar( - controller: tabController, - isScrollable: true, - tabs: createTabs(context), - ), - ], - ), - const SizedBox(height: 10), - Expanded( - child: RequestDependentWidgetBuilder( - status: restaurantProvider.requestStatus, - builder: () => createTabViewBuilder( - restaurantProvider.state!, - context, - ), - hasContentPredicate: restaurantProvider.state!.isNotEmpty, - onNullContent: Center( - child: Text( - S.of(context).no_menus, - style: const TextStyle(fontSize: 18), - ), - ), + Container( + padding: const EdgeInsets.fromLTRB(20, 20, 20, 10), + alignment: Alignment.center, + child: PageTitle( + name: S.of(context).nav_title(DrawerItem.navRestaurants.title), + center: false, + pad: false, ), ), + TabBar( + controller: tabController, + isScrollable: true, + tabs: createTabs(context), + ), ], - ); - }, + ), + const SizedBox(height: 10), + Expanded( + child: LazyConsumer>( + builder: (context, restaurants) => createTabViewBuilder( + restaurants, + context, + ), + onNullContent: Center( + child: Text( + S.of(context).no_menus, + style: const TextStyle(fontSize: 18), + ), + ), + hasContent: (List restaurants) => + restaurants.isNotEmpty, + ), + ), + ], ); } diff --git a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart index 908531997..35dfdcfe5 100644 --- a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart +++ b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart @@ -3,10 +3,8 @@ import 'package:material_design_icons_flutter/material_design_icons_flutter.dart import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/restaurant.dart'; -import 'package:uni/model/providers/lazy/restaurant_provider.dart'; import 'package:uni/utils/favorite_widget_type.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/lazy_consumer.dart'; class RestaurantPageCard extends GenericCard { RestaurantPageCard(this.restaurant, this.meals, {super.key}) @@ -43,37 +41,32 @@ class CardFavoriteButton extends StatelessWidget { @override Widget build(BuildContext context) { - return LazyConsumer( - builder: (context, restaurantProvider) { - final isFavorite = PreferencesController.getFavoriteRestaurants() - .contains(restaurant.name); - return IconButton( - icon: isFavorite ? Icon(MdiIcons.heart) : Icon(MdiIcons.heartOutline), - onPressed: () async { - final favoriteRestaurants = - PreferencesController.getFavoriteRestaurants(); - if (favoriteRestaurants.contains(restaurant.name)) { - favoriteRestaurants.remove(restaurant.name); - } else { - favoriteRestaurants.add(restaurant.name); - } - await PreferencesController.saveFavoriteRestaurants( - favoriteRestaurants, - ); - - final favoriteCardTypes = - PreferencesController.getFavoriteCards(); - if (context.mounted && - !isFavorite && - !favoriteCardTypes.contains(FavoriteWidgetType.restaurant)) { - showRestaurantCardHomeDialog( - context, - favoriteCardTypes, - PreferencesController.saveFavoriteCards, - ); - } - }, + final isFavorite = PreferencesController.getFavoriteRestaurants() + .contains(restaurant.name); + return IconButton( + icon: isFavorite ? Icon(MdiIcons.heart) : Icon(MdiIcons.heartOutline), + onPressed: () async { + final favoriteRestaurants = + PreferencesController.getFavoriteRestaurants(); + if (favoriteRestaurants.contains(restaurant.name)) { + favoriteRestaurants.remove(restaurant.name); + } else { + favoriteRestaurants.add(restaurant.name); + } + await PreferencesController.saveFavoriteRestaurants( + favoriteRestaurants, ); + + final favoriteCardTypes = PreferencesController.getFavoriteCards(); + if (context.mounted && + !isFavorite && + !favoriteCardTypes.contains(FavoriteWidgetType.restaurant)) { + showRestaurantCardHomeDialog( + context, + favoriteCardTypes, + PreferencesController.saveFavoriteCards, + ); + } }, ); } diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index 9fe05b05b..addb738ec 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -8,8 +8,6 @@ import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; -import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/schedule/widgets/schedule_slot.dart'; @@ -23,8 +21,10 @@ class SchedulePage extends StatefulWidget { class SchedulePageState extends State { @override Widget build(BuildContext context) { - return LazyConsumer( - builder: (context, lectureProvider) { + return Consumer( + builder: (context, lectureProvider, _) { + // TODO: Refactor to LazyConsumer + lectureProvider.ensureInitialized(context); return SchedulePageView( lectures: lectureProvider.state!, scheduleStatus: lectureProvider.requestStatus, @@ -45,7 +45,9 @@ class SchedulePageView extends StatefulWidget { final List lectures; final RequestStatus scheduleStatus; - final int weekDay = DateTime.now().weekday; + final int weekDay = DateTime + .now() + .weekday; static List> groupLecturesByDay(List schedule) { final aggLectures = >[]; @@ -96,8 +98,8 @@ class SchedulePageViewState extends GeneralPageViewState children: [ PageTitle( name: S.of(context).nav_title( - DrawerItem.navSchedule.title, - ), + DrawerItem.navSchedule.title, + ), ), TabBar( controller: tabController, @@ -141,11 +143,9 @@ class SchedulePageViewState extends GeneralPageViewState return tabs; } - List createSchedule( - BuildContext context, - List lectures, - RequestStatus scheduleStatus, - ) { + List createSchedule(BuildContext context, + List lectures, + RequestStatus scheduleStatus,) { final tabBarViewContent = []; for (var i = 0; i < 5; i++) { tabBarViewContent @@ -176,11 +176,9 @@ class SchedulePageViewState extends GeneralPageViewState return scheduleContent; } - Widget dayColumnBuilder( - int day, - Set dayContent, - BuildContext context, - ) { + Widget dayColumnBuilder(int day, + Set dayContent, + BuildContext context,) { return Container( key: Key('schedule-page-day-column-$day'), child: Column( @@ -190,27 +188,28 @@ class SchedulePageViewState extends GeneralPageViewState ); } - Widget createScheduleByDay( - BuildContext context, - int day, - List lectures, - RequestStatus scheduleStatus, - ) { + Widget createScheduleByDay(BuildContext context, + int day, + List lectures, + RequestStatus scheduleStatus,) { final weekday = - Provider.of(context).getWeekdaysWithLocale()[day]; + Provider.of(context).getWeekdaysWithLocale()[day]; final aggLectures = SchedulePageView.groupLecturesByDay(lectures); - return RequestDependentWidgetBuilder( - status: scheduleStatus, - builder: () => dayColumnBuilder(day, aggLectures[day], context), - hasContentPredicate: aggLectures[day].isNotEmpty, - onNullContent: Center( + + // TODO: Refactor to LazyConsumer + if (aggLectures[day].isEmpty) { + return Center( child: ImageLabel( imagePath: 'assets/images/schedule.png', - label: '${S.of(context).no_classes_on} $weekday.', + label: '${S + .of(context) + .no_classes_on} $weekday.', labelTextStyle: const TextStyle(fontSize: 15), ), - ), - ); + ); + } + + return dayColumnBuilder(day, aggLectures[day], context); } @override diff --git a/uni/test/unit/providers/exams_provider_test.dart b/uni/test/unit/providers/exams_provider_test.dart index 7d82c1831..e44eeabbd 100644 --- a/uni/test/unit/providers/exams_provider_test.dart +++ b/uni/test/unit/providers/exams_provider_test.dart @@ -60,7 +60,8 @@ void main() { 'feup', ); - final profile = Profile()..courses = [Course(id: 7474)]; + final profile = Profile() + ..courses = [Course(id: 7474)]; final session = Session(username: '', cookies: '', faculties: ['feup']); final userUcs = [sopeCourseUnit, sdisCourseUnit]; @@ -141,13 +142,14 @@ When given three exams but one is to be parsed out, .thenAnswer((_) async => throw Exception('RIP')); throwsA( - () async => provider.fetchUserExams( - parserExams, - profile, - session, - userUcs, - persistentSession: false, - ), + () async => + provider.fetchUserExams( + parserExams, + profile, + session, + userUcs, + persistentSession: false, + ), ); }); diff --git a/uni/test/unit/providers/lecture_provider_test.dart b/uni/test/unit/providers/lecture_provider_test.dart index 53302ad94..3cf831765 100644 --- a/uni/test/unit/providers/lecture_provider_test.dart +++ b/uni/test/unit/providers/lecture_provider_test.dart @@ -21,7 +21,8 @@ void main() { final fetcherMock = MockScheduleFetcher(); final mockClient = MockClient(); final mockResponse = MockResponse(); - final profile = Profile()..courses = [Course(id: 7474)]; + final profile = Profile() + ..courses = [Course(id: 7474)]; final session = Session(username: '', cookies: '', faculties: ['feup']); final day = DateTime(2021, 06); @@ -78,12 +79,13 @@ void main() { .thenAnswer((_) async => throw Exception('💥')); throwsA( - () async => provider.fetchUserLectures( - session, - profile, - fetcher: fetcherMock, - persistentSession: false, - ), + () async => + provider.fetchUserLectures( + session, + profile, + fetcher: fetcherMock, + persistentSession: false, + ), ); }); }); From 1c9c9124b7b8830e5fcfceafa131496b79b86bcc Mon Sep 17 00:00:00 2001 From: thePeras Date: Mon, 18 Dec 2023 17:23:49 +0000 Subject: [PATCH 071/334] Refactor prints balance consumer --- .../view/profile/widgets/print_info_card.dart | 118 +++++++++--------- 1 file changed, 57 insertions(+), 61 deletions(-) diff --git a/uni/lib/view/profile/widgets/print_info_card.dart b/uni/lib/view/profile/widgets/print_info_card.dart index bfaa52fa2..d58893f93 100644 --- a/uni/lib/view/profile/widgets/print_info_card.dart +++ b/uni/lib/view/profile/widgets/print_info_card.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; +import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -9,84 +10,79 @@ import 'package:uni/view/profile/widgets/create_print_mb_dialog.dart'; class PrintInfoCard extends GenericCard { PrintInfoCard({super.key}); - const PrintInfoCard.fromEditingInformation(super.key, { + const PrintInfoCard.fromEditingInformation( + super.key, { required super.editingMode, super.onDelete, }) : super.fromEditingInformation(); @override Widget buildCardContent(BuildContext context) { - return Consumer( - builder: (context, profileStateProvider, _) { - // TODO: Refactor to LazyConsumer - profileStateProvider.ensureInitialized(context); + return LazyConsumer( + builder: getPrintInfo, + hasContent: (profile) => profile.printBalance != '', + onNullContent: const Center( + child: Text( + 'Sem informação de saldo', + style: TextStyle(fontSize: 18), + ), + ), + ); + } - final profile = profileStateProvider.state!; - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Table( - columnWidths: const { - 1: FractionColumnWidth(0.4), - 2: FractionColumnWidth(.1), - }, - defaultVerticalAlignment: TableCellVerticalAlignment.middle, + Widget getPrintInfo(BuildContext context, Profile profile) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Table( + columnWidths: const { + 1: FractionColumnWidth(0.4), + 2: FractionColumnWidth(.1), + }, + defaultVerticalAlignment: TableCellVerticalAlignment.middle, + children: [ + TableRow( children: [ - TableRow( - children: [ - Container( - margin: const EdgeInsets.only( - top: 20, - bottom: 20, - left: 20, - ), - child: Text( - S - .of(context) - .available_amount, - style: Theme - .of(context) - .textTheme - .titleSmall, - ), - ), - Container( - margin: const EdgeInsets.only(right: 15), - child: Text( - profile.printBalance, - textAlign: TextAlign.end, - style: Theme - .of(context) - .textTheme - .titleLarge, - ), - ), - Container( - margin: const EdgeInsets.only(right: 5), - height: 30, - child: ElevatedButton( - style: OutlinedButton.styleFrom( - padding: EdgeInsets.zero, - ), - onPressed: () => addMoneyDialog(context), - child: const Center(child: Icon(Icons.add)), - ), + Container( + margin: const EdgeInsets.only( + top: 20, + bottom: 20, + left: 20, + ), + child: Text( + S.of(context).available_amount, + style: Theme.of(context).textTheme.titleSmall, + ), + ), + Container( + margin: const EdgeInsets.only(right: 15), + child: Text( + profile.printBalance, + textAlign: TextAlign.end, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + Container( + margin: const EdgeInsets.only(right: 5), + height: 30, + child: ElevatedButton( + style: OutlinedButton.styleFrom( + padding: EdgeInsets.zero, ), - ], + onPressed: () => addMoneyDialog(context), + child: const Center(child: Icon(Icons.add)), + ), ), ], ), ], - ); - }, + ), + ], ); } @override - String getTitle(BuildContext context) => - S - .of(context) - .prints; + String getTitle(BuildContext context) => S.of(context).prints; @override void onClick(BuildContext context) {} From 1fa640d915a0630260bfcbfaa4a425cc6d9b680c Mon Sep 17 00:00:00 2001 From: thePeras Date: Mon, 18 Dec 2023 17:24:49 +0000 Subject: [PATCH 072/334] Refactor library page consumer --- uni/lib/main.dart | 2 +- .../providers/lazy/bus_stop_provider.dart | 1 + uni/lib/view/library/library.dart | 54 ++++++++----------- 3 files changed, 25 insertions(+), 32 deletions(-) diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 8d3426922..1f199ba44 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -234,7 +234,7 @@ class ApplicationState extends State { ), '/${DrawerItem.navLibrary.title}': PageTransition.makePageTransition( - page: const LibraryPageView(), + page: const LibraryPage(), settings: settings, ), '/${DrawerItem.navUsefulInfo.title}': diff --git a/uni/lib/model/providers/lazy/bus_stop_provider.dart b/uni/lib/model/providers/lazy/bus_stop_provider.dart index e7031094e..9a9ae2095 100644 --- a/uni/lib/model/providers/lazy/bus_stop_provider.dart +++ b/uni/lib/model/providers/lazy/bus_stop_provider.dart @@ -6,6 +6,7 @@ import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; import 'package:uni/model/providers/state_providers.dart'; +//TODO (thePeras): what does > means? create a model for it class BusStopProvider extends StateProviderNotifier> { BusStopProvider() : super(cacheDuration: null); diff --git a/uni/lib/view/library/library.dart b/uni/lib/view/library/library.dart index a5173127d..84d420e3d 100644 --- a/uni/lib/view/library/library.dart +++ b/uni/lib/view/library/library.dart @@ -10,52 +10,38 @@ import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/library/widgets/library_occupation_card.dart'; -class LibraryPageView extends StatefulWidget { - const LibraryPageView({super.key}); +class LibraryPage extends StatefulWidget { + const LibraryPage({super.key}); @override - State createState() => LibraryPageViewState(); + State createState() => LibraryPageState(); } -class LibraryPageViewState extends GeneralPageViewState { +class LibraryPageState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return Consumer( - builder: (context, libraryOccupationProvider, _) { - // TODO: Refactor to LazyConsumer - libraryOccupationProvider.ensureInitialized(context); - return LibraryPage( - libraryOccupationProvider.state, - ); - }, - ); - } - - @override - Future onRefresh(BuildContext context) { - return Provider.of(context, listen: false) - .forceRefresh(context); - } -} - -class LibraryPage extends StatelessWidget { - const LibraryPage(this.occupation, {super.key}); - - final LibraryOccupation? occupation; - - @override - Widget build(BuildContext context) { return ListView( shrinkWrap: true, children: [ PageTitle(name: S.of(context).nav_title(DrawerItem.navLibrary.title)), LibraryOccupationCard(), - if (occupation != null) PageTitle(name: S.of(context).floors), - if (occupation != null) getFloorRows(context, occupation!), + PageTitle(name: S.of(context).floors), + LazyConsumer( + builder: getFloorRows, + hasContent: (occupation) => occupation.floors.isNotEmpty, + onNullContent: const Center( + child: Text( + 'Sem informação de ocupação', + style: TextStyle(fontSize: 18), + ), + ), + contentLoadingWidget: const CircularProgressIndicator(), + ), ], ); } + // This will lazy consume Widget getFloorRows(BuildContext context, LibraryOccupation occupation) { final floors = []; for (var i = 1; i < occupation.floors.length; i += 2) { @@ -131,4 +117,10 @@ class LibraryPage extends StatelessWidget { ), ); } + + @override + Future onRefresh(BuildContext context) { + return Provider.of(context, listen: false) + .forceRefresh(context); + } } From 6dddecdebf944660eaf0dc45d792e4d3dc539cf3 Mon Sep 17 00:00:00 2001 From: thePeras Date: Mon, 18 Dec 2023 17:59:34 +0000 Subject: [PATCH 073/334] Refactor buses page consumer --- .../bus_stop_next_arrivals.dart | 255 +++++++----------- 1 file changed, 98 insertions(+), 157 deletions(-) diff --git a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart index 5fe7423c3..f29a26a82 100644 --- a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart +++ b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart @@ -5,7 +5,6 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; -import 'package:uni/model/request_status.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/bus_stop_next_arrivals/widgets/bus_stop_row.dart'; import 'package:uni/view/bus_stop_selection/bus_stop_selection.dart'; @@ -13,6 +12,7 @@ import 'package:uni/view/common_widgets/expanded_image_label.dart'; import 'package:uni/view/common_widgets/last_update_timestamp.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/lazy_consumer.dart'; class BusStopNextArrivalsPage extends StatefulWidget { const BusStopNextArrivalsPage({super.key}); @@ -26,15 +26,78 @@ class BusStopNextArrivalsPageState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return Consumer(builder: (context, busProvider, _) { - // TODO: Refactor to LazyConsumer and automatic status checks - unawaited(busProvider.ensureInitialized(context)); - return ListView( + return SingleChildScrollView( + child: Column( children: [ - NextArrivals(busProvider.state!, busProvider.requestStatus), + Container( + padding: const EdgeInsets.only(bottom: 12), + child: PageTitle( + name: S.of(context).nav_title(DrawerItem.navStops.title), + ), + ), + Container( + padding: const EdgeInsets.all(8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.only(left: 10), + child: const LastUpdateTimeStamp(), + ), + IconButton( + icon: const Icon(Icons.edit), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const BusStopSelectionPage(), + ), + ), + ), + ], + ), + ), + // Page Content + LazyConsumer>( + builder: getArrivals, + hasContent: (buses) => buses.isNotEmpty, + onNullContent: Column( + children: [ + ImageLabel( + imagePath: 'assets/images/bus.png', + label: S.of(context).no_bus, + labelTextStyle: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 17, + color: Theme.of(context).colorScheme.primary, + ), + ), + Column( + children: [ + ElevatedButton( + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const BusStopSelectionPage(), + ), + ), + child: Text(S.of(context).add), + ), + ], + ), + ], + ), + contentLoadingWidget: Container( + padding: const EdgeInsets.all(22), + child: const Center(child: CircularProgressIndicator()), + ), + ) ], - ); - }); + ), + ); + } + + Widget getArrivals(BuildContext context, Map buses) { + return NextArrivals(buses); } @override @@ -45,10 +108,9 @@ class BusStopNextArrivalsPageState } class NextArrivals extends StatefulWidget { - const NextArrivals(this.buses, this.busStopStatus, {super.key}); + const NextArrivals(this.buses, {super.key}); final Map buses; - final RequestStatus busStopStatus; @override NextArrivalsState createState() => NextArrivalsState(); @@ -58,165 +120,44 @@ class NextArrivals extends StatefulWidget { class NextArrivalsState extends State { @override Widget build(BuildContext context) { - Widget contentBuilder() { - switch (widget.busStopStatus) { - case RequestStatus.successful: - return SizedBox( - height: MediaQuery.of(context).size.height, - child: Column(children: requestSuccessful(context)), - ); - case RequestStatus.busy: - return SizedBox( - height: MediaQuery.of(context).size.height, - child: Column(children: requestBusy(context)), - ); - case RequestStatus.failed: - return SizedBox( - height: MediaQuery.of(context).size.height, - child: Column(children: requestFailed(context)), - ); - case RequestStatus.none: - return Container(); - } - } - return DefaultTabController( length: widget.buses.length, - child: contentBuilder(), - ); - } - - /// Returns a list of widgets for a successfull request - - List requestSuccessful(BuildContext context) { - final result = [...getHeader(context)]; - - if (widget.buses.isNotEmpty) { - result.addAll(getContent(context)); - } else { - result - ..add( - ImageLabel( - imagePath: 'assets/images/bus.png', - label: S.of(context).no_bus, - labelTextStyle: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 17, - color: Theme.of(context).colorScheme.primary, - ), - ), - ) - ..add( - Column( - children: [ - ElevatedButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const BusStopSelectionPage(), - ), - ), - child: Text(S.of(context).add), - ), - ], - ), - ); - } - - return result; - } - - /// Returns a list of widgets for a busy request - // TODO(bdmendes): Is this ok? - List requestBusy(BuildContext context) { - return [ - getPageTitle(), - Container( - padding: const EdgeInsets.all(22), - child: const Center(child: CircularProgressIndicator()), - ), - ]; - } - - Container getPageTitle() { - return Container( - padding: const EdgeInsets.only(bottom: 12), - child: PageTitle( - name: S.of(context).nav_title(DrawerItem.navStops.title), + child: SizedBox( + height: MediaQuery.of(context).size.height * 0.6, + child: getContent(context), ), ); } - /// Returns a list of widgets for a failed request - List requestFailed(BuildContext context) { - return [ - ...getHeader(context), - Container( - padding: const EdgeInsets.only(bottom: 12), - child: Text( - 'Não foi possível obter informação', - maxLines: 2, - overflow: TextOverflow.fade, - style: Theme.of(context).textTheme.titleMedium, - ), - ), - ]; - } - - List getHeader(BuildContext context) { - return [ - getPageTitle(), - Container( - padding: const EdgeInsets.all(8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - padding: const EdgeInsets.only(left: 10), - child: const LastUpdateTimeStamp(), - ), - IconButton( - icon: const Icon(Icons.edit), - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const BusStopSelectionPage(), - ), - ), - ), - ], - ), - ), - ]; - } - - List getContent(BuildContext context) { + Widget getContent(BuildContext context) { final queryData = MediaQuery.of(context); - return [ - Container( - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide(), + return Column( + children: [ + Container( + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide(), + ), ), - ), - constraints: const BoxConstraints(maxHeight: 150), - child: Material( - child: TabBar( - isScrollable: true, - tabs: createTabs(queryData), + constraints: const BoxConstraints(maxHeight: 150), + child: Material( + child: TabBar( + isScrollable: true, + tabs: createTabs(queryData), + ), ), ), - ), - Expanded( - child: Container( - padding: const EdgeInsets.only(bottom: 92), - child: TabBarView( - children: getEachBusStopInfo(context), + Expanded( + child: Container( + padding: const EdgeInsets.only(bottom: 92), + child: TabBarView( + children: getEachBusStopInfo(context), + ), ), ), - ), - ]; + ], + ); } List createTabs(MediaQueryData queryData) { @@ -239,7 +180,7 @@ class NextArrivalsState extends State { widget.buses.forEach((stopCode, stopData) { rows.add( - ListView( + Column( children: [ Container( padding: const EdgeInsets.only( From ba39b045133e09935d94f6745013d3c395cc004c Mon Sep 17 00:00:00 2001 From: thePeras Date: Wed, 20 Dec 2023 17:08:50 +0000 Subject: [PATCH 074/334] Refactor lectures page with new unique provider aproach --- uni/lib/view/schedule/schedule.dart | 161 +++++++++++----------------- 1 file changed, 61 insertions(+), 100 deletions(-) diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index addb738ec..d3f71d692 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -3,14 +3,15 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/providers/lazy/lecture_provider.dart'; -import 'package:uni/model/request_status.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/schedule/widgets/schedule_slot.dart'; +// (thePeras) TODO: Remove this useless class class SchedulePage extends StatefulWidget { const SchedulePage({super.key}); @@ -21,46 +22,15 @@ class SchedulePage extends StatefulWidget { class SchedulePageState extends State { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, lectureProvider, _) { - // TODO: Refactor to LazyConsumer - lectureProvider.ensureInitialized(context); - return SchedulePageView( - lectures: lectureProvider.state!, - scheduleStatus: lectureProvider.requestStatus, - ); - }, - ); + return SchedulePageView(); } } /// Manages the 'schedule' sections of the app class SchedulePageView extends StatefulWidget { - SchedulePageView({ - required this.lectures, - required this.scheduleStatus, - super.key, - }); - - final List lectures; - final RequestStatus scheduleStatus; - - final int weekDay = DateTime - .now() - .weekday; - - static List> groupLecturesByDay(List schedule) { - final aggLectures = >[]; - - for (var i = 0; i < 5; i++) { - final lectures = {}; - for (var j = 0; j < schedule.length; j++) { - if (schedule[j].startTime.weekday - 1 == i) lectures.add(schedule[j]); - } - aggLectures.add(lectures); - } - return aggLectures; - } + SchedulePageView({super.key}); + + final int weekDay = DateTime.now().weekday; @override SchedulePageViewState createState() => SchedulePageViewState(); @@ -98,8 +68,8 @@ class SchedulePageViewState extends GeneralPageViewState children: [ PageTitle( name: S.of(context).nav_title( - DrawerItem.navSchedule.title, - ), + DrawerItem.navSchedule.title, + ), ), TabBar( controller: tabController, @@ -112,11 +82,20 @@ class SchedulePageViewState extends GeneralPageViewState Expanded( child: TabBarView( controller: tabController, - children: createSchedule( - context, - widget.lectures, - widget.scheduleStatus, - ), + children: Iterable.generate(5) + .map( + (day) => LazyConsumer>( + builder: (context, lectures) => dayColumnBuilder( + day, + lecturesOfDay(lectures, day), + context, + ), + hasContent: (lectures) => + lecturesOfDay(lectures, day).isNotEmpty, + onNullContent: emptyDayColumn(context, day), + ), + ) + .toList(), ), ), ], @@ -143,73 +122,55 @@ class SchedulePageViewState extends GeneralPageViewState return tabs; } - List createSchedule(BuildContext context, - List lectures, - RequestStatus scheduleStatus,) { - final tabBarViewContent = []; - for (var i = 0; i < 5; i++) { - tabBarViewContent - .add(createScheduleByDay(context, i, lectures, scheduleStatus)); - } - return tabBarViewContent; - } - - /// Returns a list of widgets for the rows with a singular class info. - List createScheduleRows(Set lectures, BuildContext context) { - final scheduleContent = []; - final lectureList = lectures.toList(); - for (var i = 0; i < lectureList.length; i++) { - final lecture = lectureList[i]; - scheduleContent.add( - ScheduleSlot( - subject: lecture.subject, - typeClass: lecture.typeClass, - rooms: lecture.room, - begin: lecture.startTime, - end: lecture.endTime, - occurrId: lecture.occurrId, - teacher: lecture.teacher, - classNumber: lecture.classNumber, - ), - ); - } - return scheduleContent; - } - - Widget dayColumnBuilder(int day, - Set dayContent, - BuildContext context,) { + Widget dayColumnBuilder( + int day, + List lectures, + BuildContext context, + ) { return Container( key: Key('schedule-page-day-column-$day'), child: Column( mainAxisSize: MainAxisSize.min, - children: createScheduleRows(dayContent, context), + children: lectures + .map( + //(thePeras) TODO: ScheduleSlot should receive a lecture instead of all these parameters + (lecture) => ScheduleSlot( + subject: lecture.subject, + typeClass: lecture.typeClass, + rooms: lecture.room, + begin: lecture.startTime, + end: lecture.endTime, + occurrId: lecture.occurrId, + teacher: lecture.teacher, + classNumber: lecture.classNumber, + ), + ) + .toList(), ), ); } - Widget createScheduleByDay(BuildContext context, - int day, - List lectures, - RequestStatus scheduleStatus,) { - final weekday = - Provider.of(context).getWeekdaysWithLocale()[day]; - final aggLectures = SchedulePageView.groupLecturesByDay(lectures); - - // TODO: Refactor to LazyConsumer - if (aggLectures[day].isEmpty) { - return Center( - child: ImageLabel( - imagePath: 'assets/images/schedule.png', - label: '${S - .of(context) - .no_classes_on} $weekday.', - labelTextStyle: const TextStyle(fontSize: 15), - ), - ); + static List lecturesOfDay(List lectures, int day) { + final filteredLectures = []; + for (var i = 0; i < lectures.length; i++) { + if (lectures[i].startTime.weekday - 1 == day) { + filteredLectures.add(lectures[i]); + } } + return filteredLectures; + } + + Widget emptyDayColumn(BuildContext context, int day) { + final weekday = + Provider.of(context).getWeekdaysWithLocale()[day]; - return dayColumnBuilder(day, aggLectures[day], context); + return Center( + child: ImageLabel( + imagePath: 'assets/images/schedule.png', + label: '${S.of(context).no_classes_on} $weekday.', + labelTextStyle: const TextStyle(fontSize: 15), + ), + ); } @override From 22e2d57c7200820ea14b486e7741ef52f1e81722 Mon Sep 17 00:00:00 2001 From: thePeras Date: Wed, 20 Dec 2023 17:32:00 +0000 Subject: [PATCH 075/334] Format and languages --- uni/lib/l10n/intl_en.arb | 4 + uni/lib/l10n/intl_pt_PT.arb | 4 + .../model/providers/lazy/exam_provider.dart | 15 +- .../pages_layouts/general/general.dart | 125 ++++++------ .../course_unit_info/course_unit_info.dart | 26 +-- .../view/home/widgets/main_cards_list.dart | 181 ++++++++---------- uni/lib/view/library/library.dart | 2 +- .../view/profile/widgets/print_info_card.dart | 2 +- .../widgets/tuition_notification_switch.dart | 4 +- .../unit/providers/exams_provider_test.dart | 18 +- .../unit/providers/lecture_provider_test.dart | 16 +- 11 files changed, 175 insertions(+), 222 deletions(-) diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 501c6d1d8..1f3802a74 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -210,6 +210,10 @@ "@no_selected_courses": {}, "no_selected_exams": "There are no exams to present", "@no_selected_exams": {}, + "no_print_info": "No print balance information", + "@no_print_info": {}, + "no_library_info": "No library occupation information available", + "@no_library_info": {}, "occurrence_type": "Type of occurrence", "@occurrence_type": {}, "other_links": "Other links", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 5e42488f3..2acce7162 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -210,6 +210,10 @@ "@no_selected_courses": {}, "no_selected_exams": "Não existem exames para apresentar", "@no_selected_exams": {}, + "no_print_info": "Sem informação de saldo", + "@no_print_info": {}, + "no_library_info": "Sem informação de ocupação", + "@no_library_info": {}, "occurrence_type": "Tipo de ocorrência", "@occurrence_type": {}, "other_links": "Outros links", diff --git a/uni/lib/model/providers/lazy/exam_provider.dart b/uni/lib/model/providers/lazy/exam_provider.dart index bb53b1f92..946596798 100644 --- a/uni/lib/model/providers/lazy/exam_provider.dart +++ b/uni/lib/model/providers/lazy/exam_provider.dart @@ -31,16 +31,17 @@ class ExamProvider extends StateProviderNotifier> { session, profile.courseUnits, persistentSession: - (PreferencesController.getPersistentUserInfo()) != null, + (PreferencesController.getPersistentUserInfo()) != null, ); } - Future> fetchUserExams(ParserExams parserExams, - Profile profile, - Session session, - List userUcs, { - required bool persistentSession, - }) async { + Future> fetchUserExams( + ParserExams parserExams, + Profile profile, + Session session, + List userUcs, { + required bool persistentSession, + }) async { final exams = await ExamFetcher(profile.courses, userUcs) .extractExams(session, parserExams); diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 27232f4c5..53b55aa76 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -51,29 +51,30 @@ abstract class GeneralPageViewState extends State { context, _loading ? const Flex( - direction: Axis.vertical, - children: [ - Expanded( - child: Center( - child: CircularProgressIndicator(), - ), - ), - ], - ) + direction: Axis.vertical, + children: [ + Expanded( + child: Center( + child: CircularProgressIndicator(), + ), + ), + ], + ) : getBody(context), ); } Widget getBody(BuildContext context); - Future buildProfileDecorationImage(BuildContext context, { + Future buildProfileDecorationImage( + BuildContext context, { bool forceRetrieval = false, }) async { final sessionProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); await sessionProvider.ensureInitialized(context); final profilePictureFile = - await ProfileProvider.fetchOrGetCachedProfilePicture( + await ProfileProvider.fetchOrGetCachedProfilePicture( sessionProvider.state!, forceRetrieval: forceRetrieval, ); @@ -86,33 +87,29 @@ abstract class GeneralPageViewState extends State { DecorationImage getProfileDecorationImage(File? profilePicture) { const fallbackPicture = AssetImage('assets/images/profile_placeholder.png'); final image = - profilePicture == null ? fallbackPicture : FileImage(profilePicture); + profilePicture == null ? fallbackPicture : FileImage(profilePicture); final result = - DecorationImage(fit: BoxFit.cover, image: image as ImageProvider); + DecorationImage(fit: BoxFit.cover, image: image as ImageProvider); return result; } Widget refreshState(BuildContext context, Widget child) { return RefreshIndicator( key: GlobalKey(), - onRefresh: () => - ProfileProvider.fetchOrGetCachedProfilePicture( - Provider - .of(context, listen: false) - .state!, - forceRetrieval: true, - ).then((value) => onRefresh(context)), + onRefresh: () => ProfileProvider.fetchOrGetCachedProfilePicture( + Provider.of(context, listen: false).state!, + forceRetrieval: true, + ).then((value) => onRefresh(context)), child: Builder( - builder: (context) => - GestureDetector( - onHorizontalDragEnd: (dragDetails) { - if (dragDetails.primaryVelocity! > 2) { - Scaffold.of(context).openDrawer(); - } - }, - child: child, - ), + builder: (context) => GestureDetector( + onHorizontalDragEnd: (dragDetails) { + if (dragDetails.primaryVelocity! > 2) { + Scaffold.of(context).openDrawer(); + } + }, + child: child, + ), ), ); } @@ -136,53 +133,40 @@ abstract class GeneralPageViewState extends State { bottom: PreferredSize( preferredSize: Size.zero, child: Container( - color: Theme - .of(context) - .dividerColor, + color: Theme.of(context).dividerColor, margin: EdgeInsets.only(left: borderMargin, right: borderMargin), height: 1.5, ), ), elevation: 0, - iconTheme: Theme - .of(context) - .iconTheme, - backgroundColor: Theme - .of(context) - .scaffoldBackgroundColor, + iconTheme: Theme.of(context).iconTheme, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, titleSpacing: 0, title: ButtonTheme( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: const RoundedRectangleBorder(), child: Builder( - builder: (context) => - TextButton( - onPressed: () { - final currentRouteName = ModalRoute - .of(context)! - .settings - .name; - if (currentRouteName != - '/${DrawerItem.navPersonalArea.title}') { - Navigator.pushNamed( - context, - '/${DrawerItem.navPersonalArea.title}', - ); - } else { - Scaffold.of(context).openDrawer(); - } - }, - child: SvgPicture.asset( - colorFilter: ColorFilter.mode( - Theme - .of(context) - .primaryColor, - BlendMode.srcIn, - ), - 'assets/images/logo_dark.svg', - height: queryData.size.height / 25, - ), + builder: (context) => TextButton( + onPressed: () { + final currentRouteName = ModalRoute.of(context)!.settings.name; + if (currentRouteName != '/${DrawerItem.navPersonalArea.title}') { + Navigator.pushNamed( + context, + '/${DrawerItem.navPersonalArea.title}', + ); + } else { + Scaffold.of(context).openDrawer(); + } + }, + child: SvgPicture.asset( + colorFilter: ColorFilter.mode( + Theme.of(context).primaryColor, + BlendMode.srcIn, ), + 'assets/images/logo_dark.svg', + height: queryData.size.height / 25, + ), + ), ), ), actions: [ @@ -195,11 +179,12 @@ abstract class GeneralPageViewState extends State { Widget getTopRightButton(BuildContext context) { return FutureBuilder( future: buildProfileDecorationImage(context), - builder: (BuildContext context, - AsyncSnapshot decorationImage,) { + builder: ( + BuildContext context, + AsyncSnapshot decorationImage, + ) { return TextButton( - onPressed: () => - { + onPressed: () => { Navigator.push( context, MaterialPageRoute( diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index 3da06627c..5b1a6c49d 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -24,13 +24,11 @@ class CourseUnitDetailPageViewState extends SecondaryPageViewState { Future loadInfo({required bool force}) async { final courseUnitsProvider = - Provider.of(context, listen: false); - final session = context - .read() - .state!; + Provider.of(context, listen: false); + final session = context.read().state!; final courseUnitSheet = - courseUnitsProvider.courseUnitsSheets[widget.courseUnit]; + courseUnitsProvider.courseUnitsSheets[widget.courseUnit]; if (courseUnitSheet == null || force) { await courseUnitsProvider.fetchCourseUnitSheet( widget.courseUnit, @@ -39,7 +37,7 @@ class CourseUnitDetailPageViewState } final courseUnitClasses = - courseUnitsProvider.courseUnitsClasses[widget.courseUnit]; + courseUnitsProvider.courseUnitsClasses[widget.courseUnit]; if (courseUnitClasses == null || force) { await courseUnitsProvider.fetchCourseUnitClasses( widget.courseUnit, @@ -71,12 +69,8 @@ class CourseUnitDetailPageViewState ), TabBar( tabs: [ - Tab(text: S - .of(context) - .course_info), - Tab(text: S - .of(context) - .course_class), + Tab(text: S.of(context).course_info), + Tab(text: S.of(context).course_class), ], ), Expanded( @@ -103,9 +97,7 @@ class CourseUnitDetailPageViewState if (sheet == null || sheet.sections.isEmpty) { return Center( child: Text( - S - .of(context) - .no_info, + S.of(context).no_info, textAlign: TextAlign.center, ), ); @@ -122,9 +114,7 @@ class CourseUnitDetailPageViewState if (classes == null || classes.isEmpty) { return Center( child: Text( - S - .of(context) - .no_class, + S.of(context).no_class, textAlign: TextAlign.center, ), ); diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index ca61fea38..e1f06199c 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -16,16 +16,17 @@ import 'package:uni/view/profile/widgets/account_info_card.dart'; import 'package:uni/view/profile/widgets/print_info_card.dart'; typedef CardCreator = GenericCard Function( - Key key, { - required bool editingMode, - void Function()? onDelete, - }); + Key key, { + required bool editingMode, + void Function()? onDelete, +}); class MainCardsList extends StatefulWidget { - const MainCardsList(this.favoriteCardTypes, - this.saveFavoriteCards, { - super.key, - }); + const MainCardsList( + this.favoriteCardTypes, + this.saveFavoriteCards, { + super.key, + }); final List favoriteCardTypes; final void Function(List) saveFavoriteCards; @@ -38,7 +39,7 @@ class MainCardsList extends StatefulWidget { FavoriteWidgetType.busStops: BusStopCard.fromEditingInformation, FavoriteWidgetType.restaurant: RestaurantCard.fromEditingInformation, FavoriteWidgetType.libraryOccupation: - LibraryOccupationCard.fromEditingInformation, + LibraryOccupationCard.fromEditingInformation, }; @override @@ -55,28 +56,25 @@ class MainCardsListState extends State { return Scaffold( body: BackButtonExitWrapper( child: SizedBox( - height: MediaQuery - .of(context) - .size - .height, + height: MediaQuery.of(context).size.height, child: isEditing ? ReorderableListView( - onReorder: reorderCard, - header: createTopBar(context), - children: favoriteCardsFromTypes( - widget.favoriteCardTypes, - context, - ), - ) + onReorder: reorderCard, + header: createTopBar(context), + children: favoriteCardsFromTypes( + widget.favoriteCardTypes, + context, + ), + ) : ListView( - children: [ - createTopBar(context), - ...favoriteCardsFromTypes( - widget.favoriteCardTypes, - context, - ), - ], - ), + children: [ + createTopBar(context), + ...favoriteCardsFromTypes( + widget.favoriteCardTypes, + context, + ), + ], + ), ), ), floatingActionButton: isEditing ? createActionButton(context) : null, @@ -85,64 +83,44 @@ class MainCardsListState extends State { Widget createActionButton(BuildContext context) { return FloatingActionButton( - onPressed: () => - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text( - S - .of(context) - .widget_prompt, - style: Theme - .of(context) - .textTheme - .headlineSmall, - ), - content: SizedBox( - height: 200, - width: 100, - child: ListView(children: getCardAdders(context)), + onPressed: () => showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text( + S.of(context).widget_prompt, + style: Theme.of(context).textTheme.headlineSmall, + ), + content: SizedBox( + height: 200, + width: 100, + child: ListView(children: getCardAdders(context)), + ), + actions: [ + TextButton( + child: Text( + S.of(context).cancel, + style: Theme.of(context).textTheme.bodyMedium, ), - actions: [ - TextButton( - child: Text( - S - .of(context) - .cancel, - style: Theme - .of(context) - .textTheme - .bodyMedium, - ), - onPressed: () => Navigator.pop(context), - ), - ], - ); - }, - ), //Add FAB functionality here - tooltip: S - .of(context) - .add_widget, - child: Icon(Icons.add, color: Theme - .of(context) - .colorScheme - .onPrimary), + onPressed: () => Navigator.pop(context), + ), + ], + ); + }, + ), //Add FAB functionality here + tooltip: S.of(context).add_widget, + child: Icon(Icons.add, color: Theme.of(context).colorScheme.onPrimary), ); } List getCardAdders(BuildContext context) { - final session = - Provider - .of(context, listen: false) - .state!; + final session = Provider.of(context, listen: false).state!; final possibleCardAdditions = MainCardsList.cardCreators.entries .where((e) => e.key.isVisible(session.faculties)) .where((e) => !widget.favoriteCardTypes.contains(e.key)) .map( - (e) => - DecoratedBox( + (e) => DecoratedBox( decoration: const BoxDecoration(), child: ListTile( title: Text( @@ -157,18 +135,17 @@ class MainCardsListState extends State { }, ), ), - ) + ) .toList(); return possibleCardAdditions.isEmpty - ? [Text(S - .of(context) - .all_widgets_added) - ] + ? [Text(S.of(context).all_widgets_added)] : possibleCardAdditions; } - Widget createTopBar(BuildContext context,) { + Widget createTopBar( + BuildContext context, + ) { return Container( padding: const EdgeInsets.fromLTRB(20, 10, 20, 0), child: Row( @@ -181,26 +158,20 @@ class MainCardsListState extends State { ), if (isEditing) ElevatedButton( - onPressed: () => - setState(() { - isEditing = false; - }), + onPressed: () => setState(() { + isEditing = false; + }), child: Text( - S - .of(context) - .edit_on, + S.of(context).edit_on, ), ) else OutlinedButton( - onPressed: () => - setState(() { - isEditing = true; - }), + onPressed: () => setState(() { + isEditing = true; + }), child: Text( - S - .of(context) - .edit_off, + S.of(context).edit_off, ), ), ], @@ -208,12 +179,12 @@ class MainCardsListState extends State { ); } - List favoriteCardsFromTypes(List cardTypes, - BuildContext context,) { + List favoriteCardsFromTypes( + List cardTypes, + BuildContext context, + ) { final userSession = - Provider - .of(context, listen: false) - .state!; + Provider.of(context, listen: false).state!; return cardTypes .where((type) => type.isVisible(userSession.faculties)) .where((type) => MainCardsList.cardCreators.containsKey(type)) @@ -227,10 +198,12 @@ class MainCardsListState extends State { }).toList(); } - void reorderCard(int oldIndex, - int newIndex,) { + void reorderCard( + int oldIndex, + int newIndex, + ) { final newFavorites = - List.from(widget.favoriteCardTypes); + List.from(widget.favoriteCardTypes); final tmp = newFavorites[oldIndex]; newFavorites ..removeAt(oldIndex) diff --git a/uni/lib/view/library/library.dart b/uni/lib/view/library/library.dart index 84d420e3d..b03574410 100644 --- a/uni/lib/view/library/library.dart +++ b/uni/lib/view/library/library.dart @@ -31,7 +31,7 @@ class LibraryPageState extends GeneralPageViewState { hasContent: (occupation) => occupation.floors.isNotEmpty, onNullContent: const Center( child: Text( - 'Sem informação de ocupação', + S.of(context).no_library_info, style: TextStyle(fontSize: 18), ), ), diff --git a/uni/lib/view/profile/widgets/print_info_card.dart b/uni/lib/view/profile/widgets/print_info_card.dart index d58893f93..96487bbc8 100644 --- a/uni/lib/view/profile/widgets/print_info_card.dart +++ b/uni/lib/view/profile/widgets/print_info_card.dart @@ -23,7 +23,7 @@ class PrintInfoCard extends GenericCard { hasContent: (profile) => profile.printBalance != '', onNullContent: const Center( child: Text( - 'Sem informação de saldo', + S.of(context).no_print_info, style: TextStyle(fontSize: 18), ), ), diff --git a/uni/lib/view/settings/widgets/tuition_notification_switch.dart b/uni/lib/view/settings/widgets/tuition_notification_switch.dart index 71eb04dc9..011de66c9 100644 --- a/uni/lib/view/settings/widgets/tuition_notification_switch.dart +++ b/uni/lib/view/settings/widgets/tuition_notification_switch.dart @@ -9,8 +9,8 @@ class TuitionNotificationSwitch extends StatefulWidget { } class _TuitionNotificationSwitchState extends State { - bool tuitionNotificationToggle = PreferencesController - .getTuitionNotificationToggle(); + bool tuitionNotificationToggle = + PreferencesController.getTuitionNotificationToggle(); Future saveTuitionNotificationToggle({required bool value}) async { await PreferencesController.setTuitionNotificationToggle(value: value); diff --git a/uni/test/unit/providers/exams_provider_test.dart b/uni/test/unit/providers/exams_provider_test.dart index e44eeabbd..7d82c1831 100644 --- a/uni/test/unit/providers/exams_provider_test.dart +++ b/uni/test/unit/providers/exams_provider_test.dart @@ -60,8 +60,7 @@ void main() { 'feup', ); - final profile = Profile() - ..courses = [Course(id: 7474)]; + final profile = Profile()..courses = [Course(id: 7474)]; final session = Session(username: '', cookies: '', faculties: ['feup']); final userUcs = [sopeCourseUnit, sdisCourseUnit]; @@ -142,14 +141,13 @@ When given three exams but one is to be parsed out, .thenAnswer((_) async => throw Exception('RIP')); throwsA( - () async => - provider.fetchUserExams( - parserExams, - profile, - session, - userUcs, - persistentSession: false, - ), + () async => provider.fetchUserExams( + parserExams, + profile, + session, + userUcs, + persistentSession: false, + ), ); }); diff --git a/uni/test/unit/providers/lecture_provider_test.dart b/uni/test/unit/providers/lecture_provider_test.dart index 3cf831765..53302ad94 100644 --- a/uni/test/unit/providers/lecture_provider_test.dart +++ b/uni/test/unit/providers/lecture_provider_test.dart @@ -21,8 +21,7 @@ void main() { final fetcherMock = MockScheduleFetcher(); final mockClient = MockClient(); final mockResponse = MockResponse(); - final profile = Profile() - ..courses = [Course(id: 7474)]; + final profile = Profile()..courses = [Course(id: 7474)]; final session = Session(username: '', cookies: '', faculties: ['feup']); final day = DateTime(2021, 06); @@ -79,13 +78,12 @@ void main() { .thenAnswer((_) async => throw Exception('💥')); throwsA( - () async => - provider.fetchUserLectures( - session, - profile, - fetcher: fetcherMock, - persistentSession: false, - ), + () async => provider.fetchUserLectures( + session, + profile, + fetcher: fetcherMock, + persistentSession: false, + ), ); }); }); From c9cf8d90bbf55394839706d0ecc05fa778bddff4 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Dec 2023 10:33:03 +0000 Subject: [PATCH 076/334] Make lectures page more testable --- .../local_storage/preferences_controller.dart | 3 +- uni/lib/generated/intl/messages_en.dart | 4 ++ uni/lib/generated/intl/messages_pt_PT.dart | 4 ++ uni/lib/generated/l10n.dart | 20 ++++++++++ .../providers/lazy/bus_stop_provider.dart | 2 +- .../providers/lazy/calendar_provider.dart | 2 - .../lazy/course_units_info_provider.dart | 1 - .../lazy/faculty_locations_provider.dart | 3 +- .../lazy/library_occupation_provider.dart | 3 +- .../providers/lazy/reference_provider.dart | 3 -- .../bus_stop_next_arrivals.dart | 2 +- .../widgets/estimated_arrival_timestamp.dart | 1 - .../bus_stop_selection.dart | 1 - uni/lib/view/course_units/course_units.dart | 10 +++-- uni/lib/view/library/library.dart | 4 +- .../profile/widgets/account_info_card.dart | 3 +- .../view/profile/widgets/print_info_card.dart | 4 +- uni/lib/view/schedule/schedule.dart | 40 +++++++++---------- .../view/Pages/schedule_page_view_test.dart | 11 ++--- 19 files changed, 69 insertions(+), 52 deletions(-) diff --git a/uni/lib/controller/local_storage/preferences_controller.dart b/uni/lib/controller/local_storage/preferences_controller.dart index 0aee5a5eb..bf231ae9c 100644 --- a/uni/lib/controller/local_storage/preferences_controller.dart +++ b/uni/lib/controller/local_storage/preferences_controller.dart @@ -5,7 +5,6 @@ import 'package:encrypt/encrypt.dart' as encrypt; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tuple/tuple.dart'; -import 'package:uni/main.dart'; import 'package:uni/model/entities/app_locale.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/utils/favorite_widget_type.dart'; @@ -15,7 +14,7 @@ import 'package:uni/utils/favorite_widget_type.dart'; /// This database stores the user's student number, password and favorite /// widgets. class PreferencesController { - // TODO: Initilizate this also on workmanager + // TODO(bdmendes): Initilizate this also on workmanager static late SharedPreferences prefs; static final iv = encrypt.IV.fromBase64('jF9jjdSEPgsKnf0jCl1GAQ=='); diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index aec972737..7ccb628cb 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -184,6 +184,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("No favorite restaurants"), "no_info": MessageLookupByLibrary.simpleMessage( "There is no information to display"), + "no_library_info": MessageLookupByLibrary.simpleMessage( + "No library occupation information available"), "no_menu_info": MessageLookupByLibrary.simpleMessage( "There is no information available about meals"), "no_menus": MessageLookupByLibrary.simpleMessage( @@ -192,6 +194,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Unnamed course"), "no_places_info": MessageLookupByLibrary.simpleMessage( "There is no information available about places"), + "no_print_info": MessageLookupByLibrary.simpleMessage( + "No print balance information"), "no_references": MessageLookupByLibrary.simpleMessage( "There are no references to pay"), "no_results": MessageLookupByLibrary.simpleMessage("No match"), diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index bb6a83237..6fc166ead 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -185,6 +185,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Sem restaurantes favoritos"), "no_info": MessageLookupByLibrary.simpleMessage( "Não existem informações para apresentar"), + "no_library_info": + MessageLookupByLibrary.simpleMessage("Sem informação de ocupação"), "no_menu_info": MessageLookupByLibrary.simpleMessage( "Não há informação disponível sobre refeições"), "no_menus": MessageLookupByLibrary.simpleMessage( @@ -193,6 +195,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Curso sem nome"), "no_places_info": MessageLookupByLibrary.simpleMessage( "Não há informação disponível sobre locais"), + "no_print_info": + MessageLookupByLibrary.simpleMessage("Sem informação de saldo"), "no_references": MessageLookupByLibrary.simpleMessage( "Não existem referências a pagar"), "no_results": MessageLookupByLibrary.simpleMessage("Sem resultados"), diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index deaa16010..4fa6476f7 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1076,6 +1076,26 @@ class S { ); } + /// `No print balance information` + String get no_print_info { + return Intl.message( + 'No print balance information', + name: 'no_print_info', + desc: '', + args: [], + ); + } + + /// `No library occupation information available` + String get no_library_info { + return Intl.message( + 'No library occupation information available', + name: 'no_library_info', + desc: '', + args: [], + ); + } + /// `Type of occurrence` String get occurrence_type { return Intl.message( diff --git a/uni/lib/model/providers/lazy/bus_stop_provider.dart b/uni/lib/model/providers/lazy/bus_stop_provider.dart index 9a9ae2095..dac95c639 100644 --- a/uni/lib/model/providers/lazy/bus_stop_provider.dart +++ b/uni/lib/model/providers/lazy/bus_stop_provider.dart @@ -6,7 +6,7 @@ import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; import 'package:uni/model/providers/state_providers.dart'; -//TODO (thePeras): what does > means? create a model for it +// TODO(thePeras): what does > means? create a model. class BusStopProvider extends StateProviderNotifier> { BusStopProvider() : super(cacheDuration: null); diff --git a/uni/lib/model/providers/lazy/calendar_provider.dart b/uni/lib/model/providers/lazy/calendar_provider.dart index 51de622ac..9884118ae 100644 --- a/uni/lib/model/providers/lazy/calendar_provider.dart +++ b/uni/lib/model/providers/lazy/calendar_provider.dart @@ -3,8 +3,6 @@ import 'dart:async'; import 'package:uni/controller/fetchers/calendar_fetcher_html.dart'; import 'package:uni/controller/local_storage/database/app_calendar_database.dart'; import 'package:uni/model/entities/calendar_event.dart'; -import 'package:uni/model/entities/profile.dart'; -import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; import 'package:uni/model/providers/state_providers.dart'; diff --git a/uni/lib/model/providers/lazy/course_units_info_provider.dart b/uni/lib/model/providers/lazy/course_units_info_provider.dart index 817bb9754..cd3fddf05 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -5,7 +5,6 @@ import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_f import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/entities/course_units/course_unit_class.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; -import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; import 'package:uni/model/providers/state_providers.dart'; diff --git a/uni/lib/model/providers/lazy/faculty_locations_provider.dart b/uni/lib/model/providers/lazy/faculty_locations_provider.dart index 7547a2abd..13af4d1bc 100644 --- a/uni/lib/model/providers/lazy/faculty_locations_provider.dart +++ b/uni/lib/model/providers/lazy/faculty_locations_provider.dart @@ -14,7 +14,8 @@ class FacultyLocationsProvider @override Future> loadFromRemote( - StateProviders stateProviders) async { + StateProviders stateProviders, + ) async { return state!; } } diff --git a/uni/lib/model/providers/lazy/library_occupation_provider.dart b/uni/lib/model/providers/lazy/library_occupation_provider.dart index 8ec915390..258acf2e7 100644 --- a/uni/lib/model/providers/lazy/library_occupation_provider.dart +++ b/uni/lib/model/providers/lazy/library_occupation_provider.dart @@ -12,7 +12,8 @@ class LibraryOccupationProvider @override Future loadFromStorage( - StateProviders stateProviders) async { + StateProviders stateProviders, + ) async { final db = LibraryOccupationDatabase(); return db.occupation(); } diff --git a/uni/lib/model/providers/lazy/reference_provider.dart b/uni/lib/model/providers/lazy/reference_provider.dart index f2e9effba..013d565ec 100644 --- a/uni/lib/model/providers/lazy/reference_provider.dart +++ b/uni/lib/model/providers/lazy/reference_provider.dart @@ -1,12 +1,9 @@ import 'dart:async'; -import 'package:flutter/cupertino.dart'; import 'package:uni/controller/fetchers/reference_fetcher.dart'; import 'package:uni/controller/local_storage/database/app_references_database.dart'; import 'package:uni/controller/parsers/parser_references.dart'; -import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/reference.dart'; -import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; import 'package:uni/model/providers/state_providers.dart'; diff --git a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart index f29a26a82..0837db990 100644 --- a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart +++ b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart @@ -90,7 +90,7 @@ class BusStopNextArrivalsPageState padding: const EdgeInsets.all(22), child: const Center(child: CircularProgressIndicator()), ), - ) + ), ], ), ); diff --git a/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart b/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart index 0cab9559c..6ea697e78 100644 --- a/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart +++ b/uni/lib/view/bus_stop_next_arrivals/widgets/estimated_arrival_timestamp.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; -import 'package:uni/view/lazy_consumer.dart'; /// Manages the section with the estimated time for the bus arrival class EstimatedArrivalTimeStamp extends StatelessWidget { diff --git a/uni/lib/view/bus_stop_selection/bus_stop_selection.dart b/uni/lib/view/bus_stop_selection/bus_stop_selection.dart index af5f946fc..14c689c5c 100644 --- a/uni/lib/view/bus_stop_selection/bus_stop_selection.dart +++ b/uni/lib/view/bus_stop_selection/bus_stop_selection.dart @@ -8,7 +8,6 @@ import 'package:uni/view/bus_stop_selection/widgets/bus_stop_search.dart'; import 'package:uni/view/bus_stop_selection/widgets/bus_stop_selection_row.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; -import 'package:uni/view/lazy_consumer.dart'; class BusStopSelectionPage extends StatefulWidget { const BusStopSelectionPage({super.key}); diff --git a/uni/lib/view/course_units/course_units.dart b/uni/lib/view/course_units/course_units.dart index 9e7afc046..d9e3fee01 100644 --- a/uni/lib/view/course_units/course_units.dart +++ b/uni/lib/view/course_units/course_units.dart @@ -51,10 +51,12 @@ class CourseUnitsPageViewState } } - return Column(children: [ - _getPageTitleAndFilters(availableYears, availableSemesters), - _getPageView(courseUnits, availableYears, availableSemesters), - ]); + return Column( + children: [ + _getPageTitleAndFilters(availableYears, availableSemesters), + _getPageView(courseUnits, availableYears, availableSemesters), + ], + ); }, hasContent: (Profile profile) => profile.courseUnits.isNotEmpty, onNullContent: Column( diff --git a/uni/lib/view/library/library.dart b/uni/lib/view/library/library.dart index b03574410..562ee2b4c 100644 --- a/uni/lib/view/library/library.dart +++ b/uni/lib/view/library/library.dart @@ -29,10 +29,10 @@ class LibraryPageState extends GeneralPageViewState { LazyConsumer( builder: getFloorRows, hasContent: (occupation) => occupation.floors.isNotEmpty, - onNullContent: const Center( + onNullContent: Center( child: Text( S.of(context).no_library_info, - style: TextStyle(fontSize: 18), + style: const TextStyle(fontSize: 18), ), ), contentLoadingWidget: const CircularProgressIndicator(), diff --git a/uni/lib/view/profile/widgets/account_info_card.dart b/uni/lib/view/profile/widgets/account_info_card.dart index 3a168e1e8..e5f7fd425 100644 --- a/uni/lib/view/profile/widgets/account_info_card.dart +++ b/uni/lib/view/profile/widgets/account_info_card.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; +import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/reference.dart'; import 'package:uni/model/providers/lazy/reference_provider.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; @@ -9,8 +10,6 @@ import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/profile/widgets/reference_section.dart'; -import '../../../model/entities/profile.dart'; - /// Manages the 'Current account' section inside the user's page (accessible /// through the top-right widget with the user picture) class AccountInfoCard extends GenericCard { diff --git a/uni/lib/view/profile/widgets/print_info_card.dart b/uni/lib/view/profile/widgets/print_info_card.dart index 96487bbc8..e9480a726 100644 --- a/uni/lib/view/profile/widgets/print_info_card.dart +++ b/uni/lib/view/profile/widgets/print_info_card.dart @@ -21,10 +21,10 @@ class PrintInfoCard extends GenericCard { return LazyConsumer( builder: getPrintInfo, hasContent: (profile) => profile.printBalance != '', - onNullContent: const Center( + onNullContent: Center( child: Text( S.of(context).no_print_info, - style: TextStyle(fontSize: 18), + style: const TextStyle(fontSize: 18), ), ), ); diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index d3f71d692..541bf8fef 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -11,7 +11,6 @@ import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/schedule/widgets/schedule_slot.dart'; -// (thePeras) TODO: Remove this useless class class SchedulePage extends StatefulWidget { const SchedulePage({super.key}); @@ -22,15 +21,18 @@ class SchedulePage extends StatefulWidget { class SchedulePageState extends State { @override Widget build(BuildContext context) { - return SchedulePageView(); + return LazyConsumer>( + builder: (context, lectures) => SchedulePageView(lectures), + hasContent: (lectures) => lectures.isNotEmpty, + onNullContent: const SchedulePageView([]), + ); } } -/// Manages the 'schedule' sections of the app class SchedulePageView extends StatefulWidget { - SchedulePageView({super.key}); + const SchedulePageView(this.lectures, {super.key}); - final int weekDay = DateTime.now().weekday; + final List lectures; @override SchedulePageViewState createState() => SchedulePageViewState(); @@ -47,7 +49,8 @@ class SchedulePageViewState extends GeneralPageViewState vsync: this, length: 5, ); - final offset = (widget.weekDay > 5) ? 0 : (widget.weekDay - 1) % 5; + final weekDay = DateTime.now().weekday; + final offset = (weekDay > 5) ? 0 : (weekDay - 1) % 5; tabController?.animateTo(tabController!.index + offset); } @@ -82,20 +85,14 @@ class SchedulePageViewState extends GeneralPageViewState Expanded( child: TabBarView( controller: tabController, - children: Iterable.generate(5) - .map( - (day) => LazyConsumer>( - builder: (context, lectures) => dayColumnBuilder( - day, - lecturesOfDay(lectures, day), - context, - ), - hasContent: (lectures) => - lecturesOfDay(lectures, day).isNotEmpty, - onNullContent: emptyDayColumn(context, day), - ), - ) - .toList(), + children: Iterable.generate(5).map((day) { + final lectures = lecturesOfDay(widget.lectures, day); + if (lectures.isEmpty) { + return emptyDayColumn(context, day); + } else { + return dayColumnBuilder(day, lectures, context); + } + }).toList(), ), ), ], @@ -133,7 +130,8 @@ class SchedulePageViewState extends GeneralPageViewState mainAxisSize: MainAxisSize.min, children: lectures .map( - //(thePeras) TODO: ScheduleSlot should receive a lecture instead of all these parameters + // TODO(thePeras): ScheduleSlot should receive a lecture + // instead of all these parameters. (lecture) => ScheduleSlot( subject: lecture.subject, typeClass: lecture.typeClass, diff --git a/uni/test/unit/view/Pages/schedule_page_view_test.dart b/uni/test/unit/view/Pages/schedule_page_view_test.dart index 59c77e725..dace7ae9c 100644 --- a/uni/test/unit/view/Pages/schedule_page_view_test.dart +++ b/uni/test/unit/view/Pages/schedule_page_view_test.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:uni/model/entities/lecture.dart'; -import 'package:uni/model/request_status.dart'; import 'package:uni/view/schedule/schedule.dart'; import 'package:uni/view/schedule/widgets/schedule_slot.dart'; @@ -95,8 +94,7 @@ void main() { testWidgets('When given one lecture on a single day', (WidgetTester tester) async { final widget = SchedulePageView( - lectures: [lecture1], - scheduleStatus: RequestStatus.successful, + [lecture1], ); await tester.pumpWidget(testableWidget(widget, providers: [])); @@ -118,8 +116,7 @@ void main() { testWidgets('When given two lectures on a single day', (WidgetTester tester) async { final widget = SchedulePageView( - lectures: [lecture1, lecture2], - scheduleStatus: RequestStatus.successful, + [lecture1, lecture2], ); await tester.pumpWidget(testableWidget(widget, providers: [])); await tester.pumpAndSettle(); @@ -136,12 +133,13 @@ void main() { findsNWidgets(2), ); }); + testWidgets('When given lectures on different days', (WidgetTester tester) async { final widget = DefaultTabController( length: daysOfTheWeek.length, child: SchedulePageView( - lectures: [ + [ lecture1, lecture2, lecture3, @@ -149,7 +147,6 @@ void main() { lecture5, lecture6, ], - scheduleStatus: RequestStatus.successful, ), ); From 409bcd90a0f113728c8c755326f10e5a28eda814 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Dec 2023 10:40:33 +0000 Subject: [PATCH 077/334] Fix bus stop card --- .../bus_stop_next_arrivals.dart | 1 - uni/lib/view/home/widgets/bus_stop_card.dart | 72 ++++++++----------- uni/lib/view/schedule/schedule.dart | 5 +- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart index 0837db990..c0eda0b68 100644 --- a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart +++ b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart @@ -56,7 +56,6 @@ class BusStopNextArrivalsPageState ], ), ), - // Page Content LazyConsumer>( builder: getArrivals, hasContent: (buses) => buses.isNotEmpty, diff --git a/uni/lib/view/home/widgets/bus_stop_card.dart b/uni/lib/view/home/widgets/bus_stop_card.dart index 025c4490e..65dffcb35 100644 --- a/uni/lib/view/home/widgets/bus_stop_card.dart +++ b/uni/lib/view/home/widgets/bus_stop_card.dart @@ -38,9 +38,36 @@ class BusStopCard extends GenericCard { ), ], ), - builder: getCardContent, - hasContent: (Map busStops) => false, - onNullContent: const CircularProgressIndicator(), + builder: (context, stopData) => Column( + children: [ + getCardTitle(context), + getBusStopsInfo(context, stopData), + ], + ), + hasContent: (Map busStops) => busStops.isNotEmpty, + onNullContent: Container( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + S.of(context).buses_personalize, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.titleSmall!.apply(), + ), + IconButton( + icon: const Icon(Icons.settings), + onPressed: () => Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const BusStopSelectionPage(), + ), + ), + ), + ], + ), + ), ); } @@ -50,45 +77,6 @@ class BusStopCard extends GenericCard { } } -/// Returns a widget with the bus stop card final content -Widget getCardContent( - BuildContext context, - Map stopData, -) { - if (stopData.isNotEmpty) { - return Column( - children: [ - getCardTitle(context), - getBusStopsInfo(context, stopData), - ], - ); - } else { - return Container( - padding: const EdgeInsets.all(8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - S.of(context).buses_personalize, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.titleSmall!.apply(), - ), - IconButton( - icon: const Icon(Icons.settings), - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const BusStopSelectionPage(), - ), - ), - ), - ], - ), - ); - } -} - /// Returns a widget for the title of the bus stops card Widget getCardTitle(BuildContext context) { return Row( diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index 541bf8fef..60825d2e9 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -102,9 +102,8 @@ class SchedulePageViewState extends GeneralPageViewState /// Returns a list of widgets empty with tabs for each day of the week. List createTabs(MediaQueryData queryData, BuildContext context) { final tabs = []; - final workWeekDays = Provider.of(context) - .getWeekdaysWithLocale() - .sublist(0, 5); + final workWeekDays = + context.read().getWeekdaysWithLocale().sublist(0, 5); workWeekDays.asMap().forEach((index, day) { tabs.add( SizedBox( From 8ab952c8926fc430f976cd3e92943ed235a552ac Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Dec 2023 11:58:05 +0000 Subject: [PATCH 078/334] Fix non integration tests --- .../background_workers/notifications.dart | 2 + .../local_storage/preferences_controller.dart | 100 ++++++------ uni/lib/main.dart | 2 +- .../lazy/course_units_info_provider.dart | 2 +- .../providers/startup/session_provider.dart | 2 +- .../providers/state_provider_notifier.dart | 6 +- uni/lib/view/exams/exams.dart | 34 ++-- .../view/exams/widgets/exam_filter_form.dart | 8 +- .../view/exams/widgets/exam_filter_menu.dart | 37 ----- .../view/exams/widgets/exam_page_title.dart | 24 ++- uni/lib/view/exams/widgets/exam_row.dart | 30 +++- uni/lib/view/home/widgets/exam_card.dart | 28 ++-- .../profile/widgets/account_info_card.dart | 9 +- uni/test/integration/src/exams_page_test.dart | 6 +- .../integration/src/schedule_page_test.dart | 6 +- .../src/exams_page_test.mocks.dart | 14 ++ .../src/schedule_page_test.mocks.dart | 146 +++++++++--------- .../providers/exams_provider_test.mocks.dart | 15 ++ .../lecture_provider_test.mocks.dart | 16 ++ uni/test/test_widget.dart | 11 +- .../unit/providers/exams_provider_test.dart | 29 +++- .../unit/providers/lecture_provider_test.dart | 9 +- .../unit/view/Pages/exams_page_view_test.dart | 26 ++-- .../view/Pages/schedule_page_view_test.dart | 4 +- uni/test/unit/view/Widgets/exam_row_test.dart | 20 ++- .../unit/view/Widgets/schedule_slot_test.dart | 4 +- 26 files changed, 355 insertions(+), 235 deletions(-) delete mode 100644 uni/lib/view/exams/widgets/exam_filter_menu.dart diff --git a/uni/lib/controller/background_workers/notifications.dart b/uni/lib/controller/background_workers/notifications.dart index ae5934de4..4657dee45 100644 --- a/uni/lib/controller/background_workers/notifications.dart +++ b/uni/lib/controller/background_workers/notifications.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:logger/logger.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:tuple/tuple.dart'; import 'package:uni/controller/background_workers/notifications/tuition_notification.dart'; import 'package:uni/controller/local_storage/notification_timeout_storage.dart'; @@ -68,6 +69,7 @@ class NotificationManager { static const Duration _notificationWorkerPeriod = Duration(hours: 1); static Future updateAndTriggerNotifications() async { + PreferencesController.prefs = await SharedPreferences.getInstance(); final userInfo = PreferencesController.getPersistentUserInfo(); final faculties = PreferencesController.getUserFaculties(); diff --git a/uni/lib/controller/local_storage/preferences_controller.dart b/uni/lib/controller/local_storage/preferences_controller.dart index bf231ae9c..8347abfc2 100644 --- a/uni/lib/controller/local_storage/preferences_controller.dart +++ b/uni/lib/controller/local_storage/preferences_controller.dart @@ -21,31 +21,31 @@ class PreferencesController { static final key = encrypt.Key.fromBase64('DT3/GTNYldhwOD3ZbpVLoAwA/mncsN7U7sJxfFn3y0A='); - static const lastUpdateTimeKeySuffix = '_last_update_time'; - static const String userNumber = 'user_number'; - static const String userPw = 'user_password'; - static const String userFaculties = 'user_faculties'; - static const String termsAndConditions = 'terms_and_conditions'; - static const String areTermsAndConditionsAcceptedKey = 'is_t&c_accepted'; - static const String tuitionNotificationsToggleKey = + static const _lastUpdateTimeKeySuffix = '_last_update_time'; + static const String _userNumber = 'user_number'; + static const String _userPw = 'user_password'; + static const String _userFaculties = 'user_faculties'; + static const String _termsAndConditions = 'terms_and_conditions'; + static const String _areTermsAndConditionsAcceptedKey = 'is_t&c_accepted'; + static const String _tuitionNotificationsToggleKey = 'tuition_notification_toogle'; - static const String usageStatsToggleKey = 'usage_stats_toogle'; - static const String themeMode = 'theme_mode'; - static const String locale = 'app_locale'; - static const String favoriteCards = 'favorite_cards'; - static final List defaultFavoriteCards = [ + static const String _usageStatsToggleKey = 'usage_stats_toogle'; + static const String _themeMode = 'theme_mode'; + static const String _locale = 'app_locale'; + static const String _favoriteCards = 'favorite_cards'; + static final List _defaultFavoriteCards = [ FavoriteWidgetType.schedule, FavoriteWidgetType.exams, FavoriteWidgetType.busStops, ]; - static const String hiddenExams = 'hidden_exams'; - static const String favoriteRestaurants = 'favorite_restaurants'; - static const String filteredExamsTypes = 'filtered_exam_types'; - static final List defaultFilteredExamTypes = Exam.displayedTypes; + static const String _hiddenExams = 'hidden_exams'; + static const String _favoriteRestaurants = 'favorite_restaurants'; + static const String _filteredExamsTypes = 'filtered_exam_types'; + static final List _defaultFilteredExamTypes = Exam.displayedTypes; /// Returns the last time the data with given key was updated. static DateTime? getLastDataClassUpdateTime(String dataKey) { - final lastUpdateTime = prefs.getString(dataKey + lastUpdateTimeKeySuffix); + final lastUpdateTime = prefs.getString(dataKey + _lastUpdateTimeKeySuffix); return lastUpdateTime != null ? DateTime.parse(lastUpdateTime) : null; } @@ -55,7 +55,7 @@ class PreferencesController { DateTime dateTime, ) async { await prefs.setString( - dataKey + lastUpdateTimeKeySuffix, + dataKey + _lastUpdateTimeKeySuffix, dateTime.toString(), ); } @@ -66,10 +66,10 @@ class PreferencesController { String pass, List faculties, ) async { - await prefs.setString(userNumber, user); - await prefs.setString(userPw, encode(pass)); + await prefs.setString(_userNumber, user); + await prefs.setString(_userPw, encode(pass)); await prefs.setStringList( - userFaculties, + _userFaculties, faculties, ); // Could be multiple faculties } @@ -78,49 +78,49 @@ class PreferencesController { static Future setTermsAndConditionsAcceptance({ required bool areAccepted, }) async { - await prefs.setBool(areTermsAndConditionsAcceptedKey, areAccepted); + await prefs.setBool(_areTermsAndConditionsAcceptedKey, areAccepted); } /// Returns whether or not the Terms and Conditions have been accepted. static bool areTermsAndConditionsAccepted() { - return prefs.getBool(areTermsAndConditionsAcceptedKey) ?? false; + return prefs.getBool(_areTermsAndConditionsAcceptedKey) ?? false; } /// Returns the hash of the last Terms and Conditions that have /// been accepted by the user. static String? getTermsAndConditionHash() { - return prefs.getString(termsAndConditions); + return prefs.getString(_termsAndConditions); } /// Sets the hash of the Terms and Conditions that have been accepted /// by the user. static Future setTermsAndConditionHash(String hashed) async { - return prefs.setString(termsAndConditions, hashed); + return prefs.setString(_termsAndConditions, hashed); } /// Gets current used theme mode. static ThemeMode getThemeMode() { - return ThemeMode.values[prefs.getInt(themeMode) ?? ThemeMode.system.index]; + return ThemeMode.values[prefs.getInt(_themeMode) ?? ThemeMode.system.index]; } /// Set new app theme mode. static Future setThemeMode(ThemeMode thmMode) async { - return prefs.setInt(themeMode, thmMode.index); + return prefs.setInt(_themeMode, thmMode.index); } /// Set app next theme mode. static Future setNextThemeMode() async { final themeIndex = getThemeMode().index; - return prefs.setInt(themeMode, (themeIndex + 1) % 3); + return prefs.setInt(_themeMode, (themeIndex + 1) % 3); } static Future setLocale(AppLocale appLocale) async { - await prefs.setString(locale, appLocale.name); + await prefs.setString(_locale, appLocale.name); } static AppLocale getLocale() { final appLocale = - prefs.getString(locale) ?? Platform.localeName.substring(0, 2); + prefs.getString(_locale) ?? Platform.localeName.substring(0, 2); return AppLocale.values.firstWhere( (e) => e.toString() == 'AppLocale.$appLocale', @@ -130,8 +130,8 @@ class PreferencesController { /// Deletes the user's student number and password. static Future removePersistentUserInfo() async { - await prefs.remove(userNumber); - await prefs.remove(userPw); + await prefs.remove(_userNumber); + await prefs.remove(_userPw); } /// Returns a tuple containing the user's student number and password. @@ -151,19 +151,19 @@ class PreferencesController { /// Returns the user's faculties static List getUserFaculties() { - final storedFaculties = prefs.getStringList(userFaculties); + final storedFaculties = prefs.getStringList(_userFaculties); return storedFaculties ?? ['feup']; // TODO(bdmendes): Store dropdown choices in the db for later storage; } /// Returns the user's student number. static String? getUserNumber() { - return prefs.getString(userNumber); + return prefs.getString(_userNumber); } /// Returns the user's password, in plain text format. static String? getUserPassword() { - final password = prefs.getString(userPw); + final password = prefs.getString(_userPw); return password != null ? decode(password) : null; } @@ -172,7 +172,7 @@ class PreferencesController { List newFavorites, ) async { await prefs.setStringList( - favoriteCards, + _favoriteCards, newFavorites.map((a) => a.index.toString()).toList(), ); } @@ -180,14 +180,14 @@ class PreferencesController { /// Returns a list containing the user's favorite widgets. static List getFavoriteCards() { final storedFavorites = prefs - .getStringList(favoriteCards) + .getStringList(_favoriteCards) ?.where( (element) => int.parse(element) < FavoriteWidgetType.values.length, ) .toList(); if (storedFavorites == null) { - return defaultFavoriteCards; + return _defaultFavoriteCards; } return storedFavorites @@ -198,21 +198,21 @@ class PreferencesController { static Future saveFavoriteRestaurants( List newFavoriteRestaurants, ) async { - await prefs.setStringList(favoriteRestaurants, newFavoriteRestaurants); + await prefs.setStringList(_favoriteRestaurants, newFavoriteRestaurants); } static List getFavoriteRestaurants() { final storedFavoriteRestaurants = - prefs.getStringList(favoriteRestaurants) ?? []; + prefs.getStringList(_favoriteRestaurants) ?? []; return storedFavoriteRestaurants; } static Future saveHiddenExams(List newHiddenExams) async { - await prefs.setStringList(hiddenExams, newHiddenExams); + await prefs.setStringList(_hiddenExams, newHiddenExams); } static List getHiddenExams() { - final storedHiddenExam = prefs.getStringList(hiddenExams) ?? []; + final storedHiddenExam = prefs.getStringList(_hiddenExams) ?? []; return storedHiddenExam; } @@ -223,18 +223,18 @@ class PreferencesController { final newTypes = newFilteredExamTypes.keys .where((type) => newFilteredExamTypes[type] ?? false) .toList(); - await prefs.setStringList(filteredExamsTypes, newTypes); + await prefs.setStringList(_filteredExamsTypes, newTypes); } /// Returns the user's exam filter settings. static Map getFilteredExams() { - final storedFilteredExamTypes = prefs.getStringList(filteredExamsTypes); + final storedFilteredExamTypes = prefs.getStringList(_filteredExamsTypes); if (storedFilteredExamTypes == null) { - return Map.fromIterable(defaultFilteredExamTypes, value: (type) => true); + return Map.fromIterable(_defaultFilteredExamTypes, value: (type) => true); } return Map.fromIterable( - defaultFilteredExamTypes, + _defaultFilteredExamTypes, value: storedFilteredExamTypes.contains, ); } @@ -262,22 +262,22 @@ class PreferencesController { } static bool getTuitionNotificationToggle() { - return prefs.getBool(tuitionNotificationsToggleKey) ?? true; + return prefs.getBool(_tuitionNotificationsToggleKey) ?? true; } static Future setTuitionNotificationToggle({ required bool value, }) async { - await prefs.setBool(tuitionNotificationsToggleKey, value); + await prefs.setBool(_tuitionNotificationsToggleKey, value); } static bool getUsageStatsToggle() { - return prefs.getBool(usageStatsToggleKey) ?? true; + return prefs.getBool(_usageStatsToggleKey) ?? true; } static Future setUsageStatsToggle({ required bool value, }) async { - await prefs.setBool(usageStatsToggleKey, value); + await prefs.setBool(_usageStatsToggleKey, value); } } diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 1f199ba44..a9f562604 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -205,7 +205,7 @@ class ApplicationState extends State { settings: settings, ), '/${DrawerItem.navExams.title}': PageTransition.makePageTransition( - page: ExamsPageView(), + page: const ExamsPageView(), settings: settings, ), '/${DrawerItem.navStops.title}': PageTransition.makePageTransition( diff --git a/uni/lib/model/providers/lazy/course_units_info_provider.dart b/uni/lib/model/providers/lazy/course_units_info_provider.dart index cd3fddf05..2c5089f4f 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -15,7 +15,7 @@ typedef ClassesMap = Map>; class CourseUnitsInfoProvider extends StateProviderNotifier> { CourseUnitsInfoProvider() : super(cacheDuration: null) { - updateState(const Tuple2({}, {})); + setState(const Tuple2({}, {})); } UnmodifiableMapView get courseUnitsSheets => diff --git a/uni/lib/model/providers/startup/session_provider.dart b/uni/lib/model/providers/startup/session_provider.dart index 2d9567519..e5cce690b 100644 --- a/uni/lib/model/providers/startup/session_provider.dart +++ b/uni/lib/model/providers/startup/session_provider.dart @@ -78,7 +78,7 @@ class SessionProvider extends StateProviderNotifier { } } - updateState(session); + setState(session); if (persistentSession) { await PreferencesController.savePersistentUserInfo( diff --git a/uni/lib/model/providers/state_provider_notifier.dart b/uni/lib/model/providers/state_provider_notifier.dart index ada5001f8..080684bfc 100644 --- a/uni/lib/model/providers/state_provider_notifier.dart +++ b/uni/lib/model/providers/state_provider_notifier.dart @@ -58,7 +58,7 @@ abstract class StateProviderNotifier extends ChangeNotifier { /// Update the current model state, notifying the listeners. /// This should be called only to modify the model after /// it has been loaded, for example as a UI callback side effect. - void updateState(T newState) { + void setState(T newState) { _state = newState; notifyListeners(); } @@ -79,7 +79,7 @@ abstract class StateProviderNotifier extends ChangeNotifier { ); try { - updateState(await loadFromStorage(StateProviders.fromContext(context))); + setState(await loadFromStorage(StateProviders.fromContext(context))); } catch (e, stackTrace) { await Sentry.captureException(e, stackTrace: stackTrace); Logger() @@ -123,7 +123,7 @@ abstract class StateProviderNotifier extends ChangeNotifier { if (!context.mounted) { return; } - updateState(await loadFromRemote(StateProviders.fromContext(context))); + setState(await loadFromRemote(StateProviders.fromContext(context))); Logger().i('Loaded $runtimeType info from remote'); _lastUpdateTime = DateTime.now(); diff --git a/uni/lib/view/exams/exams.dart b/uni/lib/view/exams/exams.dart index 06068d9a6..d7497d4a1 100644 --- a/uni/lib/view/exams/exams.dart +++ b/uni/lib/view/exams/exams.dart @@ -14,31 +14,35 @@ import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; class ExamsPageView extends StatefulWidget { - ExamsPageView({super.key}); - - final List hiddenExams = PreferencesController.getHiddenExams(); - final Map filteredExamTypes = - PreferencesController.getFilteredExams(); + const ExamsPageView({super.key}); @override State createState() => ExamsPageViewState(); } class ExamsPageViewState extends GeneralPageViewState { + List hiddenExams = PreferencesController.getHiddenExams(); + Map filteredExamTypes = + PreferencesController.getFilteredExams(); + @override Widget getBody(BuildContext context) { return LazyConsumer>( builder: (context, exams) { return ListView( children: [ + ExamPageTitle( + () => setState(() { + filteredExamTypes = PreferencesController.getFilteredExams(); + }), + ), Column( children: createExamsColumn( context, exams .where( (exam) => - widget.filteredExamTypes[ - Exam.getExamTypeLong(exam.type)] ?? + filteredExamTypes[Exam.getExamTypeLong(exam.type)] ?? true, ) .toList(), @@ -73,7 +77,7 @@ class ExamsPageViewState extends GeneralPageViewState { ]; } - final columns = [const ExamPageTitle()]; + final columns = []; final currentDayExams = []; for (var i = 0; i < exams.length; i++) { @@ -101,6 +105,7 @@ class ExamsPageViewState extends GeneralPageViewState { currentDayExams.clear(); } } + return columns; } @@ -130,7 +135,7 @@ class ExamsPageViewState extends GeneralPageViewState { } Widget createExamContext(BuildContext context, Exam exam) { - final isHidden = widget.hiddenExams.contains(exam.id); + final isHidden = hiddenExams.contains(exam.id); return Container( key: Key('$exam-exam'), margin: const EdgeInsets.fromLTRB(12, 4, 12, 0), @@ -138,7 +143,16 @@ class ExamsPageViewState extends GeneralPageViewState { color: isHidden ? Theme.of(context).hintColor : Theme.of(context).scaffoldBackgroundColor, - child: ExamRow(exam: exam, teacher: '', mainPage: false), + child: ExamRow( + exam: exam, + teacher: '', + mainPage: false, + onChangeVisibility: () { + setState(() { + hiddenExams = PreferencesController.getHiddenExams(); + }); + }, + ), ), ); } diff --git a/uni/lib/view/exams/widgets/exam_filter_form.dart b/uni/lib/view/exams/widgets/exam_filter_form.dart index 7f208ba24..95d371ad1 100644 --- a/uni/lib/view/exams/widgets/exam_filter_form.dart +++ b/uni/lib/view/exams/widgets/exam_filter_form.dart @@ -4,9 +4,10 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/exam.dart'; class ExamFilterForm extends StatefulWidget { - const ExamFilterForm(this.filteredExamsTypes, {super.key}); + const ExamFilterForm(this.filteredExamsTypes, this.onDismiss, {super.key}); final Map filteredExamsTypes; + final void Function() onDismiss; @override ExamFilterFormState createState() => ExamFilterFormState(); @@ -31,7 +32,10 @@ class ExamFilterFormState extends State { ElevatedButton( child: Text(S.of(context).confirm), onPressed: () { - PreferencesController.saveFilteredExams(widget.filteredExamsTypes); + PreferencesController.saveFilteredExams( + widget.filteredExamsTypes, + ); + widget.onDismiss(); Navigator.pop(context); }, ), diff --git a/uni/lib/view/exams/widgets/exam_filter_menu.dart b/uni/lib/view/exams/widgets/exam_filter_menu.dart deleted file mode 100644 index 0aa22e334..000000000 --- a/uni/lib/view/exams/widgets/exam_filter_menu.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:uni/controller/local_storage/preferences_controller.dart'; -import 'package:uni/model/providers/lazy/exam_provider.dart'; -import 'package:uni/view/exams/widgets/exam_filter_form.dart'; - -class ExamFilterMenu extends StatefulWidget { - const ExamFilterMenu({super.key}); - - @override - ExamFilterMenuState createState() => ExamFilterMenuState(); -} - -class ExamFilterMenuState extends State { - @override - Widget build(BuildContext context) { - return IconButton( - icon: const Icon(Icons.filter_alt), - onPressed: () { - showDialog( - context: context, - builder: (_) { - final examProvider = - Provider.of(context, listen: false); - final filteredExamsTypes = PreferencesController.getFilteredExams(); - return ChangeNotifierProvider.value( - value: examProvider, - child: ExamFilterForm( - Map.from(filteredExamsTypes), - ), - ); - }, - ); - }, - ); - } -} diff --git a/uni/lib/view/exams/widgets/exam_page_title.dart b/uni/lib/view/exams/widgets/exam_page_title.dart index ff3d46e64..2eb175645 100644 --- a/uni/lib/view/exams/widgets/exam_page_title.dart +++ b/uni/lib/view/exams/widgets/exam_page_title.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/exams/widgets/exam_filter_menu.dart'; +import 'package:uni/view/exams/widgets/exam_filter_form.dart'; class ExamPageTitle extends StatelessWidget { - const ExamPageTitle({super.key}); + const ExamPageTitle(this.onDismissFilterDialog, {super.key}); + + final void Function() onDismissFilterDialog; @override Widget build(BuildContext context) { @@ -20,7 +23,22 @@ class ExamPageTitle extends StatelessWidget { center: false, pad: false, ), - const Material(child: ExamFilterMenu()), + IconButton( + icon: const Icon(Icons.filter_alt), + onPressed: () { + showDialog( + context: context, + builder: (_) { + final filteredExamsTypes = + PreferencesController.getFilteredExams(); + return ExamFilterForm( + Map.from(filteredExamsTypes), + onDismissFilterDialog, + ); + }, + ); + }, + ), ], ), ); diff --git a/uni/lib/view/exams/widgets/exam_row.dart b/uni/lib/view/exams/widgets/exam_row.dart index bddb5b560..d636c5277 100644 --- a/uni/lib/view/exams/widgets/exam_row.dart +++ b/uni/lib/view/exams/widgets/exam_row.dart @@ -11,6 +11,7 @@ class ExamRow extends StatefulWidget { required this.exam, required this.teacher, required this.mainPage, + required this.onChangeVisibility, super.key, }); @@ -18,6 +19,8 @@ class ExamRow extends StatefulWidget { final String teacher; final bool mainPage; + final void Function() onChangeVisibility; + @override State createState() { return _ExamRowState(); @@ -25,9 +28,16 @@ class ExamRow extends StatefulWidget { } class _ExamRowState extends State { + bool isHidden = false; + + @override + void initState() { + super.initState(); + isHidden = PreferencesController.getHiddenExams().contains(widget.exam.id); + } + @override Widget build(BuildContext context) { - final isHidden = PreferencesController.hiddenExams.contains(widget.exam.id); final roomsKey = '${widget.exam.subject}-${widget.exam.rooms}-${widget.exam.beginTime}-' '${widget.exam.endTime}'; @@ -62,26 +72,30 @@ class _ExamRowState extends State { icon: !isHidden ? const Icon(Icons.visibility, size: 30) : const Icon( - Icons.visibility_off, - size: 30, - ), + Icons.visibility_off, + size: 30, + ), tooltip: isHidden ? 'Mostrar na Área Pessoal' : 'Ocultar da Área Pessoal', onPressed: () async { final hiddenExams = - PreferencesController.getHiddenExams(); + PreferencesController.getHiddenExams(); if (hiddenExams.contains(widget.exam.id)) { hiddenExams.remove(widget.exam.id); } else { - hiddenExams.remove(widget.exam.id); + hiddenExams.add(widget.exam.id); } setState(() { PreferencesController.saveHiddenExams( hiddenExams, ); + setState(() { + isHidden = !isHidden; + }); + widget.onChangeVisibility(); }); }, ), @@ -119,8 +133,8 @@ class _ExamRowState extends State { return rooms .map( (room) => - Text(room.trim(), style: Theme.of(context).textTheme.bodyMedium), - ) + Text(room.trim(), style: Theme.of(context).textTheme.bodyMedium), + ) .toList(); } diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index d0f95445a..4e349807d 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -46,19 +46,9 @@ class ExamCard extends GenericCard { Widget buildCardContent(BuildContext context) { return LazyConsumer>( builder: (context, exams) { - final filteredExams = PreferencesController.getFilteredExams(); - final hiddenExams = PreferencesController.getHiddenExams(); - final shownExams = exams - .where( - (exam) => - !hiddenExams.contains(exam.id) && - (filteredExams[Exam.getExamTypeLong(exam.type)] ?? true), - ) - .toList(); - - return generateExams(shownExams, context); + return generateExams(shownExams(exams), context); }, - hasContent: (exams) => exams.isNotEmpty, + hasContent: (exams) => shownExams(exams).isNotEmpty, onNullContent: Center( child: Text( S.of(context).no_selected_exams, @@ -69,6 +59,19 @@ class ExamCard extends GenericCard { ); } + List shownExams(List exams) { + final filteredExams = PreferencesController.getFilteredExams(); + final hiddenExams = PreferencesController.getHiddenExams(); + + return exams + .where( + (exam) => + !hiddenExams.contains(exam.id) && + (filteredExams[Exam.getExamTypeLong(exam.type)] ?? true), + ) + .toList(); + } + /// Returns a widget with all the exams. Widget generateExams(List exams, BuildContext context) { return Column( @@ -128,6 +131,7 @@ class ExamCard extends GenericCard { exam: exam, teacher: '', mainPage: true, + onChangeVisibility: () {}, ), ), ], diff --git a/uni/lib/view/profile/widgets/account_info_card.dart b/uni/lib/view/profile/widgets/account_info_card.dart index e5f7fd425..b2fe21eee 100644 --- a/uni/lib/view/profile/widgets/account_info_card.dart +++ b/uni/lib/view/profile/widgets/account_info_card.dart @@ -65,7 +65,6 @@ class AccountInfoCard extends GenericCard { Container( margin: const EdgeInsets.only( top: 8, - bottom: 20, left: 20, ), child: Text( @@ -76,7 +75,6 @@ class AccountInfoCard extends GenericCard { Container( margin: const EdgeInsets.only( top: 8, - bottom: 20, right: 30, ), child: getInfoText( @@ -98,7 +96,12 @@ class AccountInfoCard extends GenericCard { return Column( children: [ Container( - padding: const EdgeInsets.all(10), + padding: const EdgeInsets.only( + top: 30, + bottom: 10, + right: 15, + left: 15, + ), child: Row( children: [ Text( diff --git a/uni/test/integration/src/exams_page_test.dart b/uni/test/integration/src/exams_page_test.dart index 956b365a2..ec9351e1d 100644 --- a/uni/test/integration/src/exams_page_test.dart +++ b/uni/test/integration/src/exams_page_test.dart @@ -20,7 +20,9 @@ import '../../mocks/integration/src/exams_page_test.mocks.dart'; import '../../test_widget.dart'; @GenerateNiceMocks([MockSpec(), MockSpec()]) -void main() { +void main() async { + await initTestEnvironment(); + group('ExamsPage Integration Tests', () { final mockClient = MockClient(); final mockResponse = MockResponse(); @@ -68,7 +70,7 @@ void main() { final examProvider = ExamProvider(); - final widget = ExamsPageView(); + const widget = ExamsPageView(); final providers = [ ChangeNotifierProvider(create: (_) => examProvider), diff --git a/uni/test/integration/src/schedule_page_test.dart b/uni/test/integration/src/schedule_page_test.dart index 687a61abb..449515e38 100644 --- a/uni/test/integration/src/schedule_page_test.dart +++ b/uni/test/integration/src/schedule_page_test.dart @@ -31,7 +31,9 @@ class UriMatcher extends CustomMatcher { MockSpec(), MockSpec(), ]) -void main() { +void main() async { + await initTestEnvironment(); + group('SchedulePage Integration Tests', () { final mockClient = MockClient(); final mockResponse = MockResponse(); @@ -49,7 +51,7 @@ void main() { final scheduleProvider = LectureProvider(); final sessionProvider = MockSessionProvider(); - when(sessionProvider.session).thenReturn( + when(sessionProvider.state).thenReturn( Session(username: 'up1234', cookies: 'cookie', faculties: ['feup']), ); diff --git a/uni/test/mocks/integration/src/exams_page_test.mocks.dart b/uni/test/mocks/integration/src/exams_page_test.mocks.dart index e28b49f59..40a40fedc 100644 --- a/uni/test/mocks/integration/src/exams_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/exams_page_test.mocks.dart @@ -77,6 +77,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> get( Uri? url, { @@ -106,6 +107,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> post( Uri? url, { @@ -149,6 +151,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> put( Uri? url, { @@ -192,6 +195,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> patch( Uri? url, { @@ -235,6 +239,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> delete( Uri? url, { @@ -278,6 +283,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future read( Uri? url, { @@ -292,6 +298,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future.value(''), returnValueForMissingStub: _i3.Future.value(''), ) as _i3.Future); + @override _i3.Future<_i5.Uint8List> readBytes( Uri? url, { @@ -307,6 +314,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); + @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -331,6 +339,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -351,30 +360,35 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i5.Uint8List(0), returnValueForMissingStub: _i5.Uint8List(0), ) as _i5.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart index e5e3b4548..5be25a598 100644 --- a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart @@ -11,9 +11,9 @@ import 'dart:ui' as _i11; import 'package:flutter/material.dart' as _i10; import 'package:http/http.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; -import 'package:uni/model/entities/profile.dart' as _i9; import 'package:uni/model/entities/session.dart' as _i3; import 'package:uni/model/providers/startup/session_provider.dart' as _i7; +import 'package:uni/model/providers/state_providers.dart' as _i9; import 'package:uni/model/request_status.dart' as _i8; // ignore_for_file: type=lint @@ -93,6 +93,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> get( Uri? url, { @@ -122,6 +123,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> post( Uri? url, { @@ -165,6 +167,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> put( Uri? url, { @@ -208,6 +211,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> patch( Uri? url, { @@ -251,6 +255,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future<_i2.Response> delete( Uri? url, { @@ -294,6 +299,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.Response>); + @override _i4.Future read( Uri? url, { @@ -308,6 +314,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i4.Future.value(''), returnValueForMissingStub: _i4.Future.value(''), ) as _i4.Future); + @override _i4.Future<_i6.Uint8List> readBytes( Uri? url, { @@ -323,6 +330,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i4.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), ) as _i4.Future<_i6.Uint8List>); + @override _i4.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -347,6 +355,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i4.Future<_i2.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -367,30 +376,35 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i6.Uint8List(0), returnValueForMissingStub: _i6.Uint8List(0), ) as _i6.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), @@ -404,23 +418,12 @@ class MockResponse extends _i1.Mock implements _i2.Response { /// See the documentation for Mockito's code generation for more information. class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { @override - _i3.Session get session => (super.noSuchMethod( - Invocation.getter(#session), - returnValue: _FakeSession_2( - this, - Invocation.getter(#session), - ), - returnValueForMissingStub: _FakeSession_2( - this, - Invocation.getter(#session), - ), - ) as _i3.Session); - @override bool get dependsOnSession => (super.noSuchMethod( Invocation.getter(#dependsOnSession), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override set dependsOnSession(bool? _dependsOnSession) => super.noSuchMethod( Invocation.setter( @@ -429,6 +432,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override set cacheDuration(Duration? _cacheDuration) => super.noSuchMethod( Invocation.setter( @@ -437,60 +441,67 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override - _i8.RequestStatus get status => (super.noSuchMethod( - Invocation.getter(#status), + _i8.RequestStatus get requestStatus => (super.noSuchMethod( + Invocation.getter(#requestStatus), returnValue: _i8.RequestStatus.none, returnValueForMissingStub: _i8.RequestStatus.none, ) as _i8.RequestStatus); + @override bool get hasListeners => (super.noSuchMethod( Invocation.getter(#hasListeners), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override - _i4.Future loadFromStorage() => (super.noSuchMethod( + _i4.Future<_i3.Session> loadFromStorage(_i9.StateProviders? stateProviders) => + (super.noSuchMethod( Invocation.method( #loadFromStorage, - [], + [stateProviders], ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + returnValue: _i4.Future<_i3.Session>.value(_FakeSession_2( + this, + Invocation.method( + #loadFromStorage, + [stateProviders], + ), + )), + returnValueForMissingStub: _i4.Future<_i3.Session>.value(_FakeSession_2( + this, + Invocation.method( + #loadFromStorage, + [stateProviders], + ), + )), + ) as _i4.Future<_i3.Session>); + @override - _i4.Future loadFromRemote( - _i3.Session? session, - _i9.Profile? profile, - ) => + _i4.Future<_i3.Session> loadFromRemote(_i9.StateProviders? stateProviders) => (super.noSuchMethod( Invocation.method( #loadFromRemote, - [ - session, - profile, - ], + [stateProviders], ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); - @override - void restoreSession( - String? username, - String? password, - List? faculties, - ) => - super.noSuchMethod( - Invocation.method( - #restoreSession, - [ - username, - password, - faculties, - ], - ), - returnValueForMissingStub: null, - ); + returnValue: _i4.Future<_i3.Session>.value(_FakeSession_2( + this, + Invocation.method( + #loadFromRemote, + [stateProviders], + ), + )), + returnValueForMissingStub: _i4.Future<_i3.Session>.value(_FakeSession_2( + this, + Invocation.method( + #loadFromRemote, + [stateProviders], + ), + )), + ) as _i4.Future<_i3.Session>); + @override _i4.Future postAuthentication( _i10.BuildContext? context, @@ -513,22 +524,25 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override - void markAsNotInitialized() => super.noSuchMethod( + void updateState(_i3.Session? newState) => super.noSuchMethod( Invocation.method( - #markAsNotInitialized, - [], + #updateState, + [newState], ), returnValueForMissingStub: null, ); + @override - void updateStatus(_i8.RequestStatus? status) => super.noSuchMethod( + void invalidate() => super.noSuchMethod( Invocation.method( - #updateStatus, - [status], + #invalidate, + [], ), returnValueForMissingStub: null, ); + @override _i4.Future forceRefresh(_i10.BuildContext? context) => (super.noSuchMethod( @@ -539,6 +553,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); + @override _i4.Future ensureInitialized(_i10.BuildContext? context) => (super.noSuchMethod( @@ -549,25 +564,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { returnValue: _i4.Future.value(), returnValueForMissingStub: _i4.Future.value(), ) as _i4.Future); - @override - _i4.Future ensureInitializedFromRemote(_i10.BuildContext? context) => - (super.noSuchMethod( - Invocation.method( - #ensureInitializedFromRemote, - [context], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); - @override - _i4.Future ensureInitializedFromStorage() => (super.noSuchMethod( - Invocation.method( - #ensureInitializedFromStorage, - [], - ), - returnValue: _i4.Future.value(), - returnValueForMissingStub: _i4.Future.value(), - ) as _i4.Future); + @override void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -576,6 +573,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( Invocation.method( @@ -584,6 +582,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override void dispose() => super.noSuchMethod( Invocation.method( @@ -592,6 +591,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ), returnValueForMissingStub: null, ); + @override void notifyListeners() => super.noSuchMethod( Invocation.method( diff --git a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart index fd78853db..9f2e5d8ed 100644 --- a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart @@ -80,6 +80,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> get( Uri? url, { @@ -109,6 +110,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> post( Uri? url, { @@ -152,6 +154,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> put( Uri? url, { @@ -195,6 +198,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> patch( Uri? url, { @@ -238,6 +242,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future<_i2.Response> delete( Uri? url, { @@ -281,6 +286,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.Response>); + @override _i3.Future read( Uri? url, { @@ -295,6 +301,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future.value(''), returnValueForMissingStub: _i3.Future.value(''), ) as _i3.Future); + @override _i3.Future<_i5.Uint8List> readBytes( Uri? url, { @@ -310,6 +317,7 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValueForMissingStub: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); + @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( @@ -334,6 +342,7 @@ class MockClient extends _i1.Mock implements _i2.Client { ), )), ) as _i3.Future<_i2.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -357,6 +366,7 @@ class MockParserExams extends _i1.Mock implements _i6.ParserExams { returnValue: '', returnValueForMissingStub: '', ) as String); + @override _i3.Future> parseExams( _i2.Response? response, @@ -386,30 +396,35 @@ class MockResponse extends _i1.Mock implements _i2.Response { returnValue: _i5.Uint8List(0), returnValueForMissingStub: _i5.Uint8List(0), ) as _i5.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart index 3fd2208a6..49010fdd7 100644 --- a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart @@ -80,6 +80,7 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { returnValueForMissingStub: _i4.Future>.value(<_i5.Lecture>[]), ) as _i4.Future>); + @override _i2.Dates getDates() => (super.noSuchMethod( Invocation.method( @@ -101,6 +102,7 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { ), ), ) as _i2.Dates); + @override List getEndpoints(_i6.Session? session) => (super.noSuchMethod( Invocation.method( @@ -145,6 +147,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> get( Uri? url, { @@ -174,6 +177,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> post( Uri? url, { @@ -217,6 +221,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> put( Uri? url, { @@ -260,6 +265,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> patch( Uri? url, { @@ -303,6 +309,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future<_i3.Response> delete( Uri? url, { @@ -346,6 +353,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.Response>); + @override _i4.Future read( Uri? url, { @@ -360,6 +368,7 @@ class MockClient extends _i1.Mock implements _i3.Client { returnValue: _i4.Future.value(''), returnValueForMissingStub: _i4.Future.value(''), ) as _i4.Future); + @override _i4.Future<_i9.Uint8List> readBytes( Uri? url, { @@ -375,6 +384,7 @@ class MockClient extends _i1.Mock implements _i3.Client { returnValueForMissingStub: _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), ) as _i4.Future<_i9.Uint8List>); + @override _i4.Future<_i3.StreamedResponse> send(_i3.BaseRequest? request) => (super.noSuchMethod( @@ -399,6 +409,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), ) as _i4.Future<_i3.StreamedResponse>); + @override void close() => super.noSuchMethod( Invocation.method( @@ -419,30 +430,35 @@ class MockResponse extends _i1.Mock implements _i3.Response { returnValue: _i9.Uint8List(0), returnValueForMissingStub: _i9.Uint8List(0), ) as _i9.Uint8List); + @override String get body => (super.noSuchMethod( Invocation.getter(#body), returnValue: '', returnValueForMissingStub: '', ) as String); + @override int get statusCode => (super.noSuchMethod( Invocation.getter(#statusCode), returnValue: 0, returnValueForMissingStub: 0, ) as int); + @override Map get headers => (super.noSuchMethod( Invocation.getter(#headers), returnValue: {}, returnValueForMissingStub: {}, ) as Map); + @override bool get isRedirect => (super.noSuchMethod( Invocation.getter(#isRedirect), returnValue: false, returnValueForMissingStub: false, ) as bool); + @override bool get persistentConnection => (super.noSuchMethod( Invocation.getter(#persistentConnection), diff --git a/uni/test/test_widget.dart b/uni/test/test_widget.dart index 07dcd7d47..c9fc36cc9 100644 --- a/uni/test/test_widget.dart +++ b/uni/test/test_widget.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/app_locale.dart'; import 'package:uni/view/locale_notifier.dart'; +Future initTestEnvironment() async { + SharedPreferences.setMockInitialValues({}); + PreferencesController.prefs = await SharedPreferences.getInstance(); +} + Widget testableWidget( Widget widget, { List providers = const [], @@ -16,11 +23,11 @@ Widget testableWidget( ), ...providers, ], - child: wrapWidget(widget), + child: _wrapWidget(widget), ); } -Widget wrapWidget(Widget widget) { +Widget _wrapWidget(Widget widget) { return MaterialApp( localizationsDelegates: const [ S.delegate, diff --git a/uni/test/unit/providers/exams_provider_test.dart b/uni/test/unit/providers/exams_provider_test.dart index 7d82c1831..ec59490d9 100644 --- a/uni/test/unit/providers/exams_provider_test.dart +++ b/uni/test/unit/providers/exams_provider_test.dart @@ -13,11 +13,14 @@ import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/model/request_status.dart'; import '../../mocks/unit/providers/exams_provider_test.mocks.dart'; +import '../../test_widget.dart'; @GenerateNiceMocks( [MockSpec(), MockSpec(), MockSpec()], ) -void main() { +void main() async { + await initTestEnvironment(); + group('ExamProvider', () { final mockClient = MockClient(); final parserExams = MockParserExams(); @@ -80,7 +83,7 @@ void main() { when(parserExams.parseExams(any, any)) .thenAnswer((_) async => {sopeExam}); - await provider.fetchUserExams( + final exams = await provider.fetchUserExams( parserExams, profile, session, @@ -88,6 +91,8 @@ void main() { persistentSession: false, ); + provider.setState(exams); + expect(provider.state!.isNotEmpty, true); expect(provider.state, [sopeExam]); }); @@ -96,7 +101,7 @@ void main() { when(parserExams.parseExams(any, any)) .thenAnswer((_) async => {sopeExam, sdisExam}); - await provider.fetchUserExams( + final exams = await provider.fetchUserExams( parserExams, profile, session, @@ -104,6 +109,8 @@ void main() { persistentSession: false, ); + provider.setState(exams); + expect(provider.state, [sopeExam, sdisExam]); }); @@ -125,7 +132,7 @@ When given three exams but one is to be parsed out, when(parserExams.parseExams(any, any)) .thenAnswer((_) async => {sopeExam, sdisExam, specialExam}); - await provider.fetchUserExams( + final exams = await provider.fetchUserExams( parserExams, profile, session, @@ -133,6 +140,8 @@ When given three exams but one is to be parsed out, persistentSession: false, ); + provider.setState(exams); + expect(provider.state, [sopeExam, sdisExam]); }); @@ -167,7 +176,7 @@ When given three exams but one is to be parsed out, when(parserExams.parseExams(any, any)) .thenAnswer((_) async => {todayExam}); - await provider.fetchUserExams( + final exams = await provider.fetchUserExams( parserExams, profile, session, @@ -175,6 +184,8 @@ When given three exams but one is to be parsed out, persistentSession: false, ); + provider.setState(exams); + expect(provider.state, [todayExam]); }); @@ -194,7 +205,7 @@ When given three exams but one is to be parsed out, when(parserExams.parseExams(any, any)) .thenAnswer((_) async => {todayExam}); - await provider.fetchUserExams( + final exams = await provider.fetchUserExams( parserExams, profile, session, @@ -202,6 +213,8 @@ When given three exams but one is to be parsed out, persistentSession: false, ); + provider.setState(exams); + expect(provider.state, []); }); @@ -221,7 +234,7 @@ When given three exams but one is to be parsed out, when(parserExams.parseExams(any, any)) .thenAnswer((_) async => {todayExam}); - await provider.fetchUserExams( + final exams = await provider.fetchUserExams( parserExams, profile, session, @@ -229,6 +242,8 @@ When given three exams but one is to be parsed out, persistentSession: false, ); + provider.setState(exams); + expect(provider.state, [todayExam]); }); }); diff --git a/uni/test/unit/providers/lecture_provider_test.dart b/uni/test/unit/providers/lecture_provider_test.dart index 53302ad94..e9c870c4c 100644 --- a/uni/test/unit/providers/lecture_provider_test.dart +++ b/uni/test/unit/providers/lecture_provider_test.dart @@ -12,11 +12,14 @@ import 'package:uni/model/providers/lazy/lecture_provider.dart'; import 'package:uni/model/request_status.dart'; import '../../mocks/unit/providers/lecture_provider_test.mocks.dart'; +import '../../test_widget.dart'; @GenerateNiceMocks( [MockSpec(), MockSpec(), MockSpec()], ) -void main() { +void main() async { + await initTestEnvironment(); + group('Schedule Action Creator', () { final fetcherMock = MockScheduleFetcher(); final mockClient = MockClient(); @@ -63,13 +66,15 @@ void main() { when(fetcherMock.getLectures(any, any)) .thenAnswer((_) async => [lecture1, lecture2]); - await provider.fetchUserLectures( + final lectures = await provider.fetchUserLectures( session, profile, fetcher: fetcherMock, persistentSession: false, ); + provider.setState(lectures); + expect(provider.state, [lecture1, lecture2]); }); diff --git a/uni/test/unit/view/Pages/exams_page_view_test.dart b/uni/test/unit/view/Pages/exams_page_view_test.dart index f8b164904..43d1f1e48 100644 --- a/uni/test/unit/view/Pages/exams_page_view_test.dart +++ b/uni/test/unit/view/Pages/exams_page_view_test.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/view/exams/exams.dart'; @@ -10,7 +12,9 @@ import '../../../test_widget.dart'; class MockExamProvider extends Mock implements ExamProvider {} -void main() { +void main() async { + await initTestEnvironment(); + group('ExamsPage', () { const firstExamSubject = 'SOPE'; const firstExamDate = '2019-09-11'; @@ -18,8 +22,8 @@ void main() { const secondExamDate = '2019-09-12'; testWidgets('When given an empty list', (WidgetTester tester) async { - final widget = ExamsPageView(); - final examProvider = ExamProvider()..updateState([]); + const widget = ExamsPageView(); + final examProvider = ExamProvider()..setState([]); final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; @@ -41,9 +45,9 @@ void main() { 'feup', ); - final widget = ExamsPageView(); + const widget = ExamsPageView(); - final examProvider = ExamProvider()..updateState([firstExam]); + final examProvider = ExamProvider()..setState([firstExam]); final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; @@ -84,9 +88,9 @@ void main() { secondExam, ]; - final widget = ExamsPageView(); + const widget = ExamsPageView(); - final examProvider = ExamProvider()..updateState(examList); + final examProvider = ExamProvider()..setState(examList); final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; @@ -130,9 +134,9 @@ void main() { secondExam, ]; - final widget = ExamsPageView(); + const widget = ExamsPageView(); - final examProvider = ExamProvider()..updateState(examList); + final examProvider = ExamProvider()..setState(examList); final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; @@ -194,9 +198,9 @@ void main() { ); final examList = [firstExam, secondExam, thirdExam, fourthExam]; - final widget = ExamsPageView(); + const widget = ExamsPageView(); - final examProvider = ExamProvider()..updateState(examList); + final examProvider = ExamProvider()..setState(examList); final firstDayKey = [firstExam, secondExam].map((ex) => ex.toString()).join(); diff --git a/uni/test/unit/view/Pages/schedule_page_view_test.dart b/uni/test/unit/view/Pages/schedule_page_view_test.dart index dace7ae9c..025d37a3d 100644 --- a/uni/test/unit/view/Pages/schedule_page_view_test.dart +++ b/uni/test/unit/view/Pages/schedule_page_view_test.dart @@ -6,7 +6,9 @@ import 'package:uni/view/schedule/widgets/schedule_slot.dart'; import '../../../test_widget.dart'; -void main() { +void main() async { + await initTestEnvironment(); + group('SchedulePage', () { const blocks = 4; const classNumber = 'MIEIC03'; diff --git a/uni/test/unit/view/Widgets/exam_row_test.dart b/uni/test/unit/view/Widgets/exam_row_test.dart index dcdce9505..fff0163f7 100644 --- a/uni/test/unit/view/Widgets/exam_row_test.dart +++ b/uni/test/unit/view/Widgets/exam_row_test.dart @@ -2,13 +2,17 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/view/exams/widgets/exam_row.dart'; import '../../../test_widget.dart'; -void main() { +void main() async { + await initTestEnvironment(); + group('Exam Row', () { const subject = 'SOPE'; final begin = DateTime( @@ -29,7 +33,12 @@ void main() { testWidgets('When given a single room', (WidgetTester tester) async { final rooms = ['B315']; final exam = Exam('1230', begin, end, subject, rooms, '', 'feup'); - final widget = ExamRow(exam: exam, teacher: '', mainPage: true); + final widget = ExamRow( + exam: exam, + teacher: '', + mainPage: true, + onChangeVisibility: () {}, + ); final providers = [ ChangeNotifierProvider(create: (_) => ExamProvider()), @@ -51,7 +60,12 @@ void main() { testWidgets('When multiple rooms', (WidgetTester tester) async { final rooms = ['B315', 'B316', 'B330']; final exam = Exam('1230', begin, end, subject, rooms, '', 'feup'); - final widget = ExamRow(exam: exam, teacher: '', mainPage: true); + final widget = ExamRow( + exam: exam, + teacher: '', + mainPage: true, + onChangeVisibility: () {}, + ); final providers = [ ChangeNotifierProvider(create: (_) => ExamProvider()), diff --git a/uni/test/unit/view/Widgets/schedule_slot_test.dart b/uni/test/unit/view/Widgets/schedule_slot_test.dart index 4b06fdacc..d6d52114a 100644 --- a/uni/test/unit/view/Widgets/schedule_slot_test.dart +++ b/uni/test/unit/view/Widgets/schedule_slot_test.dart @@ -5,7 +5,9 @@ import 'package:uni/view/schedule/widgets/schedule_slot.dart'; import '../../../test_widget.dart'; -void main() { +void main() async { + await initTestEnvironment(); + group('Schedule Slot', () { const subject = 'SOPE'; final begin = DateTime(2021, 06, 01, 10); From 74d5da46a99680127ec6679a2ae8196c11a499cf Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Dec 2023 12:51:14 +0000 Subject: [PATCH 079/334] Fix course units loading --- .../lazy/course_units_info_provider.dart | 11 ++++++++--- .../model/providers/state_provider_notifier.dart | 14 +++++--------- uni/lib/view/lazy_consumer.dart | 5 ++++- uni/test/integration/src/exams_page_test.dart | 15 +++++++-------- uni/test/integration/src/schedule_page_test.dart | 4 ++-- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/uni/lib/model/providers/lazy/course_units_info_provider.dart b/uni/lib/model/providers/lazy/course_units_info_provider.dart index 2c5089f4f..5de088822 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -14,9 +14,14 @@ typedef ClassesMap = Map>; class CourseUnitsInfoProvider extends StateProviderNotifier> { - CourseUnitsInfoProvider() : super(cacheDuration: null) { - setState(const Tuple2({}, {})); - } + CourseUnitsInfoProvider() + : super( + cacheDuration: null, + // Const constructor is not allowed here because of the + // need for mutable maps + // ignore: prefer_const_constructors + initialState: Tuple2({}, {}), + ); UnmodifiableMapView get courseUnitsSheets => UnmodifiableMapView(state!.item1); diff --git a/uni/lib/model/providers/state_provider_notifier.dart b/uni/lib/model/providers/state_provider_notifier.dart index 080684bfc..3eff8fd5d 100644 --- a/uni/lib/model/providers/state_provider_notifier.dart +++ b/uni/lib/model/providers/state_provider_notifier.dart @@ -14,7 +14,9 @@ abstract class StateProviderNotifier extends ChangeNotifier { required this.cacheDuration, this.dependsOnSession = true, RequestStatus initialStatus = RequestStatus.busy, - }) : _requestStatus = initialStatus; + T? initialState, + }) : _requestStatus = initialStatus, + _state = initialState; /// The model that this notifier provides. /// This future will throw if the data loading fails. @@ -72,8 +74,6 @@ abstract class StateProviderNotifier extends ChangeNotifier { Future _loadFromStorage(BuildContext context) async { Logger().d('Loading $runtimeType info from storage'); - _updateStatus(RequestStatus.busy); - _lastUpdateTime = PreferencesController.getLastDataClassUpdateTime( runtimeType.toString(), ); @@ -165,12 +165,8 @@ abstract class StateProviderNotifier extends ChangeNotifier { if (!context.mounted || _state != null) { return; } - - await _loadFromStorage(context); - - if (context.mounted) { - await _loadFromRemoteFromContext(context); - } + await _loadFromStorage(context) + .then((value) => _loadFromRemoteFromContext(context)); }, timeout: _lockTimeout, ); diff --git a/uni/lib/view/lazy_consumer.dart b/uni/lib/view/lazy_consumer.dart index ea20dd42d..56996f0d1 100644 --- a/uni/lib/view/lazy_consumer.dart +++ b/uni/lib/view/lazy_consumer.dart @@ -100,7 +100,10 @@ class LazyConsumer, T2> ? builder(context, provider.state as T2) : Padding( padding: const EdgeInsets.symmetric(vertical: 10), - child: onNullContent, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: onNullContent, + ), ); } diff --git a/uni/test/integration/src/exams_page_test.dart b/uni/test/integration/src/exams_page_test.dart index ec9351e1d..b85d95c45 100644 --- a/uni/test/integration/src/exams_page_test.dart +++ b/uni/test/integration/src/exams_page_test.dart @@ -81,7 +81,7 @@ void main() async { expect(find.byKey(Key('$sopeExam-exam')), findsNothing); expect(find.byKey(Key('$mdisExam-exam')), findsNothing); - await examProvider.fetchUserExams( + final exams = await examProvider.fetchUserExams( ParserExams(), profile, Session(username: '', cookies: '', faculties: ['feup']), @@ -89,7 +89,7 @@ void main() async { persistentSession: false, ); - examProvider.invalidate(); + examProvider.setState(exams); await tester.pumpAndSettle(); expect(find.byKey(Key('$sdisExam-exam')), findsOneWidget); @@ -97,8 +97,7 @@ void main() async { expect(find.byKey(Key('$mdisExam-exam')), findsNothing); }); - // TODO(bdmendes): Bring back filtered exams tests - /*testWidgets('Filtered Exams', (WidgetTester tester) async { + testWidgets('Filtered Exams', (WidgetTester tester) async { NetworkRouter.httpClient = mockClient; final mockHtml = File('test/integration/resources/exam_example.html') .readAsStringSync(); @@ -109,7 +108,7 @@ void main() async { final examProvider = ExamProvider(); - final widget = ExamsPageView(); + const widget = ExamsPageView(); final providers = [ ChangeNotifierProvider(create: (_) => examProvider), @@ -120,7 +119,7 @@ void main() async { expect(find.byKey(Key('$sdisExam-exam')), findsNothing); expect(find.byKey(Key('$sopeExam-exam')), findsNothing); - await examProvider.fetchUserExams( + final exams = await examProvider.fetchUserExams( ParserExams(), profile, Session(username: '', cookies: '', faculties: ['feup']), @@ -128,7 +127,7 @@ void main() async { persistentSession: false, ); - examProvider.invalidate(); + examProvider.setState(exams); await tester.pumpAndSettle(); expect(find.byKey(Key('$sdisExam-exam')), findsOneWidget); @@ -167,6 +166,6 @@ void main() async { expect(find.byKey(Key('$sdisExam-exam')), findsNothing); expect(find.byKey(Key('$sopeExam-exam')), findsNothing); - });*/ + }); }); } diff --git a/uni/test/integration/src/schedule_page_test.dart b/uni/test/integration/src/schedule_page_test.dart index 449515e38..51bcdc9a3 100644 --- a/uni/test/integration/src/schedule_page_test.dart +++ b/uni/test/integration/src/schedule_page_test.dart @@ -71,13 +71,13 @@ void main() async { expect(find.byKey(const Key(scheduleSlotTimeKey1)), findsNothing); expect(find.byKey(const Key(scheduleSlotTimeKey2)), findsNothing); - await scheduleProvider.fetchUserLectures( + final lectures = await scheduleProvider.fetchUserLectures( Session(username: '', cookies: '', faculties: ['feup']), profile, persistentSession: false, ); - scheduleProvider.invalidate(); + scheduleProvider.setState(lectures); await tester.tap(find.byKey(const Key('schedule-page-tab-2'))); await tester.pumpAndSettle(); From 940ecbeb3a8665b83ae79ad669ff6d6368256308 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Dec 2023 13:17:20 +0000 Subject: [PATCH 080/334] Fix tests --- uni/lib/model/providers/state_providers.dart | 56 ++++++++++++------- uni/pubspec.lock | 20 ++++++- uni/pubspec.yaml | 1 + .../integration/src/schedule_page_test.dart | 2 + uni/test/test_widget.dart | 2 + 5 files changed, 60 insertions(+), 21 deletions(-) diff --git a/uni/lib/model/providers/state_providers.dart b/uni/lib/model/providers/state_providers.dart index b95a37f5f..cd35b33c3 100644 --- a/uni/lib/model/providers/state_providers.dart +++ b/uni/lib/model/providers/state_providers.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; @@ -13,21 +15,37 @@ import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; class StateProviders { - StateProviders( - this.lectureProvider, - this.examProvider, - this.busStopProvider, - this.restaurantProvider, - this.profileProvider, - this.courseUnitsInfoProvider, - this.sessionProvider, - this.calendarProvider, - this.libraryOccupationProvider, - this.facultyLocationsProvider, - this.referenceProvider, - ); + StateProviders(this.lectureProvider, + this.examProvider, + this.busStopProvider, + this.restaurantProvider, + this.profileProvider, + this.courseUnitsInfoProvider, + this.sessionProvider, + this.calendarProvider, + this.libraryOccupationProvider, + this.facultyLocationsProvider, + this.referenceProvider,); factory StateProviders.fromContext(BuildContext context) { + // In tests, one does not initialize all providers + // but a version of them is needed for the load methods. + if (Platform.environment.containsKey('FLUTTER_TEST')) { + return StateProviders( + LectureProvider(), + ExamProvider(), + BusStopProvider(), + RestaurantProvider(), + ProfileProvider(), + CourseUnitsInfoProvider(), + SessionProvider(), + CalendarProvider(), + LibraryOccupationProvider(), + FacultyLocationsProvider(), + ReferenceProvider(), + ); + } + final lectureProvider = Provider.of(context, listen: false); final examProvider = Provider.of(context, listen: false); @@ -38,17 +56,17 @@ class StateProviders { final courseUnitsInfoProvider = Provider.of(context, listen: false); final profileProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); final sessionProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); final calendarProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); final libraryOccupationProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); final facultyLocationsProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); final referenceProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); return StateProviders( lectureProvider, diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 4d23b1bce..cdfbe1911 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -1023,10 +1023,26 @@ packages: dependency: transitive description: name: sqflite_common - sha256: "1b92f368f44b0dee2425bb861cfa17b6f6cf3961f762ff6f941d20b33355660a" + sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.5.0+2" + sqflite_common_ffi: + dependency: "direct dev" + description: + name: sqflite_common_ffi + sha256: "35d2fce1e971707c227cc4775cc017d5eafe06c2654c3435ebd5c3ad6c170f5f" + url: "https://pub.dev" + source: hosted + version: "2.3.0+4" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb + url: "https://pub.dev" + source: hosted + version: "2.1.0" stack_trace: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 0d468489e..59c772000 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -58,6 +58,7 @@ dependencies: dev_dependencies: build_runner: ^2.4.7 + sqflite_common_ffi: ^2.3.0+4 flutter_launcher_icons: ^0.13.1 flutter_test: sdk: flutter diff --git a/uni/test/integration/src/schedule_page_test.dart b/uni/test/integration/src/schedule_page_test.dart index 51bcdc9a3..833a385e7 100644 --- a/uni/test/integration/src/schedule_page_test.dart +++ b/uni/test/integration/src/schedule_page_test.dart @@ -78,6 +78,8 @@ void main() async { ); scheduleProvider.setState(lectures); + + await tester.pumpAndSettle(); await tester.tap(find.byKey(const Key('schedule-page-tab-2'))); await tester.pumpAndSettle(); diff --git a/uni/test/test_widget.dart b/uni/test/test_widget.dart index c9fc36cc9..154b159b4 100644 --- a/uni/test/test_widget.dart +++ b/uni/test/test_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/app_locale.dart'; @@ -10,6 +11,7 @@ import 'package:uni/view/locale_notifier.dart'; Future initTestEnvironment() async { SharedPreferences.setMockInitialValues({}); PreferencesController.prefs = await SharedPreferences.getInstance(); + databaseFactory = databaseFactoryFfi; } Widget testableWidget( From 823f19703824e299e4d45e90ae41f817f0ee272b Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Dec 2023 14:22:41 +0000 Subject: [PATCH 081/334] Fix remaining tests --- uni/lib/generated/intl/messages_en.dart | 2 +- uni/lib/generated/intl/messages_pt_PT.dart | 2 +- uni/lib/generated/l10n.dart | 2 +- uni/lib/l10n/intl_en.arb | 4 +- uni/lib/l10n/intl_pt_PT.arb | 4 +- uni/lib/view/lazy_consumer.dart | 7 +-- uni/lib/view/profile/profile.dart | 4 +- uni/lib/view/schedule/schedule.dart | 59 +++++++++---------- uni/pubspec.yaml | 9 +-- .../unit/view/Pages/exams_page_view_test.dart | 2 - uni/test/unit/view/Widgets/exam_row_test.dart | 2 - 11 files changed, 43 insertions(+), 54 deletions(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 7ccb628cb..e7a668037 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -221,7 +221,7 @@ class MessageLookup extends MessageLookupByLibrary { "problem_id": MessageLookupByLibrary.simpleMessage( "Brief identification of the problem"), "reference_sigarra_help": MessageLookupByLibrary.simpleMessage( - "The generated reference data will appear in Sigarra, checking account.\\nProfile > Checking Account"), + "The generated reference data will appear in Sigarra, checking account.\nProfile > Checking Account"), "reference_success": MessageLookupByLibrary.simpleMessage( "Reference created successfully!"), "remove": MessageLookupByLibrary.simpleMessage("Delete"), diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 6fc166ead..d77b84c82 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -222,7 +222,7 @@ class MessageLookup extends MessageLookupByLibrary { "problem_id": MessageLookupByLibrary.simpleMessage( "Breve identificação do problema"), "reference_sigarra_help": MessageLookupByLibrary.simpleMessage( - "Os dados da referência gerada aparecerão no Sigarra, conta corrente.\\nPerfil > Conta Corrente"), + "Os dados da referência gerada aparecerão no Sigarra, conta corrente. Perfil > Conta Corrente"), "reference_success": MessageLookupByLibrary.simpleMessage( "Referência criada com sucesso!"), "remove": MessageLookupByLibrary.simpleMessage("Remover"), diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 4fa6476f7..eadc9bcee 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1199,7 +1199,7 @@ class S { /// `The generated reference data will appear in Sigarra, checking account.\nProfile > Checking Account` String get reference_sigarra_help { return Intl.message( - 'The generated reference data will appear in Sigarra, checking account.\\nProfile > Checking Account', + 'The generated reference data will appear in Sigarra, checking account.\nProfile > Checking Account', name: 'reference_sigarra_help', desc: '', args: [], diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 1f3802a74..4b696bfe3 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -234,7 +234,7 @@ "@prints": {}, "problem_id": "Brief identification of the problem", "@problem_id": {}, - "reference_sigarra_help": "The generated reference data will appear in Sigarra, checking account.\\nProfile > Checking Account", + "reference_sigarra_help": "The generated reference data will appear in Sigarra, checking account.\nProfile > Checking Account", "@reference_sigarra_help": {}, "reference_success": "Reference created successfully!", "@reference_success": {}, @@ -242,7 +242,7 @@ "@remove": {}, "report_error": "Report error", "@report_error": {}, - "restaurant_main_page" : "Do you want to see your favorite restaurants in the main page?", + "restaurant_main_page": "Do you want to see your favorite restaurants in the main page?", "@restaurant_main_page": {}, "room": "Room", "@room": {}, diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 2acce7162..4de2a08af 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -234,7 +234,7 @@ "@prints": {}, "problem_id": "Breve identificação do problema", "@problem_id": {}, - "reference_sigarra_help": "Os dados da referência gerada aparecerão no Sigarra, conta corrente.\\nPerfil > Conta Corrente", + "reference_sigarra_help": "Os dados da referência gerada aparecerão no Sigarra, conta corrente. Perfil > Conta Corrente", "@reference_sigarra_help": {}, "reference_success": "Referência criada com sucesso!", "@reference_success": {}, @@ -242,7 +242,7 @@ "@remove": {}, "report_error": "Reportar erro", "@report_error": {}, - "restaurant_main_page" : "Queres ver os teus restaurantes favoritos na página principal?", + "restaurant_main_page": "Queres ver os teus restaurantes favoritos na página principal?", "@restaurant_main_page": {}, "room": "Sala", "@room": {}, diff --git a/uni/lib/view/lazy_consumer.dart b/uni/lib/view/lazy_consumer.dart index 56996f0d1..1fabbb6de 100644 --- a/uni/lib/view/lazy_consumer.dart +++ b/uni/lib/view/lazy_consumer.dart @@ -98,10 +98,9 @@ class LazyConsumer, T2> return showContent ? builder(context, provider.state as T2) - : Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: SingleChildScrollView( - physics: const AlwaysScrollableScrollPhysics(), + : Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 10), child: onNullContent, ), ); diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 31d880881..210ad2f5a 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -7,6 +7,7 @@ import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/profile/widgets/account_info_card.dart'; import 'package:uni/view/profile/widgets/course_info_card.dart'; +import 'package:uni/view/profile/widgets/print_info_card.dart'; import 'package:uni/view/profile/widgets/profile_overview.dart'; import 'package:uni/view/settings/settings.dart'; @@ -42,10 +43,9 @@ class ProfilePageViewState extends SecondaryPageViewState { getProfileDecorationImage: getProfileDecorationImage, ), const Padding(padding: EdgeInsets.all(5)), - // TODO(bdmendes): Bring this back when print info is ready again - // PrintInfoCard() ...courseWidgets, AccountInfoCard(), + PrintInfoCard(), ], ); }, diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index 60825d2e9..47f266ab7 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -18,15 +18,31 @@ class SchedulePage extends StatefulWidget { SchedulePageState createState() => SchedulePageState(); } -class SchedulePageState extends State { +class SchedulePageState extends GeneralPageViewState { @override - Widget build(BuildContext context) { - return LazyConsumer>( - builder: (context, lectures) => SchedulePageView(lectures), - hasContent: (lectures) => lectures.isNotEmpty, - onNullContent: const SchedulePageView([]), + Widget getBody(BuildContext context) { + return Column( + children: [ + PageTitle( + name: S.of(context).nav_title( + DrawerItem.navSchedule.title, + ), + ), + Expanded( + child: LazyConsumer>( + builder: (context, lectures) => SchedulePageView(lectures), + hasContent: (lectures) => lectures.isNotEmpty, + onNullContent: const SchedulePageView([]), + ), + ), + ], ); } + + @override + Future onRefresh(BuildContext context) async { + await context.read().forceRefresh(context); + } } class SchedulePageView extends StatefulWidget { @@ -38,7 +54,7 @@ class SchedulePageView extends StatefulWidget { SchedulePageViewState createState() => SchedulePageViewState(); } -class SchedulePageViewState extends GeneralPageViewState +class SchedulePageViewState extends State with TickerProviderStateMixin { TabController? tabController; @@ -61,26 +77,15 @@ class SchedulePageViewState extends GeneralPageViewState } @override - Widget getBody(BuildContext context) { + Widget build(BuildContext context) { final queryData = MediaQuery.of(context); - return Column( children: [ - ListView( - shrinkWrap: true, - children: [ - PageTitle( - name: S.of(context).nav_title( - DrawerItem.navSchedule.title, - ), - ), - TabBar( - controller: tabController, - isScrollable: true, - physics: const BouncingScrollPhysics(), - tabs: createTabs(queryData, context), - ), - ], + TabBar( + controller: tabController, + isScrollable: true, + physics: const BouncingScrollPhysics(), + tabs: createTabs(queryData, context), ), Expanded( child: TabBarView( @@ -169,10 +174,4 @@ class SchedulePageViewState extends GeneralPageViewState ), ); } - - @override - Future onRefresh(BuildContext context) { - return Provider.of(context, listen: false) - .forceRefresh(context); - } } diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 59c772000..f97b15162 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -58,16 +58,11 @@ dependencies: dev_dependencies: build_runner: ^2.4.7 - sqflite_common_ffi: ^2.3.0+4 flutter_launcher_icons: ^0.13.1 flutter_test: sdk: flutter - # FIXME(luisd): Update mockito to a release version when 5.4.3 or above - # is launched - mockito: - git: - url: https://github.com/dart-lang/mockito.git - ref: "e54a006" + mockito: 5.4.4 + sqflite_common_ffi: ^2.3.0+4 test: any very_good_analysis: ^5.1.0 diff --git a/uni/test/unit/view/Pages/exams_page_view_test.dart b/uni/test/unit/view/Pages/exams_page_view_test.dart index 43d1f1e48..a5b64eb77 100644 --- a/uni/test/unit/view/Pages/exams_page_view_test.dart +++ b/uni/test/unit/view/Pages/exams_page_view_test.dart @@ -2,8 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/view/exams/exams.dart'; diff --git a/uni/test/unit/view/Widgets/exam_row_test.dart b/uni/test/unit/view/Widgets/exam_row_test.dart index fff0163f7..77bd702c2 100644 --- a/uni/test/unit/view/Widgets/exam_row_test.dart +++ b/uni/test/unit/view/Widgets/exam_row_test.dart @@ -2,8 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/view/exams/widgets/exam_row.dart'; From 02316cdb90fc281a0db7806b48c9ebba15502223 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Dec 2023 14:27:02 +0000 Subject: [PATCH 082/334] Format and remove todos --- .../local_storage/preferences_controller.dart | 1 - .../providers/lazy/bus_stop_provider.dart | 1 - uni/lib/model/providers/state_providers.dart | 36 ++++++++++--------- uni/lib/view/exams/widgets/exam_row.dart | 12 +++---- uni/pubspec.lock | 11 +++--- .../integration/src/schedule_page_test.dart | 2 +- .../unit/view/Widgets/schedule_slot_test.dart | 2 +- 7 files changed, 32 insertions(+), 33 deletions(-) diff --git a/uni/lib/controller/local_storage/preferences_controller.dart b/uni/lib/controller/local_storage/preferences_controller.dart index 8347abfc2..24b92c6a1 100644 --- a/uni/lib/controller/local_storage/preferences_controller.dart +++ b/uni/lib/controller/local_storage/preferences_controller.dart @@ -14,7 +14,6 @@ import 'package:uni/utils/favorite_widget_type.dart'; /// This database stores the user's student number, password and favorite /// widgets. class PreferencesController { - // TODO(bdmendes): Initilizate this also on workmanager static late SharedPreferences prefs; static final iv = encrypt.IV.fromBase64('jF9jjdSEPgsKnf0jCl1GAQ=='); diff --git a/uni/lib/model/providers/lazy/bus_stop_provider.dart b/uni/lib/model/providers/lazy/bus_stop_provider.dart index dac95c639..e7031094e 100644 --- a/uni/lib/model/providers/lazy/bus_stop_provider.dart +++ b/uni/lib/model/providers/lazy/bus_stop_provider.dart @@ -6,7 +6,6 @@ import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; import 'package:uni/model/providers/state_providers.dart'; -// TODO(thePeras): what does > means? create a model. class BusStopProvider extends StateProviderNotifier> { BusStopProvider() : super(cacheDuration: null); diff --git a/uni/lib/model/providers/state_providers.dart b/uni/lib/model/providers/state_providers.dart index cd35b33c3..96a94dd26 100644 --- a/uni/lib/model/providers/state_providers.dart +++ b/uni/lib/model/providers/state_providers.dart @@ -15,17 +15,19 @@ import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; class StateProviders { - StateProviders(this.lectureProvider, - this.examProvider, - this.busStopProvider, - this.restaurantProvider, - this.profileProvider, - this.courseUnitsInfoProvider, - this.sessionProvider, - this.calendarProvider, - this.libraryOccupationProvider, - this.facultyLocationsProvider, - this.referenceProvider,); + StateProviders( + this.lectureProvider, + this.examProvider, + this.busStopProvider, + this.restaurantProvider, + this.profileProvider, + this.courseUnitsInfoProvider, + this.sessionProvider, + this.calendarProvider, + this.libraryOccupationProvider, + this.facultyLocationsProvider, + this.referenceProvider, + ); factory StateProviders.fromContext(BuildContext context) { // In tests, one does not initialize all providers @@ -56,17 +58,17 @@ class StateProviders { final courseUnitsInfoProvider = Provider.of(context, listen: false); final profileProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); final sessionProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); final calendarProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); final libraryOccupationProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); final facultyLocationsProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); final referenceProvider = - Provider.of(context, listen: false); + Provider.of(context, listen: false); return StateProviders( lectureProvider, diff --git a/uni/lib/view/exams/widgets/exam_row.dart b/uni/lib/view/exams/widgets/exam_row.dart index d636c5277..4af26b10a 100644 --- a/uni/lib/view/exams/widgets/exam_row.dart +++ b/uni/lib/view/exams/widgets/exam_row.dart @@ -72,15 +72,15 @@ class _ExamRowState extends State { icon: !isHidden ? const Icon(Icons.visibility, size: 30) : const Icon( - Icons.visibility_off, - size: 30, - ), + Icons.visibility_off, + size: 30, + ), tooltip: isHidden ? 'Mostrar na Área Pessoal' : 'Ocultar da Área Pessoal', onPressed: () async { final hiddenExams = - PreferencesController.getHiddenExams(); + PreferencesController.getHiddenExams(); if (hiddenExams.contains(widget.exam.id)) { hiddenExams.remove(widget.exam.id); @@ -133,8 +133,8 @@ class _ExamRowState extends State { return rooms .map( (room) => - Text(room.trim(), style: Theme.of(context).textTheme.bodyMedium), - ) + Text(room.trim(), style: Theme.of(context).textTheme.bodyMedium), + ) .toList(); } diff --git a/uni/pubspec.lock b/uni/pubspec.lock index cdfbe1911..91bcd3353 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -640,12 +640,11 @@ packages: mockito: dependency: "direct dev" description: - path: "." - ref: e54a006 - resolved-ref: e54a00667cbe9a27de08e4c0ea355bacbe8c98d0 - url: "https://github.com/dart-lang/mockito.git" - source: git - version: "5.4.3-wip" + name: mockito + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" + url: "https://pub.dev" + source: hosted + version: "5.4.4" nested: dependency: transitive description: diff --git a/uni/test/integration/src/schedule_page_test.dart b/uni/test/integration/src/schedule_page_test.dart index 833a385e7..080616ffa 100644 --- a/uni/test/integration/src/schedule_page_test.dart +++ b/uni/test/integration/src/schedule_page_test.dart @@ -78,7 +78,7 @@ void main() async { ); scheduleProvider.setState(lectures); - + await tester.pumpAndSettle(); await tester.tap(find.byKey(const Key('schedule-page-tab-2'))); diff --git a/uni/test/unit/view/Widgets/schedule_slot_test.dart b/uni/test/unit/view/Widgets/schedule_slot_test.dart index d6d52114a..702d4007f 100644 --- a/uni/test/unit/view/Widgets/schedule_slot_test.dart +++ b/uni/test/unit/view/Widgets/schedule_slot_test.dart @@ -7,7 +7,7 @@ import '../../../test_widget.dart'; void main() async { await initTestEnvironment(); - + group('Schedule Slot', () { const subject = 'SOPE'; final begin = DateTime(2021, 06, 01, 10); From 7a69d6cfc92ddcc9664410c0eb507de7035687cb Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Mon, 25 Dec 2023 22:26:31 +0000 Subject: [PATCH 083/334] Extract exams page title --- uni/lib/view/exams/exams.dart | 66 +++++++++++++++++------------------ 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/uni/lib/view/exams/exams.dart b/uni/lib/view/exams/exams.dart index d7497d4a1..29cf0315f 100644 --- a/uni/lib/view/exams/exams.dart +++ b/uni/lib/view/exams/exams.dart @@ -27,45 +27,43 @@ class ExamsPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return LazyConsumer>( - builder: (context, exams) { - return ListView( - children: [ - ExamPageTitle( - () => setState(() { - filteredExamTypes = PreferencesController.getFilteredExams(); - }), + return ListView( + children: [ + ExamPageTitle( + () => setState(() { + filteredExamTypes = PreferencesController.getFilteredExams(); + }), + ), + LazyConsumer>( + builder: (context, exams) => Column( + children: createExamsColumn( + context, + exams + .where( + (exam) => + filteredExamTypes[Exam.getExamTypeLong(exam.type)] ?? + true, + ) + .toList(), ), - Column( - children: createExamsColumn( - context, - exams - .where( - (exam) => - filteredExamTypes[Exam.getExamTypeLong(exam.type)] ?? - true, - ) - .toList(), + ), + hasContent: (exams) => exams.isNotEmpty, + onNullContent: Center( + heightFactor: 1.2, + child: ImageLabel( + imagePath: 'assets/images/vacation.png', + label: S.of(context).no_exams_label, + labelTextStyle: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + color: Theme.of(context).colorScheme.primary, ), + sublabel: S.of(context).no_exams, + sublabelTextStyle: const TextStyle(fontSize: 15), ), - ], - ); - }, - hasContent: (List exams) => exams.isNotEmpty, - onNullContent: Center( - heightFactor: 1.2, - child: ImageLabel( - imagePath: 'assets/images/vacation.png', - label: S.of(context).no_exams_label, - labelTextStyle: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18, - color: Theme.of(context).colorScheme.primary, ), - sublabel: S.of(context).no_exams, - sublabelTextStyle: const TextStyle(fontSize: 15), ), - ), + ], ); } From d9b588faace06fdd00a6d0337f4c06acb10a15b2 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Mon, 25 Dec 2023 23:20:42 +0000 Subject: [PATCH 084/334] Fix profile provider fetcher --- uni/lib/model/providers/startup/profile_provider.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/uni/lib/model/providers/startup/profile_provider.dart b/uni/lib/model/providers/startup/profile_provider.dart index 015d3753e..39e97966c 100644 --- a/uni/lib/model/providers/startup/profile_provider.dart +++ b/uni/lib/model/providers/startup/profile_provider.dart @@ -47,7 +47,8 @@ class ProfileProvider extends StateProviderNotifier { ..feesLimit = userBalanceAndFeesLimit.item2 ..printBalance = await fetchUserPrintBalance(session); - final courseUnits = await fetchCourseUnitsAndCourseAverages(session); + final courseUnits = + await fetchCourseUnitsAndCourseAverages(session, profile); if (courseUnits != null) { profile.courseUnits = courseUnits; } @@ -124,12 +125,13 @@ class ProfileProvider extends StateProviderNotifier { Future?> fetchCourseUnitsAndCourseAverages( Session session, + Profile profile, ) async { final allCourseUnits = await AllCourseUnitsFetcher().getAllCourseUnitsAndCourseAverages( - state!.courses, + profile.courses, session, - currentCourseUnits: state!.courseUnits, + currentCourseUnits: profile.courseUnits, ); if (allCourseUnits == null) { @@ -139,7 +141,7 @@ class ProfileProvider extends StateProviderNotifier { final userPersistentInfo = PreferencesController.getPersistentUserInfo(); if (userPersistentInfo != null) { final coursesDb = AppCoursesDatabase(); - await coursesDb.saveNewCourses(state!.courses); + await coursesDb.saveNewCourses(profile.courses); final courseUnitsDatabase = AppCourseUnitsDatabase(); await courseUnitsDatabase.saveNewCourseUnits(allCourseUnits); From cd0eb29aad14f6ee7b3dad5717369f8c2ddd00ca Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Tue, 26 Dec 2023 00:00:36 +0000 Subject: [PATCH 085/334] Update pubspec lock and remove unused code --- .../model/providers/lazy/exam_provider.dart | 34 ------------------- .../widgets/bus_stop_row.dart | 3 -- uni/pubspec.lock | 2 +- 3 files changed, 1 insertion(+), 38 deletions(-) diff --git a/uni/lib/model/providers/lazy/exam_provider.dart b/uni/lib/model/providers/lazy/exam_provider.dart index 946596798..b59aa4c7b 100644 --- a/uni/lib/model/providers/lazy/exam_provider.dart +++ b/uni/lib/model/providers/lazy/exam_provider.dart @@ -53,38 +53,4 @@ class ExamProvider extends StateProviderNotifier> { return exams; } - -/*Future updateFilteredExams() async { - final exams = await AppSharedPreferences.getFilteredExams(); - _filteredExamsTypes = exams; - notifyListeners(); - } - - Future setFilteredExams(Map newFilteredExams) async { - unawaited(AppSharedPreferences.saveFilteredExams(newFilteredExams)); - _filteredExamsTypes = Map.from(newFilteredExams); - notifyListeners(); - } - - List getFilteredExams() { - return exams - .where( - (exam) => filteredExamsTypes[Exam.getExamTypeLong(exam.type)] ?? true, - ) - .toList(); - } - - Future setHiddenExams(List newHiddenExams) async { - _hiddenExams = List.from(newHiddenExams); - await AppSharedPreferences.saveHiddenExams(hiddenExams); - notifyListeners(); - } - - Future toggleHiddenExam(String newExamId) async { - _hiddenExams.contains(newExamId) - ? _hiddenExams.remove(newExamId) - : _hiddenExams.add(newExamId); - await AppSharedPreferences.saveHiddenExams(hiddenExams); - notifyListeners(); - }*/ } diff --git a/uni/lib/view/bus_stop_next_arrivals/widgets/bus_stop_row.dart b/uni/lib/view/bus_stop_next_arrivals/widgets/bus_stop_row.dart index 1cdbb688f..0661fd001 100644 --- a/uni/lib/view/bus_stop_next_arrivals/widgets/bus_stop_row.dart +++ b/uni/lib/view/bus_stop_next_arrivals/widgets/bus_stop_row.dart @@ -79,9 +79,6 @@ class BusStopRow extends StatelessWidget { ); } else { for (var i = 0; i < trips.length; i++) { -/* Color color = Theme.of(context).accentColor; - if (i == trips.length - 1) color = Colors.transparent; */ - tripRows.add( Container( padding: const EdgeInsets.all(12), diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 91bcd3353..a66c9c835 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -1363,5 +1363,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.2 <4.0.0" + dart: ">=3.1.3 <4.0.0" flutter: ">=3.13.7" From 76931f803acea5c71d5802eed327249f5d541810 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Wed, 27 Dec 2023 07:36:21 +0000 Subject: [PATCH 086/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 93343b291..28d575ccd 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.26+216 \ No newline at end of file +1.7.27+217 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index f97b15162..f40788b68 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.26+216 +version: 1.7.27+217 environment: sdk: ">=3.0.0 <4.0.0" From bfc82531bb4f469e5823d53046def03ba2eb6baf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:58:04 +0000 Subject: [PATCH 087/334] Bump sentry_flutter from 7.13.2 to 7.14.0 in /uni Bumps [sentry_flutter](https://github.com/getsentry/sentry-dart) from 7.13.2 to 7.14.0. - [Release notes](https://github.com/getsentry/sentry-dart/releases) - [Changelog](https://github.com/getsentry/sentry-dart/blob/main/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-dart/compare/7.13.2...7.14.0) --- updated-dependencies: - dependency-name: sentry_flutter dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 8 ++++---- uni/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index a66c9c835..b35a002be 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -865,18 +865,18 @@ packages: dependency: transitive description: name: sentry - sha256: e7ded42974bac5f69e4ca4ddc57d30499dd79381838f24b7e8fd9aa4139e7b79 + sha256: "89e426587b0879e53c46a0aae0eb312696d9d2d803ba14b252a65cc24b1416a2" url: "https://pub.dev" source: hosted - version: "7.13.2" + version: "7.14.0" sentry_flutter: dependency: "direct main" description: name: sentry_flutter - sha256: d6f55ec7a1f681784165021f749007712a72ff57eadf91e963331b6ae326f089 + sha256: fd089ee4e75a927be037c56815a0a54af5a519f52b803a5ffecb589bb36e2401 url: "https://pub.dev" source: hosted - version: "7.13.2" + version: "7.14.0" shared_preferences: dependency: "direct main" description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index f40788b68..86998c47a 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -46,7 +46,7 @@ dependencies: path_provider: ^2.0.0 percent_indicator: ^4.2.2 provider: ^6.1.1 - sentry_flutter: ^7.13.2 + sentry_flutter: ^7.14.0 shared_preferences: ^2.2.2 shimmer: ^3.0.0 sqflite: ^2.0.3 From 09425c2489850ed629cac5738f3d1dcacbc1740d Mon Sep 17 00:00:00 2001 From: bdmendes Date: Tue, 2 Jan 2024 19:28:53 +0000 Subject: [PATCH 088/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 28d575ccd..6cf0c6af2 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.27+217 \ No newline at end of file +1.7.28+218 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 86998c47a..55c150ff3 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.27+217 +version: 1.7.28+218 environment: sdk: ">=3.0.0 <4.0.0" From 85374dad4c7c0a474107dfb7473822645cefe4b4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:57:56 +0000 Subject: [PATCH 089/334] Bump cached_network_image from 3.3.0 to 3.3.1 in /uni Bumps [cached_network_image](https://github.com/Baseflow/flutter_cached_network_image) from 3.3.0 to 3.3.1. - [Commits](https://github.com/Baseflow/flutter_cached_network_image/compare/v3.3.0...v3.3.1) --- updated-dependencies: - dependency-name: cached_network_image dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 12 ++++++------ uni/pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index b35a002be..7d7be219b 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -141,26 +141,26 @@ packages: dependency: "direct main" description: name: cached_network_image - sha256: f98972704692ba679db144261172a8e20feb145636c617af0eb4022132a6797f + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.3.1" cached_network_image_platform_interface: dependency: transitive description: name: cached_network_image_platform_interface - sha256: "56aa42a7a01e3c9db8456d9f3f999931f1e05535b5a424271e9a38cabf066613" + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "4.0.0" cached_network_image_web: dependency: transitive description: name: cached_network_image_web - sha256: "759b9a9f8f6ccbb66c185df805fac107f05730b1dab9c64626d1008cca532257" + sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" characters: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 55c150ff3..eee9cb6c1 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -15,7 +15,7 @@ environment: dependencies: add_2_calendar: ^3.0.1 - cached_network_image: ^3.2.3 + cached_network_image: ^3.3.1 collection: ^1.16.0 connectivity_plus: ^5.0.2 crypto: ^3.0.1 From 6ad458a5f11181ff3631230bd4b2b8a9ee51d96c Mon Sep 17 00:00:00 2001 From: bdmendes Date: Tue, 2 Jan 2024 19:32:59 +0000 Subject: [PATCH 090/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 6cf0c6af2..62cd5cd2b 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.28+218 \ No newline at end of file +1.7.29+219 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index eee9cb6c1..8f7177e56 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.28+218 +version: 1.7.29+219 environment: sdk: ">=3.0.0 <4.0.0" From 97a12467923fb5e36dd6b357199acaf956d393d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 19:35:26 +0000 Subject: [PATCH 091/334] Bump flutter_svg from 2.0.7 to 2.0.9 in /uni Bumps [flutter_svg](https://github.com/dnfield/flutter_svg/tree/master/packages) from 2.0.7 to 2.0.9. - [Release notes](https://github.com/dnfield/flutter_svg/releases) - [Commits](https://github.com/dnfield/flutter_svg/commits/HEAD/packages) --- updated-dependencies: - dependency-name: flutter_svg dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 16 ++++++++-------- uni/pubspec.yaml | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 7d7be219b..e1fba8fa4 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -431,10 +431,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" + sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.9" flutter_test: dependency: "direct dev" description: flutter @@ -1238,26 +1238,26 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "670f6e07aca990b4a2bcdc08a784193c4ccdd1932620244c3a86bb72a0eac67f" + sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43" url: "https://pub.dev" source: hosted - version: "1.1.7" + version: "1.1.9+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "7451721781d967db9933b63f5733b1c4533022c0ba373a01bdd79d1a5457f69f" + sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7" url: "https://pub.dev" source: hosted - version: "1.1.7" + version: "1.1.9+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: "80a13c613c8bde758b1464a1755a7b3a8f2b6cec61fbf0f5a53c94c30f03ba2e" + sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26 url: "https://pub.dev" source: hosted - version: "1.1.7" + version: "1.1.9+1" vector_math: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 8f7177e56..af7311eb8 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -33,7 +33,7 @@ dependencies: flutter_map: ^5.0.0 flutter_map_marker_popup: ^5.0.0 flutter_markdown: ^0.6.0 - flutter_svg: ^2.0.0+1 + flutter_svg: ^2.0.9 flutter_widget_from_html_core: ^0.14.9 html: ^0.15.0 http: ^1.1.0 From 4a2138c30d0a2367a4cf60eafe3f7f5e08736f17 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Tue, 2 Jan 2024 19:39:40 +0000 Subject: [PATCH 092/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 62cd5cd2b..c85024bed 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.29+219 \ No newline at end of file +1.7.30+220 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index af7311eb8..f04cea405 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.29+219 +version: 1.7.30+220 environment: sdk: ">=3.0.0 <4.0.0" From c62c03313838050b2a767caf58f4d27f14493c2d Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 4 Jan 2024 17:05:20 +0000 Subject: [PATCH 093/334] Changing name, visibility widget and local storage bug --- .../local_storage/app_shared_preferences.dart | 2 +- uni/lib/view/home/home.dart | 21 ++++++++++--------- .../view/home/widgets/tracking_banner.dart | 4 ++-- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/uni/lib/controller/local_storage/app_shared_preferences.dart b/uni/lib/controller/local_storage/app_shared_preferences.dart index 5881f95b2..e8421f2c3 100644 --- a/uni/lib/controller/local_storage/app_shared_preferences.dart +++ b/uni/lib/controller/local_storage/app_shared_preferences.dart @@ -94,7 +94,7 @@ class AppSharedPreferences { required bool isViewed, }) async { final prefs = await SharedPreferences.getInstance(); - await prefs.setBool(isDataCollectionBannerViewedKey, true); + await prefs.setBool(isDataCollectionBannerViewedKey, isViewed); } static Future isDataCollectionBannerViewed() async { diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 13fc9ea33..8a1ae5a8a 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -36,16 +36,17 @@ class HomePageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return isBannerViewed - ? const MainCardsList() - : Column( - children: [ - BannerWidget(setBannerViewed), - const Expanded( - child: MainCardsList(), - ), - ], - ); + return Column( + children: [ + Visibility( + visible: !isBannerViewed, + child: TrackingBanner(setBannerViewed), + ), + const Expanded( + child: MainCardsList(), + ), + ], + ); } @override diff --git a/uni/lib/view/home/widgets/tracking_banner.dart b/uni/lib/view/home/widgets/tracking_banner.dart index 52204ae7e..0dc67c325 100644 --- a/uni/lib/view/home/widgets/tracking_banner.dart +++ b/uni/lib/view/home/widgets/tracking_banner.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; -class BannerWidget extends StatelessWidget { - const BannerWidget(this.onDismiss, {super.key}); +class TrackingBanner extends StatelessWidget { + const TrackingBanner(this.onDismiss, {super.key}); final void Function() onDismiss; From a4891e6497161e700b4cff682deebab35706134d Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 4 Jan 2024 17:35:09 +0000 Subject: [PATCH 094/334] Formatting --- uni/lib/view/home/home.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index e9ea3064a..6760438af 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -14,7 +14,8 @@ class HomePageView extends StatefulWidget { class HomePageViewState extends GeneralPageViewState { bool isBannerViewed = true; - List favoriteCardTypes = PreferencesController.getFavoriteCards(); + List favoriteCardTypes = + PreferencesController.getFavoriteCards(); @override void initState() { From 8c7d071062621bd75f43241a5d07019b9436ad27 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 4 Jan 2024 17:43:06 +0000 Subject: [PATCH 095/334] Fixing app version --- uni/app_version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index ac0c32f7b..c85024bed 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.30+220 +1.7.30+220 \ No newline at end of file From ebbc3fe27cf8a9ce26cc36ac5ace7af8e7264b4a Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 4 Jan 2024 18:36:05 +0000 Subject: [PATCH 096/334] Translating and abstracting code --- .../course_units_info_fetcher.dart | 28 ++++++++----------- .../parsers/parser_course_unit_info.dart | 24 ++++------------ uni/lib/generated/intl/messages_en.dart | 1 + uni/lib/generated/intl/messages_pt_PT.dart | 1 + uni/lib/generated/l10n.dart | 10 +++++++ uni/lib/l10n/intl_en.arb | 2 ++ uni/lib/l10n/intl_pt_PT.arb | 2 ++ .../course_units/course_unit_directory.dart | 8 ++++++ .../lazy/course_units_info_provider.dart | 7 ++--- .../course_unit_info/course_unit_info.dart | 6 ++-- .../widgets/course_unit_files.dart | 25 ++++++++--------- .../view/schedule/widgets/schedule_slot.dart | 8 +++--- 12 files changed, 62 insertions(+), 60 deletions(-) create mode 100644 uni/lib/model/entities/course_units/course_unit_directory.dart diff --git a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart index bd0278913..1c54029e3 100644 --- a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart +++ b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart @@ -1,10 +1,9 @@ import 'package:html/parser.dart'; -import 'package:http/http.dart'; import 'package:uni/controller/fetchers/session_dependant_fetcher.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/controller/parsers/parser_course_unit_info.dart'; import 'package:uni/model/entities/course_units/course_unit_class.dart'; -import 'package:uni/model/entities/course_units/course_unit_file.dart'; +import 'package:uni/model/entities/course_units/course_unit_directory.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/session.dart'; @@ -28,24 +27,19 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { return parseCourseUnitSheet(response); } - Future>>> fetchCourseUnitFiles( + Future> fetchCourseUnitFiles( Session session, int occurId, ) async { - final urls = - getEndpoints(session).map((url) => '${url}mob_ucurr_geral.conteudos'); - final responses = []; - for (final url in urls) { - final response = await NetworkRouter.getWithCookies( - url, - { - 'pv_ocorrencia_id': occurId.toString(), - }, - session, - ); - responses.add(response); - } - return parseFilesMultipleRequests(responses, session); + final url = '${getEndpoints(session)[0]}mob_ucurr_geral.conteudos'; + final response = await NetworkRouter.getWithCookies( + url, + { + 'pv_ocorrencia_id': occurId.toString(), + }, + session, + ); + return parseFiles(response, session); } Future getDownloadLink( diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index d93ff9e56..9bf3deac7 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -4,30 +4,18 @@ import 'package:html/parser.dart'; import 'package:http/http.dart' as http; import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart'; import 'package:uni/model/entities/course_units/course_unit_class.dart'; +import 'package:uni/model/entities/course_units/course_unit_directory.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/session.dart'; -Future>>> parseFilesMultipleRequests( - List responses, - Session session, -) async { - final files = >>[]; - for (final response in responses) { - files.add(await parseFiles(response, session)); - } - return files; -} - -Future>> parseFiles( +Future> parseFiles( http.Response response, Session session, ) async { - final folders = >{}; - final json = jsonDecode(response.body) as List; - - if (json.isEmpty) return {}; + final dirs = []; + if (json.isEmpty) return []; for (var item in json) { item = item as Map; @@ -46,9 +34,9 @@ Future>> parseFiles( files.add(courseUnitFile); } } - folders[item['nome'] as String] = files; + dirs.add(CourseUnitFileDirectory(item['nome'].toString(), files)); } - return folders; + return dirs; } Future parseCourseUnitSheet(http.Response response) async { diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index dc2124c51..d04d78835 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -126,6 +126,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Deadline for next fee:"), "fee_notification": MessageLookupByLibrary.simpleMessage("Fee deadline"), + "files": MessageLookupByLibrary.simpleMessage("Files"), "first_year_registration": MessageLookupByLibrary.simpleMessage( "Year of first registration: "), "floor": MessageLookupByLibrary.simpleMessage("Floor"), diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 3bba2a947..2b56f0c60 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -125,6 +125,7 @@ class MessageLookup extends MessageLookupByLibrary { "Data limite próxima prestação:"), "fee_notification": MessageLookupByLibrary.simpleMessage("Data limite de propina"), + "files": MessageLookupByLibrary.simpleMessage("Ficheiros"), "first_year_registration": MessageLookupByLibrary.simpleMessage("Ano da primeira inscrição: "), "floor": MessageLookupByLibrary.simpleMessage("Piso"), diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index e1c460e90..4fa6b95b5 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1246,6 +1246,16 @@ class S { ); } + /// `Files` + String get files { + return Intl.message( + 'Files', + name: 'files', + desc: '', + args: [], + ); + } + /// `School Calendar` String get school_calendar { return Intl.message( diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 41134b39f..37fe6211e 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -244,6 +244,8 @@ "@restaurant_main_page": {}, "room": "Room", "@room": {}, + "files": "Files", + "@files": {}, "school_calendar": "School Calendar", "@school_calendar": {}, "semester": "Semester", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 0529b7216..a773e58a1 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -244,6 +244,8 @@ "@restaurant_main_page": {}, "room": "Sala", "@room": {}, + "files": "Ficheiros", + "@files": {}, "school_calendar": "Calendário Escolar", "@school_calendar": {}, "semester": "Semestre", diff --git a/uni/lib/model/entities/course_units/course_unit_directory.dart b/uni/lib/model/entities/course_units/course_unit_directory.dart new file mode 100644 index 000000000..d14fa6b13 --- /dev/null +++ b/uni/lib/model/entities/course_units/course_unit_directory.dart @@ -0,0 +1,8 @@ +import 'package:uni/model/entities/course_units/course_unit_file.dart'; + +class CourseUnitFileDirectory { + CourseUnitFileDirectory(this.folderName, this.files); + + final String folderName; + final List files; +} diff --git a/uni/lib/model/providers/lazy/course_units_info_provider.dart b/uni/lib/model/providers/lazy/course_units_info_provider.dart index 77b205a26..41551d6be 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -3,7 +3,7 @@ import 'dart:collection'; import 'package:uni/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/entities/course_units/course_unit_class.dart'; -import 'package:uni/model/entities/course_units/course_unit_file.dart'; +import 'package:uni/model/entities/course_units/course_unit_directory.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; @@ -14,8 +14,7 @@ class CourseUnitsInfoProvider extends StateProviderNotifier { : super(dependsOnSession: true, cacheDuration: null, initialize: false); final Map _courseUnitsSheets = {}; final Map> _courseUnitsClasses = {}; - final Map>>> - _courseUnitsFiles = {}; + final Map> _courseUnitsFiles = {}; UnmodifiableMapView get courseUnitsSheets => UnmodifiableMapView(_courseUnitsSheets); @@ -23,7 +22,7 @@ class CourseUnitsInfoProvider extends StateProviderNotifier { UnmodifiableMapView> get courseUnitsClasses => UnmodifiableMapView(_courseUnitsClasses); - UnmodifiableMapView>>> + UnmodifiableMapView> get courseUnitsFiles => UnmodifiableMapView(_courseUnitsFiles); Future fetchCourseUnitSheet( diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index 0602c6b88..cbbc0ded1 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -81,8 +81,8 @@ class CourseUnitDetailPageViewState tabs: [ Tab(text: S.of(context).course_info), Tab(text: S.of(context).course_class), - const Tab( - text: 'Ficheiros', + Tab( + text: S.of(context).files, ), ], ), @@ -122,7 +122,7 @@ class CourseUnitDetailPageViewState Widget _courseUnitFilesView(BuildContext context) { final sheet = context - .read() + .watch() .courseUnitsFiles[widget.courseUnit]; if (sheet == null || sheet.isEmpty) { diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart index f729fec3e..7a250dfe2 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart @@ -1,20 +1,18 @@ import 'package:flutter/material.dart'; +import 'package:uni/model/entities/course_units/course_unit_directory.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/view/course_unit_info/widgets/course_unit_files_row.dart'; import 'package:uni/view/course_unit_info/widgets/course_unit_info_card.dart'; class CourseUnitFilesView extends StatelessWidget { const CourseUnitFilesView(this.files, {super.key}); - final List>> files; + final List files; @override Widget build(BuildContext context) { final cards = files - .expand( - (file) => file.entries - .where((item) => item.value.isNotEmpty) - .map((item) => _buildCard(item.key, item.value)), - ) + .where((element) => element.files.isNotEmpty) + .map((e) => _buildCard(e.folderName, e.files)) .toList(); return Container( @@ -23,12 +21,11 @@ class CourseUnitFilesView extends StatelessWidget { ); } - CourseUnitInfoCard _buildCard(String folder, List files) { - return CourseUnitInfoCard( - folder, - Column( - children: files.map(CourseUnitFilesRow.new).toList(), - ), - ); - } + CourseUnitInfoCard _buildCard(String folder, List files) => + CourseUnitInfoCard( + folder, + Column( + children: files.map(CourseUnitFilesRow.new).toList(), + ), + ); } diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index efea349fc..39fd3c33d 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:uni/controller/networking/network_router.dart'; -import 'package:uni/controller/networking/url_launcher.dart'; import 'package:uni/view/common_widgets/row_container.dart'; +import 'package:url_launcher/url_launcher.dart'; class ScheduleSlot extends StatelessWidget { const ScheduleSlot({ @@ -113,9 +113,9 @@ class SubjectButtonWidget extends StatelessWidget { 'UCURR_GERAL.FICHA_UC_VIEW?pv_ocorrencia_id=$occurrId'; } - Future _launchURL(BuildContext context) async { + Future _launchURL() async { final url = toUcLink(occurrId); - await launchUrlWithToast(context, url); + await launchUrl(Uri.parse(url)); } @override @@ -133,7 +133,7 @@ class SubjectButtonWidget extends StatelessWidget { color: Colors.grey, alignment: Alignment.centerRight, tooltip: 'Abrir página da UC no browser', - onPressed: () => _launchURL(context), + onPressed: () => _launchURL, ), ], ); From 57b2a283f6beafb205a6fa6822f86e8d28e69b76 Mon Sep 17 00:00:00 2001 From: thePeras Date: Fri, 5 Jan 2024 14:47:55 +0000 Subject: [PATCH 097/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index c85024bed..5cc3bb496 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.30+220 \ No newline at end of file +1.7.31+221 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index f04cea405..3c0799214 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.30+220 +version: 1.7.31+221 environment: sdk: ">=3.0.0 <4.0.0" From 640594543d54840db240aa37601ef28368bbad4c Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 5 Jan 2024 15:31:01 +0000 Subject: [PATCH 098/334] Changing preferences controller variables --- uni/lib/controller/local_storage/preferences_controller.dart | 4 +--- uni/lib/view/home/home.dart | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/uni/lib/controller/local_storage/preferences_controller.dart b/uni/lib/controller/local_storage/preferences_controller.dart index 76ce16d13..ade539e05 100644 --- a/uni/lib/controller/local_storage/preferences_controller.dart +++ b/uni/lib/controller/local_storage/preferences_controller.dart @@ -90,12 +90,10 @@ class PreferencesController { static Future setDataCollectionBannerViewed({ required bool isViewed, }) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setBool(isDataCollectionBannerViewedKey, isViewed); } - static Future isDataCollectionBannerViewed() async { - final prefs = await SharedPreferences.getInstance(); + static bool isDataCollectionBannerViewed() { return prefs.getBool(isDataCollectionBannerViewedKey) ?? false; } diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 6760438af..93812aba3 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -24,9 +24,8 @@ class HomePageViewState extends GeneralPageViewState { } Future checkBannerViewed() async { - final pref = await PreferencesController.isDataCollectionBannerViewed(); setState(() { - isBannerViewed = pref; + isBannerViewed = PreferencesController.isDataCollectionBannerViewed(); }); } From d4b34dde406936adb35b0bba9dadc3fa6ce0a83d Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 5 Jan 2024 15:32:17 +0000 Subject: [PATCH 099/334] Typo fix --- .../controller/local_storage/preferences_controller.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/lib/controller/local_storage/preferences_controller.dart b/uni/lib/controller/local_storage/preferences_controller.dart index ade539e05..a146e7927 100644 --- a/uni/lib/controller/local_storage/preferences_controller.dart +++ b/uni/lib/controller/local_storage/preferences_controller.dart @@ -30,7 +30,7 @@ class PreferencesController { 'tuition_notification_toogle'; static const String _usageStatsToggleKey = 'usage_stats_toogle'; static const String _themeMode = 'theme_mode'; - static const String isDataCollectionBannerViewedKey = + static const String _isDataCollectionBannerViewedKey = 'data_collection_banner'; static const String _locale = 'app_locale'; static const String _favoriteCards = 'favorite_cards'; @@ -90,11 +90,11 @@ class PreferencesController { static Future setDataCollectionBannerViewed({ required bool isViewed, }) async { - await prefs.setBool(isDataCollectionBannerViewedKey, isViewed); + await prefs.setBool(_isDataCollectionBannerViewedKey, isViewed); } static bool isDataCollectionBannerViewed() { - return prefs.getBool(isDataCollectionBannerViewedKey) ?? false; + return prefs.getBool(_isDataCollectionBannerViewedKey) ?? false; } /// Returns the hash of the last Terms and Conditions that have From 166b4a24f5bf66824a1875b5ae2ea0f8e7bd0fe4 Mon Sep 17 00:00:00 2001 From: thePeras Date: Sat, 6 Jan 2024 18:38:13 +0000 Subject: [PATCH 100/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 5cc3bb496..f5d0c7b2c 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.31+221 \ No newline at end of file +1.7.32+222 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 3c0799214..02dc1a37e 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.31+221 +version: 1.7.32+222 environment: sdk: ">=3.0.0 <4.0.0" From de8ef9ba8ebadd111566c425ef85b589850c4f06 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 6 Jan 2024 20:34:27 +0000 Subject: [PATCH 101/334] Fixing minor url launcher problem --- uni/lib/view/schedule/widgets/schedule_slot.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index 39fd3c33d..a862394cb 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/view/common_widgets/row_container.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:uni/controller/networking/url_launcher.dart'; class ScheduleSlot extends StatelessWidget { const ScheduleSlot({ @@ -113,9 +113,9 @@ class SubjectButtonWidget extends StatelessWidget { 'UCURR_GERAL.FICHA_UC_VIEW?pv_ocorrencia_id=$occurrId'; } - Future _launchURL() async { + Future _launchURL(BuildContext context) async { final url = toUcLink(occurrId); - await launchUrl(Uri.parse(url)); + await launchUrlWithToast(context, url); } @override @@ -133,7 +133,7 @@ class SubjectButtonWidget extends StatelessWidget { color: Colors.grey, alignment: Alignment.centerRight, tooltip: 'Abrir página da UC no browser', - onPressed: () => _launchURL, + onPressed: () => _launchURL(context), ), ], ); From 4da11d369aec229e7e3a47cabf5a5ac08429b15d Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 6 Jan 2024 20:34:55 +0000 Subject: [PATCH 102/334] Formatting --- .../course_unit_info/widgets/course_unit_files.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart index 55ab70b84..abd1ea77f 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files.dart @@ -18,11 +18,11 @@ class CourseUnitFilesView extends StatelessWidget { return cards.isEmpty ? Center( - child: Container( - padding: const EdgeInsets.only(left: 10, right: 10), - child: Text(S.of(context).no_files_found), - ), - ) + child: Container( + padding: const EdgeInsets.only(left: 10, right: 10), + child: Text(S.of(context).no_files_found), + ), + ) : Container( padding: const EdgeInsets.only(left: 10, right: 10), child: ListView( From 39109db50c960d7a4019fcd5718fe2a55ff8d839 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 6 Jan 2024 20:37:52 +0000 Subject: [PATCH 103/334] Lint fix --- uni/lib/view/schedule/widgets/schedule_slot.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index a862394cb..efea349fc 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:uni/controller/networking/network_router.dart'; -import 'package:uni/view/common_widgets/row_container.dart'; import 'package:uni/controller/networking/url_launcher.dart'; +import 'package:uni/view/common_widgets/row_container.dart'; class ScheduleSlot extends StatelessWidget { const ScheduleSlot({ From 633f18293f3cfa54d8861838b705ffa5393b49b5 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Mon, 8 Jan 2024 14:42:17 +0000 Subject: [PATCH 104/334] Typo and synchronous getter --- uni/lib/controller/cleanup.dart | 6 +++--- .../controller/local_storage/preferences_controller.dart | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/uni/lib/controller/cleanup.dart b/uni/lib/controller/cleanup.dart index 2e595b079..8b8c519db 100644 --- a/uni/lib/controller/cleanup.dart +++ b/uni/lib/controller/cleanup.dart @@ -43,7 +43,7 @@ Future cleanupStoredData(BuildContext context) async { } Future cleanupCachedFiles() async { - final lastCleanupDate = await PreferencesController.getLastCleanUpDate(); + final lastCleanupDate = PreferencesController.getLastCleanUpDate(); final daysSinceLastCleanup = DateTime.now().difference(lastCleanupDate).inDays; @@ -52,7 +52,7 @@ Future cleanupCachedFiles() async { } final toCleanDirectory = await getApplicationDocumentsDirectory(); - final treshold = DateTime.now().subtract(const Duration(days: 30)); + final threshold = DateTime.now().subtract(const Duration(days: 30)); final directories = toCleanDirectory.listSync(followLinks: false); for (final directory in directories) { @@ -62,7 +62,7 @@ Future cleanupCachedFiles() async { final oldFiles = files.where((file) { try { final fileDate = File(file.path).lastModifiedSync(); - return fileDate.isBefore(treshold); + return fileDate.isBefore(threshold); } catch (e) { return false; } diff --git a/uni/lib/controller/local_storage/preferences_controller.dart b/uni/lib/controller/local_storage/preferences_controller.dart index f0448a22a..8a37f12b6 100644 --- a/uni/lib/controller/local_storage/preferences_controller.dart +++ b/uni/lib/controller/local_storage/preferences_controller.dart @@ -141,12 +141,10 @@ class PreferencesController { } static Future setLastCleanUpDate(DateTime date) async { - final prefs = await SharedPreferences.getInstance(); await prefs.setString(_lastCacheCleanUpDate, date.toString()); } - static Future getLastCleanUpDate() async { - final prefs = await SharedPreferences.getInstance(); + static DateTime getLastCleanUpDate() { final date = prefs.getString(_lastCacheCleanUpDate) ?? DateTime.now().toString(); return DateTime.parse(date); From 72b2c2874033c48bcbf8d7914c9a98d2eded5f0c Mon Sep 17 00:00:00 2001 From: Clara Sousa Date: Tue, 9 Jan 2024 21:05:04 +0000 Subject: [PATCH 105/334] URI.parser(#644) --- uni/lib/controller/parsers/parser_course_units.dart | 6 ++---- uni/lib/controller/parsers/parser_courses.dart | 9 ++++----- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/uni/lib/controller/parsers/parser_course_units.dart b/uni/lib/controller/parsers/parser_course_units.dart index b7531a44b..97e2e8c61 100644 --- a/uni/lib/controller/parsers/parser_course_units.dart +++ b/uni/lib/controller/parsers/parser_course_units.dart @@ -3,8 +3,6 @@ import 'package:html/parser.dart'; import 'package:http/http.dart' as http; import 'package:uni/model/entities/course.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; -import 'package:uni/utils/url_parser.dart'; - List parseCourseUnitsAndCourseAverage( http.Response response, Course course, { @@ -48,9 +46,9 @@ List parseCourseUnitsAndCourseAverage( final year = row.children[0].innerHtml; final semester = row.children[1].innerHtml; - final occurId = getUrlQueryParameters( + final occurId = Uri.parse( row.children[2].firstChild!.attributes['href']!, - )['pv_ocorrencia_id']!; + ).queryParameters['pv_ocorrencia_id']!; final codeName = row.children[2].children[0].innerHtml; final name = row.children[3].children[0].innerHtml; final ects = row.children[5].innerHtml.replaceAll(',', '.'); diff --git a/uni/lib/controller/parsers/parser_courses.dart b/uni/lib/controller/parsers/parser_courses.dart index 10b1e6d97..8e84304e3 100644 --- a/uni/lib/controller/parsers/parser_courses.dart +++ b/uni/lib/controller/parsers/parser_courses.dart @@ -1,7 +1,6 @@ import 'package:html/parser.dart' show parse; import 'package:http/http.dart' as http; import 'package:uni/model/entities/course.dart'; -import 'package:uni/utils/url_parser.dart'; List parseMultipleCourses(List responses) { final courses = []; @@ -27,7 +26,7 @@ List _parseCourses(http.Response response) { final courseUrl = div .querySelector('.estudante-lista-curso-nome > a') ?.attributes['href']; - final courseId = getUrlQueryParameters(courseUrl ?? '')['pv_curso_id']; + final courseId = Uri.parse(courseUrl ?? '').queryParameters['pv_curso_id']; final courseState = div.querySelectorAll('.formulario td')[3].text; final courseFestId = div .querySelector('.estudante-lista-curso-detalhes > a') @@ -54,15 +53,15 @@ List _parseCourses(http.Response response) { final div = oldCourses[i]; final courseName = div.children[0].firstChild?.text?.trim(); final courseUrl = div.querySelector('a')?.attributes['href']; - final courseId = getUrlQueryParameters(courseUrl ?? '')['pv_curso_id']; + final courseId = Uri.parse(courseUrl ?? '').queryParameters['pv_curso_id']; var courseFirstEnrollment = div.children[4].text; courseFirstEnrollment = courseFirstEnrollment .substring(0, courseFirstEnrollment.indexOf('/')) .trim(); final courseState = div.children[5].text; - final courseFestId = getUrlQueryParameters( + final courseFestId = Uri.parse( div.children[6].firstChild?.attributes['href'] ?? '', - )['pv_fest_id']; + ).queryParameters['pv_fest_id']; courses.add( Course( firstEnrollment: int.parse(courseFirstEnrollment), From 98ba58bec2e1c1ab45d3a00621d5f3ab4066b397 Mon Sep 17 00:00:00 2001 From: Clara Sousa Date: Tue, 9 Jan 2024 22:23:08 +0000 Subject: [PATCH 106/334] . --- uni/lib/controller/parsers/parser_course_units.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/uni/lib/controller/parsers/parser_course_units.dart b/uni/lib/controller/parsers/parser_course_units.dart index 97e2e8c61..6843722b9 100644 --- a/uni/lib/controller/parsers/parser_course_units.dart +++ b/uni/lib/controller/parsers/parser_course_units.dart @@ -3,6 +3,7 @@ import 'package:html/parser.dart'; import 'package:http/http.dart' as http; import 'package:uni/model/entities/course.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; + List parseCourseUnitsAndCourseAverage( http.Response response, Course course, { From 5bfdb9383b28d0791f0f5619a96d33f5604472ca Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 11 Jan 2024 17:39:25 +0000 Subject: [PATCH 107/334] Implementing faculty aggregate page --- uni/lib/main.dart | 6 +- uni/lib/utils/drawer_items.dart | 2 +- uni/lib/view/faculty/faculty.dart | 79 +++++++++++++++++++ .../widgets/academic_services_card.dart | 2 +- .../widgets/copy_center_card.dart | 2 +- .../widgets/dona_bia_card.dart | 2 +- .../widgets/infodesk_card.dart | 2 +- .../widgets/link_button.dart | 0 .../widgets/multimedia_center_card.dart | 2 +- .../widgets/other_links_card.dart | 2 +- .../widgets/sigarra_links_card.dart | 2 +- .../widgets/text_components.dart | 0 uni/lib/view/useful_info/useful_info.dart | 50 ------------ 13 files changed, 90 insertions(+), 61 deletions(-) create mode 100644 uni/lib/view/faculty/faculty.dart rename uni/lib/view/{useful_info => faculty}/widgets/academic_services_card.dart (93%) rename uni/lib/view/{useful_info => faculty}/widgets/copy_center_card.dart (94%) rename uni/lib/view/{useful_info => faculty}/widgets/dona_bia_card.dart (93%) rename uni/lib/view/{useful_info => faculty}/widgets/infodesk_card.dart (93%) rename uni/lib/view/{useful_info => faculty}/widgets/link_button.dart (100%) rename uni/lib/view/{useful_info => faculty}/widgets/multimedia_center_card.dart (93%) rename uni/lib/view/{useful_info => faculty}/widgets/other_links_card.dart (93%) rename uni/lib/view/{useful_info => faculty}/widgets/sigarra_links_card.dart (95%) rename uni/lib/view/{useful_info => faculty}/widgets/text_components.dart (100%) delete mode 100644 uni/lib/view/useful_info/useful_info.dart diff --git a/uni/lib/main.dart b/uni/lib/main.dart index a9f562604..058750ac3 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -31,6 +31,7 @@ import 'package:uni/view/calendar/calendar.dart'; import 'package:uni/view/common_widgets/page_transition.dart'; import 'package:uni/view/course_units/course_units.dart'; import 'package:uni/view/exams/exams.dart'; +import 'package:uni/view/faculty/faculty.dart'; import 'package:uni/view/home/home.dart'; import 'package:uni/view/library/library.dart'; import 'package:uni/view/locale_notifier.dart'; @@ -40,7 +41,6 @@ import 'package:uni/view/restaurant/restaurant_page_view.dart'; import 'package:uni/view/schedule/schedule.dart'; import 'package:uni/view/theme.dart'; import 'package:uni/view/theme_notifier.dart'; -import 'package:uni/view/useful_info/useful_info.dart'; import 'package:workmanager/workmanager.dart'; SentryEvent? beforeSend(SentryEvent event) { @@ -237,9 +237,9 @@ class ApplicationState extends State { page: const LibraryPage(), settings: settings, ), - '/${DrawerItem.navUsefulInfo.title}': + '/${DrawerItem.navFaculty.title}': PageTransition.makePageTransition( - page: const UsefulInfoPageView(), + page: const FacultyPageView(), settings: settings, ), }; diff --git a/uni/lib/utils/drawer_items.dart b/uni/lib/utils/drawer_items.dart index 09d1e6f7d..6172e1316 100644 --- a/uni/lib/utils/drawer_items.dart +++ b/uni/lib/utils/drawer_items.dart @@ -8,7 +8,7 @@ enum DrawerItem { navRestaurants('restaurantes'), navCalendar('calendario'), navLibrary('biblioteca', faculties: {'feup'}), - navUsefulInfo('uteis', faculties: {'feup'}); + navFaculty('faculdade'); const DrawerItem(this.title, {this.faculties}); diff --git a/uni/lib/view/faculty/faculty.dart b/uni/lib/view/faculty/faculty.dart new file mode 100644 index 000000000..443415511 --- /dev/null +++ b/uni/lib/view/faculty/faculty.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/model/entities/location_group.dart'; +import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; +import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; +import 'package:uni/view/common_widgets/page_title.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/faculty/widgets/academic_services_card.dart'; +import 'package:uni/view/faculty/widgets/copy_center_card.dart'; +import 'package:uni/view/faculty/widgets/dona_bia_card.dart'; +import 'package:uni/view/faculty/widgets/infodesk_card.dart'; +import 'package:uni/view/faculty/widgets/multimedia_center_card.dart'; +import 'package:uni/view/faculty/widgets/other_links_card.dart'; +import 'package:uni/view/faculty/widgets/sigarra_links_card.dart'; +import 'package:uni/view/lazy_consumer.dart'; +import 'package:uni/view/library/widgets/library_occupation_card.dart'; +import 'package:uni/view/locations/widgets/faculty_map.dart'; + +class FacultyPageView extends StatefulWidget { + const FacultyPageView({super.key}); + + @override + State createState() => FacultyPageViewState(); +} + +class FacultyPageViewState extends GeneralPageViewState { + @override + Widget getBody(BuildContext context) { + return Column( + children: [ + const PageTitle(name: 'Faculty'), + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + LazyConsumer>( + builder: buildMapView, + hasContent: (locations) => locations.isNotEmpty, + onNullContent: const Center(child: Text('Erro')), + ), + LibraryOccupationCard(), + getUtilsSection(), + ], + ), + ), + ), + ], + ); + } + + Widget getUtilsSection() { + return const Column( + children: [ + AcademicServicesCard(), + InfoDeskCard(), + DonaBiaCard(), + CopyCenterCard(), + MultimediaCenterCard(), + SigarraLinksCard(), + OtherLinksCard(), + ], + ); + } + + Widget buildMapView(BuildContext context, List locations) { + return Container( + padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), + height: MediaQuery.of(context).size.height * 0.35, + alignment: Alignment.center, + child: FacultyMap(faculty: 'FEUP', locations: locations), + ); +} + + @override + Future onRefresh(BuildContext context) async { + return Provider.of(context, listen: false) + .forceRefresh(context); + } +} diff --git a/uni/lib/view/useful_info/widgets/academic_services_card.dart b/uni/lib/view/faculty/widgets/academic_services_card.dart similarity index 93% rename from uni/lib/view/useful_info/widgets/academic_services_card.dart rename to uni/lib/view/faculty/widgets/academic_services_card.dart index 212b670e3..be0df0857 100644 --- a/uni/lib/view/useful_info/widgets/academic_services_card.dart +++ b/uni/lib/view/faculty/widgets/academic_services_card.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; -import 'package:uni/view/useful_info/widgets/text_components.dart'; +import 'package:uni/view/faculty/widgets/text_components.dart'; class AcademicServicesCard extends GenericExpansionCard { const AcademicServicesCard({super.key}); diff --git a/uni/lib/view/useful_info/widgets/copy_center_card.dart b/uni/lib/view/faculty/widgets/copy_center_card.dart similarity index 94% rename from uni/lib/view/useful_info/widgets/copy_center_card.dart rename to uni/lib/view/faculty/widgets/copy_center_card.dart index 055610a55..69a803872 100644 --- a/uni/lib/view/useful_info/widgets/copy_center_card.dart +++ b/uni/lib/view/faculty/widgets/copy_center_card.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; -import 'package:uni/view/useful_info/widgets/text_components.dart'; +import 'package:uni/view/faculty/widgets/text_components.dart'; class CopyCenterCard extends GenericExpansionCard { const CopyCenterCard({super.key}); diff --git a/uni/lib/view/useful_info/widgets/dona_bia_card.dart b/uni/lib/view/faculty/widgets/dona_bia_card.dart similarity index 93% rename from uni/lib/view/useful_info/widgets/dona_bia_card.dart rename to uni/lib/view/faculty/widgets/dona_bia_card.dart index e08b30307..061232f8e 100644 --- a/uni/lib/view/useful_info/widgets/dona_bia_card.dart +++ b/uni/lib/view/faculty/widgets/dona_bia_card.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; -import 'package:uni/view/useful_info/widgets/text_components.dart'; +import 'package:uni/view/faculty/widgets/text_components.dart'; class DonaBiaCard extends GenericExpansionCard { const DonaBiaCard({super.key}); diff --git a/uni/lib/view/useful_info/widgets/infodesk_card.dart b/uni/lib/view/faculty/widgets/infodesk_card.dart similarity index 93% rename from uni/lib/view/useful_info/widgets/infodesk_card.dart rename to uni/lib/view/faculty/widgets/infodesk_card.dart index ddbed1d13..968f1bfd0 100644 --- a/uni/lib/view/useful_info/widgets/infodesk_card.dart +++ b/uni/lib/view/faculty/widgets/infodesk_card.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; -import 'package:uni/view/useful_info/widgets/text_components.dart'; +import 'package:uni/view/faculty/widgets/text_components.dart'; class InfoDeskCard extends GenericExpansionCard { const InfoDeskCard({super.key}); diff --git a/uni/lib/view/useful_info/widgets/link_button.dart b/uni/lib/view/faculty/widgets/link_button.dart similarity index 100% rename from uni/lib/view/useful_info/widgets/link_button.dart rename to uni/lib/view/faculty/widgets/link_button.dart diff --git a/uni/lib/view/useful_info/widgets/multimedia_center_card.dart b/uni/lib/view/faculty/widgets/multimedia_center_card.dart similarity index 93% rename from uni/lib/view/useful_info/widgets/multimedia_center_card.dart rename to uni/lib/view/faculty/widgets/multimedia_center_card.dart index 46bda1568..c71cb328d 100644 --- a/uni/lib/view/useful_info/widgets/multimedia_center_card.dart +++ b/uni/lib/view/faculty/widgets/multimedia_center_card.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; -import 'package:uni/view/useful_info/widgets/text_components.dart'; +import 'package:uni/view/faculty/widgets/text_components.dart'; class MultimediaCenterCard extends GenericExpansionCard { const MultimediaCenterCard({super.key}); diff --git a/uni/lib/view/useful_info/widgets/other_links_card.dart b/uni/lib/view/faculty/widgets/other_links_card.dart similarity index 93% rename from uni/lib/view/useful_info/widgets/other_links_card.dart rename to uni/lib/view/faculty/widgets/other_links_card.dart index 47ae82046..d4960e737 100644 --- a/uni/lib/view/useful_info/widgets/other_links_card.dart +++ b/uni/lib/view/faculty/widgets/other_links_card.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; -import 'package:uni/view/useful_info/widgets/link_button.dart'; +import 'package:uni/view/faculty/widgets/link_button.dart'; /// Manages the 'Current account' section inside the user's page (accessible /// through the top-right widget with the user picture) diff --git a/uni/lib/view/useful_info/widgets/sigarra_links_card.dart b/uni/lib/view/faculty/widgets/sigarra_links_card.dart similarity index 95% rename from uni/lib/view/useful_info/widgets/sigarra_links_card.dart rename to uni/lib/view/faculty/widgets/sigarra_links_card.dart index 91ed49da0..32c4fd76b 100644 --- a/uni/lib/view/useful_info/widgets/sigarra_links_card.dart +++ b/uni/lib/view/faculty/widgets/sigarra_links_card.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; -import 'package:uni/view/useful_info/widgets/link_button.dart'; +import 'package:uni/view/faculty/widgets/link_button.dart'; /// Manages the 'Current account' section inside the user's page (accessible /// through the top-right widget with the user picture) diff --git a/uni/lib/view/useful_info/widgets/text_components.dart b/uni/lib/view/faculty/widgets/text_components.dart similarity index 100% rename from uni/lib/view/useful_info/widgets/text_components.dart rename to uni/lib/view/faculty/widgets/text_components.dart diff --git a/uni/lib/view/useful_info/useful_info.dart b/uni/lib/view/useful_info/useful_info.dart deleted file mode 100644 index 890b54cb8..000000000 --- a/uni/lib/view/useful_info/useful_info.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; -import 'package:uni/view/useful_info/widgets/academic_services_card.dart'; -import 'package:uni/view/useful_info/widgets/copy_center_card.dart'; -import 'package:uni/view/useful_info/widgets/dona_bia_card.dart'; -import 'package:uni/view/useful_info/widgets/infodesk_card.dart'; -import 'package:uni/view/useful_info/widgets/multimedia_center_card.dart'; -import 'package:uni/view/useful_info/widgets/other_links_card.dart'; -import 'package:uni/view/useful_info/widgets/sigarra_links_card.dart'; - -class UsefulInfoPageView extends StatefulWidget { - const UsefulInfoPageView({super.key}); - - @override - State createState() => UsefulInfoPageViewState(); -} - -/// Manages the 'Useful Info' section of the app. -class UsefulInfoPageViewState extends GeneralPageViewState { - @override - Widget getBody(BuildContext context) { - return ListView( - children: [ - _getPageTitle(), - const AcademicServicesCard(), - const InfoDeskCard(), - const DonaBiaCard(), - const CopyCenterCard(), - const MultimediaCenterCard(), - const SigarraLinksCard(), - const OtherLinksCard(), - ], - ); - } - - Container _getPageTitle() { - return Container( - padding: const EdgeInsets.only(bottom: 6), - child: PageTitle( - name: S.of(context).nav_title(DrawerItem.navUsefulInfo.title), - ), - ); - } - - @override - Future onRefresh(BuildContext context) async {} -} From fb7e6b97634d2ed628a5d6d00b5a710efbeeb213 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 11 Jan 2024 19:48:16 +0000 Subject: [PATCH 108/334] DateTime extension --- uni/lib/utils/date_time_formatter.dart | 24 ++++++++++++++++++++++++ uni/lib/view/home/widgets/exam_card.dart | 9 ++++----- 2 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 uni/lib/utils/date_time_formatter.dart diff --git a/uni/lib/utils/date_time_formatter.dart b/uni/lib/utils/date_time_formatter.dart new file mode 100644 index 000000000..64be5025b --- /dev/null +++ b/uni/lib/utils/date_time_formatter.dart @@ -0,0 +1,24 @@ +import 'package:intl/intl.dart'; +import 'package:uni/model/entities/app_locale.dart'; + +extension DateTimeExtensions on DateTime { + String weekDay(AppLocale locale) { + return DateFormat.EEEE(locale.localeCode.languageCode) + .dateSymbols + .WEEKDAYS[weekday % 7]; + } + + String month(AppLocale locale) { + return DateFormat.EEEE(locale.localeCode.languageCode) + .dateSymbols + .MONTHS[this.month - 1]; + } + + String formattedDate(AppLocale locale) { + if (locale == AppLocale.pt) { + return '${weekDay(locale)}, $day de ${month(locale)}'; + } else { + return '${weekDay(locale)}, $day ${month(locale)}'; + } + } +} diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index 4e349807d..55930d70a 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -5,6 +5,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/app_locale.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; +import 'package:uni/utils/date_time_formatter.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/date_rectangle.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; @@ -117,13 +118,11 @@ class ExamCard extends GenericCard { children: [ if (locale == AppLocale.pt) ...[ DateRectangle( - date: '''${exam.weekDay(locale)}, ''' - '''${exam.begin.day} de ${exam.month(locale)}''', + date: exam.begin.formattedDate(locale), ), ] else ...[ DateRectangle( - date: '''${exam.weekDay(locale)}, ''' - '''${exam.begin.day} ${exam.month(locale)}''', + date: exam.begin.formattedDate(locale), ), ], RowContainer( @@ -152,7 +151,7 @@ class ExamCard extends GenericCard { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '${exam.begin.day} de ${exam.month(locale)}', + exam.begin.formattedDate(locale), style: Theme.of(context).textTheme.bodyLarge, ), ExamTitle( From 18d807139130c1099341e61dc312bcec77f501ae Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 12 Jan 2024 13:53:42 +0000 Subject: [PATCH 109/334] Added auxiliary method. Made some adjustments to the code --- uni/lib/view/home/widgets/exam_card.dart | 114 +++++++++++------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index aed4346e8..43afcaac1 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -20,6 +20,8 @@ class ExamCard extends GenericCard { super.onDelete, }) : super.fromEditingInformation(); + static const int maxExamsToDisplay = 4; + @override String getTitle(BuildContext context) => S.of(context).nav_title(DrawerItem.navExams.title); @@ -42,9 +44,56 @@ class ExamCard extends GenericCard { final allExams = filteredExams .where((exam) => !hiddenExams.contains(exam.id)) .toList(); + final nextExams = getPrimaryExams( + allExams, + allExams.isNotEmpty ? allExams.first : null, + ); + return RequestDependentWidgetBuilder( status: examProvider.status, - builder: () => generateExams(allExams, context), + builder: () { + if (nextExams.isEmpty) { + return Center( + child: Text( + S.of(context).no_selected_exams, + style: Theme.of(context).textTheme.titleLarge, + ), + ); + } + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + NextExamsWidget(exams: nextExams), + if (nextExams.length < maxExamsToDisplay) + Column( + children: [ + Container( + margin: const EdgeInsets.only( + right: 80, + left: 80, + top: 7, + bottom: 7, + ), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor, + ), + ), + ), + ), + RemainingExamsWidget( + exams: allExams + .where((exam) => !nextExams.contains(exam)) + .take(maxExamsToDisplay - nextExams.length) + .toList(), + ), + ], + ), + ], + ); + }, hasContentPredicate: allExams.isNotEmpty, onNullContent: Center( child: Text( @@ -58,70 +107,21 @@ class ExamCard extends GenericCard { ); } - Widget generateExams(List allExams, BuildContext context) { - final nextExams = getPrimaryExams( - allExams, - allExams.isNotEmpty ? allExams.first : null, - ); - final primaryExams = NextExamsWidget(exams: nextExams); - - final remainingExamsCount = 4 - nextExams.length; - final List remainingExams; - if (remainingExamsCount > 0) { - remainingExams = allExams - .where((exam) => !nextExams.contains(exam)) - .take(remainingExamsCount) - .toList(); - } else { - remainingExams = []; - } - final secondaryExams = RemainingExamsWidget(exams: remainingExams); - - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - primaryExams, - if (remainingExamsCount > 0) - Column( - children: [ - Container( - margin: const EdgeInsets.only( - right: 80, - left: 80, - top: 7, - bottom: 7, - ), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Theme.of(context).dividerColor, - ), - ), - ), - ), - secondaryExams, - ], - ), - ], - ); - } - List getPrimaryExams(List allExams, Exam? nextExam) { if (nextExam == null) { return []; } final sameDayExams = allExams.where((exam) { - final nextExamDate = DateTime( - nextExam.begin.year, - nextExam.begin.month, - nextExam.begin.day, - ); - final examDate = - DateTime(exam.begin.year, exam.begin.month, exam.begin.day); - return nextExamDate.isAtSameMomentAs(examDate); + return isSameDay(nextExam.begin, exam.begin); }).toList(); return sameDayExams; } + + bool isSameDay(DateTime? dateA, DateTime? dateB) { + return dateA?.year == dateB?.year && + dateA?.month == dateB?.month && + dateA?.day == dateB?.day; + } } From e9d9cec7c7827cdf3c15c55b8f03aae687c4596e Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 12 Jan 2024 15:21:58 +0000 Subject: [PATCH 110/334] Static map and card implementation --- uni/ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- uni/lib/view/faculty/faculty.dart | 20 +------ .../faculty/widgets/map_snapshot_card.dart | 58 +++++++++++++++++++ uni/lib/view/locations/locations.dart | 7 ++- .../view/locations/widgets/faculty_map.dart | 9 ++- uni/lib/view/locations/widgets/map.dart | 4 +- .../widgets/restaurant_page_card.dart | 4 +- 8 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 uni/lib/view/faculty/widgets/map_snapshot_card.dart diff --git a/uni/ios/Runner.xcodeproj/project.pbxproj b/uni/ios/Runner.xcodeproj/project.pbxproj index a63fc805e..889daa0d4 100644 --- a/uni/ios/Runner.xcodeproj/project.pbxproj +++ b/uni/ios/Runner.xcodeproj/project.pbxproj @@ -214,7 +214,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 378A17102ACF02E100B89C1C = { diff --git a/uni/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/uni/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index d01be74c1..28bc2d73f 100644 --- a/uni/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/uni/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ [ - LazyConsumer>( - builder: buildMapView, - hasContent: (locations) => locations.isNotEmpty, - onNullContent: const Center(child: Text('Erro')), - ), + MapCard(), LibraryOccupationCard(), getUtilsSection(), ], @@ -62,15 +55,6 @@ class FacultyPageViewState extends GeneralPageViewState { ); } - Widget buildMapView(BuildContext context, List locations) { - return Container( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), - height: MediaQuery.of(context).size.height * 0.35, - alignment: Alignment.center, - child: FacultyMap(faculty: 'FEUP', locations: locations), - ); -} - @override Future onRefresh(BuildContext context) async { return Provider.of(context, listen: false) diff --git a/uni/lib/view/faculty/widgets/map_snapshot_card.dart b/uni/lib/view/faculty/widgets/map_snapshot_card.dart new file mode 100644 index 000000000..d949abe9f --- /dev/null +++ b/uni/lib/view/faculty/widgets/map_snapshot_card.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/model/entities/location_group.dart'; +import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; +import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/common_widgets/generic_card.dart'; +import 'package:uni/view/lazy_consumer.dart'; +import 'package:uni/view/locations/widgets/faculty_map.dart'; + +class MapCard extends GenericCard { + MapCard({super.key}); + + const MapCard.fromEditingInformation( + super.key, { + required super.editingMode, + super.onDelete, + }) : super.fromEditingInformation(); + + @override + String getTitle(BuildContext context) => 'Locais: FEUP'; + + @override + Future onClick(BuildContext context) => + Navigator.pushNamed(context, '/${DrawerItem.navLocations.title}'); + + @override + Widget buildCardContent(BuildContext context) { + return LazyConsumer>( + builder: buildMapView, + hasContent: (locations) => locations.isNotEmpty, + onNullContent: const Center(child: Text('Erro')), + ); + } + + @override + void onRefresh(BuildContext context) { + Provider.of(context, listen: false) + .forceRefresh(context); + } + + Widget buildMapView(BuildContext context, List locations) { + return GestureDetector( + onTapDown: (TapDownDetails details) => + Navigator.pushNamed(context, '/${DrawerItem.navLocations.title}'), + child: Container( + padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), + height: MediaQuery.of(context).size.height * 0.3, + alignment: Alignment.center, + child: FacultyMap( + faculty: 'FEUP', + locations: locations, + interactiveFlags: InteractiveFlag.none, + ), + ), + ); + } +} diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index 4907b1500..5aea7b639 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/location_group.dart'; import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; @@ -36,7 +37,11 @@ class LocationsPageState extends GeneralPageViewState padding: const EdgeInsets.fromLTRB(10, 0, 10, 0), height: MediaQuery.of(context).size.height * 0.75, alignment: Alignment.center, - child: FacultyMap(faculty: 'FEUP', locations: locations), + child: FacultyMap( + faculty: 'FEUP', + locations: locations, + interactiveFlags: InteractiveFlag.all - InteractiveFlag.rotate, + ), ), hasContent: (locations) => locations.isNotEmpty, onNullContent: Center(child: Text(S.of(context).no_places_info)), diff --git a/uni/lib/view/locations/widgets/faculty_map.dart b/uni/lib/view/locations/widgets/faculty_map.dart index 1e2354e54..34b9af81c 100644 --- a/uni/lib/view/locations/widgets/faculty_map.dart +++ b/uni/lib/view/locations/widgets/faculty_map.dart @@ -4,9 +4,15 @@ import 'package:uni/model/entities/location_group.dart'; import 'package:uni/view/locations/widgets/map.dart'; class FacultyMap extends StatelessWidget { - const FacultyMap({required this.faculty, required this.locations, super.key}); + const FacultyMap({ + required this.faculty, + required this.locations, + required this.interactiveFlags, + super.key, + }); final String faculty; final List locations; + final int interactiveFlags; @override Widget build(BuildContext context) { @@ -17,6 +23,7 @@ class FacultyMap extends StatelessWidget { southWestBoundary: const LatLng(41.17670, -8.59991), center: const LatLng(41.17731, -8.59522), locations: locations, + interactiveFlags: interactiveFlags, ); default: return Container(); // Should not happen diff --git a/uni/lib/view/locations/widgets/map.dart b/uni/lib/view/locations/widgets/map.dart index c03b8b42c..6cab6a35f 100644 --- a/uni/lib/view/locations/widgets/map.dart +++ b/uni/lib/view/locations/widgets/map.dart @@ -15,6 +15,7 @@ class LocationsMap extends StatelessWidget { required this.southWestBoundary, required this.center, required this.locations, + required this.interactiveFlags, super.key, }); @@ -23,6 +24,7 @@ class LocationsMap extends StatelessWidget { final LatLng northEastBoundary; final LatLng southWestBoundary; final LatLng center; + final int interactiveFlags; @override Widget build(BuildContext context) { @@ -34,7 +36,7 @@ class LocationsMap extends StatelessWidget { swPanBoundary: southWestBoundary, center: center, zoom: 17.5, - interactiveFlags: InteractiveFlag.all - InteractiveFlag.rotate, + interactiveFlags: interactiveFlags, onTap: (tapPosition, latlng) => _popupLayerController.hideAllPopups(), ), nonRotatedChildren: [ diff --git a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart index 35dfdcfe5..6e29614a4 100644 --- a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart +++ b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart @@ -45,7 +45,7 @@ class CardFavoriteButton extends StatelessWidget { .contains(restaurant.name); return IconButton( icon: isFavorite ? Icon(MdiIcons.heart) : Icon(MdiIcons.heartOutline), - onPressed: () async { + onPressed: () { final favoriteRestaurants = PreferencesController.getFavoriteRestaurants(); if (favoriteRestaurants.contains(restaurant.name)) { @@ -53,7 +53,7 @@ class CardFavoriteButton extends StatelessWidget { } else { favoriteRestaurants.add(restaurant.name); } - await PreferencesController.saveFavoriteRestaurants( + PreferencesController.saveFavoriteRestaurants( favoriteRestaurants, ); From 9bbf5d4fbf0bfd1a03cd0042acdbdad981355707 Mon Sep 17 00:00:00 2001 From: Clara Sousa Date: Thu, 18 Jan 2024 17:13:08 +0000 Subject: [PATCH 111/334] deleted url_parser.dart --- uni/lib/utils/url_parser.dart | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 uni/lib/utils/url_parser.dart diff --git a/uni/lib/utils/url_parser.dart b/uni/lib/utils/url_parser.dart deleted file mode 100644 index 79d13c6ec..000000000 --- a/uni/lib/utils/url_parser.dart +++ /dev/null @@ -1,26 +0,0 @@ -Map getUrlQueryParameters(String url) { - final queryParameters = {}; - var queryString = url; - - final lastSlashIndex = url.lastIndexOf('/'); - if (lastSlashIndex >= 0) { - queryString = url.substring(lastSlashIndex + 1); - } - - final queryStartIndex = queryString.lastIndexOf('?'); - if (queryStartIndex < 0) { - return {}; - } - queryString = queryString.substring(queryStartIndex + 1); - - final params = queryString.split('&'); - for (final param in params) { - final keyValue = param.split('='); - if (keyValue.length != 2) { - continue; - } - queryParameters[keyValue[0].trim()] = keyValue[1].trim(); - } - - return queryParameters; -} From e000656a507daf66740498a001e8745c5d8caeec Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 19 Jan 2024 14:21:12 +0000 Subject: [PATCH 112/334] Translating --- uni/ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- uni/lib/generated/intl/messages_en.dart | 8 ++++---- uni/lib/generated/intl/messages_pt_PT.dart | 8 ++++---- uni/lib/generated/l10n.dart | 4 ++-- uni/lib/l10n/intl_en.arb | 2 +- uni/lib/l10n/intl_pt_PT.arb | 2 +- uni/lib/view/faculty/faculty.dart | 4 +++- uni/lib/view/faculty/widgets/map_snapshot_card.dart | 4 +++- 9 files changed, 20 insertions(+), 16 deletions(-) diff --git a/uni/ios/Runner.xcodeproj/project.pbxproj b/uni/ios/Runner.xcodeproj/project.pbxproj index 889daa0d4..a63fc805e 100644 --- a/uni/ios/Runner.xcodeproj/project.pbxproj +++ b/uni/ios/Runner.xcodeproj/project.pbxproj @@ -214,7 +214,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 378A17102ACF02E100B89C1C = { diff --git a/uni/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/uni/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 28bc2d73f..d01be74c1 100644 --- a/uni/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/uni/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ 'Locais: FEUP'; + String getTitle(BuildContext context) => + '${S.of(context).nav_title(DrawerItem.navLocations.title)}: FEUP'; @override Future onClick(BuildContext context) => From cb6bceccd1170a53d07b01309d0ac781fe7d2dd6 Mon Sep 17 00:00:00 2001 From: limwa Date: Fri, 19 Jan 2024 14:28:01 +0000 Subject: [PATCH 113/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index f5d0c7b2c..59dd2f0fd 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.32+222 \ No newline at end of file +1.7.33+223 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 02dc1a37e..af3d35d14 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.32+222 +version: 1.7.33+223 environment: sdk: ">=3.0.0 <4.0.0" From 10198154f968406a36c79aa0ab19aa4b3063e1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Wed, 22 Nov 2023 17:04:18 +0000 Subject: [PATCH 114/334] feat: initial plausible setup --- uni/lib/main.dart | 33 +++++++++++++++++++++++++++++++-- uni/pubspec.lock | 16 ++++++++++++++++ uni/pubspec.yaml | 2 ++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/uni/lib/main.dart b/uni/lib/main.dart index a9f562604..cb93ac304 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -6,6 +6,8 @@ import 'package:flutter/services.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:logger/logger.dart'; +import 'package:plausible_analytics/navigator_observer.dart'; +import 'package:plausible_analytics/plausible_analytics.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -95,8 +97,20 @@ Future main() async { ); }); + final plausibleUrl = dotenv.env['PLAUSIBLE_URL']; + final plausibleDomain = dotenv.env['PLAUSIBLE_DOMAIN']; + + final plausible = plausibleUrl != null && plausibleDomain != null + ? Plausible(plausibleUrl, plausibleDomain) + : null; + + if (plausible == null) { + Logger().w('Plausible is not enabled'); + } + final savedTheme = PreferencesController.getThemeMode(); final savedLocale = PreferencesController.getLocale(); + final route = await firstRoute(); await SentryFlutter.init( @@ -148,7 +162,7 @@ Future main() async { create: (_) => ThemeNotifier(savedTheme), ), ], - child: Application(route), + child: Application(route, plausible: plausible), ), ); }, @@ -159,9 +173,10 @@ Future main() async { /// This class is necessary to track the app's state for /// the current execution. class Application extends StatefulWidget { - const Application(this.initialWidget, {super.key}); + const Application(this.initialWidget, {this.plausible, super.key}); final Widget initialWidget; + final Plausible? plausible; static GlobalKey navigatorKey = GlobalKey(); @@ -171,6 +186,19 @@ class Application extends StatefulWidget { /// Manages the app depending on its current state class ApplicationState extends State { + + final navigatorObservers = []; + + @override + void initState() { + super.initState(); + + final plausible = widget.plausible; + if (plausible != null) { + navigatorObservers.add(PlausibleNavigatorObserver(plausible)); + } + } + @override Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([ @@ -192,6 +220,7 @@ class ApplicationState extends State { ], supportedLocales: S.delegate.supportedLocales, home: widget.initialWidget, + navigatorObservers: navigatorObservers, onGenerateRoute: (RouteSettings settings) { final transitions = { '/${DrawerItem.navPersonalArea.title}': diff --git a/uni/pubspec.lock b/uni/pubspec.lock index e1fba8fa4..27e3b1795 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -789,6 +789,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + plausible_analytics: + dependency: "direct main" + description: + name: plausible_analytics + sha256: be9f0b467d23cd94861737f10101431ad8b7d280dc0c14f7251e0e24655b07fa + url: "https://pub.dev" + source: hosted + version: "0.3.0" plugin_platform_interface: dependency: transitive description: @@ -1162,6 +1170,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + universal_io: + dependency: transitive + description: + name: universal_io + sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" + url: "https://pub.dev" + source: hosted + version: "2.2.2" url_launcher: dependency: "direct main" description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index af3d35d14..bb02ece86 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -48,6 +48,7 @@ dependencies: provider: ^6.1.1 sentry_flutter: ^7.14.0 shared_preferences: ^2.2.2 + plausible_analytics: ^0.3.0 shimmer: ^3.0.0 sqflite: ^2.0.3 synchronized: ^3.0.0 @@ -71,6 +72,7 @@ flutter: uses-material-design: true assets: - assets/ + - assets/env/ - assets/images/ - assets/text/ - assets/text/locations/ From 1462a1a95375b2bc1899f3d5631b6af2fc9946d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 23 Nov 2023 18:50:55 +0000 Subject: [PATCH 115/334] style: format code --- uni/lib/main.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/uni/lib/main.dart b/uni/lib/main.dart index cb93ac304..fca2cd1b9 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -101,8 +101,8 @@ Future main() async { final plausibleDomain = dotenv.env['PLAUSIBLE_DOMAIN']; final plausible = plausibleUrl != null && plausibleDomain != null - ? Plausible(plausibleUrl, plausibleDomain) - : null; + ? Plausible(plausibleUrl, plausibleDomain) + : null; if (plausible == null) { Logger().w('Plausible is not enabled'); @@ -186,7 +186,6 @@ class Application extends StatefulWidget { /// Manages the app depending on its current state class ApplicationState extends State { - final navigatorObservers = []; @override From 79724ecd732975cca1981410f45094af916b2370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 23 Nov 2023 19:00:26 +0000 Subject: [PATCH 116/334] chore: add env directory to assets --- uni/assets/env/.gitkeep | 0 uni/pubspec.yaml | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 uni/assets/env/.gitkeep diff --git a/uni/assets/env/.gitkeep b/uni/assets/env/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index bb02ece86..b5d569fa6 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -72,7 +72,7 @@ flutter: uses-material-design: true assets: - assets/ - - assets/env/ + - assets/env/.env - assets/images/ - assets/text/ - assets/text/locations/ From aecd053ca5afd4ee971398585658eae18f3558ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Mon, 8 Jan 2024 02:45:49 +0000 Subject: [PATCH 117/334] feat: add battery and connectivity checks to analytics --- uni/lib/main.dart | 48 ++++++++++++++--- uni/pubspec.lock | 58 +++++++++++++++------ uni/pubspec.yaml | 1 + uni/windows/flutter/generated_plugins.cmake | 1 + 4 files changed, 84 insertions(+), 24 deletions(-) diff --git a/uni/lib/main.dart b/uni/lib/main.dart index fca2cd1b9..849240dc2 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'package:battery_plus/battery_plus.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -60,6 +62,44 @@ Future firstRoute() async { return const LoginPageView(); } +Future createPlausible() async { + final plausibleUrl = dotenv.env['PLAUSIBLE_URL']; + final plausibleDomain = dotenv.env['PLAUSIBLE_DOMAIN']; + + if (plausibleUrl == null || plausibleDomain == null) { + return null; + } + + final plausible = Plausible(plausibleUrl, plausibleDomain); + + final connectivity = Connectivity(); + var currentConnectivity = await connectivity.checkConnectivity(); + + Future isBatteryGoodForAnalytics() async { + final battery = Battery(); + final batteryLevel = await battery.batteryLevel; + final isInBatterySaveMode = await battery.isInBatterySaveMode; + return batteryLevel > 20 && !isInBatterySaveMode; + } + + Future updateAnalyticsState() async { + plausible.enabled = await isBatteryGoodForAnalytics() && + currentConnectivity == ConnectivityResult.wifi; + } + + connectivity.onConnectivityChanged.listen((result) async { + currentConnectivity = result; + await updateAnalyticsState(); + }); + + Stream.periodic(const Duration(minutes: 1)).listen((event) async { + await updateAnalyticsState(); + }); + + await updateAnalyticsState(); + return plausible; +} + Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -97,13 +137,7 @@ Future main() async { ); }); - final plausibleUrl = dotenv.env['PLAUSIBLE_URL']; - final plausibleDomain = dotenv.env['PLAUSIBLE_DOMAIN']; - - final plausible = plausibleUrl != null && plausibleDomain != null - ? Plausible(plausibleUrl, plausibleDomain) - : null; - + final plausible = createPlausible(); if (plausible == null) { Logger().w('Plausible is not enabled'); } diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 27e3b1795..37b839cd1 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -65,6 +65,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + battery_plus: + dependency: "direct main" + description: + name: battery_plus + sha256: "0568fbba70697b8d0c34c1176faa2bc6d61c7fb211a2d2d64e493b91ff72d3f8" + url: "https://pub.dev" + source: hosted + version: "5.0.2" + battery_plus_platform_interface: + dependency: transitive + description: + name: battery_plus_platform_interface + sha256: "942707f90e2f7481dcb178df02e22a9c6971b3562b848d6a1b8c7cff9f1a1fec" + url: "https://pub.dev" + source: hosted + version: "2.0.0" boolean_selector: dependency: transitive description: @@ -205,10 +221,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: @@ -617,10 +633,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -1054,18 +1070,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1102,26 +1118,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.24.3" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "0.5.9" timelines: dependency: "direct main" description: @@ -1178,6 +1194,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.2" + upower: + dependency: transitive + description: + name: upower + sha256: cf042403154751180affa1d15614db7fa50234bc2373cd21c3db666c38543ebf + url: "https://pub.dev" + source: hosted + version: "0.7.0" url_launcher: dependency: "direct main" description: @@ -1310,10 +1334,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1379,5 +1403,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.3 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.13.7" diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index b5d569fa6..3c3da39b3 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -56,6 +56,7 @@ dependencies: tuple: ^2.0.0 url_launcher: ^6.2.2 workmanager: ^0.5.2 + battery_plus: ^5.0.2 dev_dependencies: build_runner: ^2.4.7 diff --git a/uni/windows/flutter/generated_plugins.cmake b/uni/windows/flutter/generated_plugins.cmake index a7e9181a9..353256099 100644 --- a/uni/windows/flutter/generated_plugins.cmake +++ b/uni/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + battery_plus connectivity_plus sentry_flutter url_launcher_windows From defc0c3114744327920656f7f790c8f78c8eae91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 12 Jan 2024 18:09:33 +0000 Subject: [PATCH 118/334] feat: use pointer for analytics battery refreshes --- uni/lib/main.dart | 143 +++++++----------- .../plausible/plausible_provider.dart | 99 ++++++++++++ 2 files changed, 155 insertions(+), 87 deletions(-) create mode 100644 uni/lib/model/providers/plausible/plausible_provider.dart diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 849240dc2..1dcbdcdc6 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -1,7 +1,5 @@ import 'dart:async'; -import 'package:battery_plus/battery_plus.dart'; -import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -26,6 +24,7 @@ import 'package:uni/model/providers/lazy/lecture_provider.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; import 'package:uni/model/providers/lazy/reference_provider.dart'; import 'package:uni/model/providers/lazy/restaurant_provider.dart'; +import 'package:uni/model/providers/plausible/plausible_provider.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/model/providers/state_providers.dart'; @@ -62,44 +61,6 @@ Future firstRoute() async { return const LoginPageView(); } -Future createPlausible() async { - final plausibleUrl = dotenv.env['PLAUSIBLE_URL']; - final plausibleDomain = dotenv.env['PLAUSIBLE_DOMAIN']; - - if (plausibleUrl == null || plausibleDomain == null) { - return null; - } - - final plausible = Plausible(plausibleUrl, plausibleDomain); - - final connectivity = Connectivity(); - var currentConnectivity = await connectivity.checkConnectivity(); - - Future isBatteryGoodForAnalytics() async { - final battery = Battery(); - final batteryLevel = await battery.batteryLevel; - final isInBatterySaveMode = await battery.isInBatterySaveMode; - return batteryLevel > 20 && !isInBatterySaveMode; - } - - Future updateAnalyticsState() async { - plausible.enabled = await isBatteryGoodForAnalytics() && - currentConnectivity == ConnectivityResult.wifi; - } - - connectivity.onConnectivityChanged.listen((result) async { - currentConnectivity = result; - await updateAnalyticsState(); - }); - - Stream.periodic(const Duration(minutes: 1)).listen((event) async { - await updateAnalyticsState(); - }); - - await updateAnalyticsState(); - return plausible; -} - Future main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -137,7 +98,13 @@ Future main() async { ); }); - final plausible = createPlausible(); + final plausibleUrl = dotenv.env['PLAUSIBLE_URL']; + final plausibleDomain = dotenv.env['PLAUSIBLE_DOMAIN']; + + final plausible = plausibleUrl != null && plausibleDomain != null + ? Plausible(plausibleUrl, plausibleDomain) + : null; + if (plausible == null) { Logger().w('Plausible is not enabled'); } @@ -154,49 +121,52 @@ Future main() async { }, appRunner: () { runApp( - MultiProvider( - providers: [ - ChangeNotifierProvider( - create: (context) => stateProviders.lectureProvider, - ), - ChangeNotifierProvider( - create: (context) => stateProviders.examProvider, - ), - ChangeNotifierProvider( - create: (context) => stateProviders.busStopProvider, - ), - ChangeNotifierProvider( - create: (context) => stateProviders.restaurantProvider, - ), - ChangeNotifierProvider( - create: (context) => stateProviders.profileProvider, - ), - ChangeNotifierProvider( - create: (context) => stateProviders.courseUnitsInfoProvider, - ), - ChangeNotifierProvider( - create: (context) => stateProviders.sessionProvider, - ), - ChangeNotifierProvider( - create: (context) => stateProviders.calendarProvider, - ), - ChangeNotifierProvider( - create: (context) => stateProviders.libraryOccupationProvider, - ), - ChangeNotifierProvider( - create: (context) => stateProviders.facultyLocationsProvider, - ), - ChangeNotifierProvider( - create: (context) => stateProviders.referenceProvider, - ), - ChangeNotifierProvider( - create: (_) => LocaleNotifier(savedLocale), - ), - ChangeNotifierProvider( - create: (_) => ThemeNotifier(savedTheme), - ), - ], - child: Application(route, plausible: plausible), + PlausibleProvider( + plausible: plausible, + child: MultiProvider( + providers: [ + ChangeNotifierProvider( + create: (context) => stateProviders.lectureProvider, + ), + ChangeNotifierProvider( + create: (context) => stateProviders.examProvider, + ), + ChangeNotifierProvider( + create: (context) => stateProviders.busStopProvider, + ), + ChangeNotifierProvider( + create: (context) => stateProviders.restaurantProvider, + ), + ChangeNotifierProvider( + create: (context) => stateProviders.profileProvider, + ), + ChangeNotifierProvider( + create: (context) => stateProviders.courseUnitsInfoProvider, + ), + ChangeNotifierProvider( + create: (context) => stateProviders.sessionProvider, + ), + ChangeNotifierProvider( + create: (context) => stateProviders.calendarProvider, + ), + ChangeNotifierProvider( + create: (context) => stateProviders.libraryOccupationProvider, + ), + ChangeNotifierProvider( + create: (context) => stateProviders.facultyLocationsProvider, + ), + ChangeNotifierProvider( + create: (context) => stateProviders.referenceProvider, + ), + ChangeNotifierProvider( + create: (_) => LocaleNotifier(savedLocale), + ), + ChangeNotifierProvider( + create: (_) => ThemeNotifier(savedTheme), + ), + ], + child: Application(route), + ), ), ); }, @@ -207,10 +177,9 @@ Future main() async { /// This class is necessary to track the app's state for /// the current execution. class Application extends StatefulWidget { - const Application(this.initialWidget, {this.plausible, super.key}); + const Application(this.initialWidget, {super.key}); final Widget initialWidget; - final Plausible? plausible; static GlobalKey navigatorKey = GlobalKey(); @@ -226,7 +195,7 @@ class ApplicationState extends State { void initState() { super.initState(); - final plausible = widget.plausible; + final plausible = context.read(); if (plausible != null) { navigatorObservers.add(PlausibleNavigatorObserver(plausible)); } diff --git a/uni/lib/model/providers/plausible/plausible_provider.dart b/uni/lib/model/providers/plausible/plausible_provider.dart new file mode 100644 index 000000000..bedabef7a --- /dev/null +++ b/uni/lib/model/providers/plausible/plausible_provider.dart @@ -0,0 +1,99 @@ +import 'dart:async'; + +import 'package:battery_plus/battery_plus.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/widgets.dart'; +import 'package:plausible_analytics/plausible_analytics.dart'; +import 'package:provider/provider.dart'; + +class PlausibleProvider extends StatefulWidget { + const PlausibleProvider({ + required this.plausible, + required this.child, + super.key, + }); + + final Plausible? plausible; + final Widget child; + + @override + State createState() => _PlausibleProviderState(); +} + +class _PlausibleProviderState extends State { + int _batteryLevel = 0; + bool _isInBatterySaveMode = true; + ConnectivityResult _connectivityResult = ConnectivityResult.mobile; + + bool _canUpdateBatteryState = true; + + @override + void initState() { + super.initState(); + + final plausible = widget.plausible; + if (plausible != null) { + plausible.enabled = false; + + _startListeners(plausible) + .then((_) => _updateBatteryState()) + .then((_) => _updateConnectivityState()); + } + } + + void _updateAnalyticsState() { + final plausible = widget.plausible; + if (plausible != null) { + plausible.enabled = _batteryLevel > 20 && + !_isInBatterySaveMode && + _connectivityResult == ConnectivityResult.wifi; + } + } + + Future _updateBatteryState() async { + if (!_canUpdateBatteryState) { + return; + } + + _canUpdateBatteryState = false; + Timer(const Duration(seconds: 10), () => _canUpdateBatteryState = true); + + final battery = Battery(); + _batteryLevel = await battery.batteryLevel; + _isInBatterySaveMode = await battery.isInBatterySaveMode; + + _updateAnalyticsState(); + } + + Future _updateConnectivityState() async { + final connectivity = Connectivity(); + _connectivityResult = await connectivity.checkConnectivity(); + + _updateAnalyticsState(); + } + + Future _startListeners(Plausible plausible) async { + final connectivity = Connectivity(); + connectivity.onConnectivityChanged.listen((result) { + _connectivityResult = result; + _updateAnalyticsState(); + }); + } + + @override + Widget build(BuildContext context) { + return Listener( + behavior: HitTestBehavior.translucent, + onPointerDown: (event) { + final plausible = widget.plausible; + if (plausible != null) { + _updateBatteryState(); + } + }, + child: Provider.value( + value: widget.plausible, + child: widget.child, + ), + ); + } +} From 5aaff3047743de0b775a20ac1bc8ad7ab5ea0bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 12 Jan 2024 23:14:33 +0000 Subject: [PATCH 119/334] feat: use preference settings for analytics --- .../local_storage/preferences_controller.dart | 5 +++ .../plausible/plausible_provider.dart | 35 +++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/uni/lib/controller/local_storage/preferences_controller.dart b/uni/lib/controller/local_storage/preferences_controller.dart index a146e7927..57b1bf801 100644 --- a/uni/lib/controller/local_storage/preferences_controller.dart +++ b/uni/lib/controller/local_storage/preferences_controller.dart @@ -44,6 +44,10 @@ class PreferencesController { static const String _filteredExamsTypes = 'filtered_exam_types'; static final List _defaultFilteredExamTypes = Exam.displayedTypes; + static final _statsToggleStreamController = + StreamController.broadcast(); + static final onStatsToggle = _statsToggleStreamController.stream; + /// Returns the last time the data with given key was updated. static DateTime? getLastDataClassUpdateTime(String dataKey) { final lastUpdateTime = prefs.getString(dataKey + _lastUpdateTimeKeySuffix); @@ -290,5 +294,6 @@ class PreferencesController { required bool value, }) async { await prefs.setBool(_usageStatsToggleKey, value); + _statsToggleStreamController.add(value); } } diff --git a/uni/lib/model/providers/plausible/plausible_provider.dart b/uni/lib/model/providers/plausible/plausible_provider.dart index bedabef7a..9ca92ce66 100644 --- a/uni/lib/model/providers/plausible/plausible_provider.dart +++ b/uni/lib/model/providers/plausible/plausible_provider.dart @@ -3,8 +3,10 @@ import 'dart:async'; import 'package:battery_plus/battery_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/widgets.dart'; +import 'package:logger/logger.dart'; import 'package:plausible_analytics/plausible_analytics.dart'; import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; class PlausibleProvider extends StatefulWidget { const PlausibleProvider({ @@ -24,6 +26,7 @@ class _PlausibleProviderState extends State { int _batteryLevel = 0; bool _isInBatterySaveMode = true; ConnectivityResult _connectivityResult = ConnectivityResult.mobile; + bool _isUsageStatsEnabled = true; bool _canUpdateBatteryState = true; @@ -37,16 +40,20 @@ class _PlausibleProviderState extends State { _startListeners(plausible) .then((_) => _updateBatteryState()) - .then((_) => _updateConnectivityState()); + .then((_) => _updateConnectivityState()) + .then((_) => _updateUsageStatsState()); } } void _updateAnalyticsState() { final plausible = widget.plausible; if (plausible != null) { - plausible.enabled = _batteryLevel > 20 && + plausible.enabled = _isUsageStatsEnabled && !_isInBatterySaveMode && + _batteryLevel > 20 && _connectivityResult == ConnectivityResult.wifi; + + Logger().d("Plausible enabled: ${plausible.enabled}"); } } @@ -62,6 +69,9 @@ class _PlausibleProviderState extends State { _batteryLevel = await battery.batteryLevel; _isInBatterySaveMode = await battery.isInBatterySaveMode; + Logger().d("Battery level: $_batteryLevel"); + Logger().d("Is in battery save mode: $_isInBatterySaveMode"); + _updateAnalyticsState(); } @@ -69,6 +79,16 @@ class _PlausibleProviderState extends State { final connectivity = Connectivity(); _connectivityResult = await connectivity.checkConnectivity(); + Logger().d("Connectivity result: $_connectivityResult"); + + _updateAnalyticsState(); + } + + Future _updateUsageStatsState() async { + _isUsageStatsEnabled = PreferencesController.getUsageStatsToggle(); + + Logger().d("Usage stats enabled: $_isUsageStatsEnabled"); + _updateAnalyticsState(); } @@ -76,6 +96,17 @@ class _PlausibleProviderState extends State { final connectivity = Connectivity(); connectivity.onConnectivityChanged.listen((result) { _connectivityResult = result; + + Logger().d("Connectivity result: $_connectivityResult"); + + _updateAnalyticsState(); + }); + + PreferencesController.onStatsToggle.listen((event) { + _isUsageStatsEnabled = event; + + Logger().d("Usage stats enabled: $_isUsageStatsEnabled"); + _updateAnalyticsState(); }); } From 5416985c03e6785492ce68f4eaa97b527156fb05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 19 Jan 2024 14:22:10 +0000 Subject: [PATCH 120/334] chore: fix linting issues --- .../providers/plausible/plausible_provider.dart | 16 +--------------- uni/pubspec.yaml | 7 +++---- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/uni/lib/model/providers/plausible/plausible_provider.dart b/uni/lib/model/providers/plausible/plausible_provider.dart index 9ca92ce66..7604769fd 100644 --- a/uni/lib/model/providers/plausible/plausible_provider.dart +++ b/uni/lib/model/providers/plausible/plausible_provider.dart @@ -53,7 +53,7 @@ class _PlausibleProviderState extends State { _batteryLevel > 20 && _connectivityResult == ConnectivityResult.wifi; - Logger().d("Plausible enabled: ${plausible.enabled}"); + Logger().d('Plausible enabled: ${plausible.enabled}'); } } @@ -69,9 +69,6 @@ class _PlausibleProviderState extends State { _batteryLevel = await battery.batteryLevel; _isInBatterySaveMode = await battery.isInBatterySaveMode; - Logger().d("Battery level: $_batteryLevel"); - Logger().d("Is in battery save mode: $_isInBatterySaveMode"); - _updateAnalyticsState(); } @@ -79,16 +76,11 @@ class _PlausibleProviderState extends State { final connectivity = Connectivity(); _connectivityResult = await connectivity.checkConnectivity(); - Logger().d("Connectivity result: $_connectivityResult"); - _updateAnalyticsState(); } Future _updateUsageStatsState() async { _isUsageStatsEnabled = PreferencesController.getUsageStatsToggle(); - - Logger().d("Usage stats enabled: $_isUsageStatsEnabled"); - _updateAnalyticsState(); } @@ -96,17 +88,11 @@ class _PlausibleProviderState extends State { final connectivity = Connectivity(); connectivity.onConnectivityChanged.listen((result) { _connectivityResult = result; - - Logger().d("Connectivity result: $_connectivityResult"); - _updateAnalyticsState(); }); PreferencesController.onStatsToggle.listen((event) { _isUsageStatsEnabled = event; - - Logger().d("Usage stats enabled: $_isUsageStatsEnabled"); - _updateAnalyticsState(); }); } diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 3c3da39b3..ffd26a6c4 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -15,6 +15,7 @@ environment: dependencies: add_2_calendar: ^3.0.1 + battery_plus: ^5.0.2 cached_network_image: ^3.3.1 collection: ^1.16.0 connectivity_plus: ^5.0.2 @@ -45,10 +46,10 @@ dependencies: path: ^1.8.0 path_provider: ^2.0.0 percent_indicator: ^4.2.2 + plausible_analytics: ^0.3.0 provider: ^6.1.1 sentry_flutter: ^7.14.0 shared_preferences: ^2.2.2 - plausible_analytics: ^0.3.0 shimmer: ^3.0.0 sqflite: ^2.0.3 synchronized: ^3.0.0 @@ -56,7 +57,6 @@ dependencies: tuple: ^2.0.0 url_launcher: ^6.2.2 workmanager: ^0.5.2 - battery_plus: ^5.0.2 dev_dependencies: build_runner: ^2.4.7 @@ -72,8 +72,7 @@ flutter: generate: true uses-material-design: true assets: - - assets/ - - assets/env/.env + - assets/env/ - assets/images/ - assets/text/ - assets/text/locations/ From 9b9c29ba96387c1ec3f44ccd4aeda3f547b05778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Sat, 20 Jan 2024 20:04:25 +0000 Subject: [PATCH 121/334] fix: change battery check period to 1 minute --- uni/lib/model/providers/plausible/plausible_provider.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/model/providers/plausible/plausible_provider.dart b/uni/lib/model/providers/plausible/plausible_provider.dart index 7604769fd..e60f13daa 100644 --- a/uni/lib/model/providers/plausible/plausible_provider.dart +++ b/uni/lib/model/providers/plausible/plausible_provider.dart @@ -63,7 +63,7 @@ class _PlausibleProviderState extends State { } _canUpdateBatteryState = false; - Timer(const Duration(seconds: 10), () => _canUpdateBatteryState = true); + Timer(const Duration(minutes: 1), () => _canUpdateBatteryState = true); final battery = Battery(); _batteryLevel = await battery.batteryLevel; From f9638afc7d4e0cb8572db4bc3824c8f01d79258d Mon Sep 17 00:00:00 2001 From: DGoiana Date: Mon, 22 Jan 2024 12:17:17 +0000 Subject: [PATCH 122/334] Calendar card implementation --- .../view/calendar/widgets/calendar_card.dart | 51 +++++++++++++++++++ uni/lib/view/faculty/faculty.dart | 4 +- .../faculty/widgets/map_snapshot_card.dart | 2 +- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 uni/lib/view/calendar/widgets/calendar_card.dart diff --git a/uni/lib/view/calendar/widgets/calendar_card.dart b/uni/lib/view/calendar/widgets/calendar_card.dart new file mode 100644 index 000000000..4d3a59d0b --- /dev/null +++ b/uni/lib/view/calendar/widgets/calendar_card.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/model/entities/calendar_event.dart'; +import 'package:uni/model/providers/lazy/calendar_provider.dart'; +import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/calendar/calendar.dart'; +import 'package:uni/view/common_widgets/generic_card.dart'; +import 'package:uni/view/lazy_consumer.dart'; + +class ScrollableCalendarCard extends GenericCard { + ScrollableCalendarCard({super.key}); + + const ScrollableCalendarCard.fromEditingInformation( + super.key, { + required super.editingMode, + super.onDelete, + }) : super.fromEditingInformation(); + + @override + String getTitle(BuildContext context) => + S.of(context).nav_title(DrawerItem.navCalendar.title); + + @override + Future onClick(BuildContext context) => + Navigator.pushNamed(context, '/${DrawerItem.navCalendar.title}'); + + @override + void onRefresh(BuildContext context) { + Provider.of(context, listen: false).forceRefresh(context); + } + + @override + Widget buildCardContent(BuildContext context) { + return SizedBox( + height: 200, + child: SingleChildScrollView( + child: LazyConsumer>( + builder: CalendarPageViewState().getTimeline, + hasContent: (calendar) => calendar.isNotEmpty, + onNullContent: const Center( + child: Text( + 'Nenhum evento encontrado', + style: TextStyle(fontSize: 18), + ), + ), + ), + ), + ); + } +} diff --git a/uni/lib/view/faculty/faculty.dart b/uni/lib/view/faculty/faculty.dart index f1293167e..de2d125cd 100644 --- a/uni/lib/view/faculty/faculty.dart +++ b/uni/lib/view/faculty/faculty.dart @@ -1,12 +1,13 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/calendar/widgets/calendar_card.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/faculty/widgets/academic_services_card.dart'; import 'package:uni/view/faculty/widgets/copy_center_card.dart'; -import 'package:uni/generated/l10n.dart'; import 'package:uni/view/faculty/widgets/dona_bia_card.dart'; import 'package:uni/view/faculty/widgets/infodesk_card.dart'; import 'package:uni/view/faculty/widgets/map_snapshot_card.dart'; @@ -34,6 +35,7 @@ class FacultyPageViewState extends GeneralPageViewState { children: [ MapCard(), LibraryOccupationCard(), + ScrollableCalendarCard(), getUtilsSection(), ], ), diff --git a/uni/lib/view/faculty/widgets/map_snapshot_card.dart b/uni/lib/view/faculty/widgets/map_snapshot_card.dart index 335f19ab6..dc1c07b08 100644 --- a/uni/lib/view/faculty/widgets/map_snapshot_card.dart +++ b/uni/lib/view/faculty/widgets/map_snapshot_card.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; -import 'package:uni/generated/l10n.dart'; import 'package:provider/provider.dart'; +import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/location_group.dart'; import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; import 'package:uni/utils/drawer_items.dart'; From 8bb4ad18d2075b9942b08a092fff7eea78cce8ff Mon Sep 17 00:00:00 2001 From: DGoiana Date: Mon, 22 Jan 2024 13:27:54 +0000 Subject: [PATCH 123/334] Rounding map and blocking tap propagation --- .../faculty/widgets/map_snapshot_card.dart | 23 +++++++++++-------- .../widgets/restaurant_page_card.dart | 4 ++-- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/uni/lib/view/faculty/widgets/map_snapshot_card.dart b/uni/lib/view/faculty/widgets/map_snapshot_card.dart index dc1c07b08..f6c94c5b7 100644 --- a/uni/lib/view/faculty/widgets/map_snapshot_card.dart +++ b/uni/lib/view/faculty/widgets/map_snapshot_card.dart @@ -43,16 +43,21 @@ class MapCard extends GenericCard { Widget buildMapView(BuildContext context, List locations) { return GestureDetector( - onTapDown: (TapDownDetails details) => + onTap: () => Navigator.pushNamed(context, '/${DrawerItem.navLocations.title}'), - child: Container( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), - height: MediaQuery.of(context).size.height * 0.3, - alignment: Alignment.center, - child: FacultyMap( - faculty: 'FEUP', - locations: locations, - interactiveFlags: InteractiveFlag.none, + child: AbsorbPointer( + child: Container( + padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), + height: MediaQuery.of(context).size.height * 0.3, + alignment: Alignment.center, + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: FacultyMap( + faculty: 'FEUP', + locations: locations, + interactiveFlags: InteractiveFlag.none, + ), + ), ), ), ); diff --git a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart index 6e29614a4..35dfdcfe5 100644 --- a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart +++ b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart @@ -45,7 +45,7 @@ class CardFavoriteButton extends StatelessWidget { .contains(restaurant.name); return IconButton( icon: isFavorite ? Icon(MdiIcons.heart) : Icon(MdiIcons.heartOutline), - onPressed: () { + onPressed: () async { final favoriteRestaurants = PreferencesController.getFavoriteRestaurants(); if (favoriteRestaurants.contains(restaurant.name)) { @@ -53,7 +53,7 @@ class CardFavoriteButton extends StatelessWidget { } else { favoriteRestaurants.add(restaurant.name); } - PreferencesController.saveFavoriteRestaurants( + await PreferencesController.saveFavoriteRestaurants( favoriteRestaurants, ); From 5a636bd8760f8501585f4d93b7f222f5d63bb788 Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 26 Jan 2024 13:18:07 +0000 Subject: [PATCH 124/334] Made some adjustments to accommodate recent updates --- uni/lib/view/home/widgets/exam_card.dart | 115 +++++++----------- .../view/home/widgets/next_exams_card.dart | 2 +- 2 files changed, 47 insertions(+), 70 deletions(-) diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index e6f0f69c9..1359d6f2a 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -5,7 +5,6 @@ import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/common_widgets/request_dependent_widget_builder.dart'; import 'package:uni/view/home/widgets/exam_card_shimmer.dart'; import 'package:uni/view/home/widgets/next_exams_card.dart'; import 'package:uni/view/home/widgets/remaining_exams_card.dart'; @@ -15,10 +14,10 @@ class ExamCard extends GenericCard { ExamCard({super.key}); const ExamCard.fromEditingInformation( - super.key, { - required super.editingMode, - super.onDelete, - }) : super.fromEditingInformation(); + super.key, { + required super.editingMode, + super.onDelete, + }) : super.fromEditingInformation(); static const int maxExamsToDisplay = 4; @@ -37,73 +36,53 @@ class ExamCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { - return LazyConsumer( - builder: (context, examProvider) { - final filteredExams = examProvider.getFilteredExams(); - final hiddenExams = examProvider.hiddenExams; - final allExams = filteredExams - .where((exam) => !hiddenExams.contains(exam.id)) - .toList(); + return LazyConsumer>( + builder: (context, allExams) { final nextExams = getPrimaryExams( allExams, allExams.isNotEmpty ? allExams.first : null, ); - - return RequestDependentWidgetBuilder( - status: examProvider.status, - builder: () { - if (nextExams.isEmpty) { - return Center( - child: Text( - S.of(context).no_selected_exams, - style: Theme.of(context).textTheme.titleLarge, - ), - ); - } - - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - NextExamsWidget(exams: nextExams), - if (nextExams.length < maxExamsToDisplay) - Column( - children: [ - Container( - margin: const EdgeInsets.only( - right: 80, - left: 80, - top: 7, - bottom: 7, - ), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Theme.of(context).dividerColor, - ), - ), + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + NextExamsWidget(exams: nextExams), + if (nextExams.length < maxExamsToDisplay) + Column( + children: [ + Container( + margin: const EdgeInsets.only( + right: 80, + left: 80, + top: 7, + bottom: 7, + ), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor, ), ), - RemainingExamsWidget( - exams: allExams - .where((exam) => !nextExams.contains(exam)) - .take(maxExamsToDisplay - nextExams.length) - .toList(), - ), - ], + ), + ), + RemainingExamsWidget( + exams: allExams + .where((exam) => !nextExams.contains(exam)) + .take(maxExamsToDisplay - nextExams.length) + .toList(), ), - ], - ); - }, - hasContentPredicate: allExams.isNotEmpty, - onNullContent: Center( - child: Text( - S.of(context).no_selected_exams, - style: Theme.of(context).textTheme.titleLarge, - ), - ), - contentLoadingWidget: const ExamCardShimmer().build(context), + ], + ), + ], ); }, + hasContent: (allExams) => allExams.isNotEmpty, + onNullContent: Center( + child: Text( + S.of(context).no_selected_exams, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + contentLoadingWidget: const ExamCardShimmer(), ); } @@ -112,11 +91,9 @@ class ExamCard extends GenericCard { return []; } - final sameDayExams = allExams.where((exam) { - return isSameDay(nextExam.begin, exam.begin); - }).toList(); - - return sameDayExams; + return allExams + .where((exam) => isSameDay(nextExam.begin, exam.begin)) + .toList(); } bool isSameDay(DateTime? dateA, DateTime? dateB) { @@ -124,4 +101,4 @@ class ExamCard extends GenericCard { dateA?.month == dateB?.month && dateA?.day == dateB?.day; } -} \ No newline at end of file +} diff --git a/uni/lib/view/home/widgets/next_exams_card.dart b/uni/lib/view/home/widgets/next_exams_card.dart index 829087587..e258d65bf 100644 --- a/uni/lib/view/home/widgets/next_exams_card.dart +++ b/uni/lib/view/home/widgets/next_exams_card.dart @@ -20,7 +20,6 @@ class NextExamsWidget extends StatelessWidget { DateRectangle( date: exams.isNotEmpty ? getFormattedDate(exams.first, context) : '', ), - const SizedBox(height: 8), Column( children: exams.map((exam) { return Padding( @@ -30,6 +29,7 @@ class NextExamsWidget extends StatelessWidget { exam: exam, teacher: '', mainPage: true, + onChangeVisibility: () {}, ), ), ); From 4b2ec9a70371bb2c1d95b82a25f7f155187c852b Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 26 Jan 2024 14:04:37 +0000 Subject: [PATCH 125/334] Format file --- uni/lib/view/home/widgets/exam_card.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index 1359d6f2a..ce6df7641 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -14,10 +14,10 @@ class ExamCard extends GenericCard { ExamCard({super.key}); const ExamCard.fromEditingInformation( - super.key, { - required super.editingMode, - super.onDelete, - }) : super.fromEditingInformation(); + super.key, { + required super.editingMode, + super.onDelete, + }) : super.fromEditingInformation(); static const int maxExamsToDisplay = 4; From bafa8c96dd5441127553b201e408849a9d833da5 Mon Sep 17 00:00:00 2001 From: Processing Date: Thu, 10 Aug 2023 13:22:43 +0100 Subject: [PATCH 126/334] Refactor navigation_drawer.dart --- .../pages_layouts/general/general.dart | 2 +- .../widgets/drawer_navigation_option.dart | 62 +++++++++++++++++++ .../general/widgets/logout_button.dart | 33 ++++++++++ .../general/widgets/navigation_drawer.dart | 19 +++++- .../general/widgets/theme_switch_button.dart | 30 +++++++++ 5 files changed, 142 insertions(+), 4 deletions(-) create mode 100644 uni/lib/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart create mode 100644 uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart create mode 100644 uni/lib/view/common_widgets/pages_layouts/general/widgets/theme_switch_button.dart diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 53b55aa76..9258eb45d 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -117,7 +117,7 @@ abstract class GeneralPageViewState extends State { Widget getScaffold(BuildContext context, Widget body) { return Scaffold( appBar: buildAppBar(context), - drawer: AppNavigationDrawer(parentContext: context), + drawer: const AppNavigationDrawer(), body: refreshState(context, body), ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart new file mode 100644 index 000000000..ec6d17ec4 --- /dev/null +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:uni/utils/drawer_items.dart'; + +class DrawerNavigationOption extends StatelessWidget { + const DrawerNavigationOption({required this.item, super.key}); + + final DrawerItem item; + + String getCurrentRoute(BuildContext context) => + ModalRoute.of(context)!.settings.name == null + ? DrawerItem.values.toList()[0].title + : ModalRoute.of(context)!.settings.name!.substring(1); + + void onSelectPage(String key, BuildContext context) { + final prev = getCurrentRoute(context); + + Navigator.of(context).pop(); + + if (prev != key) { + Navigator.pushNamed(context, '/$key'); + } + } + + BoxDecoration? _getSelectionDecoration(String name, BuildContext context) { + return (name == getCurrentRoute(context)) + ? BoxDecoration( + border: Border( + left: BorderSide( + color: Theme.of(context).primaryColor, + width: 3, + ), + ), + color: Theme.of(context).dividerColor, + ) + : null; + } + + @override + Widget build(BuildContext context) { + return DecoratedBox( + decoration: + _getSelectionDecoration(item.title, context) ?? const BoxDecoration(), + child: ListTile( + title: Container( + padding: const EdgeInsets.only(bottom: 3, left: 20), + child: Text( + item.title, + style: TextStyle( + fontSize: 18, + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.normal, + ), + ), + ), + dense: true, + contentPadding: EdgeInsets.zero, + selected: item.title == getCurrentRoute(context), + onTap: () => onSelectPage(item.title, context), + ), + ); + } +} diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart new file mode 100644 index 000000000..51fc8a030 --- /dev/null +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:uni/utils/drawer_items.dart'; + +class LogoutButton extends StatelessWidget { + const LogoutButton({super.key}); + + void _onLogOut(String key, BuildContext context) { + Navigator.of(context) + .pushNamedAndRemoveUntil('/$key', (Route route) => false); + } + + @override + Widget build(BuildContext context) { + final logOutText = DrawerItem.navLogOut.title; + return TextButton( + onPressed: () => _onLogOut(logOutText, context), + style: TextButton.styleFrom( + elevation: 0, + padding: const EdgeInsets.symmetric(horizontal: 5), + ), + child: Container( + padding: const EdgeInsets.all(15), + child: Text( + logOutText, + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith(color: Theme.of(context).primaryColor), + ), + ), + ); + } +} diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart index 48b38157a..740e11f3b 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart @@ -3,6 +3,9 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/logout_button.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/theme_switch_button.dart'; class AppNavigationDrawer extends StatefulWidget { const AppNavigationDrawer({required this.parentContext, super.key}); @@ -85,9 +88,13 @@ class AppNavigationDrawerState extends State { final drawerOptions = []; final userSession = context.read().state!; - for (final key in drawerItems.keys) { - if (key.isVisible(userSession.faculties)) { - drawerOptions.add(createDrawerNavigationOption(key)); + for (final item in DrawerItem.values) { + if (item.isVisible(userSession.faculties)) { + drawerOptions.add( + DrawerNavigationOption( + item: item, + ), + ); } } @@ -102,6 +109,12 @@ class AppNavigationDrawerState extends State { ), ), ), + const Row( + children: [ + Expanded(child: LogoutButton()), + ThemeSwitchButton(), + ], + ), ], ), ); diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/theme_switch_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/theme_switch_button.dart new file mode 100644 index 000000000..2b761e9c4 --- /dev/null +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/theme_switch_button.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/view/theme_notifier.dart'; + +class ThemeSwitchButton extends StatelessWidget { + const ThemeSwitchButton({super.key}); + + Icon getThemeIcon(ThemeMode theme) { + switch (theme) { + case ThemeMode.light: + return const Icon(Icons.wb_sunny); + case ThemeMode.dark: + return const Icon(Icons.nightlight_round); + case ThemeMode.system: + return const Icon(Icons.brightness_6); + } + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (context, themeNotifier, _) { + return IconButton( + icon: getThemeIcon(themeNotifier.getTheme()), + onPressed: themeNotifier.setNextTheme, + ); + }, + ); + } +} From 175817f9c79c58f4f94e8c3a12d9ad3679921d80 Mon Sep 17 00:00:00 2001 From: Processing Date: Thu, 10 Aug 2023 17:26:56 +0100 Subject: [PATCH 127/334] Refactor general.dart and secondary.dart. --- .../pages_layouts/general/general.dart | 96 ++----------------- .../general/widgets/app_bar.dart | 70 ++++++++++++++ .../general/widgets/refresh_state.dart | 23 +++++ .../general/widgets/top_right_button.dart | 57 +++++++++++ .../pages_layouts/secondary/secondary.dart | 6 +- 5 files changed, 163 insertions(+), 89 deletions(-) create mode 100644 uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart create mode 100644 uni/lib/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart create mode 100644 uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 9258eb45d..3a40ee0fd 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -2,21 +2,22 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart'; -import 'package:uni/view/profile/profile.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart'; /// Page with a hamburger menu and the user profile picture abstract class GeneralPageViewState extends State { final double borderMargin = 18; bool _loadedOnce = false; bool _loading = true; + static ImageProvider? profileImageProvider; Future onRefresh(BuildContext context); @@ -116,92 +117,13 @@ abstract class GeneralPageViewState extends State { Widget getScaffold(BuildContext context, Widget body) { return Scaffold( - appBar: buildAppBar(context), + appBar: CustomAppBar(getTopRightButton: getTopRightButton), drawer: const AppNavigationDrawer(), - body: refreshState(context, body), + body: RefreshState(onRefresh: onRefresh, child: body), ); } - /// Builds the upper bar of the app. - /// - /// This method returns an instance of `AppBar` containing the app's logo, - /// an option button and a button with the user's picture. - AppBar buildAppBar(BuildContext context) { - final queryData = MediaQuery.of(context); - - return AppBar( - bottom: PreferredSize( - preferredSize: Size.zero, - child: Container( - color: Theme.of(context).dividerColor, - margin: EdgeInsets.only(left: borderMargin, right: borderMargin), - height: 1.5, - ), - ), - elevation: 0, - iconTheme: Theme.of(context).iconTheme, - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - titleSpacing: 0, - title: ButtonTheme( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: const RoundedRectangleBorder(), - child: Builder( - builder: (context) => TextButton( - onPressed: () { - final currentRouteName = ModalRoute.of(context)!.settings.name; - if (currentRouteName != '/${DrawerItem.navPersonalArea.title}') { - Navigator.pushNamed( - context, - '/${DrawerItem.navPersonalArea.title}', - ); - } else { - Scaffold.of(context).openDrawer(); - } - }, - child: SvgPicture.asset( - colorFilter: ColorFilter.mode( - Theme.of(context).primaryColor, - BlendMode.srcIn, - ), - 'assets/images/logo_dark.svg', - height: queryData.size.height / 25, - ), - ), - ), - ), - actions: [ - getTopRightButton(context), - ], - ); - } - - // Gets a round shaped button with the photo of the current user. - Widget getTopRightButton(BuildContext context) { - return FutureBuilder( - future: buildProfileDecorationImage(context), - builder: ( - BuildContext context, - AsyncSnapshot decorationImage, - ) { - return TextButton( - onPressed: () => { - Navigator.push( - context, - MaterialPageRoute( - builder: (__) => const ProfilePageView(), - ), - ), - }, - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - shape: BoxShape.circle, - image: decorationImage.data, - ), - ), - ); - }, - ); - } + /// Gets a round shaped button with the photo of the current user. + Widget getTopRightButton(BuildContext context) => + TopRightButton(getProfileDecorationImage: getProfileDecorationImage); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart new file mode 100644 index 000000000..d082b8ec2 --- /dev/null +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:uni/utils/drawer_items.dart'; + +/// Upper bar of the app. +/// +/// This widget consists on an instance of `AppBar` containing the app's logo, +/// an option button and a button with the user's picture. +class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { + const CustomAppBar({required this.getTopRightButton, super.key}); + + static const double borderMargin = 18; + + final Widget Function(BuildContext) getTopRightButton; + + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); + + @override + AppBar build(BuildContext context) { + final queryData = MediaQuery.of(context); + + return final queryData = MediaQuery.of(context); + + return AppBar( + bottom: PreferredSize( + preferredSize: Size.zero, + child: Container( + color: Theme.of(context).dividerColor, + margin: EdgeInsets.only(left: borderMargin, right: borderMargin), + height: 1.5, + ), + ), + elevation: 0, + iconTheme: Theme.of(context).iconTheme, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + titleSpacing: 0, + title: ButtonTheme( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: const RoundedRectangleBorder(), + child: Builder( + builder: (context) => TextButton( + onPressed: () { + final currentRouteName = ModalRoute.of(context)!.settings.name; + if (currentRouteName != '/${DrawerItem.navPersonalArea.title}') { + Navigator.pushNamed( + context, + '/${DrawerItem.navPersonalArea.title}', + ); + } else { + Scaffold.of(context).openDrawer(); + } + }, + child: SvgPicture.asset( + colorFilter: ColorFilter.mode( + Theme.of(context).primaryColor, + BlendMode.srcIn, + ), + 'assets/images/logo_dark.svg', + height: queryData.size.height / 25, + ), + ), + ), + ), + actions: [ + getTopRightButton(context), + ], + ); + } +} diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart new file mode 100644 index 000000000..bea91d0d8 --- /dev/null +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/model/providers/startup/profile_provider.dart'; +import 'package:uni/model/providers/startup/session_provider.dart'; + +class RefreshState extends StatelessWidget { + const RefreshState({required this.onRefresh, required this.child, super.key}); + + final Future Function(BuildContext) onRefresh; + final Widget child; + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + key: GlobalKey(), + onRefresh: () => ProfileProvider.fetchOrGetCachedProfilePicture( + Provider.of(context, listen: false).session, + forceRetrieval: true, + ).then((value) => onRefresh(context)), + child: child, + ); + } +} diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart new file mode 100644 index 000000000..f79fcb116 --- /dev/null +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart @@ -0,0 +1,57 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/model/providers/startup/profile_provider.dart'; +import 'package:uni/model/providers/startup/session_provider.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/profile/profile.dart'; + +class TopRightButton extends StatelessWidget { + const TopRightButton({required this.getProfileDecorationImage, super.key}); + + final DecorationImage Function(File?) getProfileDecorationImage; + + Future buildProfileDecorationImage( + BuildContext context, { + bool forceRetrieval = false, + }) async { + final profilePictureFile = + await ProfileProvider.fetchOrGetCachedProfilePicture( + Provider.of(context, listen: false).session, + forceRetrieval: + forceRetrieval || GeneralPageViewState.profileImageProvider == null, + ); + return getProfileDecorationImage(profilePictureFile); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: buildProfileDecorationImage(context), + builder: ( + BuildContext context, + AsyncSnapshot decorationImage, + ) { + return TextButton( + onPressed: () => { + Navigator.push( + context, + MaterialPageRoute( + builder: (__) => const ProfilePageView(), + ), + ), + }, + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + shape: BoxShape.circle, + image: decorationImage.data, + ), + ), + ); + }, + ); + } +} diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 9266ffc0c..30aaafec3 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; /// Page with a back button on top abstract class SecondaryPageViewState @@ -7,8 +9,8 @@ abstract class SecondaryPageViewState @override Scaffold getScaffold(BuildContext context, Widget body) { return Scaffold( - appBar: buildAppBar(context), - body: refreshState(context, body), + appBar: CustomAppBar(getTopRightButton: getTopRightButton), + body: RefreshState(onRefresh: onRefresh, child: body), ); } From ffd989055aef8a3e23e09bd4e13a3dea3e2f546d Mon Sep 17 00:00:00 2001 From: Processing Date: Thu, 10 Aug 2023 18:15:16 +0100 Subject: [PATCH 128/334] Refactor generic_card.dart. --- uni/lib/view/common_widgets/generic_card.dart | 90 +++++++++---------- .../common_widgets/widgets/delete_icon.dart | 23 +++++ .../common_widgets/widgets/move_icon.dart | 14 +++ 3 files changed, 79 insertions(+), 48 deletions(-) create mode 100644 uni/lib/view/common_widgets/widgets/delete_icon.dart create mode 100644 uni/lib/view/common_widgets/widgets/move_icon.dart diff --git a/uni/lib/view/common_widgets/generic_card.dart b/uni/lib/view/common_widgets/generic_card.dart index 5d56b915a..b5787c467 100644 --- a/uni/lib/view/common_widgets/generic_card.dart +++ b/uni/lib/view/common_widgets/generic_card.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:uni/model/entities/time_utilities.dart'; +import 'package:uni/view/common_widgets/widgets/delete_icon.dart'; +import 'package:uni/view/common_widgets/widgets/move_icon.dart'; /// App default card -abstract class GenericCard extends StatefulWidget { +abstract class GenericCard extends StatelessWidget { GenericCard({Key? key}) : this.customStyle(key: key, editingMode: false, onDelete: () {}); @@ -23,17 +26,16 @@ abstract class GenericCard extends StatefulWidget { this.margin = const EdgeInsets.symmetric(vertical: 10, horizontal: 20), this.hasSmallTitle = false, }); + + static const double borderRadius = 10; + static const double padding = 12; + final EdgeInsetsGeometry margin; final Widget cardAction; final bool hasSmallTitle; final bool editingMode; final void Function()? onDelete; - @override - State createState() { - return GenericCardState(); - } - Widget buildCardContent(BuildContext context); String getTitle(BuildContext context); @@ -49,29 +51,43 @@ abstract class GenericCard extends StatefulWidget { style: Theme.of(context).textTheme.titleLarge, ); } -} -class GenericCardState extends State { - final double borderRadius = 10; - final double padding = 12; + StatelessWidget showLastRefreshedTime(String? time, BuildContext context) { + if (time == null) { + return const Text('N/A'); + } + + final parsedTime = DateTime.tryParse(time); + if (parsedTime == null) { + return const Text('N/A'); + } + + return Container( + alignment: Alignment.center, + child: Text( + 'última atualização às ${parsedTime.toTimeHourMinString()}', + style: Theme.of(context).textTheme.bodySmall, + ), + ); + } @override Widget build(BuildContext context) { return GestureDetector( onTap: () { - if (!widget.editingMode) { - widget.onClick(context); + if (!editingMode) { + onClick(context); } }, child: Card( - margin: widget.margin, + margin: margin, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadius), ), child: DecoratedBox( - decoration: BoxDecoration( - boxShadow: const [ + decoration: const BoxDecoration( + boxShadow: [ BoxShadow( color: Color.fromARGB(0x1c, 0, 0, 0), blurRadius: 7, @@ -87,7 +103,8 @@ class GenericCardState extends State { child: Container( decoration: BoxDecoration( color: Theme.of(context).cardColor, - borderRadius: BorderRadius.all(Radius.circular(borderRadius)), + borderRadius: + const BorderRadius.all(Radius.circular(borderRadius)), ), width: double.infinity, child: Column( @@ -103,8 +120,8 @@ class GenericCardState extends State { padding: const EdgeInsets.symmetric(horizontal: 15), margin: const EdgeInsets.only(top: 15, bottom: 10), child: Text( - widget.getTitle(context), - style: (widget.hasSmallTitle + getTitle(context), + style: (hasSmallTitle ? Theme.of(context).textTheme.titleLarge! : Theme.of(context) .textTheme @@ -115,24 +132,24 @@ class GenericCardState extends State { ), ), ), - widget.cardAction, - if (widget.editingMode) + cardAction, + if (editingMode) Container( alignment: Alignment.center, margin: const EdgeInsets.only(top: 8), - child: getMoveIcon(context), + child: const MoveIcon(), ), - if (widget.editingMode) getDeleteIcon(context), + if (editingMode) DeleteIcon(onDelete: onDelete), ], ), Container( - padding: EdgeInsets.only( + padding: const EdgeInsets.only( left: padding, right: padding, bottom: padding, ), - child: widget.buildCardContent(context), - ), + child: buildCardContent(context), + ) ], ), ), @@ -141,27 +158,4 @@ class GenericCardState extends State { ), ); } - - Widget getDeleteIcon(BuildContext context) { - return Flexible( - child: Container( - alignment: Alignment.centerRight, - height: 32, - child: IconButton( - iconSize: 22, - icon: const Icon(Icons.delete), - tooltip: 'Remover', - onPressed: widget.onDelete, - ), - ), - ); - } - - Widget getMoveIcon(BuildContext context) { - return Icon( - Icons.drag_handle_rounded, - color: Colors.grey.shade500, - size: 22, - ); - } } diff --git a/uni/lib/view/common_widgets/widgets/delete_icon.dart b/uni/lib/view/common_widgets/widgets/delete_icon.dart new file mode 100644 index 000000000..e2b6c8c8a --- /dev/null +++ b/uni/lib/view/common_widgets/widgets/delete_icon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class DeleteIcon extends StatelessWidget { + const DeleteIcon({this.onDelete, super.key}); + + final void Function()? onDelete; + + @override + Widget build(BuildContext context) { + return Flexible( + child: Container( + alignment: Alignment.centerRight, + height: 32, + child: IconButton( + iconSize: 22, + icon: const Icon(Icons.delete), + tooltip: 'Remover', + onPressed: onDelete, + ), + ), + ); + } +} diff --git a/uni/lib/view/common_widgets/widgets/move_icon.dart b/uni/lib/view/common_widgets/widgets/move_icon.dart new file mode 100644 index 000000000..92b28286d --- /dev/null +++ b/uni/lib/view/common_widgets/widgets/move_icon.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +class MoveIcon extends StatelessWidget { + const MoveIcon({super.key}); + + @override + Widget build(BuildContext context) { + return Icon( + Icons.drag_handle_rounded, + color: Colors.grey.shade500, + size: 22, + ); + } +} From 6671a78ab2f777193a198ac64c55ee1996179539 Mon Sep 17 00:00:00 2001 From: Processing Date: Thu, 10 Aug 2023 18:52:06 +0100 Subject: [PATCH 129/334] Refactor request_dependent_widget_builder.dart. --- .../request_dependent_widget_builder.dart | 0 .../widgets/loading_widget.dart | 74 +++++++++++++++++++ .../widgets/request_failed_message.dart | 56 ++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 uni/lib/view/common_widgets/request_dependent_widget_builder.dart create mode 100644 uni/lib/view/common_widgets/widgets/loading_widget.dart create mode 100644 uni/lib/view/common_widgets/widgets/request_failed_message.dart diff --git a/uni/lib/view/common_widgets/request_dependent_widget_builder.dart b/uni/lib/view/common_widgets/request_dependent_widget_builder.dart new file mode 100644 index 000000000..e69de29bb diff --git a/uni/lib/view/common_widgets/widgets/loading_widget.dart b/uni/lib/view/common_widgets/widgets/loading_widget.dart new file mode 100644 index 000000000..d525c5293 --- /dev/null +++ b/uni/lib/view/common_widgets/widgets/loading_widget.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:shimmer/shimmer.dart'; + +class LoadingWidget extends StatelessWidget { + const LoadingWidget({this.contentLoadingWidget, super.key}); + + final Widget? contentLoadingWidget; + + @override + Widget build(BuildContext context) { + return contentLoadingWidget == null + ? const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 20), + child: CircularProgressIndicator(), + ), + ) + : Center( + child: Shimmer.fromColors( + baseColor: Theme.of(context).highlightColor, + highlightColor: Theme.of(context).colorScheme.onPrimary, + child: contentLoadingWidget!, + ), + ); + } + + Widget requestFailedMessage() { + return FutureBuilder( + future: Connectivity().checkConnectivity(), + builder: ( + BuildContext context, + AsyncSnapshot connectivitySnapshot, + ) { + if (!connectivitySnapshot.hasData) { + return const Center( + heightFactor: 3, + child: CircularProgressIndicator(), + ); + } + + if (connectivitySnapshot.data == ConnectivityResult.none) { + return Center( + heightFactor: 3, + child: Text( + 'Sem ligação à internet', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 15, bottom: 10), + child: Center( + child: Text( + 'Aconteceu um erro ao carregar os dados', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + OutlinedButton( + onPressed: () => Navigator.pushNamed( + context, + '/${DrawerItem.navBugReport.title}', + ), + child: const Text('Reportar erro'), + ) + ], + ); + }, + ); + } +} diff --git a/uni/lib/view/common_widgets/widgets/request_failed_message.dart b/uni/lib/view/common_widgets/widgets/request_failed_message.dart new file mode 100644 index 000000000..88704bf38 --- /dev/null +++ b/uni/lib/view/common_widgets/widgets/request_failed_message.dart @@ -0,0 +1,56 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter/material.dart'; +import 'package:uni/utils/drawer_items.dart'; + +class RequestFailedMessage extends StatelessWidget { + const RequestFailedMessage({super.key}); + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: Connectivity().checkConnectivity(), + builder: ( + BuildContext context, + AsyncSnapshot connectivitySnapshot, + ) { + if (!connectivitySnapshot.hasData) { + return const Center( + heightFactor: 3, + child: CircularProgressIndicator(), + ); + } + + if (connectivitySnapshot.data == ConnectivityResult.none) { + return Center( + heightFactor: 3, + child: Text( + 'Sem ligação à internet', + style: Theme.of(context).textTheme.titleMedium, + ), + ); + } + + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 15, bottom: 10), + child: Center( + child: Text( + 'Aconteceu um erro ao carregar os dados', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + ), + OutlinedButton( + onPressed: () => Navigator.pushNamed( + context, + '/${DrawerItem.navBugReport.title}', + ), + child: const Text('Reportar erro'), + ) + ], + ); + }, + ); + } +} From 0eb022f6ca51813ed0628c3f7937ee464816de40 Mon Sep 17 00:00:00 2001 From: Processing Date: Fri, 11 Aug 2023 15:52:58 +0100 Subject: [PATCH 130/334] Change drawer items generation to declarative style. --- .../general/widgets/navigation_drawer.dart | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart index 740e11f3b..39843dfe6 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart @@ -3,10 +3,11 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/logout_button.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/theme_switch_button.dart'; +import 'drawer_navigation_option.dart'; + class AppNavigationDrawer extends StatefulWidget { const AppNavigationDrawer({required this.parentContext, super.key}); @@ -88,16 +89,6 @@ class AppNavigationDrawerState extends State { final drawerOptions = []; final userSession = context.read().state!; - for (final item in DrawerItem.values) { - if (item.isVisible(userSession.faculties)) { - drawerOptions.add( - DrawerNavigationOption( - item: item, - ), - ); - } - } - return Drawer( child: Column( children: [ @@ -105,7 +96,10 @@ class AppNavigationDrawerState extends State { child: Container( padding: const EdgeInsets.only(top: 55), child: ListView( - children: drawerOptions, + children: DrawerItem.values + .where((item) => item.isVisible(userSession.faculties)) + .map((item) => DrawerNavigationOption(item: item)) + .toList(), ), ), ), From f1027430627bbfe86da8953906d26eaaa2c04df1 Mon Sep 17 00:00:00 2001 From: Processing Date: Fri, 5 Jan 2024 10:18:42 +0000 Subject: [PATCH 131/334] Fix merge (in progress) --- .../pages_layouts/general/general.dart | 4 ++- .../general/widgets/app_bar.dart | 7 +++-- .../general/widgets/logout_button.dart | 31 +------------------ .../general/widgets/navigation_drawer.dart | 11 +------ .../widgets/loading_widget.dart | 13 +++++--- .../widgets/request_failed_message.dart | 17 +++++----- 6 files changed, 28 insertions(+), 55 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 3a40ee0fd..62fc9a2b1 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -118,7 +118,9 @@ abstract class GeneralPageViewState extends State { Widget getScaffold(BuildContext context, Widget body) { return Scaffold( appBar: CustomAppBar(getTopRightButton: getTopRightButton), - drawer: const AppNavigationDrawer(), + drawer: AppNavigationDrawer( + parentContext: context, + ), body: RefreshState(onRefresh: onRefresh, child: body), ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart index d082b8ec2..578c223e8 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart @@ -20,14 +20,15 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { AppBar build(BuildContext context) { final queryData = MediaQuery.of(context); - return final queryData = MediaQuery.of(context); - return AppBar( bottom: PreferredSize( preferredSize: Size.zero, child: Container( color: Theme.of(context).dividerColor, - margin: EdgeInsets.only(left: borderMargin, right: borderMargin), + margin: const EdgeInsets.only( + left: borderMargin, + right: borderMargin, + ), height: 1.5, ), ), diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart index 51fc8a030..5193be3cb 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart @@ -1,33 +1,4 @@ import 'package:flutter/material.dart'; import 'package:uni/utils/drawer_items.dart'; -class LogoutButton extends StatelessWidget { - const LogoutButton({super.key}); - - void _onLogOut(String key, BuildContext context) { - Navigator.of(context) - .pushNamedAndRemoveUntil('/$key', (Route route) => false); - } - - @override - Widget build(BuildContext context) { - final logOutText = DrawerItem.navLogOut.title; - return TextButton( - onPressed: () => _onLogOut(logOutText, context), - style: TextButton.styleFrom( - elevation: 0, - padding: const EdgeInsets.symmetric(horizontal: 5), - ), - child: Container( - padding: const EdgeInsets.all(15), - child: Text( - logOutText, - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: Theme.of(context).primaryColor), - ), - ), - ); - } -} +class LogoutButton {} diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart index 39843dfe6..0f58ba841 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart @@ -3,10 +3,8 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/logout_button.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/theme_switch_button.dart'; -import 'drawer_navigation_option.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart'; class AppNavigationDrawer extends StatefulWidget { const AppNavigationDrawer({required this.parentContext, super.key}); @@ -86,7 +84,6 @@ class AppNavigationDrawerState extends State { @override Widget build(BuildContext context) { - final drawerOptions = []; final userSession = context.read().state!; return Drawer( @@ -103,12 +100,6 @@ class AppNavigationDrawerState extends State { ), ), ), - const Row( - children: [ - Expanded(child: LogoutButton()), - ThemeSwitchButton(), - ], - ), ], ), ); diff --git a/uni/lib/view/common_widgets/widgets/loading_widget.dart b/uni/lib/view/common_widgets/widgets/loading_widget.dart index d525c5293..2e4c2a3c4 100644 --- a/uni/lib/view/common_widgets/widgets/loading_widget.dart +++ b/uni/lib/view/common_widgets/widgets/loading_widget.dart @@ -1,5 +1,8 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:shimmer/shimmer.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/bug_report/bug_report.dart'; class LoadingWidget extends StatelessWidget { const LoadingWidget({this.contentLoadingWidget, super.key}); @@ -60,12 +63,14 @@ class LoadingWidget extends StatelessWidget { ), ), OutlinedButton( - onPressed: () => Navigator.pushNamed( + onPressed: () => Navigator.push( context, - '/${DrawerItem.navBugReport.title}', + MaterialPageRoute( + builder: (context) => const BugReportPageView(), + ), ), - child: const Text('Reportar erro'), - ) + child: Text(S.of(context).report_error), + ), ], ); }, diff --git a/uni/lib/view/common_widgets/widgets/request_failed_message.dart b/uni/lib/view/common_widgets/widgets/request_failed_message.dart index 88704bf38..6238d966b 100644 --- a/uni/lib/view/common_widgets/widgets/request_failed_message.dart +++ b/uni/lib/view/common_widgets/widgets/request_failed_message.dart @@ -1,6 +1,7 @@ import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/bug_report/bug_report.dart'; class RequestFailedMessage extends StatelessWidget { const RequestFailedMessage({super.key}); @@ -24,7 +25,7 @@ class RequestFailedMessage extends StatelessWidget { return Center( heightFactor: 3, child: Text( - 'Sem ligação à internet', + S.of(context).check_internet, style: Theme.of(context).textTheme.titleMedium, ), ); @@ -36,18 +37,20 @@ class RequestFailedMessage extends StatelessWidget { padding: const EdgeInsets.only(top: 15, bottom: 10), child: Center( child: Text( - 'Aconteceu um erro ao carregar os dados', + S.of(context).load_error, style: Theme.of(context).textTheme.titleMedium, ), ), ), OutlinedButton( - onPressed: () => Navigator.pushNamed( + onPressed: () => Navigator.push( context, - '/${DrawerItem.navBugReport.title}', + MaterialPageRoute( + builder: (context) => const BugReportPageView(), + ), ), - child: const Text('Reportar erro'), - ) + child: Text(S.of(context).report_error), + ), ], ); }, From db9e5eef4ff6bf2f1cbab020699908e4f4e75657 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jan 2024 15:18:10 +0000 Subject: [PATCH 132/334] Fix navigation drawer item names --- .../pages_layouts/general/widgets/navigation_drawer.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart index 0f58ba841..0b8509851 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart @@ -93,9 +93,9 @@ class AppNavigationDrawerState extends State { child: Container( padding: const EdgeInsets.only(top: 55), child: ListView( - children: DrawerItem.values - .where((item) => item.isVisible(userSession.faculties)) - .map((item) => DrawerNavigationOption(item: item)) + children: drawerItems.keys + .where((key) => key.isVisible(userSession.faculties)) + .map(createDrawerNavigationOption) .toList(), ), ), From e675a255d0f8d6753464990f4b0795167d542de3 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jan 2024 15:38:21 +0000 Subject: [PATCH 133/334] Fix rebase --- .../general/widgets/refresh_state.dart | 2 +- .../general/widgets/top_right_button.dart | 2 +- uni/pubspec.lock | 34 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart index bea91d0d8..199579429 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart @@ -14,7 +14,7 @@ class RefreshState extends StatelessWidget { return RefreshIndicator( key: GlobalKey(), onRefresh: () => ProfileProvider.fetchOrGetCachedProfilePicture( - Provider.of(context, listen: false).session, + Provider.of(context, listen: false).state!, forceRetrieval: true, ).then((value) => onRefresh(context)), child: child, diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart index f79fcb116..9f56ca780 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart @@ -18,7 +18,7 @@ class TopRightButton extends StatelessWidget { }) async { final profilePictureFile = await ProfileProvider.fetchOrGetCachedProfilePicture( - Provider.of(context, listen: false).session, + Provider.of(context, listen: false).state!, forceRetrieval: forceRetrieval || GeneralPageViewState.profileImageProvider == null, ); diff --git a/uni/pubspec.lock b/uni/pubspec.lock index e1fba8fa4..f9923f69d 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -205,10 +205,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: @@ -617,10 +617,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -1046,18 +1046,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1094,26 +1094,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.24.3" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "0.5.9" timelines: dependency: "direct main" description: @@ -1294,10 +1294,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1363,5 +1363,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.3 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.13.7" From 7c4a904c16fc2b1560a3f36ee452172942100c2f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jan 2024 16:08:19 +0000 Subject: [PATCH 134/334] Fix problems with providers --- .../general/widgets/top_right_button.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart index 9f56ca780..099e71028 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart @@ -16,9 +16,12 @@ class TopRightButton extends StatelessWidget { BuildContext context, { bool forceRetrieval = false, }) async { + final sessionProvider = + Provider.of(context, listen: false); + await sessionProvider.ensureInitialized(context); final profilePictureFile = await ProfileProvider.fetchOrGetCachedProfilePicture( - Provider.of(context, listen: false).state!, + sessionProvider.state!, forceRetrieval: forceRetrieval || GeneralPageViewState.profileImageProvider == null, ); @@ -30,9 +33,9 @@ class TopRightButton extends StatelessWidget { return FutureBuilder( future: buildProfileDecorationImage(context), builder: ( - BuildContext context, - AsyncSnapshot decorationImage, - ) { + BuildContext context, + AsyncSnapshot decorationImage, + ) { return TextButton( onPressed: () => { Navigator.push( From d3ba88426831c7b0cf7d12b6da27a3b1085c5c3d Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jan 2024 16:09:05 +0000 Subject: [PATCH 135/334] Fix problems with providers --- .../pages_layouts/general/widgets/logout_button.dart | 4 ---- .../pages_layouts/general/widgets/top_right_button.dart | 6 +++--- .../common_widgets/request_dependent_widget_builder.dart | 0 3 files changed, 3 insertions(+), 7 deletions(-) delete mode 100644 uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart delete mode 100644 uni/lib/view/common_widgets/request_dependent_widget_builder.dart diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart deleted file mode 100644 index 5193be3cb..000000000 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/logout_button.dart +++ /dev/null @@ -1,4 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uni/utils/drawer_items.dart'; - -class LogoutButton {} diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart index 099e71028..3b9ab58bf 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart @@ -33,9 +33,9 @@ class TopRightButton extends StatelessWidget { return FutureBuilder( future: buildProfileDecorationImage(context), builder: ( - BuildContext context, - AsyncSnapshot decorationImage, - ) { + BuildContext context, + AsyncSnapshot decorationImage, + ) { return TextButton( onPressed: () => { Navigator.push( diff --git a/uni/lib/view/common_widgets/request_dependent_widget_builder.dart b/uni/lib/view/common_widgets/request_dependent_widget_builder.dart deleted file mode 100644 index e69de29bb..000000000 From 38b572d2016209d467ac2945e6ae32918aa1f057 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 5 Jan 2024 16:14:54 +0000 Subject: [PATCH 136/334] Fix linter --- uni/lib/controller/local_storage/file_offline_storage.dart | 1 - uni/lib/generated/l10n.dart | 1 + uni/lib/model/entities/locations/special_room_location.dart | 1 - uni/lib/model/entities/locations/store_location.dart | 1 - uni/lib/model/entities/locations/unknown_location.dart | 1 - uni/lib/view/common_widgets/generic_card.dart | 2 +- .../pages_layouts/general/widgets/navigation_drawer.dart | 2 -- uni/lib/view/home/widgets/main_cards_list.dart | 1 - uni/lib/view/settings/widgets/notifications_dialog.dart | 1 - 9 files changed, 2 insertions(+), 9 deletions(-) diff --git a/uni/lib/controller/local_storage/file_offline_storage.dart b/uni/lib/controller/local_storage/file_offline_storage.dart index 9580def16..e99580840 100644 --- a/uni/lib/controller/local_storage/file_offline_storage.dart +++ b/uni/lib/controller/local_storage/file_offline_storage.dart @@ -6,7 +6,6 @@ import 'package:http/http.dart' as http; import 'package:logger/logger.dart'; import 'package:path_provider/path_provider.dart'; import 'package:uni/controller/networking/network_router.dart'; - import 'package:uni/model/entities/session.dart'; /// The offline image storage location on the device. diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 724c0ccb3..e19e2aafd 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1,6 +1,7 @@ // GENERATED CODE - DO NOT MODIFY BY HAND import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; + import 'intl/messages_all.dart'; // ************************************************************************** diff --git a/uni/lib/model/entities/locations/special_room_location.dart b/uni/lib/model/entities/locations/special_room_location.dart index c4cd1013a..0a7c3930e 100644 --- a/uni/lib/model/entities/locations/special_room_location.dart +++ b/uni/lib/model/entities/locations/special_room_location.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - import 'package:uni/model/entities/location.dart'; class SpecialRoomLocation implements Location { diff --git a/uni/lib/model/entities/locations/store_location.dart b/uni/lib/model/entities/locations/store_location.dart index b66ea07ae..07212d049 100644 --- a/uni/lib/model/entities/locations/store_location.dart +++ b/uni/lib/model/entities/locations/store_location.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - import 'package:uni/model/entities/location.dart'; class StoreLocation implements Location { diff --git a/uni/lib/model/entities/locations/unknown_location.dart b/uni/lib/model/entities/locations/unknown_location.dart index 121ab3e4d..33ea18f06 100644 --- a/uni/lib/model/entities/locations/unknown_location.dart +++ b/uni/lib/model/entities/locations/unknown_location.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - import 'package:uni/model/entities/location.dart'; class UnknownLocation implements Location { diff --git a/uni/lib/view/common_widgets/generic_card.dart b/uni/lib/view/common_widgets/generic_card.dart index b5787c467..8d966fcda 100644 --- a/uni/lib/view/common_widgets/generic_card.dart +++ b/uni/lib/view/common_widgets/generic_card.dart @@ -149,7 +149,7 @@ abstract class GenericCard extends StatelessWidget { bottom: padding, ), child: buildCardContent(context), - ) + ), ], ), ), diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart index 0b8509851..74283bb36 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart @@ -4,8 +4,6 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart'; - class AppNavigationDrawer extends StatefulWidget { const AppNavigationDrawer({required this.parentContext, super.key}); diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index e1f06199c..859081327 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -12,7 +12,6 @@ import 'package:uni/view/home/widgets/restaurant_card.dart'; import 'package:uni/view/home/widgets/schedule_card.dart'; import 'package:uni/view/library/widgets/library_occupation_card.dart'; import 'package:uni/view/profile/widgets/account_info_card.dart'; - import 'package:uni/view/profile/widgets/print_info_card.dart'; typedef CardCreator = GenericCard Function( diff --git a/uni/lib/view/settings/widgets/notifications_dialog.dart b/uni/lib/view/settings/widgets/notifications_dialog.dart index f7bea693d..4defadfc2 100644 --- a/uni/lib/view/settings/widgets/notifications_dialog.dart +++ b/uni/lib/view/settings/widgets/notifications_dialog.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - import 'package:uni/generated/l10n.dart'; import 'package:uni/view/settings/widgets/tuition_notification_switch.dart'; From cf6fdf428ff1bfa5e19f3e8d4f71fcf4ab7e799e Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 10 Jan 2024 17:04:10 +0000 Subject: [PATCH 137/334] Delete loading_widget.dart --- .../widgets/loading_widget.dart | 79 ------------------- 1 file changed, 79 deletions(-) delete mode 100644 uni/lib/view/common_widgets/widgets/loading_widget.dart diff --git a/uni/lib/view/common_widgets/widgets/loading_widget.dart b/uni/lib/view/common_widgets/widgets/loading_widget.dart deleted file mode 100644 index 2e4c2a3c4..000000000 --- a/uni/lib/view/common_widgets/widgets/loading_widget.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:connectivity_plus/connectivity_plus.dart'; -import 'package:flutter/material.dart'; -import 'package:shimmer/shimmer.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/view/bug_report/bug_report.dart'; - -class LoadingWidget extends StatelessWidget { - const LoadingWidget({this.contentLoadingWidget, super.key}); - - final Widget? contentLoadingWidget; - - @override - Widget build(BuildContext context) { - return contentLoadingWidget == null - ? const Center( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 20), - child: CircularProgressIndicator(), - ), - ) - : Center( - child: Shimmer.fromColors( - baseColor: Theme.of(context).highlightColor, - highlightColor: Theme.of(context).colorScheme.onPrimary, - child: contentLoadingWidget!, - ), - ); - } - - Widget requestFailedMessage() { - return FutureBuilder( - future: Connectivity().checkConnectivity(), - builder: ( - BuildContext context, - AsyncSnapshot connectivitySnapshot, - ) { - if (!connectivitySnapshot.hasData) { - return const Center( - heightFactor: 3, - child: CircularProgressIndicator(), - ); - } - - if (connectivitySnapshot.data == ConnectivityResult.none) { - return Center( - heightFactor: 3, - child: Text( - 'Sem ligação à internet', - style: Theme.of(context).textTheme.titleMedium, - ), - ); - } - - return Column( - children: [ - Padding( - padding: const EdgeInsets.only(top: 15, bottom: 10), - child: Center( - child: Text( - 'Aconteceu um erro ao carregar os dados', - style: Theme.of(context).textTheme.titleMedium, - ), - ), - ), - OutlinedButton( - onPressed: () => Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const BugReportPageView(), - ), - ), - child: Text(S.of(context).report_error), - ), - ], - ); - }, - ); - } -} From 51d60c49b9d8a74ab32d9e0495d563118e6e9766 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jan 2024 15:07:57 +0000 Subject: [PATCH 138/334] Rename TopRightButton to ProfileButton. --- uni/lib/generated/intl/messages_en.dart | 6 +++--- uni/lib/generated/intl/messages_pt_PT.dart | 6 +++--- uni/lib/generated/l10n.dart | 1 - .../view/common_widgets/pages_layouts/general/general.dart | 4 ++-- .../widgets/{top_right_button.dart => profile_button.dart} | 4 ++-- 5 files changed, 10 insertions(+), 11 deletions(-) rename uni/lib/view/common_widgets/pages_layouts/general/widgets/{top_right_button.dart => profile_button.dart} (93%) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 7c248c1c7..bab6a3601 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -117,7 +117,6 @@ class MessageLookup extends MessageLookupByLibrary { "edit_on": MessageLookupByLibrary.simpleMessage("Finish editing"), "empty_text": MessageLookupByLibrary.simpleMessage("Please fill in this field"), - "exam_of": MessageLookupByLibrary.simpleMessage("of"), "exams_filter": MessageLookupByLibrary.simpleMessage("Exams Filter Settings"), "exit_confirm": @@ -187,10 +186,10 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("No favorite restaurants"), "no_info": MessageLookupByLibrary.simpleMessage( "There is no information to display"), - "no_link": - MessageLookupByLibrary.simpleMessage("We couldn\'t open the link"), "no_library_info": MessageLookupByLibrary.simpleMessage( "No library occupation information available"), + "no_link": + MessageLookupByLibrary.simpleMessage("We couldn\'t open the link"), "no_menu_info": MessageLookupByLibrary.simpleMessage( "There is no information available about meals"), "no_menus": MessageLookupByLibrary.simpleMessage( @@ -211,6 +210,7 @@ class MessageLookup extends MessageLookupByLibrary { "notifications": MessageLookupByLibrary.simpleMessage("Notifications"), "occurrence_type": MessageLookupByLibrary.simpleMessage("Type of occurrence"), + "of_month": MessageLookupByLibrary.simpleMessage("of"), "other_links": MessageLookupByLibrary.simpleMessage("Other links"), "pass_change_request": MessageLookupByLibrary.simpleMessage( "For security reasons, passwords must be changed periodically."), diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index cec2c09e6..c3b1a515a 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -116,7 +116,6 @@ class MessageLookup extends MessageLookupByLibrary { "edit_on": MessageLookupByLibrary.simpleMessage("Concluir edição"), "empty_text": MessageLookupByLibrary.simpleMessage( "Por favor preenche este campo"), - "of_month": MessageLookupByLibrary.simpleMessage("de"), "exams_filter": MessageLookupByLibrary.simpleMessage("Definições Filtro de Exames"), "exit_confirm": MessageLookupByLibrary.simpleMessage( @@ -188,10 +187,10 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Sem restaurantes favoritos"), "no_info": MessageLookupByLibrary.simpleMessage( "Não existem informações para apresentar"), - "no_link": MessageLookupByLibrary.simpleMessage( - "Não conseguimos abrir o link"), "no_library_info": MessageLookupByLibrary.simpleMessage("Sem informação de ocupação"), + "no_link": MessageLookupByLibrary.simpleMessage( + "Não conseguimos abrir o link"), "no_menu_info": MessageLookupByLibrary.simpleMessage( "Não há informação disponível sobre refeições"), "no_menus": MessageLookupByLibrary.simpleMessage( @@ -212,6 +211,7 @@ class MessageLookup extends MessageLookupByLibrary { "notifications": MessageLookupByLibrary.simpleMessage("Notificações"), "occurrence_type": MessageLookupByLibrary.simpleMessage("Tipo de ocorrência"), + "of_month": MessageLookupByLibrary.simpleMessage("de"), "other_links": MessageLookupByLibrary.simpleMessage("Outros links"), "pass_change_request": MessageLookupByLibrary.simpleMessage( "Por razões de segurança, as palavras-passe têm de ser alteradas periodicamente."), diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index e19e2aafd..724c0ccb3 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1,7 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; - import 'intl/messages_all.dart'; // ************************************************************************** diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 62fc9a2b1..940666ed0 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -10,7 +10,7 @@ import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/profile_button.dart'; /// Page with a hamburger menu and the user profile picture abstract class GeneralPageViewState extends State { @@ -127,5 +127,5 @@ abstract class GeneralPageViewState extends State { /// Gets a round shaped button with the photo of the current user. Widget getTopRightButton(BuildContext context) => - TopRightButton(getProfileDecorationImage: getProfileDecorationImage); + ProfileButton(getProfileDecorationImage: getProfileDecorationImage); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart similarity index 93% rename from uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart rename to uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart index 3b9ab58bf..a42376398 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_right_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart @@ -7,8 +7,8 @@ import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/profile/profile.dart'; -class TopRightButton extends StatelessWidget { - const TopRightButton({required this.getProfileDecorationImage, super.key}); +class ProfileButton extends StatelessWidget { + const ProfileButton({required this.getProfileDecorationImage, super.key}); final DecorationImage Function(File?) getProfileDecorationImage; From 720e61ea3af69db5abb1022c0a16d230c60d9be0 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 26 Jan 2024 15:13:12 +0000 Subject: [PATCH 139/334] Fix linter --- uni/lib/view/common_widgets/pages_layouts/general/general.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 940666ed0..ad1aaab81 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -9,8 +9,8 @@ import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/profile_button.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; /// Page with a hamburger menu and the user profile picture abstract class GeneralPageViewState extends State { From 11f37a7cf272ac30000d35aab446e4538fee7062 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Fri, 26 Jan 2024 15:39:39 +0000 Subject: [PATCH 140/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 59dd2f0fd..15804654e 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.33+223 \ No newline at end of file +1.7.34+224 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index af3d35d14..a8be1e66b 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.33+223 +version: 1.7.34+224 environment: sdk: ">=3.0.0 <4.0.0" From 2ca23c1e4c308061a11983be2bef83ebed3afd8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 15:41:40 +0000 Subject: [PATCH 141/334] Bump build_runner from 2.4.7 to 2.4.8 in /uni Bumps [build_runner](https://github.com/dart-lang/build) from 2.4.7 to 2.4.8. - [Release notes](https://github.com/dart-lang/build/releases) - [Commits](https://github.com/dart-lang/build/compare/build_runner-v2.4.7...build_runner-v2.4.8) --- updated-dependencies: - dependency-name: build_runner dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 36 ++++++++++++++++++------------------ uni/pubspec.yaml | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index f9923f69d..ec3ca3268 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -109,10 +109,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" + sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" url: "https://pub.dev" source: hosted - version: "2.4.7" + version: "2.4.8" build_runner_core: dependency: transitive description: @@ -205,10 +205,10 @@ packages: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" connectivity_plus: dependency: "direct main" description: @@ -617,10 +617,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" mgrs_dart: dependency: transitive description: @@ -1046,18 +1046,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" stream_transform: dependency: transitive description: @@ -1094,26 +1094,26 @@ packages: dependency: "direct dev" description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.5.3" timelines: dependency: "direct main" description: @@ -1294,10 +1294,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index a8be1e66b..75312c801 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -57,7 +57,7 @@ dependencies: workmanager: ^0.5.2 dev_dependencies: - build_runner: ^2.4.7 + build_runner: ^2.4.8 flutter_launcher_icons: ^0.13.1 flutter_test: sdk: flutter From 97401ce7177fe75cfec8390deadc949fa156353f Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Fri, 26 Jan 2024 15:29:25 +0000 Subject: [PATCH 142/334] Fix favorite restaurant card --- .../widgets/restaurant_page_card.dart | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart index 35dfdcfe5..8b99c8c86 100644 --- a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart +++ b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart @@ -34,32 +34,43 @@ class RestaurantPageCard extends GenericCard { void onRefresh(BuildContext context) {} } -class CardFavoriteButton extends StatelessWidget { +class CardFavoriteButton extends StatefulWidget { const CardFavoriteButton(this.restaurant, {super.key}); final Restaurant restaurant; + @override + State createState() { + return CardFavoriteButtonState(); + } +} + +class CardFavoriteButtonState extends State { @override Widget build(BuildContext context) { - final isFavorite = PreferencesController.getFavoriteRestaurants() - .contains(restaurant.name); + var isFavorite = PreferencesController.getFavoriteRestaurants() + .contains(widget.restaurant.name); return IconButton( icon: isFavorite ? Icon(MdiIcons.heart) : Icon(MdiIcons.heartOutline), onPressed: () async { final favoriteRestaurants = PreferencesController.getFavoriteRestaurants(); - if (favoriteRestaurants.contains(restaurant.name)) { - favoriteRestaurants.remove(restaurant.name); + if (favoriteRestaurants.contains(widget.restaurant.name)) { + favoriteRestaurants.remove(widget.restaurant.name); } else { - favoriteRestaurants.add(restaurant.name); + favoriteRestaurants.add(widget.restaurant.name); } - await PreferencesController.saveFavoriteRestaurants( - favoriteRestaurants, - ); + + setState(() { + PreferencesController.saveFavoriteRestaurants( + favoriteRestaurants, + ); + isFavorite = !isFavorite; + }); final favoriteCardTypes = PreferencesController.getFavoriteCards(); if (context.mounted && - !isFavorite && + isFavorite && !favoriteCardTypes.contains(FavoriteWidgetType.restaurant)) { showRestaurantCardHomeDialog( context, From 20696b39f67dc1a95286a91e00cf6244e4320552 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 15:41:39 +0000 Subject: [PATCH 143/334] Bump image from 4.1.3 to 4.1.4 in /uni Bumps [image](https://github.com/brendan-duncan/image) from 4.1.3 to 4.1.4. - [Changelog](https://github.com/brendan-duncan/image/blob/main/CHANGELOG.md) - [Commits](https://github.com/brendan-duncan/image/compare/4.1.3...4.1.4) --- updated-dependencies: - dependency-name: image dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 4 ++-- uni/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index ec3ca3268..5c1ccd0e4 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -513,10 +513,10 @@ packages: dependency: "direct main" description: name: image - sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" + sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d" url: "https://pub.dev" source: hosted - version: "4.1.3" + version: "4.1.4" intl: dependency: "direct main" description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 75312c801..eed400fe3 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -37,7 +37,7 @@ dependencies: flutter_widget_from_html_core: ^0.14.9 html: ^0.15.0 http: ^1.1.0 - image: ^4.0.13 + image: ^4.1.4 intl: ^0.18.0 latlong2: ^0.9.0 logger: ^2.0.2+1 From bc3129d5d277afde1de39c345d16167402f9cba0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 15:41:48 +0000 Subject: [PATCH 144/334] Bump flutter_widget_from_html_core from 0.14.9 to 0.14.11 in /uni Bumps [flutter_widget_from_html_core](https://github.com/daohoangson/flutter_widget_from_html/tree/master/packages) from 0.14.9 to 0.14.11. - [Release notes](https://github.com/daohoangson/flutter_widget_from_html/releases) - [Commits](https://github.com/daohoangson/flutter_widget_from_html/commits/v0.14.11/packages) --- updated-dependencies: - dependency-name: flutter_widget_from_html_core dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 4 ++-- uni/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 5c1ccd0e4..538d739aa 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -449,10 +449,10 @@ packages: dependency: "direct main" description: name: flutter_widget_from_html_core - sha256: "86d40a9f26d10011664df057c950e9c348ee1a7dbf141f295a07b0075ffd780b" + sha256: "028f4989b9ff4907466af233d50146d807772600d98a3e895662fbdb09c39225" url: "https://pub.dev" source: hosted - version: "0.14.9" + version: "0.14.11" frontend_server_client: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index eed400fe3..8bcf70b2f 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -34,7 +34,7 @@ dependencies: flutter_map_marker_popup: ^5.0.0 flutter_markdown: ^0.6.0 flutter_svg: ^2.0.9 - flutter_widget_from_html_core: ^0.14.9 + flutter_widget_from_html_core: ^0.14.11 html: ^0.15.0 http: ^1.1.0 image: ^4.1.4 From 243b72060289e27d8dc65c8df16c493bdcac3c1f Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 11 Jan 2024 15:50:14 +0000 Subject: [PATCH 145/334] Tracking banner color bug fix --- uni/lib/view/home/widgets/tracking_banner.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/home/widgets/tracking_banner.dart b/uni/lib/view/home/widgets/tracking_banner.dart index 0dc67c325..a589bbb03 100644 --- a/uni/lib/view/home/widgets/tracking_banner.dart +++ b/uni/lib/view/home/widgets/tracking_banner.dart @@ -11,7 +11,9 @@ class TrackingBanner extends StatelessWidget { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), - color: Theme.of(context).primaryColor, + color: Theme.of(context).brightness == Brightness.light + ? Theme.of(context).primaryColor + : Theme.of(context).cardColor, ), margin: const EdgeInsets.all(10), child: MaterialBanner( From ace97b555de06a0c27498bccf3a41b04c22619ed Mon Sep 17 00:00:00 2001 From: bdmendes Date: Sat, 27 Jan 2024 15:11:45 +0000 Subject: [PATCH 146/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 15804654e..89b909fdf 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.34+224 \ No newline at end of file +1.7.35+225 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index ad84c4cb4..6d372a267 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.34+224 +version: 1.7.35+225 environment: sdk: ">=3.0.0 <4.0.0" From 05493e85fc051ad875e9c83fdf75e7c207867d26 Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Sat, 27 Jan 2024 17:24:04 +0000 Subject: [PATCH 147/334] Rearrange isFavorite variable --- uni/lib/view/restaurant/widgets/restaurant_page_card.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart index 8b99c8c86..561c45ee8 100644 --- a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart +++ b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart @@ -46,9 +46,11 @@ class CardFavoriteButton extends StatefulWidget { } class CardFavoriteButtonState extends State { + bool isFavorite = false; + @override Widget build(BuildContext context) { - var isFavorite = PreferencesController.getFavoriteRestaurants() + isFavorite = PreferencesController.getFavoriteRestaurants() .contains(widget.restaurant.name); return IconButton( icon: isFavorite ? Icon(MdiIcons.heart) : Icon(MdiIcons.heartOutline), From cca0cff5041f19f527495f4d4195f4cb103d1959 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 27 Jan 2024 22:55:15 +0100 Subject: [PATCH 148/334] Calendar card modifications --- .../view/calendar/widgets/calendar_card.dart | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/uni/lib/view/calendar/widgets/calendar_card.dart b/uni/lib/view/calendar/widgets/calendar_card.dart index 4d3a59d0b..0bf66e407 100644 --- a/uni/lib/view/calendar/widgets/calendar_card.dart +++ b/uni/lib/view/calendar/widgets/calendar_card.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/calendar_event.dart'; @@ -32,20 +33,32 @@ class ScrollableCalendarCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { - return SizedBox( - height: 200, - child: SingleChildScrollView( - child: LazyConsumer>( - builder: CalendarPageViewState().getTimeline, - hasContent: (calendar) => calendar.isNotEmpty, - onNullContent: const Center( - child: Text( - 'Nenhum evento encontrado', - style: TextStyle(fontSize: 18), - ), + return Expanded( + child: LazyConsumer>( + builder: (context, events) => CalendarPageViewState() + .getTimeline(context, getFurtherEvents(events)), + hasContent: (calendar) => calendar.isNotEmpty, + onNullContent: const Center( + child: Text( + 'Nenhum evento encontrado', + style: TextStyle(fontSize: 18), ), ), ), ); } + + List getFurtherEvents(List events) { + final currentMonth = DateFormat.MMMM('pt').format(DateTime.now()); + final pinEvent = events.firstWhere((element) { + final eventDate = element.date.split(' '); + final month = eventDate.where((element) => + DateFormat.MMMM('pt').dateSymbols.MONTHS.contains(element) || + element == 'TBD'); + return month.contains(currentMonth); + }); + + return events.sublist( + events.indexOf(pinEvent), events.indexOf(pinEvent) + 3); + } } From 0ce3820e2b0dc8a992f0acf9e06bc5efcb8c9fa7 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 27 Jan 2024 23:13:24 +0100 Subject: [PATCH 149/334] Using DateFormat --- uni/lib/utils/date_time_formatter.dart | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/uni/lib/utils/date_time_formatter.dart b/uni/lib/utils/date_time_formatter.dart index 64be5025b..50064da5f 100644 --- a/uni/lib/utils/date_time_formatter.dart +++ b/uni/lib/utils/date_time_formatter.dart @@ -15,10 +15,6 @@ extension DateTimeExtensions on DateTime { } String formattedDate(AppLocale locale) { - if (locale == AppLocale.pt) { - return '${weekDay(locale)}, $day de ${month(locale)}'; - } else { - return '${weekDay(locale)}, $day ${month(locale)}'; - } + return DateFormat.yMMMMd(locale.localeCode.languageCode).format(this); } } From a0aed4a7d7bb11d3cbdb30a5a0a74d09e4403600 Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Sun, 28 Jan 2024 11:04:54 +0000 Subject: [PATCH 150/334] Initialize variable differently --- uni/lib/view/restaurant/widgets/restaurant_page_card.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart index 561c45ee8..cd6e7852a 100644 --- a/uni/lib/view/restaurant/widgets/restaurant_page_card.dart +++ b/uni/lib/view/restaurant/widgets/restaurant_page_card.dart @@ -49,9 +49,14 @@ class CardFavoriteButtonState extends State { bool isFavorite = false; @override - Widget build(BuildContext context) { + void initState() { + super.initState(); isFavorite = PreferencesController.getFavoriteRestaurants() .contains(widget.restaurant.name); + } + + @override + Widget build(BuildContext context) { return IconButton( icon: isFavorite ? Icon(MdiIcons.heart) : Icon(MdiIcons.heartOutline), onPressed: () async { From 01fa924417d99fbcd57129126b72e2c37bf4651a Mon Sep 17 00:00:00 2001 From: bdmendes Date: Sun, 28 Jan 2024 15:26:56 +0000 Subject: [PATCH 151/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 89b909fdf..d7705724a 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.35+225 \ No newline at end of file +1.7.36+226 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 05c2d5a62..8cb2e0024 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.35+225 +version: 1.7.36+226 environment: sdk: ">=3.0.0 <4.0.0" From 7ee6ceadf6b0bfc492ec20ba295619fec180a585 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:38:50 +0000 Subject: [PATCH 152/334] Bump diacritic from 0.1.4 to 0.1.5 in /uni Bumps [diacritic](https://github.com/agilord/diacritic) from 0.1.4 to 0.1.5. - [Changelog](https://github.com/agilord/diacritic/blob/master/CHANGELOG.md) - [Commits](https://github.com/agilord/diacritic/commits) --- updated-dependencies: - dependency-name: diacritic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 36 ++++++++++++++++++------------------ uni/pubspec.yaml | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 1eeb6a504..a2a141819 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -221,10 +221,10 @@ packages: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" connectivity_plus: dependency: "direct main" description: @@ -309,10 +309,10 @@ packages: dependency: "direct main" description: name: diacritic - sha256: a84e03ec2779375fb86430dbe9d8fba62c68376f2499097a5f6e75556babe706 + sha256: "96db5db6149cbe4aa3cfcbfd170aca9b7648639be7e48025f9d458517f807fe4" url: "https://pub.dev" source: hosted - version: "0.1.4" + version: "0.1.5" email_validator: dependency: "direct main" description: @@ -641,10 +641,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" mgrs_dart: dependency: transitive description: @@ -1078,18 +1078,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" stream_transform: dependency: transitive description: @@ -1126,26 +1126,26 @@ packages: dependency: "direct dev" description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.5.3" timelines: dependency: "direct main" description: @@ -1342,10 +1342,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 8cb2e0024..000dc22d2 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: crypto: ^3.0.1 cupertino_icons: ^1.0.2 currency_text_input_formatter: ^2.1.5 - diacritic: ^0.1.4 + diacritic: ^0.1.5 email_validator: ^2.0.1 encrypt: ^5.0.3 expansion_tile_card: ^3.0.0 From f17fa8d045b03d71f6eede38af0ad3d7050b71d4 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Thu, 1 Feb 2024 14:39:04 +0000 Subject: [PATCH 153/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index d7705724a..3b89259de 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.36+226 \ No newline at end of file +1.7.37+227 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 000dc22d2..ac9c3ad04 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.36+226 +version: 1.7.37+227 environment: sdk: ">=3.0.0 <4.0.0" From e1bc83c98f323cef60a71c26c13284d5b96e277a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:45:50 +0000 Subject: [PATCH 154/334] Bump path_provider from 2.1.1 to 2.1.2 in /uni Bumps [path_provider](https://github.com/flutter/packages/tree/main/packages/path_provider) from 2.1.1 to 2.1.2. - [Release notes](https://github.com/flutter/packages/releases) - [Commits](https://github.com/flutter/packages/commits/path_provider-v2.1.2/packages/path_provider) --- updated-dependencies: - dependency-name: path_provider dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- uni/pubspec.lock | 4 ++-- uni/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index a2a141819..6fe712807 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -745,10 +745,10 @@ packages: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_android: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index ac9c3ad04..fc6cb7140 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -45,7 +45,7 @@ dependencies: logger: ^2.0.2+1 material_design_icons_flutter: ^7.0.7296 path: ^1.8.0 - path_provider: ^2.0.0 + path_provider: ^2.1.2 percent_indicator: ^4.2.2 plausible_analytics: ^0.3.0 provider: ^6.1.1 From 5bf36c627cdd8806ca6f37808af20a17152caf48 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 2 Feb 2024 14:59:42 +0000 Subject: [PATCH 155/334] Solving files format --- uni/lib/controller/parsers/parser_course_unit_info.dart | 9 ++++++--- .../course_unit_info/widgets/course_unit_files_row.dart | 3 +-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 9bf3deac7..ad8751b19 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -22,12 +22,15 @@ Future> parseFiles( final files = []; for (final file in item['ficheiros'] as List) { if (file is Map) { - final fileName = file['nome'].toString(); - final fileDate = file['data_actualizacao'].toString(); + final fileName = file['nome']; + final fileDate = file['data_actualizacao']; final fileCode = file['codigo'].toString(); + final format = file['filename'] + .toString() + .substring(file['filename'].toString().indexOf('.')); final url = await CourseUnitsInfoFetcher().getDownloadLink(session); final courseUnitFile = CourseUnitFile( - '${fileName}_$fileDate', + '${fileName}_$fileDate$format', url, fileCode, ); diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index e2e73a926..8483be5a3 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -85,12 +85,11 @@ class CourseUnitFilesRowState extends State try { final result = await loadFileFromStorageOrRetrieveNew( - '${unitFile.name}.pdf', + unitFile.name, unitFile.url, session, headers: {'pct_id': unitFile.fileCode}, ); - await OpenFile.open(result!.path); } catch (e) { if (context.mounted) { From bf4a0368595f61c5cc0fb61b8df5e7b1d5e2bae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Mon, 5 Feb 2024 09:52:11 +0000 Subject: [PATCH 156/334] fix: display multiple rooms --- uni/lib/view/schedule/widgets/schedule_slot.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index efea349fc..9036d3ab0 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -64,10 +64,9 @@ class ScheduleSlot extends StatelessWidget { style: Theme.of(context).textTheme.bodyMedium, alignment: TextAlign.center, ); - final roomTextField = TextFieldWidget( - text: rooms, + final roomTextField = Text( + rooms, style: Theme.of(context).textTheme.bodyMedium, - alignment: TextAlign.right, ); return [ ScheduleTimeWidget( @@ -98,7 +97,7 @@ class ScheduleSlot extends StatelessWidget { ], ), ), - roomTextField, + roomTextField ]; } } From c0e47c4939114c421c5a232a8e70a105bb1a6e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Mon, 5 Feb 2024 10:24:37 +0000 Subject: [PATCH 157/334] style: add trailing comma --- uni/lib/view/schedule/widgets/schedule_slot.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index 9036d3ab0..1f1ee234f 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -97,7 +97,7 @@ class ScheduleSlot extends StatelessWidget { ], ), ), - roomTextField + roomTextField, ]; } } From be2702fbde63b5210396a23a642968244813ce14 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Mon, 5 Feb 2024 12:00:57 +0000 Subject: [PATCH 158/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 3b89259de..61320f0c3 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.37+227 \ No newline at end of file +1.7.38+228 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index fc6cb7140..fbc01abfa 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.37+227 +version: 1.7.38+228 environment: sdk: ">=3.0.0 <4.0.0" From 99e23fdfbe473565fc26c8b47c9dcffb9f83f059 Mon Sep 17 00:00:00 2001 From: thePeras Date: Wed, 7 Feb 2024 11:26:12 +0000 Subject: [PATCH 159/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 61320f0c3..8edf52239 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.38+228 \ No newline at end of file +1.7.39+229 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index fbc01abfa..020a8ef0c 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.38+228 +version: 1.7.39+229 environment: sdk: ">=3.0.0 <4.0.0" From bebb2c11da307b1b65993ad194b24d762f55afa5 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 7 Feb 2024 12:59:57 +0000 Subject: [PATCH 160/334] Modifying model and sorting card --- uni/lib/model/entities/calendar_event.dart | 25 ++++++++++++++++++- .../view/calendar/widgets/calendar_card.dart | 23 ++++++++--------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/uni/lib/model/entities/calendar_event.dart b/uni/lib/model/entities/calendar_event.dart index 733a3909a..d46ad8a51 100644 --- a/uni/lib/model/entities/calendar_event.dart +++ b/uni/lib/model/entities/calendar_event.dart @@ -1,12 +1,35 @@ +import 'package:intl/intl.dart'; + /// An event in the school calendar class CalendarEvent { /// Creates an instance of the class [CalendarEvent] - CalendarEvent(this.name, this.date); + CalendarEvent(this.name, this.date, {this.closeDate}) { + name = name; + date = date; + closeDate = getDateTime(); + } String name; String date; + DateTime? closeDate; /// Converts the event into a map Map toMap() { return {'name': name, 'date': date}; } + + DateTime? getDateTime() { + final splitDate = date.split(' '); + final month = splitDate.firstWhere( + (element) => + DateFormat.MMMM('pt').dateSymbols.MONTHS.contains(element) || + element == 'TBD', + ); + + try { + return DateFormat('dd MMMM yyyy', 'pt') + .parse('${splitDate[0]} $month ${splitDate.last}'); + } catch (e) { + return null; + } + } } diff --git a/uni/lib/view/calendar/widgets/calendar_card.dart b/uni/lib/view/calendar/widgets/calendar_card.dart index 0bf66e407..5bb12c95e 100644 --- a/uni/lib/view/calendar/widgets/calendar_card.dart +++ b/uni/lib/view/calendar/widgets/calendar_card.dart @@ -1,5 +1,5 @@ +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/calendar_event.dart'; @@ -49,16 +49,15 @@ class ScrollableCalendarCard extends GenericCard { } List getFurtherEvents(List events) { - final currentMonth = DateFormat.MMMM('pt').format(DateTime.now()); - final pinEvent = events.firstWhere((element) { - final eventDate = element.date.split(' '); - final month = eventDate.where((element) => - DateFormat.MMMM('pt').dateSymbols.MONTHS.contains(element) || - element == 'TBD'); - return month.contains(currentMonth); - }); - - return events.sublist( - events.indexOf(pinEvent), events.indexOf(pinEvent) + 3); + final sortedEvents = events + .where((element) => element.closeDate != null) + .sorted((a, b) => a.closeDate!.compareTo(b.closeDate!)); + final pinEvent = sortedEvents.firstWhere( + (element) => element.closeDate!.compareTo(DateTime.now()) == 1, + ); + return sortedEvents.sublist( + sortedEvents.indexOf(pinEvent), + sortedEvents.indexOf(pinEvent) + 3, + ); } } From 7246f7a5a363876c257f7fbd9b71fc6d58e98e81 Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Wed, 7 Feb 2024 14:21:40 +0000 Subject: [PATCH 161/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 8edf52239..45bd10fd8 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.39+229 \ No newline at end of file +1.7.40+230 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 020a8ef0c..ced9e9327 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.39+229 +version: 1.7.40+230 environment: sdk: ">=3.0.0 <4.0.0" From 9ed3beee0df3bacfca680902f1b072e2bb98dd23 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 7 Feb 2024 14:21:47 +0000 Subject: [PATCH 162/334] Translating --- uni/lib/generated/intl/messages_en.dart | 1 + uni/lib/generated/intl/messages_pt_PT.dart | 2 ++ uni/lib/generated/l10n.dart | 10 ++++++++++ uni/lib/l10n/intl_en.arb | 2 ++ uni/lib/l10n/intl_pt_PT.arb | 2 ++ uni/lib/view/calendar/widgets/calendar_card.dart | 12 ++++++------ uni/lib/view/faculty/faculty.dart | 2 +- 7 files changed, 24 insertions(+), 7 deletions(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index ba2734cd9..390212cb9 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -178,6 +178,7 @@ class MessageLookup extends MessageLookupByLibrary { "no_data": MessageLookupByLibrary.simpleMessage( "There is no data to show at this time"), "no_date": MessageLookupByLibrary.simpleMessage("No date"), + "no_events": MessageLookupByLibrary.simpleMessage("No events found"), "no_exams": MessageLookupByLibrary.simpleMessage( "You have no exams scheduled\n"), "no_exams_label": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 72c27a6c0..0a46267d6 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -179,6 +179,8 @@ class MessageLookup extends MessageLookupByLibrary { "no_data": MessageLookupByLibrary.simpleMessage( "Não há dados a mostrar neste momento"), "no_date": MessageLookupByLibrary.simpleMessage("Sem data"), + "no_events": + MessageLookupByLibrary.simpleMessage("Nenhum evento encontrado"), "no_exams": MessageLookupByLibrary.simpleMessage("Não possui exames marcados"), "no_exams_label": diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 7f3a4bd6c..1cb115462 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -966,6 +966,16 @@ class S { ); } + /// `No events found` + String get no_events { + return Intl.message( + 'No events found', + name: 'no_events', + desc: '', + args: [], + ); + } + /// `You have no exams scheduled\n` String get no_exams { return Intl.message( diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 50bc73f75..9cf5a663b 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -188,6 +188,8 @@ "@no_data": {}, "no_date": "No date", "@no_date": {}, + "no_events": "No events found", + "@no_events": {}, "no_exams": "You have no exams scheduled\n", "@no_exams": {}, "no_exams_label": "Looks like you are on vacation!", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 80a941fc5..f74a86818 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -190,6 +190,8 @@ "@no_date": {}, "no_exams": "Não possui exames marcados", "@no_exams": {}, + "no_events": "Nenhum evento encontrado", + "@no_events": {}, "no_exams_label": "Parece que estás de férias!", "@no_exams_label": {}, "no_favorite_restaurants": "Sem restaurantes favoritos", diff --git a/uni/lib/view/calendar/widgets/calendar_card.dart b/uni/lib/view/calendar/widgets/calendar_card.dart index 5bb12c95e..ec84889ef 100644 --- a/uni/lib/view/calendar/widgets/calendar_card.dart +++ b/uni/lib/view/calendar/widgets/calendar_card.dart @@ -9,10 +9,10 @@ import 'package:uni/view/calendar/calendar.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; -class ScrollableCalendarCard extends GenericCard { - ScrollableCalendarCard({super.key}); +class CalendarCard extends GenericCard { + CalendarCard({super.key}); - const ScrollableCalendarCard.fromEditingInformation( + const CalendarCard.fromEditingInformation( super.key, { required super.editingMode, super.onDelete, @@ -38,10 +38,10 @@ class ScrollableCalendarCard extends GenericCard { builder: (context, events) => CalendarPageViewState() .getTimeline(context, getFurtherEvents(events)), hasContent: (calendar) => calendar.isNotEmpty, - onNullContent: const Center( + onNullContent: Center( child: Text( - 'Nenhum evento encontrado', - style: TextStyle(fontSize: 18), + S.of(context).no_events, + style: const TextStyle(fontSize: 18), ), ), ), diff --git a/uni/lib/view/faculty/faculty.dart b/uni/lib/view/faculty/faculty.dart index de2d125cd..f883ec715 100644 --- a/uni/lib/view/faculty/faculty.dart +++ b/uni/lib/view/faculty/faculty.dart @@ -35,7 +35,7 @@ class FacultyPageViewState extends GeneralPageViewState { children: [ MapCard(), LibraryOccupationCard(), - ScrollableCalendarCard(), + CalendarCard(), getUtilsSection(), ], ), From eec6c1ab360a572b45596cff882049d70ece5068 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Wed, 7 Feb 2024 15:33:07 +0000 Subject: [PATCH 163/334] Fix remaining exams display --- uni/lib/model/entities/exam.dart | 3 ++- uni/lib/view/exams/widgets/exam_row.dart | 2 +- uni/lib/view/home/widgets/exam_card.dart | 12 ++++-------- uni/lib/view/home/widgets/remaining_exams_card.dart | 3 ++- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/uni/lib/model/entities/exam.dart b/uni/lib/model/entities/exam.dart index 0d27cb1bc..ed1df9e70 100644 --- a/uni/lib/model/entities/exam.dart +++ b/uni/lib/model/entities/exam.dart @@ -88,7 +88,8 @@ class Exam { @override bool operator ==(Object other) => - identical(this, other) || other is Exam && id == other.id; + identical(this, other) || + other is Exam && id == other.id && subject == other.subject; @override int get hashCode => id.hashCode; diff --git a/uni/lib/view/exams/widgets/exam_row.dart b/uni/lib/view/exams/widgets/exam_row.dart index 4af26b10a..e6175c2a1 100644 --- a/uni/lib/view/exams/widgets/exam_row.dart +++ b/uni/lib/view/exams/widgets/exam_row.dart @@ -122,7 +122,7 @@ class _ExamRowState extends State { } Widget? getExamRooms(BuildContext context) { - if (widget.exam.rooms[0] == '') return null; + if (widget.exam.rooms.isEmpty || widget.exam.rooms[0] == '') return null; return Wrap( spacing: 13, children: roomsList(context, widget.exam.rooms), diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index ce6df7641..8d7258b5e 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -40,13 +40,13 @@ class ExamCard extends GenericCard { builder: (context, allExams) { final nextExams = getPrimaryExams( allExams, - allExams.isNotEmpty ? allExams.first : null, ); return Column( mainAxisSize: MainAxisSize.min, children: [ NextExamsWidget(exams: nextExams), - if (nextExams.length < maxExamsToDisplay) + if (nextExams.length < maxExamsToDisplay && + allExams.length > nextExams.length) Column( children: [ Container( @@ -86,13 +86,9 @@ class ExamCard extends GenericCard { ); } - List getPrimaryExams(List allExams, Exam? nextExam) { - if (nextExam == null) { - return []; - } - + List getPrimaryExams(List allExams) { return allExams - .where((exam) => isSameDay(nextExam.begin, exam.begin)) + .where((exam) => isSameDay(allExams[0].begin, exam.begin)) .toList(); } diff --git a/uni/lib/view/home/widgets/remaining_exams_card.dart b/uni/lib/view/home/widgets/remaining_exams_card.dart index 8ae7230de..1b2afeb97 100644 --- a/uni/lib/view/home/widgets/remaining_exams_card.dart +++ b/uni/lib/view/home/widgets/remaining_exams_card.dart @@ -7,6 +7,7 @@ import 'package:uni/view/locale_notifier.dart'; class RemainingExamsWidget extends StatelessWidget { const RemainingExamsWidget({required this.exams, super.key}); + final List exams; @override @@ -24,7 +25,7 @@ class RemainingExamsWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '${exam.begin.day} de ${exam.month(locale)}', + '${exam.begin.day} ${exam.month(locale)}', style: Theme.of(context).textTheme.bodyLarge, ), ExamTitle( From b1b231cbdb8314de7135f14b6e73c48aeac2509b Mon Sep 17 00:00:00 2001 From: thePeras Date: Wed, 7 Feb 2024 15:56:29 +0000 Subject: [PATCH 164/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 45bd10fd8..cdf416e6b 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.40+230 \ No newline at end of file +1.7.41+231 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index ced9e9327..a80895538 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.40+230 +version: 1.7.41+231 environment: sdk: ">=3.0.0 <4.0.0" From 504aa3489fb456fb49216a45085fd8c96e496cb0 Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 5 Jan 2024 01:50:33 +0000 Subject: [PATCH 165/334] Added useMaterial3 to light and dark themes data. --- uni/lib/view/theme.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/uni/lib/view/theme.dart b/uni/lib/view/theme.dart index 1a444ec77..df20bcf8a 100644 --- a/uni/lib/view/theme.dart +++ b/uni/lib/view/theme.dart @@ -25,6 +25,7 @@ const _textTheme = TextTheme( ); ThemeData applicationLightTheme = ThemeData( + useMaterial3: true, colorScheme: ColorScheme.fromSeed( seedColor: darkRed, background: _mildWhite, @@ -77,6 +78,7 @@ ThemeData applicationLightTheme = ThemeData( ); ThemeData applicationDarkTheme = ThemeData( + useMaterial3: true, colorScheme: ColorScheme.fromSeed( seedColor: lightRed, brightness: Brightness.dark, From 5f39333fc5573f539d001469d5f8ce05c16c7a17 Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 19 Jan 2024 18:27:32 +0000 Subject: [PATCH 166/334] Simplified color scheme and made some adjustements --- .../view/home/widgets/main_cards_list.dart | 1 + uni/lib/view/theme.dart | 55 ------------------- 2 files changed, 1 insertion(+), 55 deletions(-) diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index 859081327..421981e4b 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -82,6 +82,7 @@ class MainCardsListState extends State { Widget createActionButton(BuildContext context) { return FloatingActionButton( + backgroundColor: Theme.of(context).colorScheme.secondary, onPressed: () => showDialog( context: context, builder: (BuildContext context) { diff --git a/uni/lib/view/theme.dart b/uni/lib/view/theme.dart index df20bcf8a..c7c0f37f8 100644 --- a/uni/lib/view/theme.dart +++ b/uni/lib/view/theme.dart @@ -36,45 +36,14 @@ ThemeData applicationLightTheme = ThemeData( tertiary: lightRed, onTertiary: Colors.black, ), - brightness: Brightness.light, primaryColor: darkRed, - textSelectionTheme: const TextSelectionThemeData( - selectionHandleColor: Colors.transparent, - ), - canvasColor: _mildWhite, - scaffoldBackgroundColor: _mildWhite, - cardColor: Colors.white, - hintColor: _lightGrey, dividerColor: _lightGrey, - indicatorColor: darkRed, primaryTextTheme: Typography().black.copyWith( headlineMedium: const TextStyle(color: _strongGrey), bodyLarge: const TextStyle(color: _strongGrey), ), iconTheme: const IconThemeData(color: darkRed), textTheme: _textTheme, - switchTheme: SwitchThemeData( - thumbColor: MaterialStateProperty.resolveWith( - (Set states) => - states.contains(MaterialState.selected) ? darkRed : null, - ), - trackColor: MaterialStateProperty.resolveWith( - (Set states) => - states.contains(MaterialState.selected) ? darkRed : null, - ), - ), - radioTheme: RadioThemeData( - fillColor: MaterialStateProperty.resolveWith( - (Set states) => - states.contains(MaterialState.selected) ? darkRed : null, - ), - ), - checkboxTheme: CheckboxThemeData( - fillColor: MaterialStateProperty.resolveWith( - (Set states) => - states.contains(MaterialState.selected) ? darkRed : null, - ), - ), ); ThemeData applicationDarkTheme = ThemeData( @@ -90,13 +59,7 @@ ThemeData applicationDarkTheme = ThemeData( tertiary: _lightGrey, onTertiary: _darkishBlack, ), - brightness: Brightness.dark, - textSelectionTheme: const TextSelectionThemeData( - selectionHandleColor: Colors.transparent, - ), primaryColor: _lightGrey, - canvasColor: _darkBlack, - scaffoldBackgroundColor: _darkBlack, cardColor: _mildBlack, hintColor: _darkishBlack, dividerColor: _strongGrey, @@ -104,22 +67,4 @@ ThemeData applicationDarkTheme = ThemeData( primaryTextTheme: Typography().white, iconTheme: const IconThemeData(color: _lightGrey), textTheme: _textTheme.apply(bodyColor: _lightGrey), - switchTheme: SwitchThemeData( - trackColor: MaterialStateProperty.resolveWith( - (Set states) => - states.contains(MaterialState.selected) ? _lightGrey : null, - ), - ), - radioTheme: RadioThemeData( - fillColor: MaterialStateProperty.resolveWith( - (Set states) => - states.contains(MaterialState.selected) ? _mildBlack : null, - ), - ), - checkboxTheme: CheckboxThemeData( - fillColor: MaterialStateProperty.resolveWith( - (Set states) => - states.contains(MaterialState.selected) ? _mildBlack : null, - ), - ), ); From 79585957ddacdafb8cf25501db532f47a8f6c879 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Wed, 7 Feb 2024 16:41:00 +0000 Subject: [PATCH 167/334] Fix prints button and restaurants card background --- uni/lib/generated/intl/messages_pt_PT.dart | 3 +- uni/lib/l10n/intl_pt_PT.arb | 2 +- .../view/home/widgets/restaurant_card.dart | 29 ++------ uni/lib/view/profile/profile.dart | 1 + .../view/profile/widgets/print_info_card.dart | 69 +++++++------------ uni/pubspec.lock | 2 +- 6 files changed, 36 insertions(+), 70 deletions(-) diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index a56df4f8d..ecc041c37 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -40,6 +40,7 @@ class MessageLookup extends MessageLookupByLibrary { })}"; final messages = _notInlinedMessages(_notInlinedMessages); + static Map _notInlinedMessages(_) => { "about": MessageLookupByLibrary.simpleMessage("Sobre nós"), "academic_services": @@ -73,7 +74,7 @@ class MessageLookup extends MessageLookupByLibrary { "Configura aqui os teus autocarros"), "buses_text": MessageLookupByLibrary.simpleMessage( "Os autocarros favoritos serão apresentados no widget \'Autocarros\' dos favoritos. Os restantes serão apresentados apenas na página."), - "cancel": MessageLookupByLibrary.simpleMessage("Cancelar\n"), + "cancel": MessageLookupByLibrary.simpleMessage("Cancelar"), "change": MessageLookupByLibrary.simpleMessage("Alterar"), "change_prompt": MessageLookupByLibrary.simpleMessage( "Deseja alterar a palavra-passe?"), diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 184c86d93..b674d046f 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -54,7 +54,7 @@ "@buses_text": {}, "bus_information": "Seleciona os autocarros dos quais queres informação:", "@bus_information": {}, - "cancel": "Cancelar\n", + "cancel": "Cancelar", "@cancel": {}, "change": "Alterar", "@change": {}, diff --git a/uni/lib/view/home/widgets/restaurant_card.dart b/uni/lib/view/home/widgets/restaurant_card.dart index 17a0ae005..46dcac5ce 100644 --- a/uni/lib/view/home/widgets/restaurant_card.dart +++ b/uni/lib/view/home/widgets/restaurant_card.dart @@ -8,7 +8,6 @@ import 'package:uni/model/providers/lazy/restaurant_provider.dart'; import 'package:uni/model/utils/day_of_week.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/common_widgets/row_container.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/restaurant/widgets/restaurant_slot.dart'; @@ -140,29 +139,15 @@ class RestaurantCard extends GenericCard { ), ), if (meals.isNotEmpty) - Card( - elevation: 0, - child: RowContainer( - borderColor: Colors.transparent, - color: const Color.fromARGB(0, 0, 0, 0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: createRestaurantRows(meals, context), - ), - ), + Column( + mainAxisSize: MainAxisSize.min, + children: createRestaurantRows(meals, context), ) else - Card( - elevation: 0, - child: RowContainer( - borderColor: Colors.transparent, - color: const Color.fromARGB(0, 0, 0, 0), - child: Container( - padding: const EdgeInsets.fromLTRB(9, 0, 0, 0), - width: 400, - child: Text(S.of(context).no_menu_info), - ), - ), + Container( + padding: const EdgeInsets.fromLTRB(9, 0, 0, 0), + width: 400, + child: Text(S.of(context).no_menu_info), ), const SizedBox(height: 10), ], diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 210ad2f5a..9f0d02085 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -45,6 +45,7 @@ class ProfilePageViewState extends SecondaryPageViewState { const Padding(padding: EdgeInsets.all(5)), ...courseWidgets, AccountInfoCard(), + const Padding(padding: EdgeInsets.all(5)), PrintInfoCard(), ], ); diff --git a/uni/lib/view/profile/widgets/print_info_card.dart b/uni/lib/view/profile/widgets/print_info_card.dart index e9480a726..d99b93116 100644 --- a/uni/lib/view/profile/widgets/print_info_card.dart +++ b/uni/lib/view/profile/widgets/print_info_card.dart @@ -31,51 +31,30 @@ class PrintInfoCard extends GenericCard { } Widget getPrintInfo(BuildContext context, Profile profile) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Table( - columnWidths: const { - 1: FractionColumnWidth(0.4), - 2: FractionColumnWidth(.1), - }, - defaultVerticalAlignment: TableCellVerticalAlignment.middle, - children: [ - TableRow( - children: [ - Container( - margin: const EdgeInsets.only( - top: 20, - bottom: 20, - left: 20, - ), - child: Text( - S.of(context).available_amount, - style: Theme.of(context).textTheme.titleSmall, - ), - ), - Container( - margin: const EdgeInsets.only(right: 15), - child: Text( - profile.printBalance, - textAlign: TextAlign.end, - style: Theme.of(context).textTheme.titleLarge, - ), - ), - Container( - margin: const EdgeInsets.only(right: 5), - height: 30, - child: ElevatedButton( - style: OutlinedButton.styleFrom( - padding: EdgeInsets.zero, - ), - onPressed: () => addMoneyDialog(context), - child: const Center(child: Icon(Icons.add)), - ), - ), - ], - ), - ], + return Row( + children: [ + Container( + margin: const EdgeInsets.only( + top: 20, + bottom: 20, + left: 20, + ), + child: Text( + S.of(context).available_amount, + style: Theme.of(context).textTheme.titleSmall, + ), + ), + const Spacer(), + Expanded( + child: Text( + profile.printBalance, + textAlign: TextAlign.end, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + IconButton( + onPressed: () => addMoneyDialog(context), + icon: const Icon(Icons.add), ), ], ); diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 6fe712807..37857c0cf 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -1411,5 +1411,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" + dart: ">=3.1.3 <4.0.0" flutter: ">=3.13.7" From 00e639fec274f17f35700dfc24effe55dd43a34e Mon Sep 17 00:00:00 2001 From: bdmendes Date: Wed, 7 Feb 2024 16:44:30 +0000 Subject: [PATCH 168/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index cdf416e6b..8f071ccda 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.41+231 \ No newline at end of file +1.7.42+232 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index a80895538..4fa607d55 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.41+231 +version: 1.7.42+232 environment: sdk: ">=3.0.0 <4.0.0" From 986f363ea343a6b599e9322ae208721d059cab82 Mon Sep 17 00:00:00 2001 From: Bruno Mendes <61701401+bdmendes@users.noreply.github.com> Date: Tue, 30 Jan 2024 11:23:55 +0000 Subject: [PATCH 169/334] Make dependabot ignore patch updates --- .github/dependabot.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index db82b56ed..a428d898c 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -1,8 +1,11 @@ version: 2 -enable-beta-ecosystems: true updates: - package-ecosystem: "pub" directory: "/uni" schedule: - interval: "weekly" \ No newline at end of file + interval: "daily" + ignore: + - dependency-name: "*" + update-types: ["version-update:semver-patch"] + From 7f3eb4fb88e78f3775a498fbda0e801f52d53ebc Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 26 Jan 2024 12:10:15 +0000 Subject: [PATCH 170/334] Added widgets to aggregate Academic Path page --- uni/lib/main.dart | 6 ++ uni/lib/utils/drawer_items.dart | 3 +- uni/lib/view/academic_path/academic_path.dart | 53 ++++++++++++ .../widgets/course_units_card.dart | 80 +++++++++++++++++++ 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 uni/lib/view/academic_path/academic_path.dart create mode 100644 uni/lib/view/academic_path/widgets/course_units_card.dart diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 1dcbdcdc6..734b2c102 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -29,6 +29,7 @@ import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/model/providers/state_providers.dart'; import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/academic_path/academic_path.dart'; import 'package:uni/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart'; import 'package:uni/view/calendar/calendar.dart'; import 'package:uni/view/common_widgets/page_transition.dart'; @@ -273,6 +274,11 @@ class ApplicationState extends State { page: const UsefulInfoPageView(), settings: settings, ), + '/${DrawerItem.navAcademicPath.title}': + PageTransition.makePageTransition( + page: const AcademicPathPageView(), + settings: settings, + ), }; return transitions[settings.name]; }, diff --git a/uni/lib/utils/drawer_items.dart b/uni/lib/utils/drawer_items.dart index 09d1e6f7d..a85b50d3b 100644 --- a/uni/lib/utils/drawer_items.dart +++ b/uni/lib/utils/drawer_items.dart @@ -8,7 +8,8 @@ enum DrawerItem { navRestaurants('restaurantes'), navCalendar('calendario'), navLibrary('biblioteca', faculties: {'feup'}), - navUsefulInfo('uteis', faculties: {'feup'}); + navUsefulInfo('uteis', faculties: {'feup'}), + navAcademicPath('percurso academico'); const DrawerItem(this.title, {this.faculties}); diff --git a/uni/lib/view/academic_path/academic_path.dart b/uni/lib/view/academic_path/academic_path.dart new file mode 100644 index 000000000..8ea05f02e --- /dev/null +++ b/uni/lib/view/academic_path/academic_path.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:uni/view/academic_path/widgets/course_units_card.dart'; +import 'package:uni/view/common_widgets/page_title.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/home/widgets/exam_card.dart'; +import 'package:uni/view/home/widgets/main_cards_list.dart'; +import 'package:uni/view/home/widgets/schedule_card.dart'; + +class AcademicPathPageView extends StatefulWidget { + const AcademicPathPageView({super.key}); + + @override + State createState() => AcademicPathPageViewState(); +} + +class AcademicPathPageViewState extends GeneralPageViewState { + List academicPathCards = [ + ScheduleCard(), + ExamCard(), + CourseUnitsCard(), + // Add more cards if needed + ]; + + @override + Widget getBody(BuildContext context) { + return Column( + children: [ + const PageTitle(name: 'Academic Path'), + Expanded( + child: SingleChildScrollView( + child: Column( + children: academicPathCards, + ), + ), + ), + ], + ); + } + + @override + Future onRefresh(BuildContext context) async { + final cards = academicPathCards + .map( + (e) => + MainCardsList.cardCreators[e]!(const Key(''), editingMode: false), + ) + .toList(); + + for (final card in cards) { + card.onRefresh(context); + } + } +} \ No newline at end of file diff --git a/uni/lib/view/academic_path/widgets/course_units_card.dart b/uni/lib/view/academic_path/widgets/course_units_card.dart new file mode 100644 index 000000000..6b644c776 --- /dev/null +++ b/uni/lib/view/academic_path/widgets/course_units_card.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/model/entities/course_units/course_unit.dart'; +import 'package:uni/model/entities/profile.dart'; +import 'package:uni/model/providers/startup/profile_provider.dart'; +import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/common_widgets/generic_card.dart'; +import 'package:uni/view/course_units/widgets/course_unit_card.dart'; +import 'package:uni/view/lazy_consumer.dart'; + +class CourseUnitsCard extends GenericCard { + CourseUnitsCard({super.key}); + + @override + void onRefresh(BuildContext context) { + Provider.of(context, listen: false).forceRefresh(context); + } + + @override + Widget buildCardContent(BuildContext context) { + return LazyConsumer( + builder: (context, profile) { + final courseUnits = profile.courseUnits; + + return _generateCourseUnitsCards(courseUnits, context); + }, + hasContent: (Profile profile) => profile.courseUnits.isNotEmpty, + onNullContent: Center( + heightFactor: 10, + child: Text( + S.of(context).no_course_units, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + ); + } + + Widget _generateCourseUnitsCards( + List courseUnits, + BuildContext context, + ) { + if (courseUnits.isEmpty) { + return Center( + heightFactor: 10, + child: Text( + S.of(context).no_course_units, + style: Theme.of(context).textTheme.titleLarge, + ), + ); + } + return Expanded( + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 20), + child: ListView( + shrinkWrap: true, + children: _generateCourseUnitsList(courseUnits), + ), + ), + ); + } + + List _generateCourseUnitsList(List courseUnits) { + return courseUnits.map((courseUnit) { + return Column( + children: [ + CourseUnitCard(courseUnit), + const SizedBox(height: 10), + ], + ); + }).toList(); + } + + @override + String getTitle(BuildContext context) => 'Current Course Units'; + + @override + Future onClick(BuildContext context) => + Navigator.pushNamed(context, '/${DrawerItem.navCourseUnits.title}'); +} \ No newline at end of file From f2f3758e97155073758bbb1339976024b23e645a Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 2 Feb 2024 13:00:11 +0000 Subject: [PATCH 171/334] Filter course units --- uni/lib/view/academic_path/academic_path.dart | 15 ++----- .../widgets/course_units_card.dart | 41 +++++++++---------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/uni/lib/view/academic_path/academic_path.dart b/uni/lib/view/academic_path/academic_path.dart index 8ea05f02e..18d761310 100644 --- a/uni/lib/view/academic_path/academic_path.dart +++ b/uni/lib/view/academic_path/academic_path.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:uni/view/academic_path/widgets/course_units_card.dart'; +import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/home/widgets/exam_card.dart'; -import 'package:uni/view/home/widgets/main_cards_list.dart'; import 'package:uni/view/home/widgets/schedule_card.dart'; class AcademicPathPageView extends StatefulWidget { @@ -14,7 +14,7 @@ class AcademicPathPageView extends StatefulWidget { } class AcademicPathPageViewState extends GeneralPageViewState { - List academicPathCards = [ + List academicPathCards = [ ScheduleCard(), ExamCard(), CourseUnitsCard(), @@ -39,15 +39,8 @@ class AcademicPathPageViewState extends GeneralPageViewState { @override Future onRefresh(BuildContext context) async { - final cards = academicPathCards - .map( - (e) => - MainCardsList.cardCreators[e]!(const Key(''), editingMode: false), - ) - .toList(); - - for (final card in cards) { + for (final card in academicPathCards) { card.onRefresh(context); } } -} \ No newline at end of file +} diff --git a/uni/lib/view/academic_path/widgets/course_units_card.dart b/uni/lib/view/academic_path/widgets/course_units_card.dart index 6b644c776..abb91951f 100644 --- a/uni/lib/view/academic_path/widgets/course_units_card.dart +++ b/uni/lib/view/academic_path/widgets/course_units_card.dart @@ -19,9 +19,14 @@ class CourseUnitsCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { + return LazyConsumer( builder: (context, profile) { - final courseUnits = profile.courseUnits; + final courseUnits = profile.courseUnits + .where((courseUnit) => courseUnit.enrollmentIsValid() + && courseUnit.grade == '',) + .take(5) + .toList(); return _generateCourseUnitsCards(courseUnits, context); }, @@ -49,32 +54,26 @@ class CourseUnitsCard extends GenericCard { ), ); } - return Expanded( - child: Container( - margin: const EdgeInsets.symmetric(horizontal: 20), - child: ListView( - shrinkWrap: true, - children: _generateCourseUnitsList(courseUnits), - ), - ), + + return ListView( + shrinkWrap: true, + padding: const EdgeInsets.symmetric(horizontal: 20), + children: courseUnits.map((courseUnit) { + return Column( + children: [ + CourseUnitCard(courseUnit), + const SizedBox(height: 10), + ], + ); + }).toList(), ); } - List _generateCourseUnitsList(List courseUnits) { - return courseUnits.map((courseUnit) { - return Column( - children: [ - CourseUnitCard(courseUnit), - const SizedBox(height: 10), - ], - ); - }).toList(); - } @override - String getTitle(BuildContext context) => 'Current Course Units'; + String getTitle(BuildContext context) => 'Course Units'; @override Future onClick(BuildContext context) => Navigator.pushNamed(context, '/${DrawerItem.navCourseUnits.title}'); -} \ No newline at end of file +} From 2b7f6aba6d2840b1375f29e5ce85632930389ed4 Mon Sep 17 00:00:00 2001 From: vitormpp Date: Fri, 2 Feb 2024 13:17:11 +0000 Subject: [PATCH 172/334] Translation en-pt --- uni/lib/generated/intl/messages_en.dart | 1 + uni/lib/generated/intl/messages_pt_PT.dart | 1 + uni/lib/l10n/intl_en.arb | 2 +- uni/lib/l10n/intl_pt_PT.arb | 2 +- uni/lib/utils/drawer_items.dart | 2 +- uni/lib/view/academic_path/academic_path.dart | 6 +++++- uni/lib/view/academic_path/widgets/course_units_card.dart | 3 ++- 7 files changed, 12 insertions(+), 5 deletions(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index d12932f32..369500799 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -35,6 +35,7 @@ class MessageLookup extends MessageLookupByLibrary { 'restaurantes': 'Restaurants', 'calendario': 'Calendar', 'biblioteca': 'Library', + 'percurso_academico': 'Academic Path', 'uteis': 'Utils', 'other': 'Other', })}"; diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index ecc041c37..fb6a1bcb6 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -35,6 +35,7 @@ class MessageLookup extends MessageLookupByLibrary { 'restaurantes': 'Restaurantes', 'calendario': 'Calendário', 'biblioteca': 'Biblioteca', + 'percurso_academico': 'Percurso Académico', 'uteis': 'Úteis', 'other': 'Outros', })}"; diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 86f8fe8bc..c61bd64be 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -166,7 +166,7 @@ "@min_value_reference": {}, "multimedia_center": "Multimedia center", "@multimedia_center": {}, - "nav_title": "{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} uteis{Utils} other{Other}}", + "nav_title": "{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} percurso_academico{Academic Path} uteis{Utils} other{Other}}", "@nav_title": {}, "news": "News", "@news": {}, diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index b674d046f..0dc766131 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -166,7 +166,7 @@ "@min_value_reference": {}, "multimedia_center": "Centro de multimédia", "@multimedia_center": {}, - "nav_title": "{title, select, horario{Horário} exames{Exames} area{Área Pessoal} cadeiras{Cadeiras} autocarros{Autocarros} locais{Locais} restaurantes{Restaurantes} calendario{Calendário} biblioteca{Biblioteca} uteis{Úteis} other{Outros}}", + "nav_title": "{title, select, horario{Horário} exames{Exames} area{Área Pessoal} cadeiras{Cadeiras} autocarros{Autocarros} locais{Locais} restaurantes{Restaurantes} calendario{Calendário} biblioteca{Biblioteca} percurso_academico{Percurso Académico} uteis{Úteis} other{Outros}}", "@nav_title": {}, "news": "Notícias", "@news": {}, diff --git a/uni/lib/utils/drawer_items.dart b/uni/lib/utils/drawer_items.dart index a85b50d3b..52c4e8634 100644 --- a/uni/lib/utils/drawer_items.dart +++ b/uni/lib/utils/drawer_items.dart @@ -9,7 +9,7 @@ enum DrawerItem { navCalendar('calendario'), navLibrary('biblioteca', faculties: {'feup'}), navUsefulInfo('uteis', faculties: {'feup'}), - navAcademicPath('percurso academico'); + navAcademicPath('percurso_academico'); const DrawerItem(this.title, {this.faculties}); diff --git a/uni/lib/view/academic_path/academic_path.dart b/uni/lib/view/academic_path/academic_path.dart index 18d761310..ea4b27cc1 100644 --- a/uni/lib/view/academic_path/academic_path.dart +++ b/uni/lib/view/academic_path/academic_path.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/academic_path/widgets/course_units_card.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/common_widgets/page_title.dart'; @@ -25,7 +27,8 @@ class AcademicPathPageViewState extends GeneralPageViewState { Widget getBody(BuildContext context) { return Column( children: [ - const PageTitle(name: 'Academic Path'), + PageTitle(name: + S.of(context).nav_title(DrawerItem.navAcademicPath.title),), Expanded( child: SingleChildScrollView( child: Column( @@ -37,6 +40,7 @@ class AcademicPathPageViewState extends GeneralPageViewState { ); } + @override Future onRefresh(BuildContext context) async { for (final card in academicPathCards) { diff --git a/uni/lib/view/academic_path/widgets/course_units_card.dart b/uni/lib/view/academic_path/widgets/course_units_card.dart index abb91951f..dbf9dc3e6 100644 --- a/uni/lib/view/academic_path/widgets/course_units_card.dart +++ b/uni/lib/view/academic_path/widgets/course_units_card.dart @@ -71,7 +71,8 @@ class CourseUnitsCard extends GenericCard { @override - String getTitle(BuildContext context) => 'Course Units'; + String getTitle(BuildContext context) => + S.of(context).nav_title(DrawerItem.navCourseUnits.title); @override Future onClick(BuildContext context) => From 161a8a082db23819b9cb9fdcc625932b9b450aea Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Wed, 7 Feb 2024 16:06:15 +0000 Subject: [PATCH 173/334] Fix academic card scroll --- uni/lib/view/academic_path/academic_path.dart | 16 +++----- .../widgets/course_units_card.dart | 39 +++++++++---------- 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/uni/lib/view/academic_path/academic_path.dart b/uni/lib/view/academic_path/academic_path.dart index ea4b27cc1..e06ef1d65 100644 --- a/uni/lib/view/academic_path/academic_path.dart +++ b/uni/lib/view/academic_path/academic_path.dart @@ -25,22 +25,18 @@ class AcademicPathPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return Column( + return ListView( children: [ - PageTitle(name: - S.of(context).nav_title(DrawerItem.navAcademicPath.title),), - Expanded( - child: SingleChildScrollView( - child: Column( - children: academicPathCards, - ), - ), + PageTitle( + name: S.of(context).nav_title(DrawerItem.navAcademicPath.title), + ), + Column( + children: academicPathCards, ), ], ); } - @override Future onRefresh(BuildContext context) async { for (final card in academicPathCards) { diff --git a/uni/lib/view/academic_path/widgets/course_units_card.dart b/uni/lib/view/academic_path/widgets/course_units_card.dart index dbf9dc3e6..04962e3ab 100644 --- a/uni/lib/view/academic_path/widgets/course_units_card.dart +++ b/uni/lib/view/academic_path/widgets/course_units_card.dart @@ -19,15 +19,15 @@ class CourseUnitsCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { - return LazyConsumer( builder: (context, profile) { final courseUnits = profile.courseUnits - .where((courseUnit) => courseUnit.enrollmentIsValid() - && courseUnit.grade == '',) + .where( + (courseUnit) => + courseUnit.enrollmentIsValid() && courseUnit.grade == '', + ) .take(5) .toList(); - return _generateCourseUnitsCards(courseUnits, context); }, hasContent: (Profile profile) => profile.courseUnits.isNotEmpty, @@ -42,12 +42,12 @@ class CourseUnitsCard extends GenericCard { } Widget _generateCourseUnitsCards( - List courseUnits, - BuildContext context, - ) { + List courseUnits, + BuildContext context, + ) { if (courseUnits.isEmpty) { return Center( - heightFactor: 10, + heightFactor: 3, child: Text( S.of(context).no_course_units, style: Theme.of(context).textTheme.titleLarge, @@ -55,21 +55,20 @@ class CourseUnitsCard extends GenericCard { ); } - return ListView( - shrinkWrap: true, - padding: const EdgeInsets.symmetric(horizontal: 20), - children: courseUnits.map((courseUnit) { - return Column( - children: [ - CourseUnitCard(courseUnit), - const SizedBox(height: 10), - ], - ); - }).toList(), + return Column( + children: courseUnits + .map( + (courseUnit) => Column( + children: [ + CourseUnitCard(courseUnit), + const SizedBox(height: 10), + ], + ), + ) + .toList(), ); } - @override String getTitle(BuildContext context) => S.of(context).nav_title(DrawerItem.navCourseUnits.title); From 94694a4b064887544660e9bb2d2694545bfbbefa Mon Sep 17 00:00:00 2001 From: vitormpp Date: Wed, 7 Feb 2024 19:30:08 +0000 Subject: [PATCH 174/334] Used Padding widget instead of SizedBox --- uni/lib/view/academic_path/widgets/course_units_card.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/uni/lib/view/academic_path/widgets/course_units_card.dart b/uni/lib/view/academic_path/widgets/course_units_card.dart index 04962e3ab..e2553b683 100644 --- a/uni/lib/view/academic_path/widgets/course_units_card.dart +++ b/uni/lib/view/academic_path/widgets/course_units_card.dart @@ -60,8 +60,10 @@ class CourseUnitsCard extends GenericCard { .map( (courseUnit) => Column( children: [ - CourseUnitCard(courseUnit), - const SizedBox(height: 10), + Padding( + padding: const EdgeInsets.all(5), + child: CourseUnitCard(courseUnit), + ), ], ), ) From 7312f7fb8441543e05e88fb8d183692ea862f5bb Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 7 Feb 2024 19:31:56 +0000 Subject: [PATCH 175/334] Calendar event getter --- uni/lib/model/entities/calendar_event.dart | 6 ++---- uni/lib/view/calendar/widgets/calendar_card.dart | 6 +++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/uni/lib/model/entities/calendar_event.dart b/uni/lib/model/entities/calendar_event.dart index d46ad8a51..2d96d895e 100644 --- a/uni/lib/model/entities/calendar_event.dart +++ b/uni/lib/model/entities/calendar_event.dart @@ -3,21 +3,19 @@ import 'package:intl/intl.dart'; /// An event in the school calendar class CalendarEvent { /// Creates an instance of the class [CalendarEvent] - CalendarEvent(this.name, this.date, {this.closeDate}) { + CalendarEvent(this.name, this.date) { name = name; date = date; - closeDate = getDateTime(); } String name; String date; - DateTime? closeDate; /// Converts the event into a map Map toMap() { return {'name': name, 'date': date}; } - DateTime? getDateTime() { + DateTime? get parsedStartDate { final splitDate = date.split(' '); final month = splitDate.firstWhere( (element) => diff --git a/uni/lib/view/calendar/widgets/calendar_card.dart b/uni/lib/view/calendar/widgets/calendar_card.dart index ec84889ef..4753bba42 100644 --- a/uni/lib/view/calendar/widgets/calendar_card.dart +++ b/uni/lib/view/calendar/widgets/calendar_card.dart @@ -50,10 +50,10 @@ class CalendarCard extends GenericCard { List getFurtherEvents(List events) { final sortedEvents = events - .where((element) => element.closeDate != null) - .sorted((a, b) => a.closeDate!.compareTo(b.closeDate!)); + .where((element) => element.parsedStartDate != null) + .sorted((a, b) => a.parsedStartDate!.compareTo(b.parsedStartDate!)); final pinEvent = sortedEvents.firstWhere( - (element) => element.closeDate!.compareTo(DateTime.now()) == 1, + (element) => element.parsedStartDate!.compareTo(DateTime.now()) == 1, ); return sortedEvents.sublist( sortedEvents.indexOf(pinEvent), From 9077db6ee869d13b5f8c621406b6b526f1a3b111 Mon Sep 17 00:00:00 2001 From: LuisDuarte1 Date: Wed, 7 Feb 2024 19:39:09 +0000 Subject: [PATCH 176/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 8f071ccda..5e1306432 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.42+232 \ No newline at end of file +1.7.43+233 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 4fa607d55..3ec055f77 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.42+232 +version: 1.7.43+233 environment: sdk: ">=3.0.0 <4.0.0" From ae7d8ec55e294d22d26d83e11b90f7f965966c87 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Wed, 7 Feb 2024 20:28:14 +0000 Subject: [PATCH 177/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 5e1306432..0db8d2ae9 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.43+233 \ No newline at end of file +1.7.44+234 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 3ec055f77..4a827acc5 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.43+233 +version: 1.7.44+234 environment: sdk: ">=3.0.0 <4.0.0" From 2a935be9f2f6aee553ddd8fe4a05508830f59f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Wed, 7 Feb 2024 20:03:43 +0000 Subject: [PATCH 178/334] fix: use cached profile image on navigation --- uni/.gitignore | 3 +++ .../view/common_widgets/pages_layouts/general/general.dart | 1 - .../pages_layouts/general/widgets/profile_button.dart | 4 +--- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/uni/.gitignore b/uni/.gitignore index ef53d37aa..39315f3b5 100644 --- a/uni/.gitignore +++ b/uni/.gitignore @@ -125,3 +125,6 @@ app.*.symbols !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/dev/ci/**/Gemfile.lock + +# Flutter Devtools +devtools_options.yaml diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index ad1aaab81..dcd888115 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -17,7 +17,6 @@ abstract class GeneralPageViewState extends State { final double borderMargin = 18; bool _loadedOnce = false; bool _loading = true; - static ImageProvider? profileImageProvider; Future onRefresh(BuildContext context); diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart index a42376398..98666a694 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/profile/profile.dart'; class ProfileButton extends StatelessWidget { @@ -22,8 +21,7 @@ class ProfileButton extends StatelessWidget { final profilePictureFile = await ProfileProvider.fetchOrGetCachedProfilePicture( sessionProvider.state!, - forceRetrieval: - forceRetrieval || GeneralPageViewState.profileImageProvider == null, + forceRetrieval: forceRetrieval, ); return getProfileDecorationImage(profilePictureFile); } From 30565e4ccb52b5b137e3f0a3ef215ae0da368e3c Mon Sep 17 00:00:00 2001 From: bdmendes Date: Wed, 7 Feb 2024 20:45:57 +0000 Subject: [PATCH 179/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 0db8d2ae9..51b1cff46 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.44+234 \ No newline at end of file +1.7.45+235 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 4a827acc5..b9ebc4e1c 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.44+234 +version: 1.7.45+235 environment: sdk: ">=3.0.0 <4.0.0" From b6831a482a376573336ac2d07b7787f65601ef80 Mon Sep 17 00:00:00 2001 From: vitormpp Date: Wed, 7 Feb 2024 22:57:25 +0000 Subject: [PATCH 180/334] Implemented aggregate transports page --- uni/lib/l10n/intl_en.arb | 2 +- uni/lib/l10n/intl_pt_PT.arb | 2 +- uni/lib/main.dart | 6 +++ uni/lib/utils/drawer_items.dart | 3 +- uni/lib/view/faculty/faculty.dart | 2 - uni/lib/view/home/widgets/bus_stop_card.dart | 2 + uni/lib/view/transports/transports.dart | 44 +++++++++++++++++++ .../widgets/map_snapshot_card.dart | 0 8 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 uni/lib/view/transports/transports.dart rename uni/lib/view/{faculty => transports}/widgets/map_snapshot_card.dart (100%) diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 90b7dd6bf..76532d54c 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -166,7 +166,7 @@ "@min_value_reference": {}, "multimedia_center": "Multimedia center", "@multimedia_center": {}, - "nav_title": "{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} percurso_academico{Academic Path} faculdade{Faculty} other{Other}}", + "nav_title": "{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} percurso_academico{Academic Path} transportes{Transports} faculdade{Faculty} other{Other}}", "@nav_title": {}, "news": "News", "@news": {}, diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 20cf42117..6bbf7e942 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -166,7 +166,7 @@ "@min_value_reference": {}, "multimedia_center": "Centro de multimédia", "@multimedia_center": {}, - "nav_title": "{title, select, horario{Horário} exames{Exames} area{Área Pessoal} cadeiras{Cadeiras} autocarros{Autocarros} locais{Locais} restaurantes{Restaurantes} calendario{Calendário} biblioteca{Biblioteca} percurso_academico{Percurso Académico} faculdade{Faculdade} other{Outros}}", + "nav_title": "{title, select, horario{Horário} exames{Exames} area{Área Pessoal} cadeiras{Cadeiras} autocarros{Autocarros} locais{Locais} restaurantes{Restaurantes} calendario{Calendário} biblioteca{Biblioteca} percurso_academico{Percurso Académico} transportes{Transportes} faculdade{Faculdade} other{Outros}}", "@nav_title": {}, "news": "Notícias", "@news": {}, diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 05d476d86..12d16f06f 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -45,6 +45,7 @@ import 'package:uni/view/restaurant/restaurant_page_view.dart'; import 'package:uni/view/schedule/schedule.dart'; import 'package:uni/view/theme.dart'; import 'package:uni/view/theme_notifier.dart'; +import 'package:uni/view/transports/transports.dart'; import 'package:workmanager/workmanager.dart'; SentryEvent? beforeSend(SentryEvent event) { @@ -279,6 +280,11 @@ class ApplicationState extends State { page: const AcademicPathPageView(), settings: settings, ), + '/${DrawerItem.navTransports.title}': + PageTransition.makePageTransition( + page: const TransportsPageView(), + settings: settings, + ), }; return transitions[settings.name]; }, diff --git a/uni/lib/utils/drawer_items.dart b/uni/lib/utils/drawer_items.dart index db5f5d67e..064a717f4 100644 --- a/uni/lib/utils/drawer_items.dart +++ b/uni/lib/utils/drawer_items.dart @@ -9,7 +9,8 @@ enum DrawerItem { navCalendar('calendario'), navLibrary('biblioteca', faculties: {'feup'}), navFaculty('faculdade'), - navAcademicPath('percurso_academico'); + navAcademicPath('percurso_academico'), + navTransports('transportes'); const DrawerItem(this.title, {this.faculties}); diff --git a/uni/lib/view/faculty/faculty.dart b/uni/lib/view/faculty/faculty.dart index f883ec715..9c4c560fc 100644 --- a/uni/lib/view/faculty/faculty.dart +++ b/uni/lib/view/faculty/faculty.dart @@ -10,7 +10,6 @@ import 'package:uni/view/faculty/widgets/academic_services_card.dart'; import 'package:uni/view/faculty/widgets/copy_center_card.dart'; import 'package:uni/view/faculty/widgets/dona_bia_card.dart'; import 'package:uni/view/faculty/widgets/infodesk_card.dart'; -import 'package:uni/view/faculty/widgets/map_snapshot_card.dart'; import 'package:uni/view/faculty/widgets/multimedia_center_card.dart'; import 'package:uni/view/faculty/widgets/other_links_card.dart'; import 'package:uni/view/faculty/widgets/sigarra_links_card.dart'; @@ -33,7 +32,6 @@ class FacultyPageViewState extends GeneralPageViewState { child: SingleChildScrollView( child: Column( children: [ - MapCard(), LibraryOccupationCard(), CalendarCard(), getUtilsSection(), diff --git a/uni/lib/view/home/widgets/bus_stop_card.dart b/uni/lib/view/home/widgets/bus_stop_card.dart index 65dffcb35..d77c4326c 100644 --- a/uni/lib/view/home/widgets/bus_stop_card.dart +++ b/uni/lib/view/home/widgets/bus_stop_card.dart @@ -12,6 +12,8 @@ import 'package:uni/view/lazy_consumer.dart'; /// Manages the bus stops card displayed on the user's personal area class BusStopCard extends GenericCard { + BusStopCard({super.key}); + const BusStopCard.fromEditingInformation( super.key, { required super.editingMode, diff --git a/uni/lib/view/transports/transports.dart b/uni/lib/view/transports/transports.dart new file mode 100644 index 000000000..2e9a47b96 --- /dev/null +++ b/uni/lib/view/transports/transports.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/common_widgets/generic_card.dart'; +import 'package:uni/view/common_widgets/page_title.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/home/widgets/bus_stop_card.dart'; +import 'package:uni/view/transports/widgets/map_snapshot_card.dart'; + +class TransportsPageView extends StatefulWidget { + const TransportsPageView({super.key}); + + @override + State createState() => TransportsPageViewState(); +} + +class TransportsPageViewState extends GeneralPageViewState { + List transportsCards = [ + MapCard(), + BusStopCard(), + // Add more cards if needed + ]; + + @override + Widget getBody(BuildContext context) { + return ListView( + children: [ + PageTitle( + name: S.of(context).nav_title(DrawerItem.navTransports.title), + ), + Column( + children: transportsCards, + ), + ], + ); + } + + @override + Future onRefresh(BuildContext context) async { + for (final card in transportsCards) { + card.onRefresh(context); + } + } +} diff --git a/uni/lib/view/faculty/widgets/map_snapshot_card.dart b/uni/lib/view/transports/widgets/map_snapshot_card.dart similarity index 100% rename from uni/lib/view/faculty/widgets/map_snapshot_card.dart rename to uni/lib/view/transports/widgets/map_snapshot_card.dart From 6ddb1fa4c8b52eb39bf3bbd6789db7ad7a3c29e2 Mon Sep 17 00:00:00 2001 From: vitormpp Date: Wed, 7 Feb 2024 23:10:12 +0000 Subject: [PATCH 181/334] Fixed bug in aggregate faculty page and calendar widget --- uni/lib/generated/intl/messages_en.dart | 1 + uni/lib/generated/intl/messages_pt_PT.dart | 1 + uni/lib/generated/l10n.dart | 3 +- uni/lib/main.dart | 2 +- .../view/calendar/widgets/calendar_card.dart | 18 ++++----- uni/lib/view/faculty/faculty.dart | 39 +++++++------------ 6 files changed, 28 insertions(+), 36 deletions(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 21bf4f3e5..b9bc65be7 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -36,6 +36,7 @@ class MessageLookup extends MessageLookupByLibrary { 'calendario': 'Calendar', 'biblioteca': 'Library', 'percurso_academico': 'Academic Path', + 'transportes': 'Transports', 'faculdade': 'Faculty', 'other': 'Other', })}"; diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 19a999ffc..a87b06cf8 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -36,6 +36,7 @@ class MessageLookup extends MessageLookupByLibrary { 'calendario': 'Calendário', 'biblioteca': 'Biblioteca', 'percurso_academico': 'Percurso Académico', + 'transportes': 'Transportes', 'faculdade': 'Faculdade', 'other': 'Outros', })}"; diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index c6c4c771a..ec1dde692 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -843,7 +843,7 @@ class S { ); } - /// `{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} percurso_academico{Academic Path} faculdade{Faculty} other{Other}}` + /// `{title, select, horario{Schedule} exames{Exams} area{Personal Area} cadeiras{Course Units} autocarros{Buses} locais{Places} restaurantes{Restaurants} calendario{Calendar} biblioteca{Library} percurso_academico{Academic Path} transportes{Transports} faculdade{Faculty} other{Other}}` String nav_title(Object title) { return Intl.select( title, @@ -858,6 +858,7 @@ class S { 'calendario': 'Calendar', 'biblioteca': 'Library', 'percurso_academico': 'Academic Path', + 'transportes': 'Transports', 'faculdade': 'Faculty', 'other': 'Other', }, diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 12d16f06f..a63b38cc4 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -281,7 +281,7 @@ class ApplicationState extends State { settings: settings, ), '/${DrawerItem.navTransports.title}': - PageTransition.makePageTransition( + PageTransition.makePageTransition( page: const TransportsPageView(), settings: settings, ), diff --git a/uni/lib/view/calendar/widgets/calendar_card.dart b/uni/lib/view/calendar/widgets/calendar_card.dart index 4753bba42..064879479 100644 --- a/uni/lib/view/calendar/widgets/calendar_card.dart +++ b/uni/lib/view/calendar/widgets/calendar_card.dart @@ -33,16 +33,14 @@ class CalendarCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { - return Expanded( - child: LazyConsumer>( - builder: (context, events) => CalendarPageViewState() - .getTimeline(context, getFurtherEvents(events)), - hasContent: (calendar) => calendar.isNotEmpty, - onNullContent: Center( - child: Text( - S.of(context).no_events, - style: const TextStyle(fontSize: 18), - ), + return LazyConsumer>( + builder: (context, events) => CalendarPageViewState() + .getTimeline(context, getFurtherEvents(events)), + hasContent: (calendar) => calendar.isNotEmpty, + onNullContent: Center( + child: Text( + S.of(context).no_events, + style: const TextStyle(fontSize: 18), ), ), ); diff --git a/uni/lib/view/faculty/faculty.dart b/uni/lib/view/faculty/faculty.dart index 9c4c560fc..252ec7ae8 100644 --- a/uni/lib/view/faculty/faculty.dart +++ b/uni/lib/view/faculty/faculty.dart @@ -4,6 +4,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/calendar/widgets/calendar_card.dart'; +import 'package:uni/view/common_widgets/generic_expansion_card.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/faculty/widgets/academic_services_card.dart'; @@ -25,36 +26,26 @@ class FacultyPageView extends StatefulWidget { class FacultyPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return Column( + return ListView( children: [ PageTitle(name: S.of(context).nav_title(DrawerItem.navFaculty.title)), - Expanded( - child: SingleChildScrollView( - child: Column( - children: [ - LibraryOccupationCard(), - CalendarCard(), - getUtilsSection(), - ], - ), - ), - ), + LibraryOccupationCard(), + CalendarCard(), + ...getUtilsSection(), ], ); } - Widget getUtilsSection() { - return const Column( - children: [ - AcademicServicesCard(), - InfoDeskCard(), - DonaBiaCard(), - CopyCenterCard(), - MultimediaCenterCard(), - SigarraLinksCard(), - OtherLinksCard(), - ], - ); + List getUtilsSection() { + return const [ + AcademicServicesCard(), + InfoDeskCard(), + DonaBiaCard(), + CopyCenterCard(), + MultimediaCenterCard(), + SigarraLinksCard(), + OtherLinksCard(), + ]; } @override From 1bc8b7651501a16dab35a0bbfda86c1a81593dba Mon Sep 17 00:00:00 2001 From: Sirze01 Date: Wed, 7 Feb 2024 23:20:50 +0000 Subject: [PATCH 182/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 51b1cff46..5ce09a3ce 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.45+235 \ No newline at end of file +1.7.46+236 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index b9ebc4e1c..b56ce3de0 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.45+235 +version: 1.7.46+236 environment: sdk: ">=3.0.0 <4.0.0" From 51b17b9503b82f467620f2f5510ea29088ffb910 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Wed, 7 Feb 2024 22:52:19 +0000 Subject: [PATCH 183/334] Speedup profile provider fetcher --- .../all_course_units_fetcher.dart | 26 +++++++++++-------- .../providers/startup/profile_provider.dart | 23 +++++++++++----- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/uni/lib/controller/fetchers/course_units_fetcher/all_course_units_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/all_course_units_fetcher.dart index c84932de1..ef762fc80 100644 --- a/uni/lib/controller/fetchers/course_units_fetcher/all_course_units_fetcher.dart +++ b/uni/lib/controller/fetchers/course_units_fetcher/all_course_units_fetcher.dart @@ -10,18 +10,22 @@ class AllCourseUnitsFetcher { Session session, { List? currentCourseUnits, }) async { - final allCourseUnits = []; - - for (final course in courses) { - final courseUnits = await _getAllCourseUnitsAndCourseAveragesFromCourse( - course, - session, - currentCourseUnits: currentCourseUnits, - ); - allCourseUnits.addAll(courseUnits.where((c) => c.enrollmentIsValid())); - } + final courseCourseUnits = await Future.wait( + courses + .map( + (course) => _getAllCourseUnitsAndCourseAveragesFromCourse( + course, + session, + currentCourseUnits: currentCourseUnits, + ), + ) + .toList(), + ); - return allCourseUnits; + return courseCourseUnits + .expand((l) => l) + .where((c) => c.enrollmentIsValid()) + .toList(); } Future> _getAllCourseUnitsAndCourseAveragesFromCourse( diff --git a/uni/lib/model/providers/startup/profile_provider.dart b/uni/lib/model/providers/startup/profile_provider.dart index 39e97966c..041f64286 100644 --- a/uni/lib/model/providers/startup/profile_provider.dart +++ b/uni/lib/model/providers/startup/profile_provider.dart @@ -38,17 +38,26 @@ class ProfileProvider extends StateProviderNotifier { Future loadFromRemote(StateProviders stateProviders) async { final session = stateProviders.sessionProvider.state!; - final profile = await fetchUserInfo(session); + final profileFuture = fetchUserInfo(session); + final courseUnitsFutures = profileFuture.then( + (profile) => fetchCourseUnitsAndCourseAverages(session, profile!), + ); - final userBalanceAndFeesLimit = await fetchUserFeesBalanceAndLimit(session); + final futures = await Future.wait([ + profileFuture, + fetchUserFeesBalanceAndLimit(session), + fetchUserPrintBalance(session), + ]); + final profile = futures[0] as Profile?; + final userBalanceAndFeesLimit = futures[1]! as Tuple2; + final printBalance = futures[2]! as String; profile! ..feesBalance = userBalanceAndFeesLimit.item1 ..feesLimit = userBalanceAndFeesLimit.item2 - ..printBalance = await fetchUserPrintBalance(session); + ..printBalance = printBalance; - final courseUnits = - await fetchCourseUnitsAndCourseAverages(session, profile); + final courseUnits = await courseUnitsFutures; if (courseUnits != null) { profile.courseUnits = courseUnits; } @@ -141,10 +150,10 @@ class ProfileProvider extends StateProviderNotifier { final userPersistentInfo = PreferencesController.getPersistentUserInfo(); if (userPersistentInfo != null) { final coursesDb = AppCoursesDatabase(); - await coursesDb.saveNewCourses(profile.courses); + unawaited(coursesDb.saveNewCourses(profile.courses)); final courseUnitsDatabase = AppCourseUnitsDatabase(); - await courseUnitsDatabase.saveNewCourseUnits(allCourseUnits); + unawaited(courseUnitsDatabase.saveNewCourseUnits(allCourseUnits)); } return allCourseUnits; From 8ffc662c7db17ea4b4f79d0dacaf12d348c7976d Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Wed, 7 Feb 2024 22:53:01 +0000 Subject: [PATCH 184/334] Do not depend on profile for bus --- uni/lib/model/providers/lazy/bus_stop_provider.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/model/providers/lazy/bus_stop_provider.dart b/uni/lib/model/providers/lazy/bus_stop_provider.dart index e7031094e..ddd90128c 100644 --- a/uni/lib/model/providers/lazy/bus_stop_provider.dart +++ b/uni/lib/model/providers/lazy/bus_stop_provider.dart @@ -7,7 +7,7 @@ import 'package:uni/model/providers/state_provider_notifier.dart'; import 'package:uni/model/providers/state_providers.dart'; class BusStopProvider extends StateProviderNotifier> { - BusStopProvider() : super(cacheDuration: null); + BusStopProvider() : super(cacheDuration: null, dependsOnSession: false); @override Future> loadFromStorage( From 49d4b2bc573ce009b54cc74e35d398fc7078f16c Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Wed, 7 Feb 2024 23:37:33 +0000 Subject: [PATCH 185/334] Make fetcher flow more readable and fetch from db on parallel --- .../providers/startup/profile_provider.dart | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/uni/lib/model/providers/startup/profile_provider.dart b/uni/lib/model/providers/startup/profile_provider.dart index 041f64286..0095059f3 100644 --- a/uni/lib/model/providers/startup/profile_provider.dart +++ b/uni/lib/model/providers/startup/profile_provider.dart @@ -27,10 +27,20 @@ class ProfileProvider extends StateProviderNotifier { @override Future loadFromStorage(StateProviders stateProviders) async { - final profile = await loadProfile(); + final databaseFutures = await Future.wait([ + loadProfile(), + loadCourses(), + loadCourseUnits(), + ]); + + final profile = databaseFutures[0] as Profile; + final courses = databaseFutures[1] as List; + final courseUnits = databaseFutures[2] as List; + profile - ..courses = await loadCourses() - ..courseUnits = await loadCourseUnits(); + ..courses = courses + ..courseUnits = courseUnits; + return profile; } @@ -45,19 +55,20 @@ class ProfileProvider extends StateProviderNotifier { final futures = await Future.wait([ profileFuture, + courseUnitsFutures, fetchUserFeesBalanceAndLimit(session), fetchUserPrintBalance(session), ]); final profile = futures[0] as Profile?; - final userBalanceAndFeesLimit = futures[1]! as Tuple2; - final printBalance = futures[2]! as String; + final courseUnits = futures[1] as List?; + final userBalanceAndFeesLimit = futures[2]! as Tuple2; + final printBalance = futures[3]! as String; profile! ..feesBalance = userBalanceAndFeesLimit.item1 ..feesLimit = userBalanceAndFeesLimit.item2 ..printBalance = printBalance; - final courseUnits = await courseUnitsFutures; if (courseUnits != null) { profile.courseUnits = courseUnits; } From 4bde20d8a7b2c40bb1e5722291a77230fe670e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Wed, 7 Feb 2024 11:38:51 +0000 Subject: [PATCH 186/334] Get version code from API/use semantic versioning --- .github/workflows/deploy.yaml | 101 ++++++++++++++++++++++------------ 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 3aa29e170..e1515bf5c 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -3,33 +3,88 @@ on: branches: [master, develop] jobs: - bump_version: + build: permissions: actions: "write" - name: "Bump pubspec version" + name: "Bump version and Build App Bundle" runs-on: ubuntu-latest + needs: [bump_version] env: + PROPERTIES_PATH: "android/key.properties" + JAVA_VERSION: "17.x" + FLUTTER_VERSION: "3.13.7" APP_VERSION_PATH: "uni/app_version.txt" PUBSPEC_PATH: "uni/pubspec.yaml" + defaults: + run: + working-directory: ./uni steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: token: ${{ secrets.NIAEFEUPBOT_PAT }} + fetch-depth: 0 - name: Get develop hash if: github.ref == 'refs/heads/master' run: | git fetch origin develop - current_hash=$(git rev-parse origin/develop) - echo "DEVELOPHASH=$current_hash" >> $GITHUB_ENV + git pull origin master + echo "DEVELOP_HASH=$(git rev-parse origin/develop)" >> $GITHUB_ENV + echo "MASTER_HASH=$(git rev-parse origin/master^2)" >> $GITHUB_ENV - - name: Bump flutter patch version - if: github.ref == 'refs/heads/develop' || github.sha != env.DEVELOPHASH - run: perl -i -pe 's/^(\d+\.\d+\.)(\d+)(\+)(\d+)$/$1.($2+1).($3).($4+1)/e' ${{ env.APP_VERSION_PATH }} + - name: Get latest version (develop) + if: github.ref != 'refs/heads/master' + uses: LuisDuarte1/google-play-latest-version-code@v0.2.1 + id: latest-beta-version + with: + google_service_account_json: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }} + package_name: ${{ secrets.ANDROID_PACKAGE_NAME }} + track: "beta" + + - name: Get latest production version + uses: LuisDuarte1/google-play-latest-version-code@v0.2.1 + id: latest-production-version + with: + google_service_account_json: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_JSON }} + package_name: ${{ secrets.ANDROID_PACKAGE_NAME }} + track: "production" + + - name: Bump beta version + uses: LuisDuarte1/semver-bump-environment@v1.0.0 + if: github.ref != 'refs/heads/master' + id: bump-beta-version + with: + current_environment: staging + production_version: ${{ steps.latest-production-version.outputs.latest_version_name }} + staging_version: ${{ steps.latest-beta-version.outputs.latest_version_name }} + bump_type: preminor + + - name: Bump prod version (from develop) + uses: LuisDuarte1/semver-bump-environment@v1.0.0 + if: github.ref == 'refs/heads/master' && env.MASTER_HASH == env.DEVELOP_HASH + id: bump-prod-minor-version + with: + current_environment: production + production_version: ${{ steps.latest-production-version.outputs.latest_version_name }} + bump_type: minor - - name: Bump flutter minor version - if: github.ref == 'refs/heads/master' && github.sha == env.DEVELOPHASH - run: perl -i -pe 's/^(\d+)(\.)(\d+)(\.)(\d+)(\+)(\d+)$/$1.($2).($3+1).($4).(0).($6).($7+1)/e' ${{ env.APP_VERSION_PATH }} + - name: Bump prod version (patch) + uses: LuisDuarte1/semver-bump-environment@v1.0.0 + if: github.ref == 'refs/heads/master' && env.MASTER_HASH != env.DEVELOP_HASH + id: bump-prod-patch-version + with: + current_environment: production + production_version: ${{ steps.latest-production-version.outputs.latest_version_name }} + bump_type: patch + + - name: Combine output and write new version into file + run: | + export NEW_VERSION_NAME=${{ + (steps.bump-beta-version.outcome == 'success' && steps.bump-beta-version.outputs.new_version) || + (steps.bump-prod-minor-version.outcome == 'success' && steps.bump-prod-minor-version.outputs.new_version) || + (steps.bump-prod-patch-version.outcome == 'success' && steps.bump-prod-patch-version.outputs.new_version) + }} + echo "$NEW_VERSION_NAME+$((${{steps.latest-production-version.outputs.latest_version_code}} + 1))" > ${{env.APP_VERSION_PATH}} - name: Copy app version to pubspec run: cat ${{ env.APP_VERSION_PATH }} | perl -i -pe 's/^(version:\s+)(\d+\.\d+\.\d+\+\d+)$/$1.()/e' ${{ env.PUBSPEC_PATH }} @@ -38,30 +93,6 @@ jobs: with: commit_message: "Bump app version [no ci]" - - name: Propagate master version bump to develop if master version is aad - if: github.ref == 'refs/heads/master' - run: | - git fetch origin develop - if [[ $(git diff --quiet HEAD~1 origin/develop) ]]; then - echo "Master version does not match develop version" - else - echo "Master version matches develop version" - git push --force-with-lease origin HEAD:develop - fi - - build: - name: "Build App Bundle" - runs-on: ubuntu-latest - needs: [bump_version] - env: - PROPERTIES_PATH: "android/key.properties" - JAVA_VERSION: "17.x" - FLUTTER_VERSION: "3.13.7" - defaults: - run: - working-directory: ./uni - steps: - - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: ${{env.JAVA_VERSION}} From 73839f19fddc0ab913444bf5c13a1e3680744d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Wed, 7 Feb 2024 11:43:19 +0000 Subject: [PATCH 187/334] update checkout action --- .github/workflows/app_version_integrity.yaml | 2 +- .github/workflows/deploy.yaml | 2 +- .github/workflows/format_lint_test.yaml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/app_version_integrity.yaml b/.github/workflows/app_version_integrity.yaml index d90d63e1a..d6b797fa9 100644 --- a/.github/workflows/app_version_integrity.yaml +++ b/.github/workflows/app_version_integrity.yaml @@ -7,7 +7,7 @@ jobs: env: APP_VERSION_PATH: "uni/app_version.txt" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Fetch origin target branch run: | diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index e1515bf5c..38115366d 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -130,7 +130,7 @@ jobs: runs-on: ubuntu-latest needs: [build] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Get App Bundle uses: actions/download-artifact@v3 with: diff --git a/.github/workflows/format_lint_test.yaml b/.github/workflows/format_lint_test.yaml index 8c6bb615b..6cced7e1f 100644 --- a/.github/workflows/format_lint_test.yaml +++ b/.github/workflows/format_lint_test.yaml @@ -15,7 +15,7 @@ jobs: run: working-directory: ./uni steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: subosito/flutter-action@v2 with: flutter-version: ${{ env.FLUTTER_VERSION }} @@ -30,7 +30,7 @@ jobs: run: working-directory: ./uni steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: java-version: ${{ env.JAVA_VERSION }} @@ -56,7 +56,7 @@ jobs: run: working-directory: ./uni steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-java@v3 with: java-version: ${{ env.JAVA_VERSION }} From e581b6eb2f95df4913f5e93d3c6d6ad46103ba1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Wed, 7 Feb 2024 14:29:04 +0000 Subject: [PATCH 188/334] Add documentation on master hash --- .github/workflows/deploy.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 38115366d..b0ee0cdd3 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -26,6 +26,9 @@ jobs: - name: Get develop hash if: github.ref == 'refs/heads/master' + # We get the master hash by assuming that the last commit is always a + # merge commit. This is assured by requiring pull requests. You should + # use rebase or squash merges onto the master branch. run: | git fetch origin develop git pull origin master From 3d1c25fed65f2a6d4ba71231255d53cf699da8e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Thu, 8 Feb 2024 09:17:03 +0000 Subject: [PATCH 189/334] Fix documentation on deploy yaml --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index b0ee0cdd3..1ea2786fd 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -27,7 +27,7 @@ jobs: - name: Get develop hash if: github.ref == 'refs/heads/master' # We get the master hash by assuming that the last commit is always a - # merge commit. This is assured by requiring pull requests. You should + # merge commit. This is assured by requiring pull requests. You should NOT # use rebase or squash merges onto the master branch. run: | git fetch origin develop From d7e69514d66c69d958e7ff9330343bbb5d142716 Mon Sep 17 00:00:00 2001 From: LuisDuarte1 Date: Thu, 8 Feb 2024 10:04:42 +0000 Subject: [PATCH 190/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 5ce09a3ce..cef5a0394 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.46+236 \ No newline at end of file +1.7.47+237 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index b56ce3de0..829b9f956 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.46+236 +version: 1.7.47+237 environment: sdk: ">=3.0.0 <4.0.0" From c6b8334496b8c0ce45bf3953e25ef9ad4bc7a7ce Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 9 Feb 2024 15:02:28 +0000 Subject: [PATCH 191/334] Encapsulating pulse animation --- .../view/common_widgets/pulse_animation.dart | 26 +++++++++++++++++++ .../course_unit_info/course_unit_info.dart | 11 +------- .../widgets/course_unit_files_row.dart | 17 +++--------- 3 files changed, 31 insertions(+), 23 deletions(-) create mode 100644 uni/lib/view/common_widgets/pulse_animation.dart diff --git a/uni/lib/view/common_widgets/pulse_animation.dart b/uni/lib/view/common_widgets/pulse_animation.dart new file mode 100644 index 000000000..a3b3e614c --- /dev/null +++ b/uni/lib/view/common_widgets/pulse_animation.dart @@ -0,0 +1,26 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; + +class PulseAnimation extends StatelessWidget { + const PulseAnimation( + {super.key, required this.description, required this.controller}); + final String description; + final AnimationController controller; + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: controller, + builder: (context, child) { + return Opacity( + opacity: 1 - 0.5 * sin(controller.value * pi), + child: Text( + description.substring(0, description.indexOf('_')), + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyLarge, + ), + ); + }, + ); + } +} diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index 6efc3fd06..c6d4d89be 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -125,16 +125,7 @@ class CourseUnitDetailPageViewState .watch() .courseUnitsFiles[widget.courseUnit]; - if (sheet == null || sheet.isEmpty) { - return Center( - child: Text( - S.of(context).no_info, - textAlign: TextAlign.center, - ), - ); - } - - return CourseUnitFilesView(sheet); + return CourseUnitFilesView(sheet!); } Widget _courseUnitClassesView(BuildContext context) { diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index 8483be5a3..f12ec41d9 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -5,6 +5,7 @@ import 'package:open_file_plus/open_file_plus.dart'; import 'package:provider/provider.dart'; import 'package:uni/controller/local_storage/file_offline_storage.dart'; import 'package:uni/generated/l10n.dart'; +import 'package:uni/view/common_widgets/pulse_animation.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/view/common_widgets/toast_message.dart'; @@ -58,19 +59,9 @@ class CourseUnitFilesRowState extends State }, child: Container( padding: const EdgeInsets.only(left: 10), - child: AnimatedBuilder( - animation: _controller, - builder: (context, child) { - return Opacity( - opacity: 1 - 0.5 * sin(_controller.value * pi), - child: Text( - widget.file.name - .substring(0, widget.file.name.indexOf('_')), - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.bodyLarge, - ), - ); - }, + child: PulseAnimation( + description: widget.file.name, + controller: _controller, ), ), ), From 29cf18ccdd359f449ef59f1ece428697acaba453 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 9 Feb 2024 17:43:48 +0000 Subject: [PATCH 192/334] Date formatter extension implementation --- uni/lib/utils/date_time_formatter.dart | 2 +- uni/lib/view/exams/exams.dart | 14 +++++++++----- uni/lib/view/home/widgets/exam_card.dart | 2 +- uni/lib/view/home/widgets/next_exams_card.dart | 12 +++--------- .../view/home/widgets/remaining_exams_card.dart | 3 ++- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/uni/lib/utils/date_time_formatter.dart b/uni/lib/utils/date_time_formatter.dart index 50064da5f..840b4cc91 100644 --- a/uni/lib/utils/date_time_formatter.dart +++ b/uni/lib/utils/date_time_formatter.dart @@ -15,6 +15,6 @@ extension DateTimeExtensions on DateTime { } String formattedDate(AppLocale locale) { - return DateFormat.yMMMMd(locale.localeCode.languageCode).format(this); + return DateFormat.MMMMd(locale.localeCode.languageCode).format(this); } } diff --git a/uni/lib/view/exams/exams.dart b/uni/lib/view/exams/exams.dart index 29cf0315f..1b31ca222 100644 --- a/uni/lib/view/exams/exams.dart +++ b/uni/lib/view/exams/exams.dart @@ -4,10 +4,10 @@ import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; +import 'package:uni/utils/date_time_formatter.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/common_widgets/row_container.dart'; -import 'package:uni/view/exams/widgets/day_title.dart'; import 'package:uni/view/exams/widgets/exam_page_title.dart'; import 'package:uni/view/exams/widgets/exam_row.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -120,10 +120,14 @@ class ExamsPageViewState extends GeneralPageViewState { Widget createExamsCards(BuildContext context, List exams) { final locale = Provider.of(context).getLocale(); final examCards = [ - DayTitle( - day: exams[0].begin.day.toString(), - weekDay: exams[0].weekDay(locale), - month: exams[0].month(locale), + Container( + padding: const EdgeInsets.only(top: 15, bottom: 3), + alignment: Alignment.center, + child: Text( + '${exams.first.weekDay(locale)}, ' + '${exams.first.begin.formattedDate(locale)}', + style: Theme.of(context).textTheme.titleLarge, + ), ), ]; for (var i = 0; i < exams.length; i++) { diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index d7a701ea1..d9ed6f049 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -97,4 +97,4 @@ class ExamCard extends GenericCard { dateA?.month == dateB?.month && dateA?.day == dateB?.day; } -} \ No newline at end of file +} diff --git a/uni/lib/view/home/widgets/next_exams_card.dart b/uni/lib/view/home/widgets/next_exams_card.dart index e258d65bf..88d99a672 100644 --- a/uni/lib/view/home/widgets/next_exams_card.dart +++ b/uni/lib/view/home/widgets/next_exams_card.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:uni/model/entities/app_locale.dart'; import 'package:uni/model/entities/exam.dart'; +import 'package:uni/utils/date_time_formatter.dart'; import 'package:uni/view/common_widgets/date_rectangle.dart'; import 'package:uni/view/common_widgets/row_container.dart'; import 'package:uni/view/exams/widgets/exam_row.dart'; @@ -14,11 +14,12 @@ class NextExamsWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final locale = Provider.of(context).getLocale(); return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ DateRectangle( - date: exams.isNotEmpty ? getFormattedDate(exams.first, context) : '', + date: exams.isNotEmpty ? exams.first.begin.formattedDate(locale) : '', ), Column( children: exams.map((exam) { @@ -38,11 +39,4 @@ class NextExamsWidget extends StatelessWidget { ], ); } - - String getFormattedDate(Exam exam, BuildContext context) { - final locale = Provider.of(context).getLocale(); - return locale == AppLocale.pt - ? '${exam.weekDay(locale)}, ${exam.begin.day} de ${exam.month(locale)}' - : '${exam.weekDay(locale)}, ${exam.begin.day} ${exam.month(locale)}'; - } } diff --git a/uni/lib/view/home/widgets/remaining_exams_card.dart b/uni/lib/view/home/widgets/remaining_exams_card.dart index 1b2afeb97..f6fdb1eb0 100644 --- a/uni/lib/view/home/widgets/remaining_exams_card.dart +++ b/uni/lib/view/home/widgets/remaining_exams_card.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/model/entities/exam.dart'; +import 'package:uni/utils/date_time_formatter.dart'; import 'package:uni/view/common_widgets/row_container.dart'; import 'package:uni/view/exams/widgets/exam_title.dart'; import 'package:uni/view/locale_notifier.dart'; @@ -25,7 +26,7 @@ class RemainingExamsWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '${exam.begin.day} ${exam.month(locale)}', + exam.begin.formattedDate(locale), style: Theme.of(context).textTheme.bodyLarge, ), ExamTitle( From 991b0743326d68e52b799de821fbc00379d4be46 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 9 Feb 2024 17:49:17 +0000 Subject: [PATCH 193/334] Deleting useless file --- uni/lib/view/exams/widgets/day_title.dart | 26 ----------------------- 1 file changed, 26 deletions(-) delete mode 100644 uni/lib/view/exams/widgets/day_title.dart diff --git a/uni/lib/view/exams/widgets/day_title.dart b/uni/lib/view/exams/widgets/day_title.dart deleted file mode 100644 index b0626a86b..000000000 --- a/uni/lib/view/exams/widgets/day_title.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uni/generated/l10n.dart'; - -class DayTitle extends StatelessWidget { - const DayTitle({ - required this.day, - required this.weekDay, - required this.month, - super.key, - }); - final String day; - final String weekDay; - final String month; - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.only(top: 15, bottom: 3), - alignment: Alignment.center, - child: Text( - '$weekDay, $day ${S.of(context).of_month} $month', - style: Theme.of(context).textTheme.titleLarge, - ), - ); - } -} From e40a7a65a62fa0f61c3729c36e8c5421cef92c04 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Fri, 9 Feb 2024 17:58:53 +0000 Subject: [PATCH 194/334] Fixing pre-commit auto formatting --- pre-commit-hook.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pre-commit-hook.sh b/pre-commit-hook.sh index c8f93c62b..56ec951b7 100755 --- a/pre-commit-hook.sh +++ b/pre-commit-hook.sh @@ -3,7 +3,7 @@ mkdir -p .git/hooks #it seems that are some cases where git will not create a ho tee .git/hooks/pre-commit << EOF #!/bin/sh -FILES="\$(git diff --name-only --cached | grep .*\.dart | grep -v .*\.g\.dart | grep -v .*\.mocks\.dart)" +FILES="\$(git diff --diff-filter=d --name-only --cached | grep .*\.dart | grep -v .*\.g\.dart | grep -v .*\.mocks\.dart)" [ -z "\$FILES" ] && exit 0 From 74199485abb820209c70c99a674491b3a2579699 Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Fri, 12 Jan 2024 14:30:02 +0000 Subject: [PATCH 195/334] Fixing exams page for other faculties --- uni/lib/controller/fetchers/exam_fetcher.dart | 1 - uni/lib/controller/parsers/parser_exams.dart | 19 +- uni/lib/view/exams/exams.dart | 26 +- .../integration/resources/exam2_example.html | 489 ++++++++++++++++++ .../integration/src/exams2_page_test.dart | 121 +++++ 5 files changed, 636 insertions(+), 20 deletions(-) create mode 100644 uni/test/integration/resources/exam2_example.html create mode 100644 uni/test/integration/src/exams2_page_test.dart diff --git a/uni/lib/controller/fetchers/exam_fetcher.dart b/uni/lib/controller/fetchers/exam_fetcher.dart index 9231eff88..f29eb2c68 100644 --- a/uni/lib/controller/fetchers/exam_fetcher.dart +++ b/uni/lib/controller/fetchers/exam_fetcher.dart @@ -55,7 +55,6 @@ class ExamFetcher implements SessionDependantFetcher { } } } - return exams.toList(); } } diff --git a/uni/lib/controller/parsers/parser_exams.dart b/uni/lib/controller/parsers/parser_exams.dart index 6c4cd8875..43f5b5df7 100644 --- a/uni/lib/controller/parsers/parser_exams.dart +++ b/uni/lib/controller/parsers/parser_exams.dart @@ -51,13 +51,19 @@ class ParserExams { .queryParameters['p_exa_id']!; } if (examsDay.querySelector('span.exame-sala') != null) { - rooms = - examsDay.querySelector('span.exame-sala')!.text.split(','); + rooms = examsDay + .querySelector('span.exame-sala')! + .text + .split(',') + .map((e) => e.trim()) + .toList(); } - schedule = examsDay.text.substring( - examsDay.text.indexOf(':') - 2, - examsDay.text.indexOf(':') + 9, - ); + schedule = examsDay.text.endsWith('0:00-') + ? '00:00-00:00' + : examsDay.text.substring( + examsDay.text.indexOf(':') - 2, + examsDay.text.indexOf(':') + 9, + ); final splittedSchedule = schedule!.split('-'); final begin = DateTime.parse('${dates[days]} ${splittedSchedule[0]}'); @@ -72,7 +78,6 @@ class ParserExams { examTypes[tableNum], course.faculty!, ); - examsList.add(exam); }); } diff --git a/uni/lib/view/exams/exams.dart b/uni/lib/view/exams/exams.dart index 1b31ca222..604edcc7b 100644 --- a/uni/lib/view/exams/exams.dart +++ b/uni/lib/view/exams/exams.dart @@ -35,18 +35,20 @@ class ExamsPageViewState extends GeneralPageViewState { }), ), LazyConsumer>( - builder: (context, exams) => Column( - children: createExamsColumn( - context, - exams - .where( - (exam) => - filteredExamTypes[Exam.getExamTypeLong(exam.type)] ?? - true, - ) - .toList(), - ), - ), + builder: (context, exams) { + return Column( + children: createExamsColumn( + context, + exams + .where( + (exam) => + filteredExamTypes[Exam.getExamTypeLong(exam.type)] ?? + true, + ) + .toList(), + ), + ); + }, hasContent: (exams) => exams.isNotEmpty, onNullContent: Center( heightFactor: 1.2, diff --git a/uni/test/integration/resources/exam2_example.html b/uni/test/integration/resources/exam2_example.html new file mode 100644 index 000000000..48f48badb --- /dev/null +++ b/uni/test/integration/resources/exam2_example.html @@ -0,0 +1,489 @@ + + + + + + + + + + + + + + + + + + + + + + + +FCUP - Mapa de Exames + + + + + + + + + + + +
+ +
Voc est em: Incio > Cursos/CE > Chamadas > Exames
+
+
+ + +
+
+Mapa das Instalaes +
+
+
+FC6 - Departamento de Cincia de Computadores +FC5 - Edifcio Central +FC4 - Departamento de Biologia +FC3 - Departamento de Fsica e Astronomia e Departamento GAOT +FC2 - Departamento de Qumica e Bioqumica +FC1 - Departamento de Matemtica + +
+
+
+ +
+
+
+
Atalhos
+
+ + + +
+ +
+
Opes
+ +
+
+
+

Cursos

+ +

Mapa de Exames

+

L:EF

+

Normal - poca Normal (2S)

+ + +
+ + + + + + + + + + + + + + + + + +
Segunda
2099-05-29
Tera
2099-05-30
Quarta
2099-05-31
Quinta
2099-06-01
Sexta
2099-06-02
Sbado
2099-06-03

+ + + + +
FIS2019
0:00-

+
+

+ + + + + + + + + + + + + + + + + +
Segunda
2099-06-05
Tera
2099-06-06
Quarta
2099-06-07
Quinta
2099-06-08
Sexta
2099-06-09
Sbado
2099-06-10
+ + + + +
FIS3020
0:00-

+
+ + + + +
Q1004
0:00-

+
+ + + + +
FIS2017
0:00-

+

+ + + + + + + +
FIS3022
0:00-

CCINF1002
0:00-

+

+

+ + + + + + + + + + + + + + + + + +
Segunda
2099-06-12
Tera
2099-06-13
Quarta
2099-06-14
Quinta
2099-06-15
Sexta
2099-06-16
Sbado
2099-06-17
+ + + + +
FIS2018
0:00-

+
+ + + + +
FIS1014
0:00-

+

+ + + + +
M2030
09:30-12:30

FC1122, FC1219
+
+

+ + + + + + + + + + + + + + + + +
Segunda
2099-06-19
Tera
2099-06-20
Quarta
2099-06-21
Quinta
2099-06-22
Sexta
2099-06-23
Sbado
2099-06-24
+ + + + +
M1015
09:00-12:00

FC1120, FC1122, FC1219, FC1226
+

+

+

Recurso - poca Recurso (2S)

+ + +
+ + + + + + + + + + + + + + + + + +
Segunda
2099-06-26
Tera
2099-06-27
Quarta
2099-06-28
Quinta
2099-06-29
Sexta
2099-06-30
Sbado
2099-07-01
+ + + + + + + +
CCINF1002
0:00-

FIS2019
0:00-

+
+ + + + +
FIS3022
0:00-

+
+ + + + +
FIS1018
0:00-

+

+ + + + + + + +
M2030
0:00-

M1015
0:00-

+

+

+ + + + + + + + + + + + + + + + +
Segunda
2099-07-03
Tera
2099-07-04
Quarta
2099-07-05
Quinta
2099-07-06
Sexta
2099-07-07
Sbado
2099-07-08

+ + + + +
FIS1014
0:00-

+
+ + + + +
FIS2018
0:00-

+
+ + + + +
FIS3020
0:00-

+
+ + + + + + + +
FIS2017
0:00-

Q1004
0:00-

+

+

+
+
+
+
+ +Recomendar Pgina + + +Voltar ao Topo
+
+ +Copyright 1996-2099 © Faculdade de Cincias da Universidade do Porto + I Termos e Condies + I Acessibilidade + I ndice A-Z + I Livro de Visitas +
+Pgina gerada em: 2099-04-28 s 16:13:57 + +| Poltica de Utilizao Aceitvel | Poltica de Proteo de Dados Pessoais | Denncias +

+
+ + + + + + + diff --git a/uni/test/integration/src/exams2_page_test.dart b/uni/test/integration/src/exams2_page_test.dart new file mode 100644 index 000000000..da1b6c4c6 --- /dev/null +++ b/uni/test/integration/src/exams2_page_test.dart @@ -0,0 +1,121 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart' as http; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/controller/networking/network_router.dart'; +import 'package:uni/controller/parsers/parser_exams.dart'; +import 'package:uni/model/entities/course.dart'; +import 'package:uni/model/entities/course_units/course_unit.dart'; +import 'package:uni/model/entities/exam.dart'; +import 'package:uni/model/entities/profile.dart'; +import 'package:uni/model/entities/session.dart'; +import 'package:uni/model/providers/lazy/exam_provider.dart'; +import 'package:uni/view/exams/exams.dart'; + +import '../../mocks/integration/src/exams_page_test.mocks.dart'; +import '../../test_widget.dart'; + +@GenerateNiceMocks([MockSpec(), MockSpec()]) +void main() async { + await initTestEnvironment(); + + group('ExamsPage Second Integration Tests', () { + final mockClient = MockClient(); + final mockResponse = MockResponse(); + final fis3022CourseUnit = CourseUnit( + abbreviation: 'FIS3022', + name: 'Métodos Computacionais em Engenharia', + occurrId: 0, + status: 'V', + ); + final m2030CourseUnit = CourseUnit( + abbreviation: 'M2030', + name: 'Probabilidade e Estatística', + occurrId: 0, + status: 'V', + ); + + final beginFis3022Exam = DateTime.parse('2099-06-09 00:00'); + final endFis3022Exam = DateTime.parse('2099-06-09 00:00'); + final fis3022Exam = Exam( + '33999', + beginFis3022Exam, + endFis3022Exam, + 'FIS3022', + [], + 'EN', + 'fcup', + ); + final beginM2030Exam = DateTime.parse('2099-06-17 09:30'); + final endM2030Exam = DateTime.parse('2099-06-17 12:30'); + final m2030Exam = Exam( + '34053', + beginM2030Exam, + endM2030Exam, + 'M2030', + ['FC1122', 'FC1219'], + 'EN', + 'fcup', + ); + final beginQ1004Exam = DateTime.parse('2099-06-07 00:00'); + final endQ1004Exam = DateTime.parse('2099-06-07 00:00'); + final q1004Exam = + Exam('34085', beginQ1004Exam, endQ1004Exam, 'Q1004', [], 'EN', 'fcup'); + + final filteredExams = {}; + for (final type in Exam.displayedTypes) { + filteredExams[type] = true; + } + + final profile = Profile()..courses = [Course(id: 9113, faculty: 'fcup')]; + + testWidgets('Exams', (WidgetTester tester) async { + NetworkRouter.httpClient = mockClient; + final mockHtml = File('test/integration/resources/exam2_example.html') + .readAsStringSync(encoding: latin1); + when(mockResponse.body).thenReturn(mockHtml); + when(mockResponse.statusCode).thenReturn(200); + when(mockClient.get(any, headers: anyNamed('headers'))) + .thenAnswer((_) async => mockResponse); + + final examProvider = ExamProvider(); + + const widget = ExamsPageView(); + + final providers = [ + ChangeNotifierProvider(create: (_) => examProvider), + ]; + await tester.pumpWidget(testableWidget(widget, providers: providers)); + + expect(find.byKey(Key('$fis3022Exam-exam')), findsNothing); + expect(find.byKey(Key('$m2030Exam-exam')), findsNothing); + expect(find.byKey(Key('$q1004Exam-exam')), findsNothing); + + final exams = await examProvider.fetchUserExams( + ParserExams(), + profile, + Session(username: '', cookies: '', faculties: ['fcup']), + [fis3022CourseUnit, m2030CourseUnit], + persistentSession: false, + ); + + examProvider.setState(exams); + + expect(examProvider.state!.contains(fis3022Exam), true); + expect(examProvider.state!.contains(m2030Exam), true); + + // print(exams); + + await tester.pumpAndSettle(); + + expect(find.byKey(Key('$fis3022Exam-exam')), findsOneWidget); + expect(find.byKey(Key('$m2030Exam-exam')), findsOneWidget); + expect(find.byKey(Key('$q1004Exam-exam')), findsNothing); + }); + }); +} From 39603aa4444ceae23c212d2f264148f832d2589a Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Wed, 7 Feb 2024 17:22:00 +0000 Subject: [PATCH 196/334] Change Exam Card Date View --- uni/lib/controller/parsers/parser_exams.dart | 2 +- uni/lib/view/exams/widgets/exam_row.dart | 12 +++++++----- uni/test/integration/src/exams2_page_test.dart | 2 -- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/uni/lib/controller/parsers/parser_exams.dart b/uni/lib/controller/parsers/parser_exams.dart index 43f5b5df7..0f83f2230 100644 --- a/uni/lib/controller/parsers/parser_exams.dart +++ b/uni/lib/controller/parsers/parser_exams.dart @@ -58,7 +58,7 @@ class ParserExams { .map((e) => e.trim()) .toList(); } - schedule = examsDay.text.endsWith('0:00-') + schedule = examsDay.text.endsWith('-') ? '00:00-00:00' : examsDay.text.substring( examsDay.text.indexOf(':') - 2, diff --git a/uni/lib/view/exams/widgets/exam_row.dart b/uni/lib/view/exams/widgets/exam_row.dart index e6175c2a1..cfe2a8de7 100644 --- a/uni/lib/view/exams/widgets/exam_row.dart +++ b/uni/lib/view/exams/widgets/exam_row.dart @@ -54,11 +54,13 @@ class _ExamRowState extends State { children: [ Column( mainAxisAlignment: MainAxisAlignment.center, - children: [ - ExamTime( - begin: widget.exam.beginTime, - ), - ], + children: widget.exam.beginTime != '00:00' + ? [ + ExamTime( + begin: widget.exam.beginTime, + ), + ] + : [], ), ExamTitle( subject: widget.exam.subject, diff --git a/uni/test/integration/src/exams2_page_test.dart b/uni/test/integration/src/exams2_page_test.dart index da1b6c4c6..c14fbec0d 100644 --- a/uni/test/integration/src/exams2_page_test.dart +++ b/uni/test/integration/src/exams2_page_test.dart @@ -109,8 +109,6 @@ void main() async { expect(examProvider.state!.contains(fis3022Exam), true); expect(examProvider.state!.contains(m2030Exam), true); - // print(exams); - await tester.pumpAndSettle(); expect(find.byKey(Key('$fis3022Exam-exam')), findsOneWidget); From afe5137f6fba876ded4c938aca9f52382c7e8c7d Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Fri, 9 Feb 2024 14:00:25 +0000 Subject: [PATCH 197/334] Use Regex for Parsing Exam Schedule --- uni/lib/controller/parsers/parser_exams.dart | 23 ++++++++++---------- uni/lib/model/entities/exam.dart | 2 +- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/uni/lib/controller/parsers/parser_exams.dart b/uni/lib/controller/parsers/parser_exams.dart index 0f83f2230..c018c2aef 100644 --- a/uni/lib/controller/parsers/parser_exams.dart +++ b/uni/lib/controller/parsers/parser_exams.dart @@ -27,7 +27,6 @@ class ParserExams { final examTypes = []; var rooms = []; String? subject; - String? schedule; var id = '0'; var days = 0; var tableNum = 0; @@ -58,17 +57,17 @@ class ParserExams { .map((e) => e.trim()) .toList(); } - schedule = examsDay.text.endsWith('-') - ? '00:00-00:00' - : examsDay.text.substring( - examsDay.text.indexOf(':') - 2, - examsDay.text.indexOf(':') + 9, - ); - final splittedSchedule = schedule!.split('-'); - final begin = - DateTime.parse('${dates[days]} ${splittedSchedule[0]}'); - final end = - DateTime.parse('${dates[days]} ${splittedSchedule[1]}'); + final DateTime begin; + final DateTime end; + if (!examsDay.text.endsWith('-')) { + final rx = RegExp(r'(\d{2}:\d{2})-(\d{2}:\d{2})'); + final match = rx.allMatches(examsDay.text).first; + begin = DateTime.parse('${dates[days]} ${match.group(1)!}'); + end = DateTime.parse('${dates[days]} ${match.group(2)!}'); + } else { + begin = DateTime.parse('${dates[days]} 00:00'); + end = DateTime.parse('${dates[days]} 00:00'); + } final exam = Exam( id, begin, diff --git a/uni/lib/model/entities/exam.dart b/uni/lib/model/entities/exam.dart index ed1df9e70..466902014 100644 --- a/uni/lib/model/entities/exam.dart +++ b/uni/lib/model/entities/exam.dart @@ -83,7 +83,7 @@ class Exam { @override String toString() { - return '''$id - $subject - ${begin.year} - $month - ${begin.day} - $beginTime-$endTime - $type - $rooms - $weekDay'''; + return '''$id - $subject - ${begin.year} - $month - ${begin.day} - $beginTime-$endTime - $type - $rooms - $weekDay'''; } @override From 36e083cc17821114eb2bdba60a894e57dc31c2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Sat, 10 Feb 2024 22:01:07 +0000 Subject: [PATCH 198/334] ci: create env file for deployments --- .github/workflows/deploy.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 1ea2786fd..ba8436e65 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -116,6 +116,9 @@ jobs: echo "keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}" >> ${{env.PROPERTIES_PATH}} echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" >> ${{env.PROPERTIES_PATH}} + - name: Create .env file + run: echo "${{env.UNI_ENV_FILE}}" > ./assets/env/.env + - name: Build Android App Bundle run: | flutter pub get From d12fccbbff26df1f4e592a88873378d58522354d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Sat, 10 Feb 2024 22:23:14 +0000 Subject: [PATCH 199/334] ci: remove nonexistant dependency --- .github/workflows/deploy.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index ba8436e65..981089234 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -8,7 +8,6 @@ jobs: actions: "write" name: "Bump version and Build App Bundle" runs-on: ubuntu-latest - needs: [bump_version] env: PROPERTIES_PATH: "android/key.properties" JAVA_VERSION: "17.x" From fcd03c2c4d3d47fa91951e6ca6674b2da99d63be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Sat, 10 Feb 2024 22:25:22 +0000 Subject: [PATCH 200/334] chore: create .env.example --- uni/assets/env/.env.example | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 uni/assets/env/.env.example diff --git a/uni/assets/env/.env.example b/uni/assets/env/.env.example new file mode 100644 index 000000000..f0f399f01 --- /dev/null +++ b/uni/assets/env/.env.example @@ -0,0 +1,3 @@ +# Plausbile +PLAUSIBLE_URL=https://plausible.example.com +PLAUSIBLE_DOMAIN=your.plausible.domain From 1ac20a90a20b813ed10653ccb77b899e2df7ff4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Sat, 10 Feb 2024 23:07:31 +0000 Subject: [PATCH 201/334] Fix app_version and pubspec paths Because the deploy action changes the cwd kekw --- .github/workflows/deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 981089234..cf760a9e4 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -12,8 +12,8 @@ jobs: PROPERTIES_PATH: "android/key.properties" JAVA_VERSION: "17.x" FLUTTER_VERSION: "3.13.7" - APP_VERSION_PATH: "uni/app_version.txt" - PUBSPEC_PATH: "uni/pubspec.yaml" + APP_VERSION_PATH: "app_version.txt" + PUBSPEC_PATH: "pubspec.yaml" defaults: run: working-directory: ./uni From 16403b485fb011de30eb56387c3d97c6ba56fd9c Mon Sep 17 00:00:00 2001 From: LuisDuarte1 Date: Sat, 10 Feb 2024 23:23:20 +0000 Subject: [PATCH 202/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index cef5a0394..ea2c0d1e0 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.7.47+237 \ No newline at end of file +1.8.0-beta.0+228 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 829b9f956..04a1ec855 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,8 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.7.47+237 +version: 1.8.0-beta.0+228 + environment: sdk: ">=3.0.0 <4.0.0" From e5f12e44c7fb764364d351d5864a62bbfaa649be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Sat, 10 Feb 2024 23:27:16 +0000 Subject: [PATCH 203/334] ci: fix missing var --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index cf760a9e4..9181b62d2 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -116,7 +116,7 @@ jobs: echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" >> ${{env.PROPERTIES_PATH}} - name: Create .env file - run: echo "${{env.UNI_ENV_FILE}}" > ./assets/env/.env + run: echo "${{vars.UNI_ENV_FILE}}" > ./assets/env/.env - name: Build Android App Bundle run: | From 96ba9c329fa6ae5e95e07d9f9661f988f4063691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Sat, 10 Feb 2024 23:44:28 +0000 Subject: [PATCH 204/334] Make deploy workflow use github environment depending on branch ref name --- .github/workflows/deploy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 9181b62d2..8ba00a858 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -8,6 +8,8 @@ jobs: actions: "write" name: "Bump version and Build App Bundle" runs-on: ubuntu-latest + environment: + name: ${{ github.ref_name }} env: PROPERTIES_PATH: "android/key.properties" JAVA_VERSION: "17.x" From 87df88ee1dbe22231d158a67cb26c780e7ac6329 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Mon, 12 Feb 2024 16:33:43 +0000 Subject: [PATCH 205/334] Handling different opening results --- uni/lib/generated/intl/messages_en.dart | 10 +++ uni/lib/generated/intl/messages_pt_PT.dart | 12 +++- uni/lib/generated/l10n.dart | 58 +++++++++++++++-- uni/lib/l10n/intl_en.arb | 14 ++++- uni/lib/l10n/intl_pt_PT.arb | 14 ++++- .../view/common_widgets/pulse_animation.dart | 17 +++-- .../widgets/course_unit_files_row.dart | 63 ++++++++++++++----- 7 files changed, 153 insertions(+), 35 deletions(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index f1c673060..de7261274 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -114,6 +114,8 @@ class MessageLookup extends MessageLookupByLibrary { "D. Beatriz\'s stationery store"), "dona_bia_building": MessageLookupByLibrary.simpleMessage( "Floor -1 of building B (B-142)"), + "download_error": + MessageLookupByLibrary.simpleMessage("Error downloading the file"), "ects": MessageLookupByLibrary.simpleMessage("ECTS performed: "), "edit_off": MessageLookupByLibrary.simpleMessage("Edit"), "edit_on": MessageLookupByLibrary.simpleMessage("Finish editing"), @@ -166,6 +168,8 @@ class MessageLookup extends MessageLookupByLibrary { "nav_title": m2, "news": MessageLookupByLibrary.simpleMessage("News"), "no": MessageLookupByLibrary.simpleMessage("No"), + "no_app": MessageLookupByLibrary.simpleMessage( + "No app found to open the file"), "no_bus": MessageLookupByLibrary.simpleMessage("Don\'t miss any bus!"), "no_bus_stops": MessageLookupByLibrary.simpleMessage("No configured stops"), @@ -217,12 +221,16 @@ class MessageLookup extends MessageLookupByLibrary { "occurrence_type": MessageLookupByLibrary.simpleMessage("Type of occurrence"), "of_month": MessageLookupByLibrary.simpleMessage("of"), + "open_error": + MessageLookupByLibrary.simpleMessage("Error opening the file"), "other_links": MessageLookupByLibrary.simpleMessage("Other links"), "pass_change_request": MessageLookupByLibrary.simpleMessage( "For security reasons, passwords must be changed periodically."), "password": MessageLookupByLibrary.simpleMessage("password"), "pendent_references": MessageLookupByLibrary.simpleMessage("Pending references"), + "permission_denied": + MessageLookupByLibrary.simpleMessage("Permission denied"), "personal_assistance": MessageLookupByLibrary.simpleMessage("Face-to-face assistance"), "press_again": @@ -256,6 +264,8 @@ class MessageLookup extends MessageLookupByLibrary { "student_number": MessageLookupByLibrary.simpleMessage("student number"), "success": MessageLookupByLibrary.simpleMessage("Sent with success"), + "successful_open": + MessageLookupByLibrary.simpleMessage("File opened successfully"), "tele_assistance": MessageLookupByLibrary.simpleMessage("Telephone assistance"), "tele_personal_assistance": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 56896f6ad..76da6d9ff 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -113,6 +113,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Papelaria D. Beatriz"), "dona_bia_building": MessageLookupByLibrary.simpleMessage( "Piso -1 do edifício B (B-142)"), + "download_error": MessageLookupByLibrary.simpleMessage( + "Erro ao descarregar o ficheiro"), "ects": MessageLookupByLibrary.simpleMessage("ECTS realizados: "), "edit_off": MessageLookupByLibrary.simpleMessage("Editar"), "edit_on": MessageLookupByLibrary.simpleMessage("Concluir edição"), @@ -153,7 +155,7 @@ class MessageLookup extends MessageLookupByLibrary { "library_occupation": MessageLookupByLibrary.simpleMessage("Ocupação da Biblioteca"), "load_error": MessageLookupByLibrary.simpleMessage( - "Aconteceu um erro ao carregar os dados"), + "Erro ao carregar a informação"), "loading_terms": MessageLookupByLibrary.simpleMessage( "Carregando os Termos e Condições..."), "login": MessageLookupByLibrary.simpleMessage("Entrar"), @@ -166,6 +168,8 @@ class MessageLookup extends MessageLookupByLibrary { "nav_title": m2, "news": MessageLookupByLibrary.simpleMessage("Notícias"), "no": MessageLookupByLibrary.simpleMessage("Não"), + "no_app": MessageLookupByLibrary.simpleMessage( + "Nenhuma aplicação encontrada para abrir o ficheiro"), "no_bus": MessageLookupByLibrary.simpleMessage( "Não percas nenhum autocarro!"), "no_bus_stops": MessageLookupByLibrary.simpleMessage( @@ -219,12 +223,16 @@ class MessageLookup extends MessageLookupByLibrary { "occurrence_type": MessageLookupByLibrary.simpleMessage("Tipo de ocorrência"), "of_month": MessageLookupByLibrary.simpleMessage("de"), + "open_error": + MessageLookupByLibrary.simpleMessage("Erro ao abrir o ficheiro"), "other_links": MessageLookupByLibrary.simpleMessage("Outros links"), "pass_change_request": MessageLookupByLibrary.simpleMessage( "Por razões de segurança, as palavras-passe têm de ser alteradas periodicamente."), "password": MessageLookupByLibrary.simpleMessage("palavra-passe"), "pendent_references": MessageLookupByLibrary.simpleMessage("Referências pendentes"), + "permission_denied": + MessageLookupByLibrary.simpleMessage("Sem permissão"), "personal_assistance": MessageLookupByLibrary.simpleMessage("Atendimento presencial"), "press_again": MessageLookupByLibrary.simpleMessage( @@ -258,6 +266,8 @@ class MessageLookup extends MessageLookupByLibrary { "student_number": MessageLookupByLibrary.simpleMessage("número de estudante"), "success": MessageLookupByLibrary.simpleMessage("Enviado com sucesso"), + "successful_open": + MessageLookupByLibrary.simpleMessage("Ficheiro aberto com sucesso"), "tele_assistance": MessageLookupByLibrary.simpleMessage("Atendimento telefónico"), "tele_personal_assistance": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 1923c90cc..30773b6a0 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -763,11 +763,11 @@ class S { ); } - /// `Error loading the information` - String get load_error { + /// `Error downloading the file` + String get download_error { return Intl.message( - 'Error loading the information', - name: 'load_error', + 'Error downloading the file', + name: 'download_error', desc: '', args: [], ); @@ -1228,6 +1228,56 @@ class S { ); } + /// `File opened successfully` + String get successful_open { + return Intl.message( + 'File opened successfully', + name: 'successful_open', + desc: '', + args: [], + ); + } + + /// `Permission denied` + String get permission_denied { + return Intl.message( + 'Permission denied', + name: 'permission_denied', + desc: '', + args: [], + ); + } + + /// `Error opening the file` + String get open_error { + return Intl.message( + 'Error opening the file', + name: 'open_error', + desc: '', + args: [], + ); + } + + /// `No app found to open the file` + String get no_app { + return Intl.message( + 'No app found to open the file', + name: 'no_app', + desc: '', + args: [], + ); + } + + /// `Error loading the information` + String get load_error { + return Intl.message( + 'Error loading the information', + name: 'load_error', + desc: '', + args: [], + ); + } + /// `Prints` String get prints { return Intl.message( diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 4dc0cc3f1..bf8d2121c 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -150,8 +150,8 @@ }, "library_occupation": "Library Occupation", "@library_occupation": {}, - "load_error": "Error loading the information", - "@load_error": {}, + "download_error": "Error downloading the file", + "@download_error": {}, "loading_terms": "Loading Terms and Conditions...", "@loading_terms": {}, "login": "Login", @@ -240,6 +240,16 @@ "@press_again": {}, "print": "Print", "@print": {}, + "successful_open": "File opened successfully", + "@successful_open": {}, + "permission_denied": "Permission denied", + "@permission_denied": {}, + "open_error": "Error opening the file", + "@open_error": {}, + "no_app": "No app found to open the file", + "@no_app": {}, + "load_error": "Error loading the information", + "@load_error": {}, "prints": "Prints", "@prints": {}, "problem_id": "Brief identification of the problem", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 42cf70ce8..56dbd3945 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -150,10 +150,20 @@ "time": {} } }, + "load_error": "Erro ao carregar a informação", + "@load_error": {}, "library_occupation": "Ocupação da Biblioteca", "@library_occupation": {}, - "load_error": "Aconteceu um erro ao carregar os dados", - "@load_error": {}, + "download_error": "Erro ao descarregar o ficheiro", + "@download_error": {}, + "successful_open": "Ficheiro aberto com sucesso", + "@successful_open": {}, + "permission_denied": "Sem permissão", + "@permission_denied": {}, + "open_error": "Erro ao abrir o ficheiro", + "@open_error": {}, + "no_app": "Nenhuma aplicação encontrada para abrir o ficheiro", + "@no_app": {}, "loading_terms": "Carregando os Termos e Condições...", "@loading_terms": {}, "login": "Entrar", diff --git a/uni/lib/view/common_widgets/pulse_animation.dart b/uni/lib/view/common_widgets/pulse_animation.dart index a3b3e614c..45476c9b2 100644 --- a/uni/lib/view/common_widgets/pulse_animation.dart +++ b/uni/lib/view/common_widgets/pulse_animation.dart @@ -2,23 +2,22 @@ import 'dart:math'; import 'package:flutter/material.dart'; class PulseAnimation extends StatelessWidget { - const PulseAnimation( - {super.key, required this.description, required this.controller}); - final String description; + const PulseAnimation({ + required this.child, + required this.controller, + super.key, + }); + final Widget child; final AnimationController controller; @override Widget build(BuildContext context) { return AnimatedBuilder( animation: controller, - builder: (context, child) { + builder: (context, _) { return Opacity( opacity: 1 - 0.5 * sin(controller.value * pi), - child: Text( - description.substring(0, description.indexOf('_')), - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.bodyLarge, - ), + child: child, ); }, ); diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart index f12ec41d9..88c26c6dc 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_files_row.dart @@ -1,13 +1,13 @@ -import 'dart:math'; +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:open_file_plus/open_file_plus.dart'; import 'package:provider/provider.dart'; import 'package:uni/controller/local_storage/file_offline_storage.dart'; import 'package:uni/generated/l10n.dart'; -import 'package:uni/view/common_widgets/pulse_animation.dart'; import 'package:uni/model/entities/course_units/course_unit_file.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; +import 'package:uni/view/common_widgets/pulse_animation.dart'; import 'package:uni/view/common_widgets/toast_message.dart'; class CourseUnitFilesRow extends StatefulWidget { @@ -60,8 +60,13 @@ class CourseUnitFilesRowState extends State child: Container( padding: const EdgeInsets.only(left: 10), child: PulseAnimation( - description: widget.file.name, controller: _controller, + child: Text( + widget.file.name + .substring(0, widget.file.name.indexOf('_')), + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyLarge, + ), ), ), ), @@ -72,24 +77,48 @@ class CourseUnitFilesRowState extends State } Future openFile(BuildContext context, CourseUnitFile unitFile) async { - final session = context.read().state!; + final session = context.read().state; + + final result = await loadFileFromStorageOrRetrieveNew( + unitFile.name, + unitFile.url, + session, + headers: {'pct_id': unitFile.fileCode}, + ); - try { - final result = await loadFileFromStorageOrRetrieveNew( - unitFile.name, - unitFile.url, - session, - headers: {'pct_id': unitFile.fileCode}, - ); - await OpenFile.open(result!.path); - } catch (e) { + if (result?.path != null) { + final resultType = await OpenFile.open(result!.path); + if (context.mounted) handleFileOpening(resultType.type, context); + } else { if (context.mounted) { - await ToastMessage.error( - context, - S.of(context).load_error, - ); + await ToastMessage.error(context, S.of(context).download_error); } } + _controller.reset(); } + + void handleFileOpening(ResultType resultType, BuildContext context) { + switch (resultType) { + case ResultType.done: + ToastMessage.success( + context, + S.of(context).successful_open, + ); + case ResultType.error: + ToastMessage.error( + context, + S.of(context).open_error, + ); + case ResultType.noAppToOpen: + ToastMessage.warning( + context, + S.of(context).no_app, + ); + case ResultType.permissionDenied: + ToastMessage.warning(context, S.of(context).permission_denied); + case ResultType.fileNotFound: + ToastMessage.error(context, S.of(context).download_error); + } + } } From 48d2b3528d4fd45ae6dd2ff25351dfd320516b94 Mon Sep 17 00:00:00 2001 From: Processing Date: Tue, 8 Aug 2023 21:49:34 +0100 Subject: [PATCH 206/334] Add Navbar. --- uni/lib/utils/navbar_items.dart | 43 +++++++++++++ .../pages_layouts/general/general.dart | 3 + .../widgets/bottom_navigation_bar.dart | 62 +++++++++++++++++++ uni/lib/view/locations/locations.dart | 2 +- uni/pubspec.lock | 32 +++++----- 5 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 uni/lib/utils/navbar_items.dart create mode 100644 uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart new file mode 100644 index 000000000..1ee8ef393 --- /dev/null +++ b/uni/lib/utils/navbar_items.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +enum NavbarItem { + navPersonalArea( + 'Área Pessoal', + Icons.home_outlined, + ['Área Pessoal'], + ), + navStudentArea( + 'Área do Estudante', + Icons.school_outlined, + ['Horário', 'Exames', 'Cadeiras', 'Calendário'], + ), + navMap( + 'Mapa e Transportes', + Icons.map_outlined, + ['Autocarros', 'Locais'], + ), + navUsefulInfo( + 'Úteis', + Icons.domain_outlined, + ['Restaurantes', 'Biblioteca', 'Úteis'], + ), + navCurrentAccount( + 'Conta Corrente', + Icons.account_balance_wallet_outlined, + [], + ); + + const NavbarItem(this.label, this.icon, this.routes); + + final String label; + final IconData icon; + final List routes; + // TODO(Process-ing): Transform into single route when the new pages are done + + BottomNavigationBarItem toBottomNavigationBarItem() { + return BottomNavigationBarItem( + icon: Icon(icon), + label: label, + ); + } +} diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index dcd888115..50882cccd 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -8,6 +8,8 @@ import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; +import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/profile_button.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; @@ -120,6 +122,7 @@ abstract class GeneralPageViewState extends State { drawer: AppNavigationDrawer( parentContext: context, ), + bottomNavigationBar: AppBottomNavbar(parentContext: context), body: RefreshState(onRefresh: onRefresh, child: body), ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart new file mode 100644 index 000000000..1a9d7c2a6 --- /dev/null +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; +import 'package:uni/utils/navbar_items.dart'; + +class AppBottomNavbar extends StatefulWidget { + const AppBottomNavbar({ required this.parentContext, super.key }); + + final BuildContext parentContext; + + @override + State createState() { + return AppBottomNavbarState(); + } +} + +class AppBottomNavbarState extends State { + AppBottomNavbarState(); + + static final List navbarItems = NavbarItem.values + .map((item) => item.toBottomNavigationBarItem()) + .toList(); + + String _getCurrentRoute() => + ModalRoute.of(widget.parentContext)!.settings.name == null + ? NavbarItem.values.toList()[0].label + : ModalRoute.of(widget.parentContext)!.settings.name!.substring(1); + + int _getCurrentIndex() { + final currentRoute = _getCurrentRoute(); + + for (final item in NavbarItem.values) { + if (item.routes.contains(currentRoute)) { + return item.index; + } + } + + return NavbarItem.navPersonalArea.index; + } + + void _onItemTapped(int index) { + final prev = _getCurrentRoute(); + final item = NavbarItem.values[index]; + final key = item.routes.isNotEmpty + ? item.routes[0] + : NavbarItem.navPersonalArea.routes[0]; + + if (prev != key) { + Navigator.of(context).pop(); + Navigator.pushNamed(context, '/$key'); + } + } + + @override + Widget build(BuildContext context) { + return BottomNavigationBar( + items: navbarItems, + currentIndex: _getCurrentIndex(), + selectedItemColor: Theme.of(context).primaryColor, + unselectedItemColor: Theme.of(context).colorScheme.onSurface, + onTap: _onItemTapped, + ); + } +} diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index b6019305d..b59d2415e 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -56,7 +56,7 @@ class LocationsPageViewState extends State { @override Widget build(BuildContext context) { - return Column( + return ListView( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 27418c39d..e64f0c11b 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -221,10 +221,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: @@ -641,10 +641,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -1087,18 +1087,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1135,26 +1135,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.24.3" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "0.5.9" timelines: dependency: "direct main" description: @@ -1351,10 +1351,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: From dc44ad2afb9ec40f25b420e33d0bea9ded5cdf2f Mon Sep 17 00:00:00 2001 From: Processing Date: Wed, 9 Aug 2023 19:00:39 +0100 Subject: [PATCH 207/334] Run dart format. --- uni/lib/utils/navbar_items.dart | 30 +++++++++---------- .../widgets/bottom_navigation_bar.dart | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index 1ee8ef393..7c3771cab 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -2,29 +2,29 @@ import 'package:flutter/material.dart'; enum NavbarItem { navPersonalArea( - 'Área Pessoal', - Icons.home_outlined, - ['Área Pessoal'], + 'Área Pessoal', + Icons.home_outlined, + ['Área Pessoal'], ), navStudentArea( - 'Área do Estudante', - Icons.school_outlined, - ['Horário', 'Exames', 'Cadeiras', 'Calendário'], + 'Área do Estudante', + Icons.school_outlined, + ['Horário', 'Exames', 'Cadeiras', 'Calendário'], ), navMap( - 'Mapa e Transportes', - Icons.map_outlined, - ['Autocarros', 'Locais'], + 'Mapa e Transportes', + Icons.map_outlined, + ['Autocarros', 'Locais'], ), navUsefulInfo( - 'Úteis', - Icons.domain_outlined, - ['Restaurantes', 'Biblioteca', 'Úteis'], + 'Úteis', + Icons.domain_outlined, + ['Restaurantes', 'Biblioteca', 'Úteis'], ), navCurrentAccount( - 'Conta Corrente', - Icons.account_balance_wallet_outlined, - [], + 'Conta Corrente', + Icons.account_balance_wallet_outlined, + [], ); const NavbarItem(this.label, this.icon, this.routes); diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index 1a9d7c2a6..a1966bb8b 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:uni/utils/navbar_items.dart'; class AppBottomNavbar extends StatefulWidget { - const AppBottomNavbar({ required this.parentContext, super.key }); + const AppBottomNavbar({required this.parentContext, super.key}); final BuildContext parentContext; From 6a667f3b25961078dce83d09b18552bdadae4084 Mon Sep 17 00:00:00 2001 From: Processing Date: Fri, 11 Aug 2023 17:20:12 +0100 Subject: [PATCH 208/334] =?UTF-8?q?Change=20"=C3=9Ateis"=20navbar=20item?= =?UTF-8?q?=20to=20"Faculdade".?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- uni/lib/utils/navbar_items.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index 7c3771cab..b46b3b730 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -9,17 +9,17 @@ enum NavbarItem { navStudentArea( 'Área do Estudante', Icons.school_outlined, - ['Horário', 'Exames', 'Cadeiras', 'Calendário'], + ['Horário', 'Exames', 'Cadeiras'], ), navMap( 'Mapa e Transportes', Icons.map_outlined, ['Autocarros', 'Locais'], ), - navUsefulInfo( - 'Úteis', + navFaculty( + 'Faculdade', Icons.domain_outlined, - ['Restaurantes', 'Biblioteca', 'Úteis'], + ['Restaurantes', 'Calendário', 'Biblioteca', 'Úteis'], ), navCurrentAccount( 'Conta Corrente', From 38772c32ef3a3ef61f10c7c5c6e8a1b908c3e555 Mon Sep 17 00:00:00 2001 From: Processing Date: Fri, 11 Aug 2023 17:22:10 +0100 Subject: [PATCH 209/334] Remove "Conta corrente" navbar item. --- uni/lib/utils/navbar_items.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index b46b3b730..711021a11 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -20,11 +20,6 @@ enum NavbarItem { 'Faculdade', Icons.domain_outlined, ['Restaurantes', 'Calendário', 'Biblioteca', 'Úteis'], - ), - navCurrentAccount( - 'Conta Corrente', - Icons.account_balance_wallet_outlined, - [], ); const NavbarItem(this.label, this.icon, this.routes); From 70050be1ea4816c9a46edb1739918c9e941d875b Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jan 2024 16:52:29 +0000 Subject: [PATCH 210/334] Update bottom navbar design --- uni/lib/utils/navbar_items.dart | 27 +++++++++---------- .../widgets/bottom_navigation_bar.dart | 18 +++++++++---- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index 711021a11..130a99eb6 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -2,29 +2,28 @@ import 'package:flutter/material.dart'; enum NavbarItem { navPersonalArea( - 'Área Pessoal', Icons.home_outlined, - ['Área Pessoal'], + ['area'], ), - navStudentArea( - 'Área do Estudante', + navAcademicPath( Icons.school_outlined, - ['Horário', 'Exames', 'Cadeiras'], + ['horario', 'exames', 'cadeiras'], ), - navMap( - 'Mapa e Transportes', - Icons.map_outlined, - ['Autocarros', 'Locais'], + navTransports( + Icons.directions_bus_filled_outlined, + ['autocarros'] ), navFaculty( - 'Faculdade', Icons.domain_outlined, - ['Restaurantes', 'Calendário', 'Biblioteca', 'Úteis'], + ['locais', 'biblioteca', 'uteis'], + ), + navRestaurants( + Icons.local_cafe_outlined, + ['restaurantes'], ); - const NavbarItem(this.label, this.icon, this.routes); + const NavbarItem(this.icon, this.routes); - final String label; final IconData icon; final List routes; // TODO(Process-ing): Transform into single route when the new pages are done @@ -32,7 +31,7 @@ enum NavbarItem { BottomNavigationBarItem toBottomNavigationBarItem() { return BottomNavigationBarItem( icon: Icon(icon), - label: label, + label: '', ); } } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index a1966bb8b..7eecbbfa5 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:uni/utils/drawer_items.dart'; import 'package:uni/utils/navbar_items.dart'; class AppBottomNavbar extends StatefulWidget { @@ -21,7 +22,7 @@ class AppBottomNavbarState extends State { String _getCurrentRoute() => ModalRoute.of(widget.parentContext)!.settings.name == null - ? NavbarItem.values.toList()[0].label + ? DrawerItem.navPersonalArea.title : ModalRoute.of(widget.parentContext)!.settings.name!.substring(1); int _getCurrentIndex() { @@ -33,7 +34,7 @@ class AppBottomNavbarState extends State { } } - return NavbarItem.navPersonalArea.index; + return -1; } void _onItemTapped(int index) { @@ -41,7 +42,7 @@ class AppBottomNavbarState extends State { final item = NavbarItem.values[index]; final key = item.routes.isNotEmpty ? item.routes[0] - : NavbarItem.navPersonalArea.routes[0]; + : DrawerItem.navPersonalArea.title; if (prev != key) { Navigator.of(context).pop(); @@ -51,11 +52,18 @@ class AppBottomNavbarState extends State { @override Widget build(BuildContext context) { + final currentIndex = _getCurrentIndex(); return BottomNavigationBar( items: navbarItems, - currentIndex: _getCurrentIndex(), - selectedItemColor: Theme.of(context).primaryColor, + type: currentIndex == -1 ? BottomNavigationBarType.fixed + : BottomNavigationBarType.shifting, + currentIndex: currentIndex == -1 ? 0 : currentIndex, + selectedItemColor: currentIndex == -1 + ? Theme.of(context).colorScheme.onSurface + : Theme.of(context).primaryColor, unselectedItemColor: Theme.of(context).colorScheme.onSurface, + showSelectedLabels: false, + showUnselectedLabels: false, onTap: _onItemTapped, ); } From 28426dcbd2702f7f3b74b9643a12d9cd09c8e0d8 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jan 2024 17:42:27 +0000 Subject: [PATCH 211/334] Fix initial bottom bar layout --- uni/lib/utils/navbar_items.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index 130a99eb6..607705410 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; enum NavbarItem { navPersonalArea( Icons.home_outlined, - ['area'], + ['area', ''], ), navAcademicPath( Icons.school_outlined, From 80b23506e72625dbb72915fca92c605537998421 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jan 2024 18:41:27 +0000 Subject: [PATCH 212/334] Add bottom navbar to secondary page and fix profile routing --- uni/lib/main.dart | 9 +++++++++ uni/lib/utils/drawer_items.dart | 2 ++ uni/lib/utils/navbar_items.dart | 5 +---- .../general/widgets/bottom_navigation_bar.dart | 6 +++--- .../pages_layouts/general/widgets/profile_button.dart | 8 +++----- .../pages_layouts/secondary/secondary.dart | 2 ++ uni/lib/view/profile/profile.dart | 8 +++----- 7 files changed, 23 insertions(+), 17 deletions(-) diff --git a/uni/lib/main.dart b/uni/lib/main.dart index a21e099b4..f7825e259 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -42,8 +42,10 @@ import 'package:uni/view/library/library.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/locations/locations.dart'; import 'package:uni/view/login/login.dart'; +import 'package:uni/view/profile/profile.dart'; import 'package:uni/view/restaurant/restaurant_page_view.dart'; import 'package:uni/view/schedule/schedule.dart'; +import 'package:uni/view/settings/settings.dart'; import 'package:uni/view/theme.dart'; import 'package:uni/view/theme_notifier.dart'; import 'package:uni/view/transports/transports.dart'; @@ -288,6 +290,13 @@ class ApplicationState extends State { page: const TransportsPageView(), settings: settings, ), + '/${DrawerItem.navProfile.title}': + MaterialPageRoute( + builder: (__) => const ProfilePageView(), + ), + '/${DrawerItem.navSettings.title}': MaterialPageRoute( + builder: (_) => const SettingsPage(), + ), }; return transitions[settings.name]; }, diff --git a/uni/lib/utils/drawer_items.dart b/uni/lib/utils/drawer_items.dart index 064a717f4..d79c74c32 100644 --- a/uni/lib/utils/drawer_items.dart +++ b/uni/lib/utils/drawer_items.dart @@ -10,6 +10,8 @@ enum DrawerItem { navLibrary('biblioteca', faculties: {'feup'}), navFaculty('faculdade'), navAcademicPath('percurso_academico'), + navProfile('perfil'), + navSettings('definicoes'), navTransports('transportes'); const DrawerItem(this.title, {this.faculties}); diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index 607705410..7b13ab60f 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -9,10 +9,7 @@ enum NavbarItem { Icons.school_outlined, ['horario', 'exames', 'cadeiras'], ), - navTransports( - Icons.directions_bus_filled_outlined, - ['autocarros'] - ), + navTransports(Icons.directions_bus_filled_outlined, ['autocarros']), navFaculty( Icons.domain_outlined, ['locais', 'biblioteca', 'uteis'], diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index 7eecbbfa5..cb6464220 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -55,16 +55,16 @@ class AppBottomNavbarState extends State { final currentIndex = _getCurrentIndex(); return BottomNavigationBar( items: navbarItems, - type: currentIndex == -1 ? BottomNavigationBarType.fixed - : BottomNavigationBarType.shifting, + onTap: _onItemTapped, currentIndex: currentIndex == -1 ? 0 : currentIndex, + type: BottomNavigationBarType.fixed, + iconSize: 32, selectedItemColor: currentIndex == -1 ? Theme.of(context).colorScheme.onSurface : Theme.of(context).primaryColor, unselectedItemColor: Theme.of(context).colorScheme.onSurface, showSelectedLabels: false, showUnselectedLabels: false, - onTap: _onItemTapped, ); } } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart index 98666a694..37a7bbae7 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; -import 'package:uni/view/profile/profile.dart'; +import 'package:uni/utils/drawer_items.dart'; class ProfileButton extends StatelessWidget { const ProfileButton({required this.getProfileDecorationImage, super.key}); @@ -36,11 +36,9 @@ class ProfileButton extends StatelessWidget { ) { return TextButton( onPressed: () => { - Navigator.push( + Navigator.pushNamed( context, - MaterialPageRoute( - builder: (__) => const ProfilePageView(), - ), + '/${DrawerItem.navProfile.title}', ), }, child: Container( diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 30aaafec3..41bde31b0 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; /// Page with a back button on top @@ -10,6 +11,7 @@ abstract class SecondaryPageViewState Scaffold getScaffold(BuildContext context, Widget body) { return Scaffold( appBar: CustomAppBar(getTopRightButton: getTopRightButton), + bottomNavigationBar: AppBottomNavbar(parentContext: context,), body: RefreshState(onRefresh: onRefresh, child: body), ); } diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 9f0d02085..64fab5fa4 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -3,13 +3,13 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; +import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/profile/widgets/account_info_card.dart'; import 'package:uni/view/profile/widgets/course_info_card.dart'; import 'package:uni/view/profile/widgets/print_info_card.dart'; import 'package:uni/view/profile/widgets/profile_overview.dart'; -import 'package:uni/view/settings/settings.dart'; class ProfilePageView extends StatefulWidget { const ProfilePageView({super.key}); @@ -61,11 +61,9 @@ class ProfilePageViewState extends SecondaryPageViewState { padding: const EdgeInsets.fromLTRB(0, 10, 20, 10), child: IconButton( icon: const Icon(Icons.settings), - onPressed: () => Navigator.push( + onPressed: () => Navigator.pushNamed( context, - MaterialPageRoute( - builder: (_) => const SettingsPage(), - ), + '/${DrawerItem.navSettings.title}', ), ), ); From 84f95986f8d293a1c843532eb50e8696279e105e Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 18 Jan 2024 19:12:44 +0000 Subject: [PATCH 213/334] Fix bottom navbar route handling --- .../general/widgets/bottom_navigation_bar.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index cb6464220..597dd0f36 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -20,13 +20,14 @@ class AppBottomNavbarState extends State { .map((item) => item.toBottomNavigationBarItem()) .toList(); - String _getCurrentRoute() => - ModalRoute.of(widget.parentContext)!.settings.name == null - ? DrawerItem.navPersonalArea.title - : ModalRoute.of(widget.parentContext)!.settings.name!.substring(1); + String? _getCurrentRoute() => + ModalRoute.of(widget.parentContext)!.settings.name?.substring(1); int _getCurrentIndex() { final currentRoute = _getCurrentRoute(); + if (_getCurrentRoute() == null) { + return -1; + } for (final item in NavbarItem.values) { if (item.routes.contains(currentRoute)) { From 90cc6a4280baabc22771fb550b2c84b1490fb804 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 3 Feb 2024 13:33:42 +0000 Subject: [PATCH 214/334] Refactor AppTopNavbar; Refactor home page top navbar --- .../pages_layouts/general/general.dart | 15 +--- .../general/widgets/app_bar.dart | 78 ++++++++----------- .../general/widgets/profile_button.dart | 9 ++- .../pages_layouts/secondary/secondary.dart | 22 ++++-- uni/lib/view/home/home.dart | 19 +++++ .../view/home/widgets/main_cards_list.dart | 4 +- uni/lib/view/home/widgets/uni_button.dart | 41 ++++++++++ uni/lib/view/locations/locations.dart | 2 +- uni/lib/view/profile/profile.dart | 2 +- 9 files changed, 125 insertions(+), 67 deletions(-) create mode 100644 uni/lib/view/home/widgets/uni_button.dart diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 50882cccd..add0df1c0 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -8,15 +8,11 @@ import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; -import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/profile_button.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; /// Page with a hamburger menu and the user profile picture abstract class GeneralPageViewState extends State { - final double borderMargin = 18; bool _loadedOnce = false; bool _loading = true; @@ -118,16 +114,13 @@ abstract class GeneralPageViewState extends State { Widget getScaffold(BuildContext context, Widget body) { return Scaffold( - appBar: CustomAppBar(getTopRightButton: getTopRightButton), - drawer: AppNavigationDrawer( - parentContext: context, - ), + appBar: getTopNavbar(context), bottomNavigationBar: AppBottomNavbar(parentContext: context), body: RefreshState(onRefresh: onRefresh, child: body), ); } - /// Gets a round shaped button with the photo of the current user. - Widget getTopRightButton(BuildContext context) => - ProfileButton(getProfileDecorationImage: getProfileDecorationImage); + AppTopNavbar? getTopNavbar(BuildContext context) { + return null; + } } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart index 578c223e8..21ab961fd 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart @@ -1,71 +1,59 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:uni/utils/drawer_items.dart'; /// Upper bar of the app. /// /// This widget consists on an instance of `AppBar` containing the app's logo, /// an option button and a button with the user's picture. -class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { - const CustomAppBar({required this.getTopRightButton, super.key}); +class AppTopNavbar extends StatelessWidget implements PreferredSizeWidget { + const AppTopNavbar({ + this.title, + this.rightButton, + this.leftButton, + super.key, + }); static const double borderMargin = 18; - final Widget Function(BuildContext) getTopRightButton; + final String? title; + final Widget? rightButton; + final Widget? leftButton; @override Size get preferredSize => const Size.fromHeight(kToolbarHeight); @override AppBar build(BuildContext context) { - final queryData = MediaQuery.of(context); - return AppBar( - bottom: PreferredSize( - preferredSize: Size.zero, - child: Container( - color: Theme.of(context).dividerColor, - margin: const EdgeInsets.only( - left: borderMargin, - right: borderMargin, - ), - height: 1.5, - ), - ), + automaticallyImplyLeading: false, elevation: 0, iconTheme: Theme.of(context).iconTheme, backgroundColor: Theme.of(context).scaffoldBackgroundColor, titleSpacing: 0, - title: ButtonTheme( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: const RoundedRectangleBorder(), - child: Builder( - builder: (context) => TextButton( - onPressed: () { - final currentRouteName = ModalRoute.of(context)!.settings.name; - if (currentRouteName != '/${DrawerItem.navPersonalArea.title}') { - Navigator.pushNamed( - context, - '/${DrawerItem.navPersonalArea.title}', - ); - } else { - Scaffold.of(context).openDrawer(); - } - }, - child: SvgPicture.asset( - colorFilter: ColorFilter.mode( - Theme.of(context).primaryColor, - BlendMode.srcIn, - ), - 'assets/images/logo_dark.svg', - height: queryData.size.height / 25, + title: Stack( + alignment: Alignment.center, + children: [ + Align( + alignment: Alignment.centerLeft, + child: leftButton, + ), + Center( + child: Text( + title ?? '', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + color: Theme.of(context) + .primaryTextTheme + .headlineMedium + ?.color, + ), ), ), - ), + Align( + alignment: Alignment.centerRight, + child: rightButton, + ), + ], ), - actions: [ - getTopRightButton(context), - ], ); } } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart index 37a7bbae7..ad68c29a2 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart @@ -41,9 +41,14 @@ class ProfileButton extends StatelessWidget { '/${DrawerItem.navProfile.title}', ), }, + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + minimumSize: Size.zero, + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), child: Container( - width: 40, - height: 40, + width: 50, + height: 50, decoration: BoxDecoration( shape: BoxShape.circle, image: decorationImage.data, diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 41bde31b0..950b24b85 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -10,14 +10,26 @@ abstract class SecondaryPageViewState @override Scaffold getScaffold(BuildContext context, Widget body) { return Scaffold( - appBar: CustomAppBar(getTopRightButton: getTopRightButton), - bottomNavigationBar: AppBottomNavbar(parentContext: context,), + appBar: AppTopNavbar( + title: getTitle(), + leftButton: const Padding( + padding: EdgeInsets.symmetric(horizontal: 5), + child: BackButton(), + ), + rightButton: getTopRightButton(context), + ), + bottomNavigationBar: AppBottomNavbar( + parentContext: context, + ), body: RefreshState(onRefresh: onRefresh, child: body), ); } - @override - Widget getTopRightButton(BuildContext context) { - return Container(); + String? getTitle() { + return null; + } + + Widget? getTopRightButton(BuildContext context) { + return null; } } diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 93812aba3..6164b26ba 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -2,8 +2,11 @@ import 'package:flutter/material.dart'; import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/utils/favorite_widget_type.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/profile_button.dart'; import 'package:uni/view/home/widgets/main_cards_list.dart'; import 'package:uni/view/home/widgets/tracking_banner.dart'; +import 'package:uni/view/home/widgets/uni_button.dart'; class HomePageView extends StatefulWidget { const HomePageView({super.key}); @@ -69,4 +72,20 @@ class HomePageViewState extends GeneralPageViewState { card.onRefresh(context); } } + + @override + AppTopNavbar? getTopNavbar(BuildContext context) { + return AppTopNavbar( + leftButton: const Padding( + padding: EdgeInsets.symmetric(horizontal: 20), + child: UniButton(), + ), + rightButton: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: ProfileButton( + getProfileDecorationImage: getProfileDecorationImage, + ), + ), + ); + } } diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index 421981e4b..8ba1227e7 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -184,9 +184,9 @@ class MainCardsListState extends State { BuildContext context, ) { final userSession = - Provider.of(context, listen: false).state!; + Provider.of(context, listen: false).state; return cardTypes - .where((type) => type.isVisible(userSession.faculties)) + .where((type) => type.isVisible(userSession?.faculties ?? [])) .where((type) => MainCardsList.cardCreators.containsKey(type)) .map((type) { final i = cardTypes.indexOf(type); diff --git a/uni/lib/view/home/widgets/uni_button.dart b/uni/lib/view/home/widgets/uni_button.dart new file mode 100644 index 000000000..3732be9d7 --- /dev/null +++ b/uni/lib/view/home/widgets/uni_button.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:uni/utils/drawer_items.dart'; + +class UniButton extends StatelessWidget { + const UniButton({super.key}); + + @override + Widget build(BuildContext context) { + return ButtonTheme( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: const RoundedRectangleBorder(), + child: Builder( + builder: (context) => TextButton( + onPressed: () { + final currentRouteName = ModalRoute.of(context)!.settings.name; + if (currentRouteName != '/${DrawerItem.navPersonalArea.title}') { + Navigator.pushNamed( + context, + '/${DrawerItem.navPersonalArea.title}', + ); + } else { + Scaffold.of(context).openDrawer(); + } + }, + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + child: SvgPicture.asset( + colorFilter: ColorFilter.mode( + Theme.of(context).primaryColor, + BlendMode.srcIn, + ), + 'assets/images/logo_dark.svg', + height: 35, + ), + ), + ), + ); + } +} diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index b59d2415e..b6019305d 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -56,7 +56,7 @@ class LocationsPageViewState extends State { @override Widget build(BuildContext context) { - return ListView( + return Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 64fab5fa4..05782aee2 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -58,7 +58,7 @@ class ProfilePageViewState extends SecondaryPageViewState { @override Widget getTopRightButton(BuildContext context) { return Container( - padding: const EdgeInsets.fromLTRB(0, 10, 20, 10), + padding: const EdgeInsets.fromLTRB(0, 10, 5, 10), child: IconButton( icon: const Icon(Icons.settings), onPressed: () => Navigator.pushNamed( From 8d169f8e29a81f28c661de23cdf8d5ce84fb4e15 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 3 Feb 2024 14:30:20 +0000 Subject: [PATCH 215/334] Refactor other pages; Change bottom navbar layout --- uni/lib/utils/navbar_items.dart | 13 ++++++----- uni/lib/view/common_widgets/page_title.dart | 2 +- .../pages_layouts/general/general.dart | 2 +- .../{app_bar.dart => top_navigation_bar.dart} | 0 .../pages_layouts/secondary/secondary.dart | 2 +- uni/lib/view/home/home.dart | 2 +- .../view/restaurant/restaurant_page_view.dart | 23 ++++++++----------- uni/lib/view/settings/settings.dart | 7 +++--- 8 files changed, 25 insertions(+), 26 deletions(-) rename uni/lib/view/common_widgets/pages_layouts/general/widgets/{app_bar.dart => top_navigation_bar.dart} (100%) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index 7b13ab60f..c58cefee5 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -7,22 +7,23 @@ enum NavbarItem { ), navAcademicPath( Icons.school_outlined, - ['horario', 'exames', 'cadeiras'], + ['cadeiras', 'horario', 'exames'], + ), + navRestaurants( + Icons.local_cafe_outlined, + ['restaurantes'], ), - navTransports(Icons.directions_bus_filled_outlined, ['autocarros']), navFaculty( Icons.domain_outlined, ['locais', 'biblioteca', 'uteis'], ), - navRestaurants( - Icons.local_cafe_outlined, - ['restaurantes'], - ); + navTransports(Icons.map_outlined, ['autocarros']); const NavbarItem(this.icon, this.routes); final IconData icon; final List routes; + // TODO(Process-ing): Transform into single route when the new pages are done BottomNavigationBarItem toBottomNavigationBarItem() { diff --git a/uni/lib/view/common_widgets/page_title.dart b/uni/lib/view/common_widgets/page_title.dart index af5bbeccd..b0c97610f 100644 --- a/uni/lib/view/common_widgets/page_title.dart +++ b/uni/lib/view/common_widgets/page_title.dart @@ -21,7 +21,7 @@ class PageTitle extends StatelessWidget { ), ); return Container( - padding: pad ? const EdgeInsets.fromLTRB(20, 20, 20, 10) : null, + padding: pad ? const EdgeInsets.fromLTRB(20, 30, 20, 10) : null, alignment: center ? Alignment.center : null, child: title, ); diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index add0df1c0..54625cdac 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -7,9 +7,9 @@ import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart'; /// Page with a hamburger menu and the user profile picture abstract class GeneralPageViewState extends State { diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart similarity index 100% rename from uni/lib/view/common_widgets/pages_layouts/general/widgets/app_bar.dart rename to uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 950b24b85..97d8263d3 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart'; /// Page with a back button on top abstract class SecondaryPageViewState diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 6164b26ba..2e8a04dc8 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/utils/favorite_widget_type.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/app_bar.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/profile_button.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart'; import 'package:uni/view/home/widgets/main_cards_list.dart'; import 'package:uni/view/home/widgets/tracking_banner.dart'; import 'package:uni/view/home/widgets/uni_button.dart'; diff --git a/uni/lib/view/restaurant/restaurant_page_view.dart b/uni/lib/view/restaurant/restaurant_page_view.dart index 2a894a5da..eb60a4660 100644 --- a/uni/lib/view/restaurant/restaurant_page_view.dart +++ b/uni/lib/view/restaurant/restaurant_page_view.dart @@ -60,21 +60,18 @@ class _RestaurantPageViewState extends GeneralPageViewState ], ), const SizedBox(height: 10), - Expanded( - child: LazyConsumer>( - builder: (context, restaurants) => createTabViewBuilder( - restaurants, - context, - ), - onNullContent: Center( - child: Text( - S.of(context).no_menus, - style: const TextStyle(fontSize: 18), - ), + LazyConsumer>( + builder: (context, restaurants) => createTabViewBuilder( + restaurants, + context, + ), + onNullContent: Center( + child: Text( + S.of(context).no_menus, + style: const TextStyle(fontSize: 18), ), - hasContent: (List restaurants) => - restaurants.isNotEmpty, ), + hasContent: (List restaurants) => restaurants.isNotEmpty, ), ], ); diff --git a/uni/lib/view/settings/settings.dart b/uni/lib/view/settings/settings.dart index c1f0210a9..0354ea919 100644 --- a/uni/lib/view/settings/settings.dart +++ b/uni/lib/view/settings/settings.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/view/about/about.dart'; import 'package:uni/view/bug_report/bug_report.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/settings/widgets/locale_switch_button.dart'; import 'package:uni/view/settings/widgets/logout_confirm_dialog.dart'; @@ -24,8 +23,7 @@ class SettingsPageState extends SecondaryPageViewState { Widget getBody(BuildContext context) { return Column( children: [ - PageTitle(name: S.of(context).settings), - const Padding(padding: EdgeInsets.only(top: 10)), + const Padding(padding: EdgeInsets.only(top: 50)), Expanded( child: Container( padding: const EdgeInsets.symmetric(horizontal: 20), @@ -95,4 +93,7 @@ class SettingsPageState extends SecondaryPageViewState { @override Future onRefresh(BuildContext context) async {} + + @override + String? getTitle() => S.of(context).settings; } From 09f7b458419304e580f96dfa6693b85acfb02fd7 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 3 Feb 2024 14:37:40 +0000 Subject: [PATCH 216/334] Add calendar page to faculty bottom navbar button --- uni/lib/utils/navbar_items.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index c58cefee5..dde10d3b9 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -7,7 +7,7 @@ enum NavbarItem { ), navAcademicPath( Icons.school_outlined, - ['cadeiras', 'horario', 'exames'], + ['horario', 'exames', 'cadeiras'], ), navRestaurants( Icons.local_cafe_outlined, @@ -15,7 +15,7 @@ enum NavbarItem { ), navFaculty( Icons.domain_outlined, - ['locais', 'biblioteca', 'uteis'], + ['locais', 'biblioteca', 'uteis', 'calendario'], ), navTransports(Icons.map_outlined, ['autocarros']); From c7a0563dcfff51a6e85bb629ba653487c3c1f7e3 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Sat, 3 Feb 2024 16:49:19 +0000 Subject: [PATCH 217/334] Refactor remaining pages --- uni/lib/utils/navbar_items.dart | 4 +- .../bus_stop_next_arrivals.dart | 14 ++---- .../bus_stop_selection.dart | 5 +- uni/lib/view/calendar/calendar.dart | 14 ++---- .../pages_layouts/general/general.dart | 4 ++ .../pages_layouts/secondary/secondary.dart | 23 ++++++---- uni/lib/view/course_units/course_units.dart | 18 ++++---- uni/lib/view/exams/exams.dart | 24 ++++++---- .../exams/widgets/exam_filter_button.dart | 28 +++++++++++ .../view/exams/widgets/exam_page_title.dart | 46 ------------------- uni/lib/view/library/library.dart | 8 ++-- uni/lib/view/locations/locations.dart | 13 ++---- .../view/restaurant/restaurant_page_view.dart | 32 +++++-------- uni/lib/view/schedule/schedule.dart | 27 ++++------- uni/lib/view/useful_info/useful_info.dart | 0 uni/test/integration/src/exams_page_test.dart | 4 +- 16 files changed, 119 insertions(+), 145 deletions(-) create mode 100644 uni/lib/view/exams/widgets/exam_filter_button.dart delete mode 100644 uni/lib/view/exams/widgets/exam_page_title.dart create mode 100644 uni/lib/view/useful_info/useful_info.dart diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index dde10d3b9..c9d5e880e 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -15,9 +15,9 @@ enum NavbarItem { ), navFaculty( Icons.domain_outlined, - ['locais', 'biblioteca', 'uteis', 'calendario'], + ['biblioteca', 'uteis', 'calendario'], ), - navTransports(Icons.map_outlined, ['autocarros']); + navTransports(Icons.map_outlined, ['locais', 'autocarros']); const NavbarItem(this.icon, this.routes); diff --git a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart index c0eda0b68..18db3ba0e 100644 --- a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart +++ b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart @@ -10,8 +10,7 @@ import 'package:uni/view/bus_stop_next_arrivals/widgets/bus_stop_row.dart'; import 'package:uni/view/bus_stop_selection/bus_stop_selection.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; import 'package:uni/view/common_widgets/last_update_timestamp.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; class BusStopNextArrivalsPage extends StatefulWidget { @@ -23,18 +22,12 @@ class BusStopNextArrivalsPage extends StatefulWidget { /// Manages the 'Bus arrivals' section inside the user's personal area class BusStopNextArrivalsPageState - extends GeneralPageViewState { + extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { return SingleChildScrollView( child: Column( children: [ - Container( - padding: const EdgeInsets.only(bottom: 12), - child: PageTitle( - name: S.of(context).nav_title(DrawerItem.navStops.title), - ), - ), Container( padding: const EdgeInsets.all(8), child: Row( @@ -104,6 +97,9 @@ class BusStopNextArrivalsPageState return Provider.of(context, listen: false) .forceRefresh(context); } + + @override + String? getTitle() => S.of(context).nav_title(DrawerItem.navStops.title); } class NextArrivals extends StatefulWidget { diff --git a/uni/lib/view/bus_stop_selection/bus_stop_selection.dart b/uni/lib/view/bus_stop_selection/bus_stop_selection.dart index 14c689c5c..99b7d530f 100644 --- a/uni/lib/view/bus_stop_selection/bus_stop_selection.dart +++ b/uni/lib/view/bus_stop_selection/bus_stop_selection.dart @@ -6,7 +6,6 @@ import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; import 'package:uni/view/bus_stop_selection/widgets/bus_stop_search.dart'; import 'package:uni/view/bus_stop_selection/widgets/bus_stop_selection_row.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; class BusStopSelectionPage extends StatefulWidget { @@ -49,7 +48,6 @@ class BusStopSelectionPageState bottom: 20, ), children: [ - PageTitle(name: S.of(context).configured_buses), Container( padding: const EdgeInsets.all(20), child: Text( @@ -85,4 +83,7 @@ class BusStopSelectionPageState @override Future onRefresh(BuildContext context) async {} + + @override + String? getTitle() => S.of(context).configured_buses; } diff --git a/uni/lib/view/calendar/calendar.dart b/uni/lib/view/calendar/calendar.dart index 39526d3fa..51189e489 100644 --- a/uni/lib/view/calendar/calendar.dart +++ b/uni/lib/view/calendar/calendar.dart @@ -6,8 +6,7 @@ import 'package:uni/model/entities/calendar_event.dart'; import 'package:uni/model/providers/lazy/calendar_provider.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/calendar/widgets/calendar_tile.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; class CalendarPageView extends StatefulWidget { @@ -17,17 +16,11 @@ class CalendarPageView extends StatefulWidget { State createState() => CalendarPageViewState(); } -class CalendarPageViewState extends GeneralPageViewState { +class CalendarPageViewState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { return ListView( children: [ - Container( - padding: const EdgeInsets.only(bottom: 6), - child: PageTitle( - name: S.of(context).nav_title(DrawerItem.navCalendar.title), - ), - ), LazyConsumer>( builder: getTimeline, hasContent: (calendar) => calendar.isNotEmpty, @@ -67,4 +60,7 @@ class CalendarPageViewState extends GeneralPageViewState { return Provider.of(context, listen: false) .forceRefresh(context); } + + @override + String? getTitle() => S.of(context).nav_title(DrawerItem.navCalendar.title); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 54625cdac..26aa4724a 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -8,6 +8,7 @@ import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart'; @@ -116,6 +117,9 @@ abstract class GeneralPageViewState extends State { return Scaffold( appBar: getTopNavbar(context), bottomNavigationBar: AppBottomNavbar(parentContext: context), + drawer: AppNavigationDrawer( + parentContext: context, + ), body: RefreshState(onRefresh: onRefresh, child: body), ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 97d8263d3..0978c2423 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart'; @@ -10,14 +11,7 @@ abstract class SecondaryPageViewState @override Scaffold getScaffold(BuildContext context, Widget body) { return Scaffold( - appBar: AppTopNavbar( - title: getTitle(), - leftButton: const Padding( - padding: EdgeInsets.symmetric(horizontal: 5), - child: BackButton(), - ), - rightButton: getTopRightButton(context), - ), + appBar: getTopNavbar(context), bottomNavigationBar: AppBottomNavbar( parentContext: context, ), @@ -32,4 +26,17 @@ abstract class SecondaryPageViewState Widget? getTopRightButton(BuildContext context) { return null; } + + @override + @nonVirtual + AppTopNavbar? getTopNavbar(BuildContext context) { + return AppTopNavbar( + title: getTitle(), + leftButton: const Padding( + padding: EdgeInsets.symmetric(horizontal: 5), + child: BackButton(), + ), + rightButton: getTopRightButton(context), + ); + } } diff --git a/uni/lib/view/course_units/course_units.dart b/uni/lib/view/course_units/course_units.dart index d9e3fee01..5ac4ed385 100644 --- a/uni/lib/view/course_units/course_units.dart +++ b/uni/lib/view/course_units/course_units.dart @@ -6,8 +6,7 @@ import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/course_units/widgets/course_unit_card.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -23,7 +22,7 @@ class CourseUnitsPageView extends StatefulWidget { } class CourseUnitsPageViewState - extends GeneralPageViewState { + extends SecondaryPageViewState { String? selectedSchoolYear; String? selectedSemester; @@ -53,7 +52,7 @@ class CourseUnitsPageViewState return Column( children: [ - _getPageTitleAndFilters(availableYears, availableSemesters), + _getFilters(availableYears, availableSemesters), _getPageView(courseUnits, availableYears, availableSemesters), ], ); @@ -61,7 +60,7 @@ class CourseUnitsPageViewState hasContent: (Profile profile) => profile.courseUnits.isNotEmpty, onNullContent: Column( children: [ - _getPageTitleAndFilters([], []), + _getFilters([], []), Center( heightFactor: 10, child: Text( @@ -94,16 +93,13 @@ class CourseUnitsPageViewState return _generateCourseUnitsCards(filteredCourseUnits, context); } - Widget _getPageTitleAndFilters( + Widget _getFilters( List availableYears, List availableSemesters, ) { return Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ - PageTitle( - name: S.of(context).nav_title(DrawerItem.navCourseUnits.title), - ), const Spacer(), DropdownButtonHideUnderline( child: DropdownButton( @@ -224,4 +220,8 @@ class CourseUnitsPageViewState return Provider.of(context, listen: false) .forceRefresh(context); } + + @override + String? getTitle() => + S.of(context).nav_title(DrawerItem.navCourseUnits.title); } diff --git a/uni/lib/view/exams/exams.dart b/uni/lib/view/exams/exams.dart index 604edcc7b..94b7da9ed 100644 --- a/uni/lib/view/exams/exams.dart +++ b/uni/lib/view/exams/exams.dart @@ -5,10 +5,11 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/utils/date_time_formatter.dart'; +import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/common_widgets/row_container.dart'; -import 'package:uni/view/exams/widgets/exam_page_title.dart'; +import 'package:uni/view/exams/widgets/exam_filter_button.dart'; import 'package:uni/view/exams/widgets/exam_row.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; @@ -20,7 +21,7 @@ class ExamsPageView extends StatefulWidget { State createState() => ExamsPageViewState(); } -class ExamsPageViewState extends GeneralPageViewState { +class ExamsPageViewState extends SecondaryPageViewState { List hiddenExams = PreferencesController.getHiddenExams(); Map filteredExamTypes = PreferencesController.getFilteredExams(); @@ -29,11 +30,6 @@ class ExamsPageViewState extends GeneralPageViewState { Widget getBody(BuildContext context) { return ListView( children: [ - ExamPageTitle( - () => setState(() { - filteredExamTypes = PreferencesController.getFilteredExams(); - }), - ), LazyConsumer>( builder: (context, exams) { return Column( @@ -166,4 +162,16 @@ class ExamsPageViewState extends GeneralPageViewState { return Provider.of(context, listen: false) .forceRefresh(context); } + + @override + String? getTitle() => S.of(context).nav_title(DrawerItem.navExams.title); + + @override + Widget? getTopRightButton(BuildContext context) { + return ExamFilterButton( + () => setState(() { + filteredExamTypes = PreferencesController.getFilteredExams(); + }), + ); + } } diff --git a/uni/lib/view/exams/widgets/exam_filter_button.dart b/uni/lib/view/exams/widgets/exam_filter_button.dart new file mode 100644 index 000000000..b05f0ce61 --- /dev/null +++ b/uni/lib/view/exams/widgets/exam_filter_button.dart @@ -0,0 +1,28 @@ +import 'package:flutter/material.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; +import 'package:uni/view/exams/widgets/exam_filter_form.dart'; + +class ExamFilterButton extends StatelessWidget { + const ExamFilterButton(this.onDismissFilterDialog, {super.key}); + + final void Function() onDismissFilterDialog; + + @override + Widget build(BuildContext context) { + return IconButton( + icon: const Icon(Icons.filter_list), + onPressed: () { + showDialog( + context: context, + builder: (_) { + final filteredExamsTypes = PreferencesController.getFilteredExams(); + return ExamFilterForm( + Map.from(filteredExamsTypes), + onDismissFilterDialog, + ); + }, + ); + }, + ); + } +} diff --git a/uni/lib/view/exams/widgets/exam_page_title.dart b/uni/lib/view/exams/widgets/exam_page_title.dart deleted file mode 100644 index 2eb175645..000000000 --- a/uni/lib/view/exams/widgets/exam_page_title.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uni/controller/local_storage/preferences_controller.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/exams/widgets/exam_filter_form.dart'; - -class ExamPageTitle extends StatelessWidget { - const ExamPageTitle(this.onDismissFilterDialog, {super.key}); - - final void Function() onDismissFilterDialog; - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 10), - alignment: Alignment.center, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - PageTitle( - name: S.of(context).nav_title(DrawerItem.navExams.title), - center: false, - pad: false, - ), - IconButton( - icon: const Icon(Icons.filter_alt), - onPressed: () { - showDialog( - context: context, - builder: (_) { - final filteredExamsTypes = - PreferencesController.getFilteredExams(); - return ExamFilterForm( - Map.from(filteredExamsTypes), - onDismissFilterDialog, - ); - }, - ); - }, - ), - ], - ), - ); - } -} diff --git a/uni/lib/view/library/library.dart b/uni/lib/view/library/library.dart index 562ee2b4c..ffc5eef98 100644 --- a/uni/lib/view/library/library.dart +++ b/uni/lib/view/library/library.dart @@ -6,7 +6,7 @@ import 'package:uni/model/entities/library_occupation.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/library/widgets/library_occupation_card.dart'; @@ -17,13 +17,12 @@ class LibraryPage extends StatefulWidget { State createState() => LibraryPageState(); } -class LibraryPageState extends GeneralPageViewState { +class LibraryPageState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { return ListView( shrinkWrap: true, children: [ - PageTitle(name: S.of(context).nav_title(DrawerItem.navLibrary.title)), LibraryOccupationCard(), PageTitle(name: S.of(context).floors), LazyConsumer( @@ -123,4 +122,7 @@ class LibraryPageState extends GeneralPageViewState { return Provider.of(context, listen: false) .forceRefresh(context); } + + @override + String? getTitle() => S.of(context).nav_title(DrawerItem.navLibrary.title); } diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index b6019305d..101175268 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -5,8 +5,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/location_group.dart'; import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locations/widgets/faculty_map.dart'; @@ -17,7 +16,7 @@ class LocationsPage extends StatefulWidget { LocationsPageState createState() => LocationsPageState(); } -class LocationsPageState extends GeneralPageViewState +class LocationsPageState extends SecondaryPageViewState with SingleTickerProviderStateMixin { ScrollController? scrollViewController; @@ -36,6 +35,9 @@ class LocationsPageState extends GeneralPageViewState @override Future onRefresh(BuildContext context) async {} + + @override + String? getTitle() => S.of(context).nav_title(DrawerItem.navLocations.title); } class LocationsPageView extends StatefulWidget { @@ -61,11 +63,6 @@ class LocationsPageViewState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - PageTitle( - name: '${S.of(context).nav_title(DrawerItem.navLocations.title)}:' - ' ${getLocation()}', - center: false, - ), Container( width: 150, height: 40, diff --git a/uni/lib/view/restaurant/restaurant_page_view.dart b/uni/lib/view/restaurant/restaurant_page_view.dart index eb60a4660..f9a18e9c7 100644 --- a/uni/lib/view/restaurant/restaurant_page_view.dart +++ b/uni/lib/view/restaurant/restaurant_page_view.dart @@ -6,8 +6,7 @@ import 'package:uni/model/entities/restaurant.dart'; import 'package:uni/model/providers/lazy/restaurant_provider.dart'; import 'package:uni/model/utils/day_of_week.dart'; import 'package:uni/utils/drawer_items.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/restaurant/widgets/restaurant_page_card.dart'; @@ -20,7 +19,8 @@ class RestaurantPageView extends StatefulWidget { State createState() => _RestaurantPageViewState(); } -class _RestaurantPageViewState extends GeneralPageViewState +class _RestaurantPageViewState + extends SecondaryPageViewState with SingleTickerProviderStateMixin { late List aggRestaurant; late TabController tabController; @@ -40,24 +40,10 @@ class _RestaurantPageViewState extends GeneralPageViewState Widget getBody(BuildContext context) { return Column( children: [ - ListView( - shrinkWrap: true, - children: [ - Container( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 10), - alignment: Alignment.center, - child: PageTitle( - name: S.of(context).nav_title(DrawerItem.navRestaurants.title), - center: false, - pad: false, - ), - ), - TabBar( - controller: tabController, - isScrollable: true, - tabs: createTabs(context), - ), - ], + TabBar( + controller: tabController, + isScrollable: true, + tabs: createTabs(context), ), const SizedBox(height: 10), LazyConsumer>( @@ -173,4 +159,8 @@ class _RestaurantPageViewState extends GeneralPageViewState return Provider.of(context, listen: false) .forceRefresh(context); } + + @override + String? getTitle() => + S.of(context).nav_title(DrawerItem.navRestaurants.title); } diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index 47f266ab7..d3b3e1539 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -5,8 +5,7 @@ import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/providers/lazy/lecture_provider.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/schedule/widgets/schedule_slot.dart'; @@ -18,24 +17,13 @@ class SchedulePage extends StatefulWidget { SchedulePageState createState() => SchedulePageState(); } -class SchedulePageState extends GeneralPageViewState { +class SchedulePageState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { - return Column( - children: [ - PageTitle( - name: S.of(context).nav_title( - DrawerItem.navSchedule.title, - ), - ), - Expanded( - child: LazyConsumer>( - builder: (context, lectures) => SchedulePageView(lectures), - hasContent: (lectures) => lectures.isNotEmpty, - onNullContent: const SchedulePageView([]), - ), - ), - ], + return LazyConsumer>( + builder: (context, lectures) => SchedulePageView(lectures), + hasContent: (lectures) => lectures.isNotEmpty, + onNullContent: const SchedulePageView([]), ); } @@ -43,6 +31,9 @@ class SchedulePageState extends GeneralPageViewState { Future onRefresh(BuildContext context) async { await context.read().forceRefresh(context); } + + @override + String? getTitle() => S.of(context).nav_title(DrawerItem.navSchedule.title); } class SchedulePageView extends StatefulWidget { diff --git a/uni/lib/view/useful_info/useful_info.dart b/uni/lib/view/useful_info/useful_info.dart new file mode 100644 index 000000000..e69de29bb diff --git a/uni/test/integration/src/exams_page_test.dart b/uni/test/integration/src/exams_page_test.dart index b85d95c45..c0bb1160b 100644 --- a/uni/test/integration/src/exams_page_test.dart +++ b/uni/test/integration/src/exams_page_test.dart @@ -132,13 +132,13 @@ void main() async { await tester.pumpAndSettle(); expect(find.byKey(Key('$sdisExam-exam')), findsOneWidget); expect(find.byKey(Key('$sopeExam-exam')), findsOneWidget); - expect(find.byIcon(Icons.filter_alt), findsOneWidget); + expect(find.byIcon(Icons.filter_list), findsOneWidget); filteredExams['ExamDoesNotExist'] = true; await tester.pumpAndSettle(); - final filterButton = find.widgetWithIcon(IconButton, Icons.filter_alt); + final filterButton = find.widgetWithIcon(IconButton, Icons.filter_list); expect(filterButton, findsOneWidget); await tester.tap(filterButton); From 753743a036db6c1a384824a5e421076aeab646bd Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 5 Feb 2024 10:48:20 +0000 Subject: [PATCH 218/334] Change select item color in bottom navbar --- uni/lib/view/about/about.dart | 5 +++ uni/lib/view/bug_report/bug_report.dart | 5 +++ .../widgets/bottom_navigation_bar.dart | 2 +- .../pages_layouts/secondary/secondary.dart | 4 +-- .../course_unit_info/course_unit_info.dart | 5 +++ uni/lib/view/profile/profile.dart | 5 +++ uni/lib/view/theme.dart | 3 +- uni/pubspec.lock | 32 +++++++++---------- 8 files changed, 40 insertions(+), 21 deletions(-) diff --git a/uni/lib/view/about/about.dart b/uni/lib/view/about/about.dart index a0c17a67c..037e195bc 100644 --- a/uni/lib/view/about/about.dart +++ b/uni/lib/view/about/about.dart @@ -37,4 +37,9 @@ class AboutPageViewState extends SecondaryPageViewState { @override Future onRefresh(BuildContext context) async {} + + @override + String? getTitle() { + return null; + } } diff --git a/uni/lib/view/bug_report/bug_report.dart b/uni/lib/view/bug_report/bug_report.dart index 26b9ae931..413c1859e 100644 --- a/uni/lib/view/bug_report/bug_report.dart +++ b/uni/lib/view/bug_report/bug_report.dart @@ -22,4 +22,9 @@ class BugReportPageViewState extends SecondaryPageViewState { @override Future onRefresh(BuildContext context) async {} + + @override + String? getTitle() { + return null; + } } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index 597dd0f36..84f3dbed9 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -62,7 +62,7 @@ class AppBottomNavbarState extends State { iconSize: 32, selectedItemColor: currentIndex == -1 ? Theme.of(context).colorScheme.onSurface - : Theme.of(context).primaryColor, + : Theme.of(context).colorScheme.secondary, unselectedItemColor: Theme.of(context).colorScheme.onSurface, showSelectedLabels: false, showUnselectedLabels: false, diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 0978c2423..08c741631 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -19,9 +19,7 @@ abstract class SecondaryPageViewState ); } - String? getTitle() { - return null; - } + String? getTitle(); Widget? getTopRightButton(BuildContext context) { return null; diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index c6d4d89be..d29aea7b8 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -144,4 +144,9 @@ class CourseUnitDetailPageViewState return CourseUnitClassesView(classes); } + + @override + String? getTitle() { + return null; + } } diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 05782aee2..b452dd3e5 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -74,4 +74,9 @@ class ProfilePageViewState extends SecondaryPageViewState { return Provider.of(context, listen: false) .forceRefresh(context); } + + @override + String? getTitle() { + return null; + } } diff --git a/uni/lib/view/theme.dart b/uni/lib/view/theme.dart index c7c0f37f8..72585db48 100644 --- a/uni/lib/view/theme.dart +++ b/uni/lib/view/theme.dart @@ -9,6 +9,7 @@ const Color _strongGrey = Color.fromARGB(255, 90, 90, 90); const Color _mildBlack = Color.fromARGB(255, 43, 43, 43); const Color _darkishBlack = Color.fromARGB(255, 43, 43, 43); const Color _darkBlack = Color.fromARGB(255, 27, 27, 27); +const Color _lightBlue = Color.fromARGB(255, 172, 193, 206); const _textTheme = TextTheme( displayLarge: TextStyle(fontSize: 40, fontWeight: FontWeight.w400), @@ -54,7 +55,7 @@ ThemeData applicationDarkTheme = ThemeData( background: _darkBlack, primary: _lightGrey, onPrimary: _darkishBlack, - secondary: _lightGrey, + secondary: _lightBlue, onSecondary: _darkishBlack, tertiary: _lightGrey, onTertiary: _darkishBlack, diff --git a/uni/pubspec.lock b/uni/pubspec.lock index e64f0c11b..27418c39d 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -221,10 +221,10 @@ packages: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" connectivity_plus: dependency: "direct main" description: @@ -641,10 +641,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" mgrs_dart: dependency: transitive description: @@ -1087,18 +1087,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" stream_transform: dependency: transitive description: @@ -1135,26 +1135,26 @@ packages: dependency: "direct dev" description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.5.3" timelines: dependency: "direct main" description: @@ -1351,10 +1351,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: From 4a40b55b67ea8f13e209c698f01bf39ec3fe5d66 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 5 Feb 2024 11:11:06 +0000 Subject: [PATCH 219/334] Fix page routing on bottom navbar click --- .../pages_layouts/general/widgets/bottom_navigation_bar.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index 84f3dbed9..93eefb7c1 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -46,7 +46,9 @@ class AppBottomNavbarState extends State { : DrawerItem.navPersonalArea.title; if (prev != key) { - Navigator.of(context).pop(); + if (prev != '') { + Navigator.pop(context); + } Navigator.pushNamed(context, '/$key'); } } From 51b93f58484efd85533a81ef4baccd99e146233a Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 5 Feb 2024 11:30:49 +0000 Subject: [PATCH 220/334] Fix locations search bar positioning --- uni/lib/view/locations/locations.dart | 49 +++++++++++++-------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index 101175268..e688650fa 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -58,13 +58,12 @@ class LocationsPageViewState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return LayoutBuilder( + builder: (context, constraints) { + return Column( children: [ Container( - width: 150, + width: constraints.maxWidth - 40, height: 40, margin: const EdgeInsets.fromLTRB(20, 10, 20, 0), child: TextFormField( @@ -84,27 +83,27 @@ class LocationsPageViewState extends State { ), ), ), - ], - ), - const SizedBox(height: 10), - Expanded( - child: Container( - height: 10, - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - alignment: Alignment.center, - child: FacultyMap( - faculty: getLocation(), - locations: widget.locations, - searchFilter: searchTerms, - interactiveFlags: InteractiveFlag.all - InteractiveFlag.rotate, - // TODO(bdmendes): add support for multiple faculties + const SizedBox(height: 10), + Expanded( + child: Container( + height: 10, + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + alignment: Alignment.center, + child: FacultyMap( + faculty: getLocation(), + locations: widget.locations, + searchFilter: searchTerms, + interactiveFlags: InteractiveFlag.all - InteractiveFlag.rotate, + // TODO(bdmendes): add support for multiple faculties + ), + ), + ), + const SizedBox( + height: 20, ), - ), - ), - const SizedBox( - height: 20, - ), - ], + ], + ); + }, ); } From 22ef10d8300779e71a8af064182fba94530772cc Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 5 Feb 2024 11:42:48 +0000 Subject: [PATCH 221/334] Remove AppNavigationDrawer --- .../pages_layouts/general/general.dart | 4 - .../general/widgets/navigation_drawer.dart | 105 ------------------ 2 files changed, 109 deletions(-) delete mode 100644 uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 26aa4724a..54625cdac 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -8,7 +8,6 @@ import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart'; @@ -117,9 +116,6 @@ abstract class GeneralPageViewState extends State { return Scaffold( appBar: getTopNavbar(context), bottomNavigationBar: AppBottomNavbar(parentContext: context), - drawer: AppNavigationDrawer( - parentContext: context, - ), body: RefreshState(onRefresh: onRefresh, child: body), ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart deleted file mode 100644 index 74283bb36..000000000 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/navigation_drawer.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/model/providers/startup/session_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; - -class AppNavigationDrawer extends StatefulWidget { - const AppNavigationDrawer({required this.parentContext, super.key}); - - final BuildContext parentContext; - - @override - State createState() { - return AppNavigationDrawerState(); - } -} - -class AppNavigationDrawerState extends State { - AppNavigationDrawerState(); - - Map drawerItems = {}; - - @override - void initState() { - super.initState(); - - drawerItems = {}; - for (final element in DrawerItem.values) { - drawerItems[element] = _onSelectPage; - } - } - - String getCurrentRoute() => - ModalRoute.of(widget.parentContext)!.settings.name == null - ? drawerItems.keys.toList()[0].title - : ModalRoute.of(widget.parentContext)!.settings.name!.substring(1); - - void _onSelectPage(String key) { - final prev = getCurrentRoute(); - Navigator.of(context).pop(); - if (prev != key) { - Navigator.pushNamed(context, '/$key'); - } - } - - BoxDecoration? _getSelectionDecoration(String name) { - return (name == getCurrentRoute()) - ? BoxDecoration( - border: Border( - left: BorderSide( - color: Theme.of(context).primaryColor, - width: 3, - ), - ), - color: Theme.of(context).dividerColor, - ) - : null; - } - - Widget createDrawerNavigationOption(DrawerItem d) { - return DecoratedBox( - decoration: _getSelectionDecoration(d.title) ?? const BoxDecoration(), - child: ListTile( - title: Container( - padding: const EdgeInsets.only(bottom: 3, left: 20), - child: Text( - S.of(context).nav_title(d.title), - style: TextStyle( - fontSize: 18, - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.normal, - ), - ), - ), - dense: true, - contentPadding: EdgeInsets.zero, - selected: d.title == getCurrentRoute(), - onTap: () => drawerItems[d]!(d.title), - ), - ); - } - - @override - Widget build(BuildContext context) { - final userSession = context.read().state!; - - return Drawer( - child: Column( - children: [ - Expanded( - child: Container( - padding: const EdgeInsets.only(top: 55), - child: ListView( - children: drawerItems.keys - .where((key) => key.isVisible(userSession.faculties)) - .map(createDrawerNavigationOption) - .toList(), - ), - ), - ), - ], - ), - ); - } -} From e4f198be4bcbf53375999cc4b84a62b0b73a632d Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 7 Feb 2024 18:55:43 +0000 Subject: [PATCH 222/334] Use PageTitle in AppTopNavbar --- .../general/widgets/top_navigation_bar.dart | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart index 21ab961fd..2830086bc 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:uni/view/common_widgets/page_title.dart'; /// Upper bar of the app. /// @@ -37,15 +38,9 @@ class AppTopNavbar extends StatelessWidget implements PreferredSizeWidget { child: leftButton, ), Center( - child: Text( - title ?? '', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.headlineMedium?.copyWith( - color: Theme.of(context) - .primaryTextTheme - .headlineMedium - ?.color, - ), + child: PageTitle( + name: title ?? '', + pad: false, ), ), Align( From 21eca377d2b1882655b2989209b8cf4a83f73d31 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 7 Feb 2024 19:00:35 +0000 Subject: [PATCH 223/334] Refactor DrawerItem to NavigationItem --- uni/lib/main.dart | 33 ++++++++++--------- ...rawer_items.dart => navigation_items.dart} | 6 ++-- .../bus_stop_next_arrivals.dart | 4 +-- uni/lib/view/calendar/calendar.dart | 5 +-- .../widgets/bottom_navigation_bar.dart | 4 +-- .../widgets/drawer_navigation_option.dart | 14 ++++---- .../general/widgets/profile_button.dart | 4 +-- uni/lib/view/course_units/course_units.dart | 4 +-- uni/lib/view/exams/exams.dart | 4 +-- .../widgets/academic_services_card.dart | 4 +-- .../faculty/widgets/copy_center_card.dart | 4 +-- .../view/faculty/widgets/dona_bia_card.dart | 4 +-- .../view/faculty/widgets/infodesk_card.dart | 4 +-- .../widgets/multimedia_center_card.dart | 4 +-- uni/lib/view/home/widgets/bus_stop_card.dart | 6 ++-- uni/lib/view/home/widgets/exam_card.dart | 6 ++-- .../view/home/widgets/restaurant_card.dart | 8 ++--- uni/lib/view/home/widgets/schedule_card.dart | 6 ++-- uni/lib/view/home/widgets/uni_button.dart | 7 ++-- uni/lib/view/library/library.dart | 5 +-- .../widgets/library_occupation_card.dart | 4 +-- uni/lib/view/locations/locations.dart | 5 +-- uni/lib/view/login/login.dart | 4 +-- uni/lib/view/profile/profile.dart | 4 +-- .../view/restaurant/restaurant_page_view.dart | 4 +-- uni/lib/view/schedule/schedule.dart | 5 +-- 26 files changed, 85 insertions(+), 77 deletions(-) rename uni/lib/utils/{drawer_items.dart => navigation_items.dart} (87%) diff --git a/uni/lib/main.dart b/uni/lib/main.dart index f7825e259..1fa78458d 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -29,7 +29,7 @@ import 'package:uni/model/providers/plausible/plausible_provider.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/model/providers/state_providers.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/academic_path/academic_path.dart'; import 'package:uni/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart'; import 'package:uni/view/calendar/calendar.dart'; @@ -232,69 +232,72 @@ class ApplicationState extends State { navigatorObservers: navigatorObservers, onGenerateRoute: (RouteSettings settings) { final transitions = { - '/${DrawerItem.navPersonalArea.title}': + '/${NavigationItem.navPersonalArea.route}': PageTransition.makePageTransition( page: const HomePageView(), settings: settings, ), - '/${DrawerItem.navSchedule.title}': + '/${NavigationItem.navSchedule.route}': PageTransition.makePageTransition( page: const SchedulePage(), settings: settings, ), - '/${DrawerItem.navExams.title}': PageTransition.makePageTransition( + '/${NavigationItem.navExams.route}': + PageTransition.makePageTransition( page: const ExamsPageView(), settings: settings, ), - '/${DrawerItem.navStops.title}': PageTransition.makePageTransition( + '/${NavigationItem.navStops.route}': + PageTransition.makePageTransition( page: const BusStopNextArrivalsPage(), settings: settings, ), - '/${DrawerItem.navCourseUnits.title}': + '/${NavigationItem.navCourseUnits.route}': PageTransition.makePageTransition( page: const CourseUnitsPageView(), settings: settings, ), - '/${DrawerItem.navLocations.title}': + '/${NavigationItem.navLocations.route}': PageTransition.makePageTransition( page: const LocationsPage(), settings: settings, ), - '/${DrawerItem.navRestaurants.title}': + '/${NavigationItem.navRestaurants.route}': PageTransition.makePageTransition( page: const RestaurantPageView(), settings: settings, ), - '/${DrawerItem.navCalendar.title}': + '/${NavigationItem.navCalendar.route}': PageTransition.makePageTransition( page: const CalendarPageView(), settings: settings, ), - '/${DrawerItem.navLibrary.title}': + '/${NavigationItem.navLibrary.route}': PageTransition.makePageTransition( page: const LibraryPage(), settings: settings, ), - '/${DrawerItem.navFaculty.title}': + '/${NavigationItem.navFaculty.route}': PageTransition.makePageTransition( page: const FacultyPageView(), settings: settings, ), - '/${DrawerItem.navAcademicPath.title}': + '/${NavigationItem.navAcademicPath.route}': PageTransition.makePageTransition( page: const AcademicPathPageView(), settings: settings, ), - '/${DrawerItem.navTransports.title}': + '/${NavigationItem.navTransports.route}': PageTransition.makePageTransition( page: const TransportsPageView(), settings: settings, ), - '/${DrawerItem.navProfile.title}': + '/${NavigationItem.navProfile.route}': MaterialPageRoute( builder: (__) => const ProfilePageView(), ), - '/${DrawerItem.navSettings.title}': MaterialPageRoute( + '/${NavigationItem.navSettings.route}': + MaterialPageRoute( builder: (_) => const SettingsPage(), ), }; diff --git a/uni/lib/utils/drawer_items.dart b/uni/lib/utils/navigation_items.dart similarity index 87% rename from uni/lib/utils/drawer_items.dart rename to uni/lib/utils/navigation_items.dart index d79c74c32..67078affc 100644 --- a/uni/lib/utils/drawer_items.dart +++ b/uni/lib/utils/navigation_items.dart @@ -1,4 +1,4 @@ -enum DrawerItem { +enum NavigationItem { navPersonalArea('area'), navSchedule('horario'), navExams('exames'), @@ -14,9 +14,9 @@ enum DrawerItem { navSettings('definicoes'), navTransports('transportes'); - const DrawerItem(this.title, {this.faculties}); + const NavigationItem(this.route, {this.faculties}); - final String title; + final String route; final Set? faculties; bool isVisible(List userFaculties) { diff --git a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart index 18db3ba0e..1c32963da 100644 --- a/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart +++ b/uni/lib/view/bus_stop_next_arrivals/bus_stop_next_arrivals.dart @@ -5,7 +5,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/bus_stop_next_arrivals/widgets/bus_stop_row.dart'; import 'package:uni/view/bus_stop_selection/bus_stop_selection.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; @@ -99,7 +99,7 @@ class BusStopNextArrivalsPageState } @override - String? getTitle() => S.of(context).nav_title(DrawerItem.navStops.title); + String? getTitle() => S.of(context).nav_title(NavigationItem.navStops.route); } class NextArrivals extends StatefulWidget { diff --git a/uni/lib/view/calendar/calendar.dart b/uni/lib/view/calendar/calendar.dart index 51189e489..d17686068 100644 --- a/uni/lib/view/calendar/calendar.dart +++ b/uni/lib/view/calendar/calendar.dart @@ -4,7 +4,7 @@ import 'package:timelines/timelines.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/calendar_event.dart'; import 'package:uni/model/providers/lazy/calendar_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/calendar/widgets/calendar_tile.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -62,5 +62,6 @@ class CalendarPageViewState extends SecondaryPageViewState { } @override - String? getTitle() => S.of(context).nav_title(DrawerItem.navCalendar.title); + String? getTitle() => + S.of(context).nav_title(NavigationItem.navCalendar.route); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index 93eefb7c1..7f54ddfc2 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:uni/utils/drawer_items.dart'; import 'package:uni/utils/navbar_items.dart'; +import 'package:uni/utils/navigation_items.dart'; class AppBottomNavbar extends StatefulWidget { const AppBottomNavbar({required this.parentContext, super.key}); @@ -43,7 +43,7 @@ class AppBottomNavbarState extends State { final item = NavbarItem.values[index]; final key = item.routes.isNotEmpty ? item.routes[0] - : DrawerItem.navPersonalArea.title; + : NavigationItem.navPersonalArea.route; if (prev != key) { if (prev != '') { diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart index ec6d17ec4..b7092271d 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/drawer_navigation_option.dart @@ -1,14 +1,14 @@ import 'package:flutter/material.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; class DrawerNavigationOption extends StatelessWidget { const DrawerNavigationOption({required this.item, super.key}); - final DrawerItem item; + final NavigationItem item; String getCurrentRoute(BuildContext context) => ModalRoute.of(context)!.settings.name == null - ? DrawerItem.values.toList()[0].title + ? NavigationItem.values.toList()[0].route : ModalRoute.of(context)!.settings.name!.substring(1); void onSelectPage(String key, BuildContext context) { @@ -39,12 +39,12 @@ class DrawerNavigationOption extends StatelessWidget { Widget build(BuildContext context) { return DecoratedBox( decoration: - _getSelectionDecoration(item.title, context) ?? const BoxDecoration(), + _getSelectionDecoration(item.route, context) ?? const BoxDecoration(), child: ListTile( title: Container( padding: const EdgeInsets.only(bottom: 3, left: 20), child: Text( - item.title, + item.route, style: TextStyle( fontSize: 18, color: Theme.of(context).primaryColor, @@ -54,8 +54,8 @@ class DrawerNavigationOption extends StatelessWidget { ), dense: true, contentPadding: EdgeInsets.zero, - selected: item.title == getCurrentRoute(context), - onTap: () => onSelectPage(item.title, context), + selected: item.route == getCurrentRoute(context), + onTap: () => onSelectPage(item.route, context), ), ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart index ad68c29a2..6ca44e2ec 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; class ProfileButton extends StatelessWidget { const ProfileButton({required this.getProfileDecorationImage, super.key}); @@ -38,7 +38,7 @@ class ProfileButton extends StatelessWidget { onPressed: () => { Navigator.pushNamed( context, - '/${DrawerItem.navProfile.title}', + '/${NavigationItem.navProfile.route}', ), }, style: TextButton.styleFrom( diff --git a/uni/lib/view/course_units/course_units.dart b/uni/lib/view/course_units/course_units.dart index 5ac4ed385..8e9383405 100644 --- a/uni/lib/view/course_units/course_units.dart +++ b/uni/lib/view/course_units/course_units.dart @@ -5,7 +5,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/course_units/widgets/course_unit_card.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -223,5 +223,5 @@ class CourseUnitsPageViewState @override String? getTitle() => - S.of(context).nav_title(DrawerItem.navCourseUnits.title); + S.of(context).nav_title(NavigationItem.navCourseUnits.route); } diff --git a/uni/lib/view/exams/exams.dart b/uni/lib/view/exams/exams.dart index 94b7da9ed..4adcf4d7f 100644 --- a/uni/lib/view/exams/exams.dart +++ b/uni/lib/view/exams/exams.dart @@ -5,7 +5,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; import 'package:uni/utils/date_time_formatter.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/common_widgets/row_container.dart'; @@ -164,7 +164,7 @@ class ExamsPageViewState extends SecondaryPageViewState { } @override - String? getTitle() => S.of(context).nav_title(DrawerItem.navExams.title); + String? getTitle() => S.of(context).nav_title(NavigationItem.navExams.route); @override Widget? getTopRightButton(BuildContext context) { diff --git a/uni/lib/view/faculty/widgets/academic_services_card.dart b/uni/lib/view/faculty/widgets/academic_services_card.dart index be0df0857..535df4280 100644 --- a/uni/lib/view/faculty/widgets/academic_services_card.dart +++ b/uni/lib/view/faculty/widgets/academic_services_card.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; import 'package:uni/view/faculty/widgets/text_components.dart'; @@ -12,7 +12,7 @@ class AcademicServicesCard extends GenericExpansionCard { return Column( children: [ h1( - S.of(context).nav_title(DrawerItem.navSchedule.title), + S.of(context).nav_title(NavigationItem.navSchedule.route), context, initial: true, ), diff --git a/uni/lib/view/faculty/widgets/copy_center_card.dart b/uni/lib/view/faculty/widgets/copy_center_card.dart index 69a803872..b555df81a 100644 --- a/uni/lib/view/faculty/widgets/copy_center_card.dart +++ b/uni/lib/view/faculty/widgets/copy_center_card.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; import 'package:uni/view/faculty/widgets/text_components.dart'; @@ -12,7 +12,7 @@ class CopyCenterCard extends GenericExpansionCard { return Column( children: [ h1( - S.of(context).nav_title(DrawerItem.navSchedule.title), + S.of(context).nav_title(NavigationItem.navSchedule.route), context, initial: true, ), diff --git a/uni/lib/view/faculty/widgets/dona_bia_card.dart b/uni/lib/view/faculty/widgets/dona_bia_card.dart index 061232f8e..fdabeeb2f 100644 --- a/uni/lib/view/faculty/widgets/dona_bia_card.dart +++ b/uni/lib/view/faculty/widgets/dona_bia_card.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; import 'package:uni/view/faculty/widgets/text_components.dart'; @@ -12,7 +12,7 @@ class DonaBiaCard extends GenericExpansionCard { return Column( children: [ h1( - S.of(context).nav_title(DrawerItem.navSchedule.title), + S.of(context).nav_title(NavigationItem.navSchedule.route), context, initial: true, ), diff --git a/uni/lib/view/faculty/widgets/infodesk_card.dart b/uni/lib/view/faculty/widgets/infodesk_card.dart index 968f1bfd0..a256ff999 100644 --- a/uni/lib/view/faculty/widgets/infodesk_card.dart +++ b/uni/lib/view/faculty/widgets/infodesk_card.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; import 'package:uni/view/faculty/widgets/text_components.dart'; @@ -12,7 +12,7 @@ class InfoDeskCard extends GenericExpansionCard { return Column( children: [ h1( - S.of(context).nav_title(DrawerItem.navSchedule.title), + S.of(context).nav_title(NavigationItem.navSchedule.route), context, initial: true, ), diff --git a/uni/lib/view/faculty/widgets/multimedia_center_card.dart b/uni/lib/view/faculty/widgets/multimedia_center_card.dart index c71cb328d..66c2f7406 100644 --- a/uni/lib/view/faculty/widgets/multimedia_center_card.dart +++ b/uni/lib/view/faculty/widgets/multimedia_center_card.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; import 'package:uni/view/faculty/widgets/text_components.dart'; @@ -12,7 +12,7 @@ class MultimediaCenterCard extends GenericExpansionCard { return Column( children: [ h1( - S.of(context).nav_title(DrawerItem.navSchedule.title), + S.of(context).nav_title(NavigationItem.navSchedule.route), context, initial: true, ), diff --git a/uni/lib/view/home/widgets/bus_stop_card.dart b/uni/lib/view/home/widgets/bus_stop_card.dart index d77c4326c..70a8e9218 100644 --- a/uni/lib/view/home/widgets/bus_stop_card.dart +++ b/uni/lib/view/home/widgets/bus_stop_card.dart @@ -3,7 +3,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/bus_stop.dart'; import 'package:uni/model/providers/lazy/bus_stop_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/bus_stop_next_arrivals/widgets/bus_stop_row.dart'; import 'package:uni/view/bus_stop_selection/bus_stop_selection.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; @@ -22,11 +22,11 @@ class BusStopCard extends GenericCard { @override String getTitle(BuildContext context) => - S.of(context).nav_title(DrawerItem.navStops.title); + S.of(context).nav_title(NavigationItem.navStops.route); @override Future onClick(BuildContext context) => - Navigator.pushNamed(context, '/${DrawerItem.navStops.title}'); + Navigator.pushNamed(context, '/${NavigationItem.navStops.route}'); @override Widget buildCardContent(BuildContext context) { diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index d9ed6f049..9e7e89473 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -3,7 +3,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/home/widgets/exam_card_shimmer.dart'; import 'package:uni/view/home/widgets/next_exams_card.dart'; @@ -23,11 +23,11 @@ class ExamCard extends GenericCard { @override String getTitle(BuildContext context) => - S.of(context).nav_title(DrawerItem.navExams.title); + S.of(context).nav_title(NavigationItem.navExams.route); @override Future onClick(BuildContext context) => - Navigator.pushNamed(context, '/${DrawerItem.navExams.title}'); + Navigator.pushNamed(context, '/${NavigationItem.navExams.route}'); @override void onRefresh(BuildContext context) { diff --git a/uni/lib/view/home/widgets/restaurant_card.dart b/uni/lib/view/home/widgets/restaurant_card.dart index 46dcac5ce..ac16adc59 100644 --- a/uni/lib/view/home/widgets/restaurant_card.dart +++ b/uni/lib/view/home/widgets/restaurant_card.dart @@ -6,7 +6,7 @@ import 'package:uni/model/entities/meal.dart'; import 'package:uni/model/entities/restaurant.dart'; import 'package:uni/model/providers/lazy/restaurant_provider.dart'; import 'package:uni/model/utils/day_of_week.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/restaurant/widgets/restaurant_slot.dart'; @@ -22,11 +22,11 @@ class RestaurantCard extends GenericCard { @override String getTitle(BuildContext context) => - S.of(context).nav_title(DrawerItem.navRestaurants.title); + S.of(context).nav_title(NavigationItem.navRestaurants.route); @override Future onClick(BuildContext context) => - Navigator.pushNamed(context, '/${DrawerItem.navRestaurants.title}'); + Navigator.pushNamed(context, '/${NavigationItem.navRestaurants.route}'); @override void onRefresh(BuildContext context) { @@ -62,7 +62,7 @@ class RestaurantCard extends GenericCard { OutlinedButton( onPressed: () => Navigator.pushNamed( context, - '/${DrawerItem.navRestaurants.title}', + '/${NavigationItem.navRestaurants.route}', ), child: Text(S.of(context).add), ), diff --git a/uni/lib/view/home/widgets/schedule_card.dart b/uni/lib/view/home/widgets/schedule_card.dart index 3f0ecd86d..edac76541 100644 --- a/uni/lib/view/home/widgets/schedule_card.dart +++ b/uni/lib/view/home/widgets/schedule_card.dart @@ -6,7 +6,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/providers/lazy/lecture_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/date_rectangle.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/home/widgets/schedule_card_shimmer.dart'; @@ -119,9 +119,9 @@ class ScheduleCard extends GenericCard { @override String getTitle(BuildContext context) => - S.of(context).nav_title(DrawerItem.navSchedule.title); + S.of(context).nav_title(NavigationItem.navSchedule.route); @override Future onClick(BuildContext context) => - Navigator.pushNamed(context, '/${DrawerItem.navSchedule.title}'); + Navigator.pushNamed(context, '/${NavigationItem.navSchedule.route}'); } diff --git a/uni/lib/view/home/widgets/uni_button.dart b/uni/lib/view/home/widgets/uni_button.dart index 3732be9d7..e9ba53d25 100644 --- a/uni/lib/view/home/widgets/uni_button.dart +++ b/uni/lib/view/home/widgets/uni_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; class UniButton extends StatelessWidget { const UniButton({super.key}); @@ -14,10 +14,11 @@ class UniButton extends StatelessWidget { builder: (context) => TextButton( onPressed: () { final currentRouteName = ModalRoute.of(context)!.settings.name; - if (currentRouteName != '/${DrawerItem.navPersonalArea.title}') { + if (currentRouteName != + '/${NavigationItem.navPersonalArea.route}') { Navigator.pushNamed( context, - '/${DrawerItem.navPersonalArea.title}', + '/${NavigationItem.navPersonalArea.route}', ); } else { Scaffold.of(context).openDrawer(); diff --git a/uni/lib/view/library/library.dart b/uni/lib/view/library/library.dart index ffc5eef98..a86bc19c0 100644 --- a/uni/lib/view/library/library.dart +++ b/uni/lib/view/library/library.dart @@ -4,7 +4,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/library_occupation.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -124,5 +124,6 @@ class LibraryPageState extends SecondaryPageViewState { } @override - String? getTitle() => S.of(context).nav_title(DrawerItem.navLibrary.title); + String? getTitle() => + S.of(context).nav_title(NavigationItem.navLibrary.route); } diff --git a/uni/lib/view/library/widgets/library_occupation_card.dart b/uni/lib/view/library/widgets/library_occupation_card.dart index 49fedb3a3..d167efdf2 100644 --- a/uni/lib/view/library/widgets/library_occupation_card.dart +++ b/uni/lib/view/library/widgets/library_occupation_card.dart @@ -4,7 +4,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/library_occupation.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -23,7 +23,7 @@ class LibraryOccupationCard extends GenericCard { @override Future onClick(BuildContext context) => - Navigator.pushNamed(context, '/${DrawerItem.navLibrary.title}'); + Navigator.pushNamed(context, '/${NavigationItem.navLibrary.route}'); @override void onRefresh(BuildContext context) { diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index e688650fa..3e3c87856 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -4,7 +4,7 @@ import 'package:flutter_map/flutter_map.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/location_group.dart'; import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locations/widgets/faculty_map.dart'; @@ -37,7 +37,8 @@ class LocationsPageState extends SecondaryPageViewState Future onRefresh(BuildContext context) async {} @override - String? getTitle() => S.of(context).nav_title(DrawerItem.navLocations.title); + String? getTitle() => + S.of(context).nav_title(NavigationItem.navLocations.route); } class LocationsPageView extends StatefulWidget { diff --git a/uni/lib/view/login/login.dart b/uni/lib/view/login/login.dart index 17d1076b7..c1451e9f5 100644 --- a/uni/lib/view/login/login.dart +++ b/uni/lib/view/login/login.dart @@ -9,7 +9,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/login_exceptions.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/model/providers/state_providers.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/toast_message.dart'; import 'package:uni/view/home/widgets/exit_app_dialog.dart'; import 'package:uni/view/login/widgets/inputs.dart'; @@ -64,7 +64,7 @@ class LoginPageViewState extends State { if (context.mounted) { await Navigator.pushReplacementNamed( context, - '/${DrawerItem.navPersonalArea.title}', + '/${NavigationItem.navPersonalArea.route}', ); setState(() { _loggingIn = false; diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index b452dd3e5..9051987b0 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/profile/widgets/account_info_card.dart'; @@ -63,7 +63,7 @@ class ProfilePageViewState extends SecondaryPageViewState { icon: const Icon(Icons.settings), onPressed: () => Navigator.pushNamed( context, - '/${DrawerItem.navSettings.title}', + '/${NavigationItem.navSettings.route}', ), ), ); diff --git a/uni/lib/view/restaurant/restaurant_page_view.dart b/uni/lib/view/restaurant/restaurant_page_view.dart index f9a18e9c7..17e4d2d5d 100644 --- a/uni/lib/view/restaurant/restaurant_page_view.dart +++ b/uni/lib/view/restaurant/restaurant_page_view.dart @@ -5,7 +5,7 @@ import 'package:uni/model/entities/meal.dart'; import 'package:uni/model/entities/restaurant.dart'; import 'package:uni/model/providers/lazy/restaurant_provider.dart'; import 'package:uni/model/utils/day_of_week.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; @@ -162,5 +162,5 @@ class _RestaurantPageViewState @override String? getTitle() => - S.of(context).nav_title(DrawerItem.navRestaurants.title); + S.of(context).nav_title(NavigationItem.navRestaurants.route); } diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index d3b3e1539..bccfe528a 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -3,7 +3,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/providers/lazy/lecture_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -33,7 +33,8 @@ class SchedulePageState extends SecondaryPageViewState { } @override - String? getTitle() => S.of(context).nav_title(DrawerItem.navSchedule.title); + String? getTitle() => + S.of(context).nav_title(NavigationItem.navSchedule.route); } class SchedulePageView extends StatefulWidget { From 8f769ee54ad1d8f9d314a83e2ef74bf7b81bfd3f Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 7 Feb 2024 20:47:38 +0000 Subject: [PATCH 224/334] Fix rebase --- uni/lib/view/academic_path/academic_path.dart | 4 ++-- uni/lib/view/academic_path/widgets/course_units_card.dart | 6 +++--- uni/lib/view/calendar/widgets/calendar_card.dart | 6 +++--- uni/lib/view/faculty/faculty.dart | 4 ++-- uni/lib/view/locations/locations.dart | 3 ++- uni/lib/view/transports/widgets/map_snapshot_card.dart | 8 ++++---- uni/lib/view/useful_info/useful_info.dart | 1 + 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/uni/lib/view/academic_path/academic_path.dart b/uni/lib/view/academic_path/academic_path.dart index e06ef1d65..f10d82fe5 100644 --- a/uni/lib/view/academic_path/academic_path.dart +++ b/uni/lib/view/academic_path/academic_path.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/academic_path/widgets/course_units_card.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/common_widgets/page_title.dart'; @@ -28,7 +28,7 @@ class AcademicPathPageViewState extends GeneralPageViewState { return ListView( children: [ PageTitle( - name: S.of(context).nav_title(DrawerItem.navAcademicPath.title), + name: S.of(context).nav_title(NavigationItem.navAcademicPath.route), ), Column( children: academicPathCards, diff --git a/uni/lib/view/academic_path/widgets/course_units_card.dart b/uni/lib/view/academic_path/widgets/course_units_card.dart index e2553b683..828f3e43b 100644 --- a/uni/lib/view/academic_path/widgets/course_units_card.dart +++ b/uni/lib/view/academic_path/widgets/course_units_card.dart @@ -4,7 +4,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/course_units/widgets/course_unit_card.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -73,9 +73,9 @@ class CourseUnitsCard extends GenericCard { @override String getTitle(BuildContext context) => - S.of(context).nav_title(DrawerItem.navCourseUnits.title); + S.of(context).nav_title(NavigationItem.navCourseUnits.route); @override Future onClick(BuildContext context) => - Navigator.pushNamed(context, '/${DrawerItem.navCourseUnits.title}'); + Navigator.pushNamed(context, '/${NavigationItem.navCourseUnits.route}'); } diff --git a/uni/lib/view/calendar/widgets/calendar_card.dart b/uni/lib/view/calendar/widgets/calendar_card.dart index 064879479..e0b2131e8 100644 --- a/uni/lib/view/calendar/widgets/calendar_card.dart +++ b/uni/lib/view/calendar/widgets/calendar_card.dart @@ -4,7 +4,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/calendar_event.dart'; import 'package:uni/model/providers/lazy/calendar_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/calendar/calendar.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; @@ -20,11 +20,11 @@ class CalendarCard extends GenericCard { @override String getTitle(BuildContext context) => - S.of(context).nav_title(DrawerItem.navCalendar.title); + S.of(context).nav_title(NavigationItem.navCalendar.route); @override Future onClick(BuildContext context) => - Navigator.pushNamed(context, '/${DrawerItem.navCalendar.title}'); + Navigator.pushNamed(context, '/${NavigationItem.navCalendar.route}'); @override void onRefresh(BuildContext context) { diff --git a/uni/lib/view/faculty/faculty.dart b/uni/lib/view/faculty/faculty.dart index 252ec7ae8..4f97fe104 100644 --- a/uni/lib/view/faculty/faculty.dart +++ b/uni/lib/view/faculty/faculty.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/calendar/widgets/calendar_card.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; import 'package:uni/view/common_widgets/page_title.dart'; @@ -28,7 +28,7 @@ class FacultyPageViewState extends GeneralPageViewState { Widget getBody(BuildContext context) { return ListView( children: [ - PageTitle(name: S.of(context).nav_title(DrawerItem.navFaculty.title)), + PageTitle(name: S.of(context).nav_title(NavigationItem.navFaculty.route)), LibraryOccupationCard(), CalendarCard(), ...getUtilsSection(), diff --git a/uni/lib/view/locations/locations.dart b/uni/lib/view/locations/locations.dart index 3e3c87856..0d8dd55b7 100644 --- a/uni/lib/view/locations/locations.dart +++ b/uni/lib/view/locations/locations.dart @@ -94,7 +94,8 @@ class LocationsPageViewState extends State { faculty: getLocation(), locations: widget.locations, searchFilter: searchTerms, - interactiveFlags: InteractiveFlag.all - InteractiveFlag.rotate, + interactiveFlags: + InteractiveFlag.all - InteractiveFlag.rotate, // TODO(bdmendes): add support for multiple faculties ), ), diff --git a/uni/lib/view/transports/widgets/map_snapshot_card.dart b/uni/lib/view/transports/widgets/map_snapshot_card.dart index df946b14f..5ba79c1f5 100644 --- a/uni/lib/view/transports/widgets/map_snapshot_card.dart +++ b/uni/lib/view/transports/widgets/map_snapshot_card.dart @@ -4,7 +4,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/location_group.dart'; import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locations/widgets/faculty_map.dart'; @@ -20,11 +20,11 @@ class MapCard extends GenericCard { @override String getTitle(BuildContext context) => - '${S.of(context).nav_title(DrawerItem.navLocations.title)}: FEUP'; + '${S.of(context).nav_title(NavigationItem.navLocations.route)}: FEUP'; @override Future onClick(BuildContext context) => - Navigator.pushNamed(context, '/${DrawerItem.navLocations.title}'); + Navigator.pushNamed(context, '/${NavigationItem.navLocations.route}'); @override Widget buildCardContent(BuildContext context) { @@ -44,7 +44,7 @@ class MapCard extends GenericCard { Widget buildMapView(BuildContext context, List locations) { return GestureDetector( onTap: () => - Navigator.pushNamed(context, '/${DrawerItem.navLocations.title}'), + Navigator.pushNamed(context, '/${NavigationItem.navLocations.route}'), child: AbsorbPointer( child: Container( padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), diff --git a/uni/lib/view/useful_info/useful_info.dart b/uni/lib/view/useful_info/useful_info.dart index e69de29bb..8b1378917 100644 --- a/uni/lib/view/useful_info/useful_info.dart +++ b/uni/lib/view/useful_info/useful_info.dart @@ -0,0 +1 @@ + From 900a89f765e239d390479a6ae59d27f9f83393f3 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 7 Feb 2024 21:03:29 +0000 Subject: [PATCH 225/334] Fix padding on the buttons in top navbar --- .../pages_layouts/general/widgets/profile_button.dart | 5 ----- .../pages_layouts/secondary/secondary.dart | 2 +- uni/lib/view/exams/exams.dart | 11 +++++++---- uni/lib/view/home/home.dart | 4 ++-- uni/lib/view/home/widgets/uni_button.dart | 3 --- uni/lib/view/profile/profile.dart | 2 +- 6 files changed, 11 insertions(+), 16 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart index 6ca44e2ec..6dd27146e 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart @@ -41,11 +41,6 @@ class ProfileButton extends StatelessWidget { '/${NavigationItem.navProfile.route}', ), }, - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - minimumSize: Size.zero, - tapTargetSize: MaterialTapTargetSize.shrinkWrap, - ), child: Container( width: 50, height: 50, diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 08c741631..6a8e69f0a 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -31,7 +31,7 @@ abstract class SecondaryPageViewState return AppTopNavbar( title: getTitle(), leftButton: const Padding( - padding: EdgeInsets.symmetric(horizontal: 5), + padding: EdgeInsets.symmetric(horizontal: 8), child: BackButton(), ), rightButton: getTopRightButton(context), diff --git a/uni/lib/view/exams/exams.dart b/uni/lib/view/exams/exams.dart index 4adcf4d7f..46b019d8f 100644 --- a/uni/lib/view/exams/exams.dart +++ b/uni/lib/view/exams/exams.dart @@ -168,10 +168,13 @@ class ExamsPageViewState extends SecondaryPageViewState { @override Widget? getTopRightButton(BuildContext context) { - return ExamFilterButton( - () => setState(() { - filteredExamTypes = PreferencesController.getFilteredExams(); - }), + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: ExamFilterButton( + () => setState(() { + filteredExamTypes = PreferencesController.getFilteredExams(); + }), + ), ); } } diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 2e8a04dc8..477f149ba 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -77,11 +77,11 @@ class HomePageViewState extends GeneralPageViewState { AppTopNavbar? getTopNavbar(BuildContext context) { return AppTopNavbar( leftButton: const Padding( - padding: EdgeInsets.symmetric(horizontal: 20), + padding: EdgeInsets.symmetric(horizontal: 8), child: UniButton(), ), rightButton: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), + padding: const EdgeInsets.symmetric(horizontal: 8), child: ProfileButton( getProfileDecorationImage: getProfileDecorationImage, ), diff --git a/uni/lib/view/home/widgets/uni_button.dart b/uni/lib/view/home/widgets/uni_button.dart index e9ba53d25..f2ca043c1 100644 --- a/uni/lib/view/home/widgets/uni_button.dart +++ b/uni/lib/view/home/widgets/uni_button.dart @@ -24,9 +24,6 @@ class UniButton extends StatelessWidget { Scaffold.of(context).openDrawer(); } }, - style: TextButton.styleFrom( - padding: EdgeInsets.zero, - ), child: SvgPicture.asset( colorFilter: ColorFilter.mode( Theme.of(context).primaryColor, diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 9051987b0..0fd3e272a 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -58,7 +58,7 @@ class ProfilePageViewState extends SecondaryPageViewState { @override Widget getTopRightButton(BuildContext context) { return Container( - padding: const EdgeInsets.fromLTRB(0, 10, 5, 10), + padding: const EdgeInsets.fromLTRB(0, 10, 8, 10), child: IconButton( icon: const Icon(Icons.settings), onPressed: () => Navigator.pushNamed( From 161917b0b85483f960058a7b8b2899c7a8079222 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 8 Feb 2024 13:27:09 +0000 Subject: [PATCH 226/334] Integrate academic and faculty pages --- uni/lib/utils/navbar_items.dart | 4 ++-- uni/lib/view/academic_path/academic_path.dart | 17 +++++----------- uni/lib/view/calendar/calendar.dart | 20 ++++++++----------- .../widgets/bottom_navigation_bar.dart | 7 ++++--- uni/lib/view/faculty/faculty.dart | 9 ++++----- .../view/restaurant/restaurant_page_view.dart | 16 +++++---------- 6 files changed, 28 insertions(+), 45 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index c9d5e880e..bd20afb75 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -7,7 +7,7 @@ enum NavbarItem { ), navAcademicPath( Icons.school_outlined, - ['horario', 'exames', 'cadeiras'], + ['percurso_academico'], ), navRestaurants( Icons.local_cafe_outlined, @@ -15,7 +15,7 @@ enum NavbarItem { ), navFaculty( Icons.domain_outlined, - ['biblioteca', 'uteis', 'calendario'], + ['faculdade'], ), navTransports(Icons.map_outlined, ['locais', 'autocarros']); diff --git a/uni/lib/view/academic_path/academic_path.dart b/uni/lib/view/academic_path/academic_path.dart index f10d82fe5..b856cc8a0 100644 --- a/uni/lib/view/academic_path/academic_path.dart +++ b/uni/lib/view/academic_path/academic_path.dart @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/academic_path/widgets/course_units_card.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/home/widgets/exam_card.dart'; import 'package:uni/view/home/widgets/schedule_card.dart'; @@ -25,15 +22,11 @@ class AcademicPathPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return ListView( - children: [ - PageTitle( - name: S.of(context).nav_title(NavigationItem.navAcademicPath.route), - ), - Column( - children: academicPathCards, - ), - ], + return Padding( + padding: const EdgeInsets.only(top: 10), + child: ListView( + children: academicPathCards, + ), ); } diff --git a/uni/lib/view/calendar/calendar.dart b/uni/lib/view/calendar/calendar.dart index d17686068..3054a50b6 100644 --- a/uni/lib/view/calendar/calendar.dart +++ b/uni/lib/view/calendar/calendar.dart @@ -19,19 +19,15 @@ class CalendarPageView extends StatefulWidget { class CalendarPageViewState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { - return ListView( - children: [ - LazyConsumer>( - builder: getTimeline, - hasContent: (calendar) => calendar.isNotEmpty, - onNullContent: const Center( - child: Text( - 'Nenhum evento encontrado', - style: TextStyle(fontSize: 18), - ), - ), + return LazyConsumer>( + builder: getTimeline, + hasContent: (calendar) => calendar.isNotEmpty, + onNullContent: const Center( + child: Text( + 'Nenhum evento encontrado', + style: TextStyle(fontSize: 18), ), - ], + ), ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index 7f54ddfc2..d3d49b916 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -46,10 +46,11 @@ class AppBottomNavbarState extends State { : NavigationItem.navPersonalArea.route; if (prev != key) { - if (prev != '') { - Navigator.pop(context); + if (prev == '') { + Navigator.pushNamed(context, '/$key'); + } else { + Navigator.pushReplacementNamed(context, '/$key'); } - Navigator.pushNamed(context, '/$key'); } } diff --git a/uni/lib/view/faculty/faculty.dart b/uni/lib/view/faculty/faculty.dart index 4f97fe104..0eadd3d2d 100644 --- a/uni/lib/view/faculty/faculty.dart +++ b/uni/lib/view/faculty/faculty.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; -import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/calendar/widgets/calendar_card.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/faculty/widgets/academic_services_card.dart'; import 'package:uni/view/faculty/widgets/copy_center_card.dart'; @@ -26,13 +23,15 @@ class FacultyPageView extends StatefulWidget { class FacultyPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return ListView( + return Padding( + padding: const EdgeInsets.only(top: 10), + child: ListView( children: [ - PageTitle(name: S.of(context).nav_title(NavigationItem.navFaculty.route)), LibraryOccupationCard(), CalendarCard(), ...getUtilsSection(), ], + ), ); } diff --git a/uni/lib/view/restaurant/restaurant_page_view.dart b/uni/lib/view/restaurant/restaurant_page_view.dart index 17e4d2d5d..a711105f1 100644 --- a/uni/lib/view/restaurant/restaurant_page_view.dart +++ b/uni/lib/view/restaurant/restaurant_page_view.dart @@ -5,8 +5,7 @@ import 'package:uni/model/entities/meal.dart'; import 'package:uni/model/entities/restaurant.dart'; import 'package:uni/model/providers/lazy/restaurant_provider.dart'; import 'package:uni/model/utils/day_of_week.dart'; -import 'package:uni/utils/navigation_items.dart'; -import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/restaurant/widgets/restaurant_page_card.dart'; @@ -19,8 +18,7 @@ class RestaurantPageView extends StatefulWidget { State createState() => _RestaurantPageViewState(); } -class _RestaurantPageViewState - extends SecondaryPageViewState +class _RestaurantPageViewState extends GeneralPageViewState with SingleTickerProviderStateMixin { late List aggRestaurant; late TabController tabController; @@ -44,8 +42,8 @@ class _RestaurantPageViewState controller: tabController, isScrollable: true, tabs: createTabs(context), + padding: const EdgeInsets.only(top: 20), ), - const SizedBox(height: 10), LazyConsumer>( builder: (context, restaurants) => createTabViewBuilder( restaurants, @@ -82,7 +80,7 @@ class _RestaurantPageViewState ), ); } - return ListView(children: restaurantsWidgets); + return ListView(padding: EdgeInsets.zero, children: restaurantsWidgets); }).toList(); return Expanded( @@ -133,7 +131,7 @@ class _RestaurantPageViewState final meals = restaurant.getMealsOfDay(day); if (meals.isEmpty) { return Container( - margin: const EdgeInsets.only(top: 10, bottom: 5), + margin: const EdgeInsets.only(bottom: 5), key: Key('restaurant-page-day-column-$day'), child: Column( mainAxisSize: MainAxisSize.min, @@ -159,8 +157,4 @@ class _RestaurantPageViewState return Provider.of(context, listen: false) .forceRefresh(context); } - - @override - String? getTitle() => - S.of(context).nav_title(NavigationItem.navRestaurants.route); } From 3c538b9e6b7651cff5d32a1151e452cd07191b2c Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 8 Feb 2024 13:52:51 +0000 Subject: [PATCH 227/334] Fix rebase --- uni/lib/view/faculty/faculty.dart | 18 +++++++++--------- uni/lib/view/transports/transports.dart | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/uni/lib/view/faculty/faculty.dart b/uni/lib/view/faculty/faculty.dart index 0eadd3d2d..9a8e03c49 100644 --- a/uni/lib/view/faculty/faculty.dart +++ b/uni/lib/view/faculty/faculty.dart @@ -23,15 +23,15 @@ class FacultyPageView extends StatefulWidget { class FacultyPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(top: 10), - child: ListView( - children: [ - LibraryOccupationCard(), - CalendarCard(), - ...getUtilsSection(), - ], - ), + return Padding( + padding: const EdgeInsets.only(top: 10), + child: ListView( + children: [ + LibraryOccupationCard(), + CalendarCard(), + ...getUtilsSection(), + ], + ), ); } diff --git a/uni/lib/view/transports/transports.dart b/uni/lib/view/transports/transports.dart index 2e9a47b96..33026b01e 100644 --- a/uni/lib/view/transports/transports.dart +++ b/uni/lib/view/transports/transports.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/drawer_items.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; @@ -26,7 +26,7 @@ class TransportsPageViewState extends GeneralPageViewState { return ListView( children: [ PageTitle( - name: S.of(context).nav_title(DrawerItem.navTransports.title), + name: S.of(context).nav_title(NavigationItem.navTransports.route), ), Column( children: transportsCards, From e2186c680e90ba739214ad789b2127cc542cee10 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 8 Feb 2024 14:08:09 +0000 Subject: [PATCH 228/334] Integrate transports page on bottom navbar --- uni/lib/utils/navbar_items.dart | 5 ++- uni/lib/view/academic_path/academic_path.dart | 7 ++-- uni/lib/view/calendar/calendar.dart | 33 ++++++++++--------- uni/lib/view/faculty/faculty.dart | 15 ++++----- uni/lib/view/transports/transports.dart | 12 +------ 5 files changed, 31 insertions(+), 41 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index bd20afb75..1170e9919 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -17,7 +17,10 @@ enum NavbarItem { Icons.domain_outlined, ['faculdade'], ), - navTransports(Icons.map_outlined, ['locais', 'autocarros']); + navTransports( + Icons.map_outlined, + ['transportes'], + ); const NavbarItem(this.icon, this.routes); diff --git a/uni/lib/view/academic_path/academic_path.dart b/uni/lib/view/academic_path/academic_path.dart index b856cc8a0..9e4bff366 100644 --- a/uni/lib/view/academic_path/academic_path.dart +++ b/uni/lib/view/academic_path/academic_path.dart @@ -22,11 +22,8 @@ class AcademicPathPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(top: 10), - child: ListView( - children: academicPathCards, - ), + return ListView( + children: academicPathCards, ); } diff --git a/uni/lib/view/calendar/calendar.dart b/uni/lib/view/calendar/calendar.dart index 3054a50b6..e65739d68 100644 --- a/uni/lib/view/calendar/calendar.dart +++ b/uni/lib/view/calendar/calendar.dart @@ -32,21 +32,24 @@ class CalendarPageViewState extends SecondaryPageViewState { } Widget getTimeline(BuildContext context, List calendar) { - return FixedTimeline.tileBuilder( - theme: TimelineTheme.of(context).copyWith( - connectorTheme: TimelineTheme.of(context) - .connectorTheme - .copyWith(thickness: 2, color: Theme.of(context).dividerColor), - indicatorTheme: TimelineTheme.of(context) - .indicatorTheme - .copyWith(size: 15, color: Theme.of(context).primaryColor), - ), - builder: TimelineTileBuilder.fromStyle( - contentsAlign: ContentsAlign.alternating, - contentsBuilder: (_, index) => CalendarTile(text: calendar[index].name), - oppositeContentsBuilder: (_, index) => - CalendarTile(text: calendar[index].date, isOpposite: true), - itemCount: calendar.length, + return SingleChildScrollView( + child: FixedTimeline.tileBuilder( + theme: TimelineTheme.of(context).copyWith( + connectorTheme: TimelineTheme.of(context) + .connectorTheme + .copyWith(thickness: 2, color: Theme.of(context).dividerColor), + indicatorTheme: TimelineTheme.of(context) + .indicatorTheme + .copyWith(size: 15, color: Theme.of(context).primaryColor), + ), + builder: TimelineTileBuilder.fromStyle( + contentsAlign: ContentsAlign.alternating, + contentsBuilder: (_, index) => + CalendarTile(text: calendar[index].name), + oppositeContentsBuilder: (_, index) => + CalendarTile(text: calendar[index].date, isOpposite: true), + itemCount: calendar.length, + ), ), ); } diff --git a/uni/lib/view/faculty/faculty.dart b/uni/lib/view/faculty/faculty.dart index 9a8e03c49..c8786f83f 100644 --- a/uni/lib/view/faculty/faculty.dart +++ b/uni/lib/view/faculty/faculty.dart @@ -23,15 +23,12 @@ class FacultyPageView extends StatefulWidget { class FacultyPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(top: 10), - child: ListView( - children: [ - LibraryOccupationCard(), - CalendarCard(), - ...getUtilsSection(), - ], - ), + return ListView( + children: [ + LibraryOccupationCard(), + CalendarCard(), + ...getUtilsSection(), + ], ); } diff --git a/uni/lib/view/transports/transports.dart b/uni/lib/view/transports/transports.dart index 33026b01e..ff6fa3cf3 100644 --- a/uni/lib/view/transports/transports.dart +++ b/uni/lib/view/transports/transports.dart @@ -1,8 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/home/widgets/bus_stop_card.dart'; import 'package:uni/view/transports/widgets/map_snapshot_card.dart'; @@ -24,14 +21,7 @@ class TransportsPageViewState extends GeneralPageViewState { @override Widget getBody(BuildContext context) { return ListView( - children: [ - PageTitle( - name: S.of(context).nav_title(NavigationItem.navTransports.route), - ), - Column( - children: transportsCards, - ), - ], + children: transportsCards, ); } From 63f9b156a25a6549b61f72d46c27486ca5e5f72c Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 8 Feb 2024 15:43:22 +0000 Subject: [PATCH 229/334] Fix initial route; fix bottom navbar navigation --- uni/lib/main.dart | 1 + uni/lib/utils/navbar_items.dart | 19 +++++++------------ .../widgets/bottom_navigation_bar.dart | 16 ++++++---------- .../view/restaurant/restaurant_page_view.dart | 2 +- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 1fa78458d..265e06f2e 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -228,6 +228,7 @@ class ApplicationState extends State { GlobalCupertinoLocalizations.delegate, ], supportedLocales: S.delegate.supportedLocales, + initialRoute: '/area', home: widget.initialWidget, navigatorObservers: navigatorObservers, onGenerateRoute: (RouteSettings settings) { diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index 1170e9919..62fa7c5e4 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -1,33 +1,28 @@ import 'package:flutter/material.dart'; enum NavbarItem { - navPersonalArea( - Icons.home_outlined, - ['area', ''], - ), + navPersonalArea(Icons.home_outlined, 'area'), navAcademicPath( Icons.school_outlined, - ['percurso_academico'], + 'percurso_academico', ), navRestaurants( Icons.local_cafe_outlined, - ['restaurantes'], + 'restaurantes', ), navFaculty( Icons.domain_outlined, - ['faculdade'], + 'faculdade', ), navTransports( Icons.map_outlined, - ['transportes'], + 'transportes', ); - const NavbarItem(this.icon, this.routes); + const NavbarItem(this.icon, this.route); final IconData icon; - final List routes; - - // TODO(Process-ing): Transform into single route when the new pages are done + final String route; BottomNavigationBarItem toBottomNavigationBarItem() { return BottomNavigationBarItem( diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index d3d49b916..1ec1dfd00 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:uni/utils/navbar_items.dart'; -import 'package:uni/utils/navigation_items.dart'; class AppBottomNavbar extends StatefulWidget { const AppBottomNavbar({required this.parentContext, super.key}); @@ -30,7 +29,7 @@ class AppBottomNavbarState extends State { } for (final item in NavbarItem.values) { - if (item.routes.contains(currentRoute)) { + if (item.route == currentRoute) { return item.index; } } @@ -41,16 +40,13 @@ class AppBottomNavbarState extends State { void _onItemTapped(int index) { final prev = _getCurrentRoute(); final item = NavbarItem.values[index]; - final key = item.routes.isNotEmpty - ? item.routes[0] - : NavigationItem.navPersonalArea.route; + final key = item.route; if (prev != key) { - if (prev == '') { - Navigator.pushNamed(context, '/$key'); - } else { - Navigator.pushReplacementNamed(context, '/$key'); - } + Navigator.pushNamed( + context, + '/$key', + ); } } diff --git a/uni/lib/view/restaurant/restaurant_page_view.dart b/uni/lib/view/restaurant/restaurant_page_view.dart index a711105f1..000477fe0 100644 --- a/uni/lib/view/restaurant/restaurant_page_view.dart +++ b/uni/lib/view/restaurant/restaurant_page_view.dart @@ -42,7 +42,7 @@ class _RestaurantPageViewState extends GeneralPageViewState controller: tabController, isScrollable: true, tabs: createTabs(context), - padding: const EdgeInsets.only(top: 20), + padding: const EdgeInsets.only(top: 40), ), LazyConsumer>( builder: (context, restaurants) => createTabViewBuilder( From be2887a0b7e25f07aa22fd974afe241d3bb73a80 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 9 Feb 2024 11:31:51 +0000 Subject: [PATCH 230/334] Convert bottom navbar to stateful widget --- .../pages_layouts/general/general.dart | 2 +- .../widgets/bottom_navigation_bar.dart | 33 +++++++------------ .../general/widgets/top_navigation_bar.dart | 4 --- .../pages_layouts/secondary/secondary.dart | 4 +-- 4 files changed, 13 insertions(+), 30 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 54625cdac..d658d897c 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -115,7 +115,7 @@ abstract class GeneralPageViewState extends State { Widget getScaffold(BuildContext context, Widget body) { return Scaffold( appBar: getTopNavbar(context), - bottomNavigationBar: AppBottomNavbar(parentContext: context), + bottomNavigationBar: const AppBottomNavbar(), body: RefreshState(onRefresh: onRefresh, child: body), ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index 1ec1dfd00..3dcff6c31 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -1,30 +1,19 @@ import 'package:flutter/material.dart'; import 'package:uni/utils/navbar_items.dart'; -class AppBottomNavbar extends StatefulWidget { - const AppBottomNavbar({required this.parentContext, super.key}); - - final BuildContext parentContext; - - @override - State createState() { - return AppBottomNavbarState(); - } -} - -class AppBottomNavbarState extends State { - AppBottomNavbarState(); +class AppBottomNavbar extends StatelessWidget { + const AppBottomNavbar({super.key}); static final List navbarItems = NavbarItem.values .map((item) => item.toBottomNavigationBarItem()) .toList(); - String? _getCurrentRoute() => - ModalRoute.of(widget.parentContext)!.settings.name?.substring(1); + String? _getCurrentRoute(BuildContext context) => + ModalRoute.of(context)!.settings.name?.substring(1); - int _getCurrentIndex() { - final currentRoute = _getCurrentRoute(); - if (_getCurrentRoute() == null) { + int _getCurrentIndex(BuildContext context) { + final currentRoute = _getCurrentRoute(context); + if (_getCurrentRoute(context) == null) { return -1; } @@ -37,8 +26,8 @@ class AppBottomNavbarState extends State { return -1; } - void _onItemTapped(int index) { - final prev = _getCurrentRoute(); + void _onItemTapped(BuildContext context, int index) { + final prev = _getCurrentRoute(context); final item = NavbarItem.values[index]; final key = item.route; @@ -52,10 +41,10 @@ class AppBottomNavbarState extends State { @override Widget build(BuildContext context) { - final currentIndex = _getCurrentIndex(); + final currentIndex = _getCurrentIndex(context); return BottomNavigationBar( items: navbarItems, - onTap: _onItemTapped, + onTap: (int index) => _onItemTapped(context, index), currentIndex: currentIndex == -1 ? 0 : currentIndex, type: BottomNavigationBarType.fixed, iconSize: 32, diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart index 2830086bc..78ed67669 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart @@ -1,10 +1,6 @@ import 'package:flutter/material.dart'; import 'package:uni/view/common_widgets/page_title.dart'; -/// Upper bar of the app. -/// -/// This widget consists on an instance of `AppBar` containing the app's logo, -/// an option button and a button with the user's picture. class AppTopNavbar extends StatelessWidget implements PreferredSizeWidget { const AppTopNavbar({ this.title, diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 6a8e69f0a..594ca87d7 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -12,9 +12,7 @@ abstract class SecondaryPageViewState Scaffold getScaffold(BuildContext context, Widget body) { return Scaffold( appBar: getTopNavbar(context), - bottomNavigationBar: AppBottomNavbar( - parentContext: context, - ), + bottomNavigationBar: const AppBottomNavbar(), body: RefreshState(onRefresh: onRefresh, child: body), ); } From 5618e95b3539920673162ad166df5976630ccad5 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 9 Feb 2024 17:36:44 +0000 Subject: [PATCH 231/334] Use NavigationItem in NavbarItem. --- uni/lib/utils/navbar_items.dart | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index 62fa7c5e4..855ce501e 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -1,28 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:uni/utils/navigation_items.dart'; enum NavbarItem { - navPersonalArea(Icons.home_outlined, 'area'), - navAcademicPath( - Icons.school_outlined, - 'percurso_academico', - ), - navRestaurants( - Icons.local_cafe_outlined, - 'restaurantes', - ), - navFaculty( - Icons.domain_outlined, - 'faculdade', - ), - navTransports( - Icons.map_outlined, - 'transportes', - ); + navPersonalArea(Icons.home_outlined, NavigationItem.navPersonalArea), + navAcademicPath(Icons.school_outlined, NavigationItem.navAcademicPath), + navRestaurants(Icons.local_cafe_outlined, NavigationItem.navRestaurants), + navFaculty(Icons.domain_outlined, NavigationItem.navFaculty), + navTransports(Icons.map_outlined, NavigationItem.navTransports); - const NavbarItem(this.icon, this.route); + const NavbarItem(this.icon, this.item); final IconData icon; - final String route; + final NavigationItem item; BottomNavigationBarItem toBottomNavigationBarItem() { return BottomNavigationBarItem( @@ -30,4 +19,8 @@ enum NavbarItem { label: '', ); } + + String get route { + return item.route; + } } From 69261fd3cb8f4be29b07348958fd0ceaabf78ff9 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 9 Feb 2024 17:38:03 +0000 Subject: [PATCH 232/334] Use navPersonalArea in the app initial route. --- uni/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 265e06f2e..21dfe91fd 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -228,7 +228,7 @@ class ApplicationState extends State { GlobalCupertinoLocalizations.delegate, ], supportedLocales: S.delegate.supportedLocales, - initialRoute: '/area', + initialRoute: '/${NavigationItem.navPersonalArea.route}', home: widget.initialWidget, navigatorObservers: navigatorObservers, onGenerateRoute: (RouteSettings settings) { From 8695ed6d38e49a80ae1eae5bac7b11f4309efd08 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 9 Feb 2024 18:59:16 +0000 Subject: [PATCH 233/334] Make navbar items filled when selected --- uni/lib/utils/navbar_items.dart | 29 +++++++++++++------ .../widgets/bottom_navigation_bar.dart | 14 ++++++--- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index 855ce501e..ffb3fa505 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -2,20 +2,31 @@ import 'package:flutter/material.dart'; import 'package:uni/utils/navigation_items.dart'; enum NavbarItem { - navPersonalArea(Icons.home_outlined, NavigationItem.navPersonalArea), - navAcademicPath(Icons.school_outlined, NavigationItem.navAcademicPath), - navRestaurants(Icons.local_cafe_outlined, NavigationItem.navRestaurants), - navFaculty(Icons.domain_outlined, NavigationItem.navFaculty), - navTransports(Icons.map_outlined, NavigationItem.navTransports); + navPersonalArea( + Icons.home_outlined, Icons.home, NavigationItem.navPersonalArea), + navAcademicPath( + Icons.school_outlined, Icons.school, NavigationItem.navAcademicPath), + navRestaurants(Icons.local_cafe_outlined, Icons.local_cafe, + NavigationItem.navRestaurants), + navFaculty(Icons.domain_outlined, Icons.domain, NavigationItem.navFaculty), + navTransports(Icons.map_outlined, Icons.map, NavigationItem.navTransports); - const NavbarItem(this.icon, this.item); + const NavbarItem(this.unselectedIcon, this.selectedIcon, this.item); - final IconData icon; + final IconData unselectedIcon; + final IconData selectedIcon; final NavigationItem item; - BottomNavigationBarItem toBottomNavigationBarItem() { + BottomNavigationBarItem toUnselectedBottomNavigationBarItem() { return BottomNavigationBarItem( - icon: Icon(icon), + icon: Icon(unselectedIcon), + label: '', + ); + } + + BottomNavigationBarItem toSelectedBottomNavigationBarItem() { + return BottomNavigationBarItem( + icon: Icon(selectedIcon), label: '', ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index 3dcff6c31..e177bb49f 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -4,10 +4,6 @@ import 'package:uni/utils/navbar_items.dart'; class AppBottomNavbar extends StatelessWidget { const AppBottomNavbar({super.key}); - static final List navbarItems = NavbarItem.values - .map((item) => item.toBottomNavigationBarItem()) - .toList(); - String? _getCurrentRoute(BuildContext context) => ModalRoute.of(context)!.settings.name?.substring(1); @@ -42,6 +38,16 @@ class AppBottomNavbar extends StatelessWidget { @override Widget build(BuildContext context) { final currentIndex = _getCurrentIndex(context); + final navbarItems = []; + for (var index = 0; index < NavbarItem.values.length; index++) { + final item = NavbarItem.values[index]; + navbarItems.insert( + index, + index == currentIndex + ? item.toSelectedBottomNavigationBarItem() + : item.toUnselectedBottomNavigationBarItem()); + } + return BottomNavigationBar( items: navbarItems, onTap: (int index) => _onItemTapped(context, index), From b200bff3fadc7a6b38b6b508b4e67b87cd702a79 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Fri, 9 Feb 2024 19:02:35 +0000 Subject: [PATCH 234/334] Fix linter --- uni/lib/utils/navbar_items.dart | 17 +++++++++++++---- .../general/widgets/bottom_navigation_bar.dart | 9 +++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index ffb3fa505..01c7785c4 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -3,11 +3,20 @@ import 'package:uni/utils/navigation_items.dart'; enum NavbarItem { navPersonalArea( - Icons.home_outlined, Icons.home, NavigationItem.navPersonalArea), + Icons.home_outlined, + Icons.home, + NavigationItem.navPersonalArea, + ), navAcademicPath( - Icons.school_outlined, Icons.school, NavigationItem.navAcademicPath), - navRestaurants(Icons.local_cafe_outlined, Icons.local_cafe, - NavigationItem.navRestaurants), + Icons.school_outlined, + Icons.school, + NavigationItem.navAcademicPath, + ), + navRestaurants( + Icons.local_cafe_outlined, + Icons.local_cafe, + NavigationItem.navRestaurants, + ), navFaculty(Icons.domain_outlined, Icons.domain, NavigationItem.navFaculty), navTransports(Icons.map_outlined, Icons.map, NavigationItem.navTransports); diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index e177bb49f..9b4a89614 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -42,10 +42,11 @@ class AppBottomNavbar extends StatelessWidget { for (var index = 0; index < NavbarItem.values.length; index++) { final item = NavbarItem.values[index]; navbarItems.insert( - index, - index == currentIndex - ? item.toSelectedBottomNavigationBarItem() - : item.toUnselectedBottomNavigationBarItem()); + index, + index == currentIndex + ? item.toSelectedBottomNavigationBarItem() + : item.toUnselectedBottomNavigationBarItem(), + ); } return BottomNavigationBar( From 5f08afc03d69a69a24ad48743e13ac34b80169ca Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 14 Feb 2024 14:31:34 +0000 Subject: [PATCH 235/334] Change uni button to icon --- uni/lib/view/home/home.dart | 4 +-- uni/lib/view/home/widgets/uni_button.dart | 39 ----------------------- uni/lib/view/home/widgets/uni_icon.dart | 24 ++++++++++++++ 3 files changed, 26 insertions(+), 41 deletions(-) delete mode 100644 uni/lib/view/home/widgets/uni_button.dart create mode 100644 uni/lib/view/home/widgets/uni_icon.dart diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 477f149ba..860eeb62c 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -6,7 +6,7 @@ import 'package:uni/view/common_widgets/pages_layouts/general/widgets/profile_bu import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart'; import 'package:uni/view/home/widgets/main_cards_list.dart'; import 'package:uni/view/home/widgets/tracking_banner.dart'; -import 'package:uni/view/home/widgets/uni_button.dart'; +import 'package:uni/view/home/widgets/uni_icon.dart'; class HomePageView extends StatefulWidget { const HomePageView({super.key}); @@ -78,7 +78,7 @@ class HomePageViewState extends GeneralPageViewState { return AppTopNavbar( leftButton: const Padding( padding: EdgeInsets.symmetric(horizontal: 8), - child: UniButton(), + child: UniIcon(), ), rightButton: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), diff --git a/uni/lib/view/home/widgets/uni_button.dart b/uni/lib/view/home/widgets/uni_button.dart deleted file mode 100644 index f2ca043c1..000000000 --- a/uni/lib/view/home/widgets/uni_button.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:uni/utils/navigation_items.dart'; - -class UniButton extends StatelessWidget { - const UniButton({super.key}); - - @override - Widget build(BuildContext context) { - return ButtonTheme( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: const RoundedRectangleBorder(), - child: Builder( - builder: (context) => TextButton( - onPressed: () { - final currentRouteName = ModalRoute.of(context)!.settings.name; - if (currentRouteName != - '/${NavigationItem.navPersonalArea.route}') { - Navigator.pushNamed( - context, - '/${NavigationItem.navPersonalArea.route}', - ); - } else { - Scaffold.of(context).openDrawer(); - } - }, - child: SvgPicture.asset( - colorFilter: ColorFilter.mode( - Theme.of(context).primaryColor, - BlendMode.srcIn, - ), - 'assets/images/logo_dark.svg', - height: 35, - ), - ), - ), - ); - } -} diff --git a/uni/lib/view/home/widgets/uni_icon.dart b/uni/lib/view/home/widgets/uni_icon.dart new file mode 100644 index 000000000..96cdad894 --- /dev/null +++ b/uni/lib/view/home/widgets/uni_icon.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; + +class UniIcon extends StatelessWidget { + const UniIcon({super.key}); + + @override + Widget build(BuildContext context) { + return ButtonTheme( + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: const RoundedRectangleBorder(), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: SvgPicture.asset( + colorFilter: ColorFilter.mode( + Theme.of(context).primaryColor, + BlendMode.srcIn, + ), + 'assets/images/logo_dark.svg', + height: 35, + ), + )); + } +} From 142741ebe4c58fc7daceb5d2ff670b09385a0c07 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Thu, 15 Feb 2024 09:51:52 +0000 Subject: [PATCH 236/334] Fix linter --- uni/lib/view/home/widgets/uni_icon.dart | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/uni/lib/view/home/widgets/uni_icon.dart b/uni/lib/view/home/widgets/uni_icon.dart index 96cdad894..bfc4ee7f7 100644 --- a/uni/lib/view/home/widgets/uni_icon.dart +++ b/uni/lib/view/home/widgets/uni_icon.dart @@ -7,18 +7,19 @@ class UniIcon extends StatelessWidget { @override Widget build(BuildContext context) { return ButtonTheme( - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: const RoundedRectangleBorder(), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: SvgPicture.asset( - colorFilter: ColorFilter.mode( - Theme.of(context).primaryColor, - BlendMode.srcIn, - ), - 'assets/images/logo_dark.svg', - height: 35, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: const RoundedRectangleBorder(), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: SvgPicture.asset( + colorFilter: ColorFilter.mode( + Theme.of(context).primaryColor, + BlendMode.srcIn, ), - )); + 'assets/images/logo_dark.svg', + height: 35, + ), + ), + ); } } From d695fdf9a4b5de8e4287856da00e3fceab16762b Mon Sep 17 00:00:00 2001 From: DGoiana Date: Mon, 19 Feb 2024 14:45:07 +0000 Subject: [PATCH 237/334] Fixing null check error on load --- .../course_units_info_fetcher.dart | 36 +++++++++++-------- .../lazy/course_units_info_provider.dart | 1 - 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart index 1c54029e3..266aa3bf5 100644 --- a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart +++ b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart @@ -19,12 +19,16 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { ) async { // if course unit is not from the main faculty, Sigarra redirects final url = '${getEndpoints(session)[0]}ucurr_geral.ficha_uc_view'; - final response = await NetworkRouter.getWithCookies( - url, - {'pv_ocorrencia_id': occurrId.toString()}, - session, - ); - return parseCourseUnitSheet(response); + try { + final response = await NetworkRouter.getWithCookies( + url, + {'pv_ocorrencia_id': occurrId.toString()}, + session, + ); + return parseCourseUnitSheet(response); + } catch (_) { + return CourseUnitSheet({}); + } } Future> fetchCourseUnitFiles( @@ -32,14 +36,18 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { int occurId, ) async { final url = '${getEndpoints(session)[0]}mob_ucurr_geral.conteudos'; - final response = await NetworkRouter.getWithCookies( - url, - { - 'pv_ocorrencia_id': occurId.toString(), - }, - session, - ); - return parseFiles(response, session); + try { + final response = await NetworkRouter.getWithCookies( + url, + { + 'pv_ocorrencia_id': occurId.toString(), + }, + session, + ); + return parseFiles(response, session); + } catch (_) { + return [CourseUnitFileDirectory('', [])]; + } } Future getDownloadLink( diff --git a/uni/lib/model/providers/lazy/course_units_info_provider.dart b/uni/lib/model/providers/lazy/course_units_info_provider.dart index bdd0ea13c..d661a6184 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -40,7 +40,6 @@ class CourseUnitsInfoProvider ) async { state!.item1[courseUnit] = await CourseUnitsInfoFetcher() .fetchCourseUnitSheet(session, courseUnit.occurrId); - notifyListeners(); } Future fetchCourseUnitClasses( From e6919d798b7590ce50e6a333ec851ac9e5b8d592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Tue, 20 Feb 2024 09:50:54 +0000 Subject: [PATCH 238/334] fix: use new endpoint for calendar --- uni/lib/controller/fetchers/calendar_fetcher_html.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/controller/fetchers/calendar_fetcher_html.dart b/uni/lib/controller/fetchers/calendar_fetcher_html.dart index e08119850..e835a5de5 100644 --- a/uni/lib/controller/fetchers/calendar_fetcher_html.dart +++ b/uni/lib/controller/fetchers/calendar_fetcher_html.dart @@ -11,7 +11,7 @@ class CalendarFetcherHtml implements SessionDependantFetcher { // TODO(bdmendes): Implement parsers for all faculties // and dispatch for different fetchers final url = '${NetworkRouter.getBaseUrl('feup')}' - 'web_base.gera_pagina?p_pagina=página%20estática%20genérica%20106'; + 'web_base.gera_pagina?p_pagina=calend%c3%a1rio%20escolar'; return [url]; } From 417c250a0b82c51267daaa5f792a6d8bbceeee56 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Tue, 20 Feb 2024 11:11:53 +0000 Subject: [PATCH 239/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index ea2c0d1e0..1675101ac 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.0+228 +1.9.0-beta.0+229 From 7c150e6aa436396a3159d1f7005a42fd09441c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Tue, 20 Feb 2024 19:05:08 +0000 Subject: [PATCH 240/334] Fix bump type --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 8ba00a858..9e4f9e07e 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -61,7 +61,7 @@ jobs: current_environment: staging production_version: ${{ steps.latest-production-version.outputs.latest_version_name }} staging_version: ${{ steps.latest-beta-version.outputs.latest_version_name }} - bump_type: preminor + bump_type: prerelease - name: Bump prod version (from develop) uses: LuisDuarte1/semver-bump-environment@v1.0.0 From 6e8820c6cc3332646c4fd4ff5ade965650057b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Tue, 20 Feb 2024 19:18:40 +0000 Subject: [PATCH 241/334] Fix copy app version to pubspec --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 9e4f9e07e..a32eff1c9 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -91,7 +91,7 @@ jobs: echo "$NEW_VERSION_NAME+$((${{steps.latest-production-version.outputs.latest_version_code}} + 1))" > ${{env.APP_VERSION_PATH}} - name: Copy app version to pubspec - run: cat ${{ env.APP_VERSION_PATH }} | perl -i -pe 's/^(version:\s+)(\d+\.\d+\.\d+\+\d+)$/$1.()/e' ${{ env.PUBSPEC_PATH }} + run: cat ${{ env.APP_VERSION_PATH }} | tr -d '\n' | perl -i -pe 's/^(version:\s+)(.+)$/$1.()/e' ${{ env.PUBSPEC_PATH }} - uses: stefanzweifel/git-auto-commit-action@v4 with: From 6fded74a6cec9757d2f344a492aaa3b38c422b32 Mon Sep 17 00:00:00 2001 From: LuisDuarte1 Date: Tue, 20 Feb 2024 19:25:47 +0000 Subject: [PATCH 242/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 1675101ac..f6a1f48b5 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.9.0-beta.0+229 +1.8.0-beta.1+229 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 5c6e5a5fb..e0cb58cb2 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.0+228 +version: 1.8.0-beta.1+229 environment: From eec569df9b6a9a8e9e550678223da6fe2b557a8e Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 21 Feb 2024 14:15:58 +0000 Subject: [PATCH 243/334] Null check awareness --- .../course_units_info_fetcher.dart | 36 ++++++++----------- .../course_unit_info/course_unit_info.dart | 13 +++++-- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart index 266aa3bf5..1c54029e3 100644 --- a/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart +++ b/uni/lib/controller/fetchers/course_units_fetcher/course_units_info_fetcher.dart @@ -19,16 +19,12 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { ) async { // if course unit is not from the main faculty, Sigarra redirects final url = '${getEndpoints(session)[0]}ucurr_geral.ficha_uc_view'; - try { - final response = await NetworkRouter.getWithCookies( - url, - {'pv_ocorrencia_id': occurrId.toString()}, - session, - ); - return parseCourseUnitSheet(response); - } catch (_) { - return CourseUnitSheet({}); - } + final response = await NetworkRouter.getWithCookies( + url, + {'pv_ocorrencia_id': occurrId.toString()}, + session, + ); + return parseCourseUnitSheet(response); } Future> fetchCourseUnitFiles( @@ -36,18 +32,14 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { int occurId, ) async { final url = '${getEndpoints(session)[0]}mob_ucurr_geral.conteudos'; - try { - final response = await NetworkRouter.getWithCookies( - url, - { - 'pv_ocorrencia_id': occurId.toString(), - }, - session, - ); - return parseFiles(response, session); - } catch (_) { - return [CourseUnitFileDirectory('', [])]; - } + final response = await NetworkRouter.getWithCookies( + url, + { + 'pv_ocorrencia_id': occurId.toString(), + }, + session, + ); + return parseFiles(response, session); } Future getDownloadLink( diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index d29aea7b8..d57b7f367 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -121,11 +121,20 @@ class CourseUnitDetailPageViewState } Widget _courseUnitFilesView(BuildContext context) { - final sheet = context + final files = context .watch() .courseUnitsFiles[widget.courseUnit]; - return CourseUnitFilesView(sheet!); + if (files == null || files.isEmpty) { + return Center( + child: Text( + S.of(context).no_files_found, + textAlign: TextAlign.center, + ), + ); + } + + return CourseUnitFilesView(files); } Widget _courseUnitClassesView(BuildContext context) { From d453ea01e21a820ae2ba30e61f5172055929f3c9 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 21 Feb 2024 16:06:48 +0000 Subject: [PATCH 244/334] Handling internet exception at general level --- .../pages_layouts/general/general.dart | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index d658d897c..d49ba5384 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -1,10 +1,12 @@ import 'dart:async'; import 'dart:io'; +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart'; @@ -15,6 +17,7 @@ import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_naviga abstract class GeneralPageViewState extends State { bool _loadedOnce = false; bool _loading = true; + bool _connected = true; Future onRefresh(BuildContext context); @@ -34,8 +37,15 @@ abstract class GeneralPageViewState extends State { try { await onLoad(context); } catch (e, stackTrace) { - Logger().e('Failed to load page info: $e\n$stackTrace'); - await Sentry.captureException(e, stackTrace: stackTrace); + final connectivity = await Connectivity().checkConnectivity(); + if (connectivity == ConnectivityResult.none) { + setState(() { + _connected = false; + }); + } else { + Logger().e('Failed to load page info: $e\n$stackTrace'); + await Sentry.captureException(e, stackTrace: stackTrace); + } } if (mounted) { @@ -45,6 +55,22 @@ abstract class GeneralPageViewState extends State { } }); + if (!_connected) { + return getScaffold( + context, + Flex( + direction: Axis.vertical, + children: [ + Expanded( + child: Center( + child: Text(S.of(context).check_internet), + ), + ), + ], + ), + ); + } + return getScaffold( context, _loading From 995192dfd7a8f4d31486a858d08ed0abbb32ded6 Mon Sep 17 00:00:00 2001 From: vitormpp Date: Wed, 21 Feb 2024 16:17:15 +0000 Subject: [PATCH 245/334] Implemented faculty-filter --- .../view/common_widgets/faculty_filter.dart | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 uni/lib/view/common_widgets/faculty_filter.dart diff --git a/uni/lib/view/common_widgets/faculty_filter.dart b/uni/lib/view/common_widgets/faculty_filter.dart new file mode 100644 index 000000000..caa9055a7 --- /dev/null +++ b/uni/lib/view/common_widgets/faculty_filter.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; + +class FacultyFilter extends StatelessWidget { + const FacultyFilter({ + required this.faculties, + required this.builder, + super.key, + }); + + final List faculties; + final Widget Function(BuildContext context, List authorizedFaculties) + builder; + + @override + Widget build(BuildContext context) { + final authorizedFaculties = PreferencesController.getUserFaculties() + .where( + faculties.contains, + ) + .toList(); + + return authorizedFaculties.isNotEmpty + ? builder(context, authorizedFaculties) + : Container(); + } +} From 1c19a2bc6619caa3f639b1c2293050397706464d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Wed, 21 Feb 2024 14:40:48 +0000 Subject: [PATCH 246/334] Fix version code superseeding by eagerly deploying to the develop branch when there's a merge to master (cherry picked from commit 1d06afaef015581d973eb4a5688fd79caad1be10) --- .github/workflows/deploy.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index a32eff1c9..2a0b36d39 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -1,7 +1,8 @@ on: push: branches: [master, develop] - + workflow_dispatch: +name: Deploy Action jobs: build: permissions: @@ -164,3 +165,9 @@ jobs: whatsNewDirectory: whatsnew track: production status: completed + + - name: Propagate version to develop + if: github.ref == 'refs/heads/master' + run: | + cat ${{ secrets.NIAEFEUPBOT_PAT }} | gh auth login --with-token + gh workflow run 'Deploy Action' --ref develop \ No newline at end of file From 83e3099c764a44bb90d4d22590bb3f6a8ef5318e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Wed, 21 Feb 2024 14:57:27 +0000 Subject: [PATCH 247/334] Use echo instead of cat on github action (cherry picked from commit 02f195fb31f14f5448d8a2a2fc3d130b91670619) --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 2a0b36d39..6bbf7c31d 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -169,5 +169,5 @@ jobs: - name: Propagate version to develop if: github.ref == 'refs/heads/master' run: | - cat ${{ secrets.NIAEFEUPBOT_PAT }} | gh auth login --with-token + echo '${{ secrets.NIAEFEUPBOT_PAT }}' | gh auth login --with-token gh workflow run 'Deploy Action' --ref develop \ No newline at end of file From 6ae88ef66498bd7207144eba6f09fda7903707ef Mon Sep 17 00:00:00 2001 From: LuisDuarte1 Date: Thu, 22 Feb 2024 09:34:22 +0000 Subject: [PATCH 248/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index f6a1f48b5..4e55466d4 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.1+229 +1.8.0-beta.2+234 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index e0cb58cb2..48a620160 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.1+229 +version: 1.8.0-beta.2+234 environment: From cbdb0b738ffa7f3c074147b8eb7dda140193257e Mon Sep 17 00:00:00 2001 From: niaefeup-admin Date: Thu, 22 Feb 2024 09:46:45 +0000 Subject: [PATCH 249/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 4e55466d4..afb8cf655 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.2+234 +1.8.0-beta.3+236 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 48a620160..61fcaca46 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.2+234 +version: 1.8.0-beta.3+236 environment: From 36e9c148101668cf0662f527bc6665e6c7525549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Wed, 21 Feb 2024 15:48:00 +0000 Subject: [PATCH 250/334] feat: navbar improvements --- uni/lib/utils/navbar_items.dart | 9 +++-- .../widgets/bottom_navigation_bar.dart | 35 +++++++++++-------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index 01c7785c4..d29ebba6e 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; import 'package:uni/utils/navigation_items.dart'; enum NavbarItem { @@ -26,17 +27,21 @@ enum NavbarItem { final IconData selectedIcon; final NavigationItem item; - BottomNavigationBarItem toUnselectedBottomNavigationBarItem() { + BottomNavigationBarItem toUnselectedBottomNavigationBarItem( + BuildContext context) { return BottomNavigationBarItem( icon: Icon(unselectedIcon), label: '', + tooltip: S.of(context).nav_title(item.route), ); } - BottomNavigationBarItem toSelectedBottomNavigationBarItem() { + BottomNavigationBarItem toSelectedBottomNavigationBarItem( + BuildContext context) { return BottomNavigationBarItem( icon: Icon(selectedIcon), label: '', + tooltip: S.of(context).nav_title(item.route), ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index 9b4a89614..96ba07636 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -44,23 +44,28 @@ class AppBottomNavbar extends StatelessWidget { navbarItems.insert( index, index == currentIndex - ? item.toSelectedBottomNavigationBarItem() - : item.toUnselectedBottomNavigationBarItem(), + ? item.toSelectedBottomNavigationBarItem(context) + : item.toUnselectedBottomNavigationBarItem(context), ); } - return BottomNavigationBar( - items: navbarItems, - onTap: (int index) => _onItemTapped(context, index), - currentIndex: currentIndex == -1 ? 0 : currentIndex, - type: BottomNavigationBarType.fixed, - iconSize: 32, - selectedItemColor: currentIndex == -1 - ? Theme.of(context).colorScheme.onSurface - : Theme.of(context).colorScheme.secondary, - unselectedItemColor: Theme.of(context).colorScheme.onSurface, - showSelectedLabels: false, - showUnselectedLabels: false, - ); + return Theme( + data: Theme.of(context).copyWith( + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + ), + child: BottomNavigationBar( + items: navbarItems, + onTap: (int index) => _onItemTapped(context, index), + currentIndex: currentIndex == -1 ? 0 : currentIndex, + type: BottomNavigationBarType.fixed, + iconSize: 32, + selectedItemColor: currentIndex == -1 + ? Theme.of(context).colorScheme.onSurface + : Theme.of(context).colorScheme.secondary, + unselectedItemColor: Theme.of(context).colorScheme.onSurface, + showSelectedLabels: false, + showUnselectedLabels: false, + )); } } From b1aaf3b85a1dafa154d898631cf5ea06d141b497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 22 Feb 2024 01:16:01 +0000 Subject: [PATCH 251/334] style: format code --- uni/lib/utils/navbar_items.dart | 6 ++-- .../widgets/bottom_navigation_bar.dart | 35 ++++++++++--------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index d29ebba6e..e557aee6e 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -28,7 +28,8 @@ enum NavbarItem { final NavigationItem item; BottomNavigationBarItem toUnselectedBottomNavigationBarItem( - BuildContext context) { + BuildContext context, + ) { return BottomNavigationBarItem( icon: Icon(unselectedIcon), label: '', @@ -37,7 +38,8 @@ enum NavbarItem { } BottomNavigationBarItem toSelectedBottomNavigationBarItem( - BuildContext context) { + BuildContext context, + ) { return BottomNavigationBarItem( icon: Icon(selectedIcon), label: '', diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index 96ba07636..a72ee955c 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -50,22 +50,23 @@ class AppBottomNavbar extends StatelessWidget { } return Theme( - data: Theme.of(context).copyWith( - splashColor: Colors.transparent, - highlightColor: Colors.transparent, - ), - child: BottomNavigationBar( - items: navbarItems, - onTap: (int index) => _onItemTapped(context, index), - currentIndex: currentIndex == -1 ? 0 : currentIndex, - type: BottomNavigationBarType.fixed, - iconSize: 32, - selectedItemColor: currentIndex == -1 - ? Theme.of(context).colorScheme.onSurface - : Theme.of(context).colorScheme.secondary, - unselectedItemColor: Theme.of(context).colorScheme.onSurface, - showSelectedLabels: false, - showUnselectedLabels: false, - )); + data: Theme.of(context).copyWith( + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + ), + child: BottomNavigationBar( + items: navbarItems, + onTap: (int index) => _onItemTapped(context, index), + currentIndex: currentIndex == -1 ? 0 : currentIndex, + type: BottomNavigationBarType.fixed, + iconSize: 32, + selectedItemColor: currentIndex == -1 + ? Theme.of(context).colorScheme.onSurface + : Theme.of(context).colorScheme.secondary, + unselectedItemColor: Theme.of(context).colorScheme.onSurface, + showSelectedLabels: false, + showUnselectedLabels: false, + ), + ); } } From e1f7d97699973550d4969456b0e2879bb842a603 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Fri, 23 Feb 2024 10:46:47 +0000 Subject: [PATCH 252/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index afb8cf655..54390c886 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.3+236 +1.8.0-beta.4+237 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 61fcaca46..bb580268e 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.3+236 +version: 1.8.0-beta.4+237 environment: From 6d72ef6f82264de50fcaeb5bd859486f1957cb0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 22 Feb 2024 00:53:51 +0000 Subject: [PATCH 253/334] feat: add title to general page --- uni/lib/view/academic_path/academic_path.dart | 6 +++ uni/lib/view/common_widgets/page_title.dart | 2 +- .../pages_layouts/general/general.dart | 12 ++++- .../general/widgets/top_navigation_bar.dart | 51 +++++++++++-------- .../pages_layouts/secondary/secondary.dart | 6 +-- uni/lib/view/faculty/faculty.dart | 6 +++ uni/lib/view/home/home.dart | 14 ++--- .../view/home/widgets/main_cards_list.dart | 3 +- uni/lib/view/home/widgets/uni_icon.dart | 15 +++--- .../view/restaurant/restaurant_page_view.dart | 6 ++- uni/lib/view/transports/transports.dart | 6 +++ 11 files changed, 82 insertions(+), 45 deletions(-) diff --git a/uni/lib/view/academic_path/academic_path.dart b/uni/lib/view/academic_path/academic_path.dart index 9e4bff366..243b6aa08 100644 --- a/uni/lib/view/academic_path/academic_path.dart +++ b/uni/lib/view/academic_path/academic_path.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/academic_path/widgets/course_units_card.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; @@ -20,6 +22,10 @@ class AcademicPathPageViewState extends GeneralPageViewState { // Add more cards if needed ]; + @override + String? getTitle() => + S.of(context).nav_title(NavigationItem.navAcademicPath.route); + @override Widget getBody(BuildContext context) { return ListView( diff --git a/uni/lib/view/common_widgets/page_title.dart b/uni/lib/view/common_widgets/page_title.dart index b0c97610f..ccc46a440 100644 --- a/uni/lib/view/common_widgets/page_title.dart +++ b/uni/lib/view/common_widgets/page_title.dart @@ -22,7 +22,7 @@ class PageTitle extends StatelessWidget { ); return Container( padding: pad ? const EdgeInsets.fromLTRB(20, 30, 20, 10) : null, - alignment: center ? Alignment.center : null, + alignment: center ? Alignment.center : Alignment.centerLeft, child: title, ); } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index d658d897c..40d1de245 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -8,6 +8,7 @@ import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/profile_button.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/refresh_state.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart'; @@ -62,6 +63,8 @@ abstract class GeneralPageViewState extends State { ); } + String? getTitle(); + Widget getBody(BuildContext context); Future buildProfileDecorationImage( @@ -114,13 +117,18 @@ abstract class GeneralPageViewState extends State { Widget getScaffold(BuildContext context, Widget body) { return Scaffold( - appBar: getTopNavbar(context), bottomNavigationBar: const AppBottomNavbar(), + appBar: getTopNavbar(context), body: RefreshState(onRefresh: onRefresh, child: body), ); } AppTopNavbar? getTopNavbar(BuildContext context) { - return null; + return AppTopNavbar( + title: this.getTitle(), + rightButton: ProfileButton( + getProfileDecorationImage: getProfileDecorationImage, + ), + ); } } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart index 78ed67669..5c263cc49 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart @@ -15,6 +15,33 @@ class AppTopNavbar extends StatelessWidget implements PreferredSizeWidget { final Widget? rightButton; final Widget? leftButton; + Widget _createTopWidgets(BuildContext context) { + return Padding( + padding: EdgeInsets.fromLTRB(leftButton == null ? 20 : 12, 0, 8, 8), + child: Row( + children: [ + if (leftButton != null) + Padding( + padding: const EdgeInsets.only(right: 8), + child: leftButton, + ), + Expanded( + child: PageTitle( + name: title ?? '', + pad: false, + center: false, + ), + ), + if (rightButton != null) + Padding( + padding: const EdgeInsets.only(left: 8), + child: rightButton, + ), + ], + ), + ); + } + @override Size get preferredSize => const Size.fromHeight(kToolbarHeight); @@ -24,27 +51,11 @@ class AppTopNavbar extends StatelessWidget implements PreferredSizeWidget { automaticallyImplyLeading: false, elevation: 0, iconTheme: Theme.of(context).iconTheme, - backgroundColor: Theme.of(context).scaffoldBackgroundColor, + backgroundColor: Theme.of(context).colorScheme.background, + shadowColor: Theme.of(context).dividerColor, + surfaceTintColor: Theme.of(context).colorScheme.onSecondary, titleSpacing: 0, - title: Stack( - alignment: Alignment.center, - children: [ - Align( - alignment: Alignment.centerLeft, - child: leftButton, - ), - Center( - child: PageTitle( - name: title ?? '', - pad: false, - ), - ), - Align( - alignment: Alignment.centerRight, - child: rightButton, - ), - ], - ), + title: _createTopWidgets(context), ); } } diff --git a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 594ca87d7..b06dc6fce 100644 --- a/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/uni/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -17,6 +17,7 @@ abstract class SecondaryPageViewState ); } + @override String? getTitle(); Widget? getTopRightButton(BuildContext context) { @@ -28,10 +29,7 @@ abstract class SecondaryPageViewState AppTopNavbar? getTopNavbar(BuildContext context) { return AppTopNavbar( title: getTitle(), - leftButton: const Padding( - padding: EdgeInsets.symmetric(horizontal: 8), - child: BackButton(), - ), + leftButton: const BackButton(), rightButton: getTopRightButton(context), ); } diff --git a/uni/lib/view/faculty/faculty.dart b/uni/lib/view/faculty/faculty.dart index c8786f83f..5db7bf9b0 100644 --- a/uni/lib/view/faculty/faculty.dart +++ b/uni/lib/view/faculty/faculty.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:uni/generated/l10n.dart'; import 'package:uni/model/providers/lazy/library_occupation_provider.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/calendar/widgets/calendar_card.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; @@ -21,6 +23,10 @@ class FacultyPageView extends StatefulWidget { } class FacultyPageViewState extends GeneralPageViewState { + @override + String? getTitle() => + S.of(context).nav_title(NavigationItem.navFaculty.route); + @override Widget getBody(BuildContext context) { return ListView( diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 860eeb62c..514f5020f 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -73,18 +73,18 @@ class HomePageViewState extends GeneralPageViewState { } } + @override + String? getTitle() => null; + @override AppTopNavbar? getTopNavbar(BuildContext context) { return AppTopNavbar( leftButton: const Padding( - padding: EdgeInsets.symmetric(horizontal: 8), - child: UniIcon(), - ), - rightButton: Padding( padding: const EdgeInsets.symmetric(horizontal: 8), - child: ProfileButton( - getProfileDecorationImage: getProfileDecorationImage, - ), + child: const UniIcon(), + ), + rightButton: ProfileButton( + getProfileDecorationImage: getProfileDecorationImage, ), ); } diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index 8ba1227e7..54c5e2ab0 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -66,6 +66,7 @@ class MainCardsListState extends State { ), ) : ListView( + padding: EdgeInsets.zero, children: [ createTopBar(context), ...favoriteCardsFromTypes( @@ -147,7 +148,7 @@ class MainCardsListState extends State { BuildContext context, ) { return Container( - padding: const EdgeInsets.fromLTRB(20, 10, 20, 0), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/uni/lib/view/home/widgets/uni_icon.dart b/uni/lib/view/home/widgets/uni_icon.dart index bfc4ee7f7..ec62efebc 100644 --- a/uni/lib/view/home/widgets/uni_icon.dart +++ b/uni/lib/view/home/widgets/uni_icon.dart @@ -9,16 +9,13 @@ class UniIcon extends StatelessWidget { return ButtonTheme( materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, shape: const RoundedRectangleBorder(), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: SvgPicture.asset( - colorFilter: ColorFilter.mode( - Theme.of(context).primaryColor, - BlendMode.srcIn, - ), - 'assets/images/logo_dark.svg', - height: 35, + child: SvgPicture.asset( + colorFilter: ColorFilter.mode( + Theme.of(context).primaryColor, + BlendMode.srcIn, ), + 'assets/images/logo_dark.svg', + height: 35, ), ); } diff --git a/uni/lib/view/restaurant/restaurant_page_view.dart b/uni/lib/view/restaurant/restaurant_page_view.dart index 000477fe0..9a69298a4 100644 --- a/uni/lib/view/restaurant/restaurant_page_view.dart +++ b/uni/lib/view/restaurant/restaurant_page_view.dart @@ -5,6 +5,7 @@ import 'package:uni/model/entities/meal.dart'; import 'package:uni/model/entities/restaurant.dart'; import 'package:uni/model/providers/lazy/restaurant_provider.dart'; import 'package:uni/model/utils/day_of_week.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locale_notifier.dart'; @@ -34,6 +35,10 @@ class _RestaurantPageViewState extends GeneralPageViewState scrollViewController = ScrollController(); } + @override + String? getTitle() => + S.of(context).nav_title(NavigationItem.navRestaurants.route); + @override Widget getBody(BuildContext context) { return Column( @@ -42,7 +47,6 @@ class _RestaurantPageViewState extends GeneralPageViewState controller: tabController, isScrollable: true, tabs: createTabs(context), - padding: const EdgeInsets.only(top: 40), ), LazyConsumer>( builder: (context, restaurants) => createTabViewBuilder( diff --git a/uni/lib/view/transports/transports.dart b/uni/lib/view/transports/transports.dart index ff6fa3cf3..fa51ab31b 100644 --- a/uni/lib/view/transports/transports.dart +++ b/uni/lib/view/transports/transports.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/home/widgets/bus_stop_card.dart'; @@ -18,6 +20,10 @@ class TransportsPageViewState extends GeneralPageViewState { // Add more cards if needed ]; + @override + String? getTitle() => + S.of(context).nav_title(NavigationItem.navTransports.route); + @override Widget getBody(BuildContext context) { return ListView( From 94bcf02e80e78bc98b368238ddc1bce8c3e3bb47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 22 Feb 2024 00:55:44 +0000 Subject: [PATCH 254/334] chore: fix linting issues --- uni/lib/view/home/home.dart | 4 ++-- uni/lib/view/home/widgets/main_cards_list.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 514f5020f..a994a5f8f 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -80,8 +80,8 @@ class HomePageViewState extends GeneralPageViewState { AppTopNavbar? getTopNavbar(BuildContext context) { return AppTopNavbar( leftButton: const Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: const UniIcon(), + padding: EdgeInsets.symmetric(horizontal: 8), + child: UniIcon(), ), rightButton: ProfileButton( getProfileDecorationImage: getProfileDecorationImage, diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index 54c5e2ab0..ad9167dbc 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -148,7 +148,7 @@ class MainCardsListState extends State { BuildContext context, ) { return Container( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 0), + padding: const EdgeInsets.symmetric(horizontal: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ From f832ffc338287172ad2532304c327da56e082d04 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Fri, 23 Feb 2024 11:08:49 +0000 Subject: [PATCH 255/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 54390c886..a22120ef8 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.4+237 +1.8.0-beta.5+238 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index bb580268e..b14fe4f41 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.4+237 +version: 1.8.0-beta.5+238 environment: From 3d5049278c6448fc133013a28354bd60756126da Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 24 Feb 2024 11:17:22 +0000 Subject: [PATCH 256/334] Hnadling socket exception --- uni/lib/model/providers/lazy/course_units_info_provider.dart | 3 +++ .../view/common_widgets/pages_layouts/general/general.dart | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/uni/lib/model/providers/lazy/course_units_info_provider.dart b/uni/lib/model/providers/lazy/course_units_info_provider.dart index d661a6184..69d653bfd 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -40,6 +40,7 @@ class CourseUnitsInfoProvider ) async { state!.item1[courseUnit] = await CourseUnitsInfoFetcher() .fetchCourseUnitSheet(session, courseUnit.occurrId); + notifyListeners(); } Future fetchCourseUnitClasses( @@ -48,6 +49,7 @@ class CourseUnitsInfoProvider ) async { state!.item2[courseUnit] = await CourseUnitsInfoFetcher() .fetchCourseUnitClasses(session, courseUnit.occurrId); + notifyListeners(); } Future fetchCourseUnitFiles( @@ -56,6 +58,7 @@ class CourseUnitsInfoProvider ) async { state!.item3[courseUnit] = await CourseUnitsInfoFetcher() .fetchCourseUnitFiles(session, courseUnit.occurrId); + notifyListeners(); } @override diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index d49ba5384..54c193f63 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:io'; -import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; @@ -37,8 +36,7 @@ abstract class GeneralPageViewState extends State { try { await onLoad(context); } catch (e, stackTrace) { - final connectivity = await Connectivity().checkConnectivity(); - if (connectivity == ConnectivityResult.none) { + if (e is SocketException) { setState(() { _connected = false; }); From 4e48a59514b271c4c98649afc95e3a59088b65b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Sat, 24 Feb 2024 23:44:04 +0000 Subject: [PATCH 257/334] fix: profile button clipped on navbar --- .../general/widgets/profile_button.dart | 18 ++++++++++-------- .../general/widgets/top_navigation_bar.dart | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart index 6dd27146e..98f5e3025 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart @@ -34,20 +34,22 @@ class ProfileButton extends StatelessWidget { BuildContext context, AsyncSnapshot decorationImage, ) { - return TextButton( + return IconButton( + style: IconButton.styleFrom( + padding: const EdgeInsets.all(5), + shape: const CircleBorder(), + ), onPressed: () => { Navigator.pushNamed( context, '/${NavigationItem.navProfile.route}', ), }, - child: Container( - width: 50, - height: 50, - decoration: BoxDecoration( - shape: BoxShape.circle, - image: decorationImage.data, - ), + icon: CircleAvatar( + radius: 20, + foregroundImage: decorationImage.data?.image, + backgroundImage: + const AssetImage('assets/images/profile_placeholder.png'), ), ); }, diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart index 5c263cc49..fb1bbab51 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart @@ -17,7 +17,7 @@ class AppTopNavbar extends StatelessWidget implements PreferredSizeWidget { Widget _createTopWidgets(BuildContext context) { return Padding( - padding: EdgeInsets.fromLTRB(leftButton == null ? 20 : 12, 0, 8, 8), + padding: EdgeInsets.fromLTRB(leftButton == null ? 20 : 12, 0, 20, 0), child: Row( children: [ if (leftButton != null) From 7f8a71e77120f6bd3511dc61299b15dcdd143bd7 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sun, 25 Feb 2024 00:07:16 +0000 Subject: [PATCH 258/334] Adding illustration --- uni/assets/images/no_wifi.png | Bin 0 -> 80704 bytes .../pages_layouts/general/general.dart | 26 ++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) create mode 100644 uni/assets/images/no_wifi.png diff --git a/uni/assets/images/no_wifi.png b/uni/assets/images/no_wifi.png new file mode 100644 index 0000000000000000000000000000000000000000..b407d9480dacc1ec0c05641fa2c66815984eefad GIT binary patch literal 80704 zcmeFY^;2Bm_9omkjk^q>KG6)1hlarNH2Z6xAA50M*l6y zupiX{QwfKQ_ca0$UkpZ^`Mb?AxH2dL`8&OO=(rq(@aN&-{p)8@2XhK8q~zS}`*~Z5 zGyD6MrU8dB{%aEWsR$4j1z0iw5fg?=41!Gs#om+3`LE9cLU21E|9$}YMm7P2R0`=* z;{~4o_k#%uh3@}riGMyq!ls~1rQq9>{+}l>VX)ON(EoYKzkWm^4yGx>hR7`cdySXx zB%)Sc#Q66|G_nC9nn7zN#Q!0ptn7r~|9%295Q3DDAdgAnM*TmVhyWomcp?0I4Pe;> z3gG2PL0|0uV;2;ZQpf+ba~d%TMsdff*nb_wKfk6a+9mzpXA>b71d)}cMT~33`VSd_ z!-4&eW&hvd{J+Bi!1{kh4pF9UO4+AI=jpxIzA9IDhkDAZQ{9{@#YR`>6#Jpk&ggp#I zj(Snd%+>|?D%v`Ff6pU+mrs<$~tmQvNS)__u}D;+iJO&oF+%%v7mUf z-Ezq#72OJ&pz0lu3H7wUVWq^w(;^|{TRR_0w8-_n3krlq@R`UQ3%*i@Sifx6Zdc9d z#iNLUA;>5w$ze+#`%R*3M$2OavZvAsG;kzYuzE$BTIc0*xuKC9Dw>gnpd{u1yD!xn zh6kGTxMxf5wdGz-SKTJ0G*ah%oT#uga#ug_Pxi=L;U@S(XXuYtc~a8Tp)ET(=Z`q1 zWKX`*($Z4k_XVty`O^TVruU3sz%Q>i#mwM7-&)_K6XJb00nW9=&iKR9Q%jwUlC}tybH7UAc zLVLceEtsf4Rg?ohMg|UVWjh%U>+a%j-NE$iw;b8Posl!GLMh%ifQyaw0dWZfz^X`w zW^#M>8@gPwJXg?T(0++HgTw{Q7Zlbg56Dg2&{30|>APEgID@3od(F@fxCMlMr{HD6 z`CYdtCNB+7FDF-RyCqLwQ3B55SPMd|q-(gs*5IK0ngvg@E>vkI`g~AFkn1S_tfIGL zHC`Dhw(;VYN8`M(U)(bG>uNU}sXja2)zi&8H)0G8Ej+$vV_kSzdcS70a5u~2tpL5w zBWNu=d)4?JL+~#ZD}y6B|NM+;Q>%4TQaCvD=ul^pwn(!vf%66c>#D7J$DE z75nj|6u~Oz#Zi-#{o7WWo(Y52x5u9j#TuE}qylL{I&p_v0qSr_xb42-fFiExcQg7> zH7f?mPr_@ct+#1?H|ZIbaMUCx%;13`l#M#jZN^nQlOHf{PwknQ?+;k?z)1)^B!srW z#mAC)E=m!g`89Iqz#OI`PNf0YvVdLm1YFSzTeQ__JN%`<+PX`Z+UDpwMmFAQN`UD~ znk`G{QwGN0T(J|u6mz13u!6#}v)?!{@t`S|^=)B3|>9VTLUd2%m;FR_9FAjLCX0kXE zaF@uCv-5Kd;_H(Pj1bi#@BVr&QIvOqMwpbG+;EJHjO_y68C@i%S^A3ZP^dy;06xQP zV+Zz|n8-DGWJ0Q7(p2#qlMy|7WCZahQnm;Zrf%af7x4tBtHD|lY9k2~o2e7kQG}QarINy967J&S@GVX$TcBdh{Y!8ds1H)iA^ zZp4l0saQ0Rd#O7Q*q)#$H9waKMn&(a{IIZ2`V4(t!873O^qu znk-OhG9u1ot|}kSyd0lHQ@}#a)XGgM3sG&9C!$ZVc;5_cq&)dv$xB67f_J=`&sGc%rSlzkMxlJ7NU7ep>i zRu;LsPd8VlrkoyUgBh7Ee-V79UnUx``D;VBt9hcnchPK>hIKr=3=I54`iF1WL=cw^ z5v(nPKNKTHF-vGwlPs7QcZGzL3pWkD>i1bm(Dd~5yekjyx2=te(op9n-%UG0Vi-QXK2H$h-OTbx{!Gx9?b(b5^1-fGbh__@x))k4_$j0l;r z^~xNhgKdn3)uD<5W?b`~nT(hwHzp~5gDn4q(R3qdZB6EYx9Lw`l1-mb^6u`YrAgEy z2@3g_=>a#%l3G&7Wc9UuJSJl$T_hbt%r?ZR5bj4*Hn?s)?FY9(SPaz8mVztgbIiar zF+f}eKB)y*EX%KuVr9?Ezv&hoUg7kE`Si%fNc?OpAW4)bFW!~*usk*MgQu8KZ-YgU z61F%tzicbaRh&fgX&Q0v^Jd3edn@{)Y?t*fr#PJpEqk-8R)xD}uLzQT)|!u4DQWu{ zF-#2^#`bXUM`Ed%$_h3bjLNj#odR1>Y2*T{O<+-Idaec$eHtDt=q)n7$Va7psj*9- z0WBkT{Yt!EcAhu$_xtgGN*oH2SMc2PyycRx`qdS3C0dsT_6*f-L>#^r}Oel#1-=4mFb<}gSG!LJ-U7>~}v=~Jn8q|~F zsSoOci$G?8A?b=mD&76@38mDU$?T{SoBda?$pbDTCKk<&$uhi70}DBc608hvM^-=@ z7EGrR>}1r*tdYMbbUpm~p|s#i>Hl}Wp^&I33A^wW-_=4i;>J(Cw#z!>1gaiC^}W?h z8anUV+E_FQ-$Rzbvk_uthhlnWh_xnzMgRp6_OT?rktK;i}!C6w~+d*I|n8tLcu6mvE+q_jbj*0JPu)b=RCo*rI=29 zj%wgUr)s;&=)}F>4U=W_b>Fg+($e0|AREywyup&q0K7@agqjRMHHebMtgNiQQSz=d z6_r#AA;97I{Yu)?Ef}xZiNqWR|8O_meeMbRp%#r7g_H_Y(nax6OH%ANVa(%6L(iMm zhQ;5i_nTT;{G4RoXEq&{7V$K%!F@dI3#t?auO%qA=8MKjBP)erj!BR3730#oLG2bN z_qD`s9}A}$e)1huE9$g=z{Ay&5>t|g9oonx1{8q3f?e{0vH9^TE4Tfy(I4HWM0iq2 zGXLvKq@k{ybR`badllH;{-E5nEMO}|V@tQVi*1TZqZX-GJboC;JO8QWO4a0Ng@#%* z?o(4QoQmwNRDk9(=y`@}yn->~>G9rIz13BBGC|h=Zmmwu<@tQ(m#Z!uS}0Dj0A`_F z^@fCejP4u>mrAd`_su+Z23_^ZViFSwzTnHq;5#Y$Ps}$w$J)P`7~v~#y6OpUXvmv+ z5Lo$)`2~uK;3e2PxDJa}I@(wrehE+NOXkzQ2#u|rcqInWqYS(~CwSP5?57*qu;I2>sWxiG0`O_0E%4LDf4hO7RF z#q%z;s~t~NJe5B1`wbMaErIz)XadiPisC!?;j&i2u=)7MqP^{}$KQas|9!?*8s;&6 zo}+EoL)hEVwe1;HRY%$TUNfb_4u@LtsZq!bxD@D;C}5POpg(1ZB=1bR;z+ZM=Pqhy zb6jtGIf;p^x`K|YyuAB{*l1yJPT=zfatwGcRB!TAk+TSJdJB()9~SyN*vF_syZ%ZH zH_!#JMpiq^i1=hR;WOu9NT3H$-NQ)mSVlo!&r%9Ax$VTf(m-(ApDE}{PUPV-%8W;x zLIBwMrFd!|4dCilkeGF<|7_eJd;hUh8*c}>uoWY^q#d)dTE|aI8h9HnfQDeyG${V) zC3L|o*s=wxs=?^2iXg-nfPgG@>n zeyFC))fV$3(m_UbH;cFU)$Z@Nlt^zt_;mQRHztQ(IvUlaR2h3>T9mdKm?yO2W@ZfR z6Cp?{aUJ$2x1A1-6LaIXRw7iYMgYa1CeS5H2Y8DYZMDhB%#*gE%h0o=S|Wy$tW^5e zSG;P2s!9V@M~Zp3d^>S|N#v$3t!vcJeqBA+OR81Ge9$!?CAG8P$DJOhS)N7@+)C3R z65^DMuOsZJm}W@0Qn1U7VhMzN(7*ze$*mgVXq{vX=o)yg5WH0khmyt`ns6;lP*g-i zUsWDZKdedk=Fi^5tbxmxY~Lri1n#~xDRW7ctOFj5!mT?|D>&`JOt zZ6#?(s-;eGdW0q}+?w*RFN53~BA5`AkAf|k0b=5@bAD&PL?KOr;PcmNyg?@32tLPa z!-n_ye6J=}x4Na~=6AbfJ}bZw$`mrW@PXhAP1<;7SexAUw3n9*S85B>zk?WC_(dy}7ia1*1=SRh#{Mu>H?oT7DJ|fQrzYhg}J11!b{Ka?d z2?AnbFXI{KVc5_ne13Ofo78BkD3=mvTM#$M*~?lktEaX7YTbXw;GK?PuJhU7Z=^ajTD@O z@L%}I;Ys{2$`DraTs$Es$(n6Lv~=)$d|f`?1zG6-Jv&o#yd;zru8IiCy`?95~SDw#Oc!&8l8za*TH zzR7w6@oNI~<}^9a|2XHCcUoGu_xOxswCh+PflPu@3-lPjTerSGwz4~uQVq}RAmVeG zl*hV|)O1uoSa`NVD~PgKl0?NH*8>;Gp^H!2`%SCZ!mMIl>`$wW z{5M$}t4ILV4L>!$3S$Mn6EohM(%sw{_Oq}olt7lkEk?)W$V;(%iwt{$5Nw=!6hQ5D zId`}F&?Lw$6_f3vw5mHXgP8V>7>lz9QR)HOe57)gYF1pF9(aIXy|kpNp)XhwiC8cB z5_W8q{?3gMW1={jkU4)o{kc0(XH8u+UO^{^-Q0>)ngo(J3#BWH+nfKgmU!#n{xL@C zODl1U(AQ12Ux$wtb0QxuYwmh;gGBmeP84)YmXsmtrBG#0_OSfb0_ z_q|{7bPomUS7#bc@6rDL{Y&h!(72qNwQR1C#MZ*a!l{~>y|56)40s>1eK`_(1a^a)M{{A1W zo*Hr(^|HO^vYwq&A3l#@m?w5yd?$!Qn#2Dng{mTZc{pjh_{FGwvY{q)DtrDIHH*jX zjt_*y3-33Ak%q$(X!@aGTj%t{tQ)jJi49hu&&Pxa;;Oq>JfM}={aZabx=-YE?zY3r zP5<`+dKSJSB$E%Y=1LC%K+qk4c%WeXcsJ4yq}ZOerkR?qV%x4xNQ1(jDo7~ zSl9rr5khT->Z9|_?6~kZuLaRCt-V1{ltm0f>%1RTVB@#=-Q_Vp2E?{}T)^d^#}WuD z0hfbAq5AG!8jszef{Ne8M9@9}yJ|&Lf>-&I%ma;-ZCr@!K`f3p(5HQwkw=xPFvgs2 zeuA{e+M|@O3jTaw>g&s+$!Z>$v@3<3Ci_MXA4SOdieW``;bv=vd4uSsqd5f?PY$f< z^M%BF3E7#=V`$Bwfe~iG+uD>L_Sh}QBOOFUyl%`=&>^{};{+`YYzPArR*Laxh1(V>#Y7PDY2;zO@zCmp<3yvs3d&EghS#Q!H6>p4(U>5MUcb%qC0GdASnfpDLE#v^#NNM zSA%1=xXE9?o!P5KGtEZ4?GyidLm!j9#J_*b+bOT4T!5UY1R$3UztsXAmZb^A&|`f| z#-j6LCiq3J(uAnl1r!w4q88Raf6@bgxDa0mZr*HBi}-B)x|0lJK+Da#_+2t4dcT*} z-7Uewz<`9x>9wwWdD}5oYyz7s3H`Z+)dSLvM5bGG5=%3y)<=iJB?NfC8lC702V;pv zv_2Cqd?!(nh2c9&$f_jvJV1h78I%9pT&hI;PAl*JOHHJh&5MC<$mTjsMyzKCPa65$ z*64Ds`UD^`A}WFrdLd0;y0*KIToIe?a0%!c0~FRa;wMPM8m??->lX5*7<;x7vW&n5 z-9EeiGp7p2{tlziJBdas4YkEmjohrp-S1xs_|OAZkFWLJmdp(Eo2?cmKzDOsRm6hv zvhuQ%pkG>@7-eN;Vkx-B`X^Vx_)M-By`d>h4&tC89QpSN}79q(IT@mVX4|w;Ow!RX7z%PICZQ6m|nQVk5syDWS zICeO6xIBiGz|iCAqAPgJ_cbqXOSQSi-BQ(U zCRKji?)SP^xQ8gGNA+7R~M|>cs=3oWlD;aN>YfCHVobAyop-2W-p~1f4 zMygFp64Q8Go8(fF{hS7;RUl7aSr#N8E*{~MpxGPE+5MpCF{Jbd#Nb8fEI(9G)(jeL zdn)69&Nw;Iazm=Fujg{x2q9Q`IGFU@x$%L)gv8jE8ByY?AUNgw<|iPBmO;tI-eHnB zPhL_#ouzjr|1~!<$Q6p6{{52i_)DjFPYq^CfoAhNEZq;+^PAGV4*Zarz#2VXp~Um2 zxF|^FH}qSH;SyG#WauzEu4YQls3U|2FFmNjYV8e@Br9lBEnGMARp3&$dI>W_fWmRh zNk_;oH>UaKJH46<~kadCB%+L!KUwuaEuqKEJ}z#H4utsX#%R z(RJWgNB{*aA%28VCSw2tI3g3A+6W_64AW^lk1mx4YAh-gM&?V?AKoSd0Qj$704#`# zY{R@7YS#w1+5RFzW~^niHPNkMg5X=n8~g3y`5s9*SgD!(GDb+CSS{TJQJtqxdq}R~ zhHK0ZgrS+k{i@EZb(^XVTUXP{*zMQgkx)Q0ww=}Mm4R6HisIy`EpPSWXR}4F$_FY} z6^#PzELU0W2dE+rBsusG?RQAEK0<~HSOJrQs`}@kc=UH5tM}ZR~k}_ z5=EiY3VY;b{*XiWu$d$<0DfA(Kes33+#axa6%kbk4{hWiZrP=sb``q*@Yt|7^X)Np zIFS}laS|ej4{cO()W?xb3@jT=%yM|;d@eC14tmMPf#^ov%v5r8`I`Dn1fxusCAT$ic{I zQea1VfQvb@Ol3F=`yEcd7SV`%N<|XzrhQI*cXrZ4Jx0o&fBn4Y6`OrdGE^(@t2P@A zl);4}o?pfV1C8)CK4TKR3M>{JhF_l_`7q`^Pgdp|QFF|c5rs0DxX@@PpA^!BHOt;d z<<@~g6p#4Qq;Nq7@GBq_2}EQU@ffJYkA7)XJshM-1vb*y8LeAe8tPI=wmld6pDYvA zx$sbjz-oAuRB*;CXBDc}94fyEE01)G)a`^Bi-LOD_eASd4n4OAP;6k$hthug^&Gtt zaOeq(ZFv%7jM#OCV4*IbC0Tj#B6moBj-BI!Uz8ad%+JDdQ7$b*g_5wxAd+|=Ohik^ zkO=b$X2I=Sr-qqaxRY2k@3+#mR4$US+w>lVeL(8Pi7LB6@MFtWXL@pHdTRQ9XAu@F zb1sxm_&tHf3dhtI%hWcm*viwb0f8tSQIqba#RCBVTqbQ`p1#ZpdYp;1#Bf5i< z<$?>82d#NMZ+6B1vccc8urlpcq-13cvyBkw~)yW+4A6+d=n2v4cEhIF7W72P2;3a+_^P zJC|{AFqaA$!B!mz1Ptm=PRUqa7uza?$K@>86LTI7i024?vr`P|U-5M{Ah*tZja=$w zrG`R$uZ0;x;489v9#iAkp(q)Q9Z2|sD8_$DY`fB4w;#*9RQD4p>`lC77QC?Y8z|~K zv8b}3fH;a-m|@$1hCSO^7~*7CYrkI@L?@4ThxyS&j^%GHUIB^DA{UegIJkt*sSu2I z?mFL3-;{5|k;7bAg}Ch(rrcMaci~b0^g%WzW}0hM5td}!)S7H^IMjOejn~A06xY(t z*goBo8DspTumfSAZKaJh0EJ40#S-7#K*U*E2~jw z@U+3&k82BY1UMh};c437E#ctC>x{Gsc}&$m$+vy#4IeZcEx!&^&bsQ}WF;_@OX^tk zRQhp9S-cU%##l__>boHD@nbRWn8)8xn9rY|DjJsK&2)R*49MF$--m$^5$9 zre7_Yw$=7g*U+cLbmCl+LJj;eQ9gy_@}=mehJ=fE`Ui_z#;5&kC(l0}jDBI6j3iU< zfFi+%T8N|MAK-6^ws>a0|2YYjSboUlvXN9ZFp>`sjgLSaN2;1i;G;Z=@%=l#jpiR& zwHyKWBWo2Uo=qBThV?(A5L#h2sC9oBG`YF}%QoE|=<(n2A z4Vq2aEfgN9Ii!f8jK;gwZDB@U^W@ftNt{aO<~{7W-%%(-Asd^rN%e|pTf}%$x_bQ| zE%~TUvH*8Kf$|bo)zPxs4ce$EX5DpnzVY?jO-Vz2Tx<-~OoZXIJ>Dj3j4%Uhm{n_{ zy*s84+{$VMqug`|ctdH}-(txoY01K%1QE}4FJG^mUd&&kwtO#EZM0_*-dJv0?YycR z6X}?#rTofB2tRAK*7KXY&DT3OV1cEmP(-u~WzAZ#$w-KYu`$e`Yo}R&K))YG3Jc2q zJzx9yXjLhX|F3?Zj+D~;!7=}0BU`*ARn>HA!dX3oDKU*)>*&t8uU1wAQ0`Aa1b`o@ z`bFd=GD8A?y-iehUv;@o_q3UD7iOj=RX$jONzzBij({n{>{xKvv=dGY+&u5OLx%O! zOW!dQG47uzd;-e6jH+)Oz&Wp^Aux^zAs!k9U`4slENR<#lL1 zH&8c&iq|pz`>)~)==GusmkQ_yQx@W69wWu5fNP+Yh`oDJ+>eONMzwcSJGpsR8)12A zmwX*YY`-q8C^FF_P}_&s3zt!LbKz_F;d}|G~^w>4s$Y)g)K^8zq+?`>k$CPhx5*v(KV3 zQ>mK7)jZ!Md~N=t?IIHr`0!BM3ov@7+c;p#fF|9qB>MCWfeKn^AGOUqyAl z{uBPzEAi)dMp9}TB&z*!)~1V)O=XXri%I32&(j<>-b%dnUT#kuyl5?3Hfuo`E|O9e zD&D&3>_?WJErnOkrPHpz1^g?$7J{3-9j?h7RSf-Jn|s%&%V6;N98IdkKl$3Fb@7i) zyFH@xu-||OlSd`W0H4cvQ zU}la8M;tDtbL9BBFcfQbA`<_nwXC-1(ZH_*TcTTjcV$6 zNt4+bx-KRmpSET3==W?F8LeXpn~Hh5JaOMGO2}i< zIMzs0x^!_-=ifqXE;hfg3jZzovZ@eb)E-~CkE1l%URP{-P#e=L@Drlrr>_2cKBh*C zn>0L^8hk09&;JZxfhAHdnWaV5~AwM+aQX(e5v3YCz5q0?}!YDhg{t8V+W z=@08I3$}|~BQ{Et*@2gj*jBsPxR7g_MN{~R zj^`gy7}$J6Ouck_)MP)u!^{43mwzoC|Kpi0#suQ^A=YI@dG_*anpuP39O6BWdJ>J+ zuUen7LV8j1a?!_Z#3^3D5?(50^u4qLi6fQY?Jc%#kN6#yf_5lSP;F(bz>*{o?iyyB1Sn(a7;;b_L`Q`|{Q5aki7^BX0HjE*_4F@SXe5`dBV%~?^GwbF!Y>Xvlcm$(ifh*P22MH>wLcE=I>l6PrSnp z%mj)~S~&6-s4u7v167s$@^G4(yKJv#7LJTuTfpZ_0ujwDGNHhv(;{97F&~e)FMk3n z$lZ=~{{#xVi88rfcSFyt?Pv;DCi*GNr2!njGw_Z5CCcUl7LFyZ8x%eHv!F1#_+6h+ z$GI4Uc2egJT%40W&6rnZpPL4EtpU!>ONecXbv+EuTA!D3;{(eR>Kkn>x8kz0*=cjX zJq5^K6cDquQps(;_y!up{2NdI?xL#b**`f&89a{t@M_7RsD zmxrgOJ`++>QiqZpOeu*qZD~vAW1T*${p7w^mU2#^yXUtZnh0R{3c(D_-!QL9rt+t0 zGNyd6?vYEIbtE@Q&H2)4str9ar$urEzHUwXEcxdB$-eJiKe?~m&n4~b&=y{QeKoh( zO3h0{5b5Vqe;Sk||FfJ6AJLI>)wGb69_^>AZH715GxH z<7uwdqJABAdUCx?^*{7Eb8>JQO%gcZ16vIEdTH0jWO{L~1+*B^$b#2paFro4;Z&nVOt=gf6@ zw~$9^xEAkdy?stvwKp-*MOzmLXxqE^_rK& z3XL;U&fxoW5&+ZjgD<7@>Y91yAwreD{%0*ST1($%{)a82D=aN1%CA-n+);(YWrq(_ zd5BYRfO5NuC-zYQ$}NY*4iqEIXOfeD;0K%J{d`539h?srFodkxLh=bd7}H#Rt4t}& zr*(gDxA^`ZFJ090d_)`leN$!r@h>b4-)6I+#>&EcuqoJhrKEWpvg{d$gQGbj@SPuj z;MOi0H+pNZ8yE4HY?7p7ixnEV*R|G;k;h%9jrVT03k!DSFt5`2ADtwnv4!jR63BV? z&+oncHMA}-7e-Y8LSyivRs^_Ks4sCJE{vgJ?o;2-ogd$qYCFEEs8f7sF&=o6@);dB zR6-Ry3flLOv! znWtr3dcNjLiR*R*NH&n@RC{j0E3wn?tMi1%4# zcr+{D!c6py8KTYzw6s>Ol}oHDSkeK_O7m{4=cF1hLmKu!3VnV!8%a|5ovaRZg=tJj zFfmZWIzS8+QwJL(QGt@XYVA%CQr7-NCXagDUg}=^U2gfab&1aT86h{b!qyKOJ9^f) zSSFrA;93ip@L7w-b6ydsH@h!2naQkWn8Gt>aNVjYiW*N{(E_BLez9>!ATHUr+7)X$ zWXecO%eSu4MZ=#;mEgt2!ATJX11|2h%-X%XNXLF#i;)N(au{W2HR73qLYJymwImX1 zcp$|f6vknVKOr@zakJSJ!RN6{=GcH+RNDfb~r@pXz@%IbSv1I|#6n-&`ZaDUTU2z;(IFp=rHuW_Zx*NRIM2P(+D(25*F;A#-Qc-(yZ#8laQ64}b zrYr2!zIXfOimvFRF4_9|E!p?z769U^?*SuXt&nhs^N)#ML#s9$^5frHefG2YSs1#S zTl7n0;L}{?dw0WDa6}YL%yncj+uXhOnmDt96HLR9zH|Og79=T{ZNoSyL7@#&VzpF=_S#hbc>y=^-hFPvo^kj$sT)pW!Ws0y zG%=b?Zz+jx08A(iQK&PPP-aK%@%5MrPS5qdLUNd$J|zlsl4RZ$;+&cs!`J90kioYmw6>M-RjuniqUq-HJ_9|Ueh*I|Li9nGDE#ALlpM+rf;FS zxm?>ZJs8KWMU_`UUxD<8|4QQ_@#ecy5BZQA5+~j~8o6kIGE*q#>S>=(^1Vdmz>E>C z%;QT5(;33IvvE_CO!wn_6rssPm%#NAx`R^oz%(F@8-HQk;PV{?j-$2%Q@Po!N7yy+~%7hk54Z*Ma zAOE`Mi>M9Q#vPLiRfvQC?7h0-lQ%qnZ@U*D!Fcs&rF&O3&%rhd0ZUyc3$sF1hYEh4 zl5;W&3f+Wf&3!m1nS5n>6D$C5;b-yL%O?HV>8nqNo^*t4IZQ)mO9DCB@YG-= zz}}~Vxb|K$36rm@?U%2<9RG0HCD_6%aP5bOW_l8?21|w43`Ff3tji=;uj1nnTGpH) z<0;r`R|r3BvJPcvAXVs1WO|?OjQd8w%zOR3*zU2ovb=}5ku!^G3A%U6t z=TO3-`7HkK-OS9+Hz7|iWo11>gQ^6V%8X+E_p_HnWdvbyI|)%OS0@KYGh7v;_GmMg zB9`qlFCpqazs&*zmL)fYk%@`Zq2=$21CQOxih?mVeH4a-7M9AAv5S5=ad;^f`Uw;J zYjz>ZpM~$3aTVr7v5^AJ_$h`gE4t1&a5?T$eE?-h>to4W$}(aC1_IR(5ziRop1a;Hn2v zG(WdG?4kuDJlmW5wqw>1`&8+qxp@lh3fbpGwcSI+Z#Xa!vxf;M_f64&Ft3CGARFAY zQe!V*E=O#??CWoeClz&4x3(65F!dxPCBcW0xgC05M!lLDS!62MHk4qrz6FhesS(vq zx15R3={*~EBon3V$=>{V>eT1C`O9A?>a$xKi9R6m<DiDe5_zr>R)<$Psj%{= z@B0R9?pV1i3J&#CYz{)(xS%(TG%UL_4MS&<$+-f;+^MNEvuFG}%-E4an|z}z-UOU+ z?@PxU=;VIK-08u_DnP7X^izp`g7d{v3W75<_9E$WvmWz!SMR*N^cv~x+5H%D&nTYy z=N9K+LU5zP9r;lGI>%6&rLky-3Y|`+{4+gIY5j=}mZ@A;edB$39FEdEWopuJWdmg; zpJRozMigXCVp8a(O*^|2DK2&=OsA7^$V92>=&US!e-O4-y1N^==h`jid>_LR6;zts zE-CZU@~sT)dksdQ7L(XB4%yv#5$!#=^P%>s$U8j#roO$Lkrq=n$dQjo`AbgFm=oDGickF^w$#Lp)$~!>Y%Cxb+HF>+T4YT|E zJ3U6=*iQUcear3}^bd*M*c_I=Jwoa_^@J531&*{Ae;3^kLy&M(#zxKIE;|H!*vWvD z%S5wQ*_}@kO~vJ{4;_2o;J!C0m&kvn*nrCL^W z>`7S18#|&GQiSyjoz9WR3)bvx{V~U=LpjuQ-%lZmR~ZVQON)wZTwEP;HqBf=b+0>m_dAKW8C*x~bv<*; z+Dd9o*8vG({nh>a)5w3%pjxIv1_uX!8+w>roj-eS9u9G_Kz;N;fX$)g=O-nU#f&60 z{F`BE4Qbzt7oYZcJ61a?w143)Ei7(0V^M{FzPlpS3KGqu=-x_SRn`l3vOZGcux4kVG53yYO{HKMm13Z-- zyn0N|ICK{BS~@LlG72$4stk{m$1WAI{vF__55HWCb{^%pi#A(5-l27XGCsZsWt4L= zF`)!?QRp-xiR2-trfxY7*T}H7Sa&d={H#Kgoz1v|a3p*n89KPKmBYG+P?Cz+)F zwKg}=>St(U{nccHxU3){$~;KsnhXJ*r`~Sy)6gG_zTC8|tj1Gg+r5pQ?DKkAnm1#$ z<)#3aD#g^)deoe(?{ee7075Wqp!0wc%X%stATZ`~`6VUvo;_it!S9}G_uK(YeppLW zy7e7o8k~d^$0Xb=q>#92-i&)Z>yBn6azCi)z@K(aUiE*TJeG;^_#-U5VQIP5{oxmj zxU30!WwR6`YthM!;bNoRd0r;p(^MR(1~Ky`F#f2mm&QHRXg6j=)FdyLq`7x}AO9g{ zx-F}w_!w4rw)G`-VX&jrMhi6mNC&vErl$psv0q$lY#iz~+QZj6FWMc?akz1s4vXNb zo^P|XUexsGdZe}yeqMHFR;Mp2Z23}%&tMy8BcZH38Yh>W$Jg*S`~aSbDCV7154#~Z z>gI;W=Zu>}VcoQ<>sy}`_;po7J+H=5;I(C(DO{LaLVdLNH;_&qd#E0!xXax^$< zxndWw){Y9r-&lx7Ddn-NMFF;SR6m+i_m20^=l5ycva^@GB>e+Y(YQRgUbW1f#E1}9 z`(F*?`jQkt+W~F|3ZUnhfqK{;kCzxnnHLwFI9%MJ>vqD-NhI#Vo^P9g9`V2%dejN3 z)SMgxA^V5jNv{jbw%W8^^p>{DeN&bpA~H6-U^8z?HRdFd@Ajc0%hQ1A18hXXIQ}0X+Gw^JzC_$H)hh zv*f)F;e4z)%yo#SMuYTvE{3xW8=0-);|v)-%jocMdG^WF=C{k;2|rzy3EHV;Aid~j zkf&xK3*;_ximkqJ^u>h0e^r+J@9A_WutstFo2yVO&Tz9L3(o#^mlIe06zNxg46K%3Yz~y#U?B5wHVn-bb#{>h{dUM8O6TXDtFXKC5#q z?N449p#Rkipl*37PKoD!XmSUuG)Xlk!n+n|FcpdT-vjk7m5RAS=aKHKw`)9$-9x5P>2IR~cku+%@HpU0J&_rXqSOes z3?<~Xv`o)9HG=P-`(;(ZGcTBb`MFW>! z>_%2k^b+kO!92Wu`mOLx_LARR7cr`}e9u}^MqV8g38i8C-m*B%0;t6XJ_GZYU^Eu> zTIQ_e*YU!S5!9``*0F-4p6ROe7rFbbkDGyyWo78ITy{R3p0SpnSpr^w^n!stP;T|HX#PV-u zn0omxpZvMbR-3(~OfysZWT4%r!=R<+ zj0!dHVo}RnfE$uApz_Gr;P%PsrbTw`j5eRfKc1#=}oz8gd>a5)o_B(~WPYbo# zl9Q9Wjw+O`QRgm}#dr<0yJ!~vDItHcPq|`hYHEuGZ^@smA6sLE&2-_0T*W6JjEN@d z5i))`ciQDWD8+`QGLVGdbc8z&ZFKygp+AP z*uMs38}xAyA)^*CTimJKzXkMO100X#T;EFjg`j`}0slI@Xy5ky-DtM*_!~VDPy(Z2 zFwCAhdQiC6mF2`zucXeWI29RZG$X`Pk^L(@=l9`)Ivqp67y2G8prubEqu4XqgixiW z@_X-KU(D^ZCffD12goY{k>+Le2RvSa0_;l?gp{xFH`kl&?xD>gJ-6bQA2f=c&@t{W z#uYOLj{9M+{XRDiCBPM?S=6&l1C3#*5))2;nNqWg?qnyg*vUJ9cZB2sL(Dux6tkaZ zcB`E?=8kS^>!P7YSMk;CWmK|uH2Kp@t`rt0M9qN9~R zHL4j=lSRs<_?ZH}3AC3#-p*YMvdcQ^`9jvZS6&DIV*%QXA&_H2=wHLU@447mH=a~X zKj>Rl7RPTX;}!5<+RsfFkVXtj$Orua(+`!EAzSRWrs}EdWK(S#5Mo3Ph$WZwU0owdzCNHzh?e#Xm#}aHm6ROg zfbkD(OBDPS=AQh`%&IpvbgRDZD@vRF!k5`J)~KPlY5s|laO}ID?lGD!IO=UI7Hu|% zPGZrx>i>Ow(<6mIp^j_;Xx+H)ux{@pU&@X$d0(x4WyMt-%N*YSoq#@h=11jBb~sLwzV z_j6K``t)mFZv|9-u4`t`-g{;a8&=}ix_x9hNFv zyYaK%KyT`5DrPGG6GzsSKw)P93cU^F%pv>*kl{W3L{G!cjmo(>D*bCD91fX_eiMYb zw{A#Pqq_uCsq{Ao1Yc*7a&vE`>stOh3h6t9_Hr7|fX%si`%t&i*4A=WUyRONUynK| zvmA#d6fltTRL@Y;(z2CiX4VuVB{q}y^Lie4(!<3A+C=9RI5~k~VJl9mNbcyl9`!rU zFvEr5{)voiDF3|&f;#qFQvyHwDoI|C+k8t*wF-VMJ^amo7El5(f>6C`U^U5ha~&5* z=B$G(xj+vPg$H2W7TRX86Vx-Ti!!C=O+Ft}Q2KT!QhR3ycdgWA2KyU@I!G|D5o6A|ZLW3Nkwge62u4R)-H8hB`J*%bT-?l?vs$|)L2)?Y@nviFS0Ea96V@?h=9OtJ28IN>ht!vSyWV% zHAYKQH$FDD3Pmg8yIpR+Av_a^#nfO>mTu#GJk8L(XLylV)mq%icxa+{6KK)kw=1G>w6(eZSUy>g~PFdo$O z{;UAp^m!7nuUJ4_1j|D1Z|Q5gsEyuhz<@H0%BRwxintYll$BFyApQ4cW(?=l!+Uz+ zwjXX5x^3{U7y6*FKcDv$N{$X}kFB+mPnXx7G{lUPpy-gSq29H{+8=H2xCs__LN_}- z8O~-~*V@jTxLyc&iVsJ_BHSi~rJrPym&*}BwNL~XY zXntI0m3v#(2(+J7Y#B!o`D#$emV9ohv4$ws5@Q79iy5`C817#q%}a{iXDE<~M*Q zgL1Zbl2S?M$7GCTKf<<#g&Z4Nn6<|K^sCWsu(m?Iim=6XGQFV5er11wMvQV)^*t}g)4V>8Oa@t;` zUt8n)mLe*2<0!WBb;WWE%_^15w1f`R-R|GJvS~gfs9qDslwZ!EoSqWfhjc*&!=$<8 zLjQ9D?*44)?F_p_Ao#b$&fzseuA(@Jsn#MJ7%?cy#cy482UIFg?%byjf0vSEi*d?V zLl594sqwg*`frO~?==M)vFmlOT+JFr`$x(y$Ha72QevX&Oo7HzFlCneF80Uk)60K; zUxZ)Akz&=ojk_W0?Txfc+3y!mGV))BCkp2Fn;nhS#BdW2bn^JJbG^~ScO{FY z2+UX8CSe*Z))*-_pf&h(=;l_1|tijN*QD#w8jZZDO6EPWCS@M4)!tN1HC9XLQncMZ35B-2NA@9R9_tXYjd4ZK8nN+uQrr z9QwBNy%>Q`7m!KJhpAer;kpv6tgH-Eg1}{-G7$LoVO^5xy8r$vEixTnX0O#I>lTKVxuIKw;3nIFk;5Gv%gn$1uefS%? zAi8K6jv*>0?oX^r8r5$nCuMnFp&Jj)EK{5}frrjh7HJDEJ|U)B8bglRn8n4#pIJ=o z-CP%ei>vqlE}y5lFfAzrJlb*-{kq{yA0xxp{T{Gdui4i}|J^wH3y7CMeRz0SNx*vS zc9rci#Z~=qxnW3QEVs?*F5GU@A#btsSM*Qo-|o-H3G8B^JF{xh+fV)Z|1#X8nj3=5 zU6troU>X+4;b3J*`ZzhuVc1oA50FfRpw=3D&~Q2F((T8m`yF&aQV58Md(0HA@1^Y6 z)^=^MeS`S5WWLgr74Y>IOMapvH}@&JxUBuh_FzLwvhfz%ziI0o9yGV5$mew?-+IgE zsdJ6pZhd*>O5qk=IHce3c%x#t?v8=e{?y|r>&?!QnbzvF-L`{ZBJD26OF!*yhzd*! zPVJ-)hz&Mpv$?4#6i+M?w;fig>Odw0=Lq{J8S(%P)_D#_C&r&RJQopPI2ruUpZ{e2 z{Ykm!^fDcdn(b^eOsYB|J1PFBmUmdKJq|u2A&7`6*{K2!f``27Ee;R=wSYJY2xzHc_Y?+fv-vO=2jL8$XWiO;tv(MH8AG#pU-s_UGr$;9WUBEiR9k1`)amsBMz6pm<8>z<6pmWlp2YhlM7zfjnKrH%n(jw(r== zpF&>7^*IbV3IN_Yi7Mga*jU$|BO^i4kz(S!8E6@j#F9cW=(Z?b&bj2otT!KxSl>*! z>rwpfsrjBrqjvg~T-}s;_kI_}9~FtTE?!5iZQ{Qv3itc|io*;Tj!{xxhJfsOzB-#N!KHQ)$JN z0DpsVkora8|4e7@wdCH_K;OM#(=_Rb(x|%5YD-9xIed89{8!!NvtoUg@^uDk3-6T7 zb}|66P-&HK2d89ov3A>Sgq+?j-cH3W=s50q)oReCILRL) z5~Tg&^hYECjLK$y;?Lrm>~Baz z)oCiQ!3rCBRcL@XM5%~JNQ;|Gx?`bCYcx-m^ekOf=QlP9`807N@=793j*ga z1AvGRbJZX8%a$3p!_s&_5^W)kV3z!mx7^%o>9WgrL2PAHE!RA%h)yD)j@jh*$bV0S z<2G5xWr8L+Gprz>#j=~=lJmIZ!6cik_HM9bwNPGLiw2jE)xPK%L z35)cmy-m==%>GLR0cBHAkx~KCS+bBbSWqu6^gFbZ5GQ?LgdYw9z#zZ^P?HFQF6VFZ zTla#?*eqO8rO&NZ_km4rEw8*hOpm%6Uzl$AcdC1r0DX%#kkbc20u2-gKhkhaKxhG9}^F4dva7R^;#Q9Clxy#`Ga5c=buf7yc$= z7p|~;artTx9#G$`up@%t5W*ADR_FZZ?m}>gxAWi?SNa%SvzR)aOFILDQ1O0b(MBj^ zY%wIr{>FcbqKcTLj)*6^(lMVTMEc*?aUHE=Dnug=Qq*Dps6Lx5vMv_ih?yk07VmEQ z&Q@W6u~PDOZ8YeZg=`7-$K*GU8=<#(-!t_%^yW7~O#>}L#ZYQZK4tFn?*SbUkZg$i zAvWCM%3y0lshy8F`2iWq{=lp~@8Pn99C$X;U{uL?R_*x0(L?LwB#|DPt}b4nAAr2N z#PrZu6m+0t(7dg!5W$5d7g5aV(_1{|7PFBiYXQNXYo*W4Yt3n^jb3Lf05LB5LbJjp`Vq=mI>i*)pPQE6yQQfToMfe~?QK|yE;Rxl<{IA&3B>{4RD#GTi0 z_>~&MV77BSdD5LbZ#x(l(O*S1BJ(Z7r`U0-`sU)}bfZmXxZcV;4i zSJMnqQIqFI(#Z#mbEc?LBCQyt7KbSf#9H&!$xq|gN=(r`90Yh6!v_RLazcPWf)&J5C_XZuGXpdc%G%Gkidv)EGTzQwh?xY(Yr#jS*%2R{9xv9t)ksV zeclcX2I*)UE5Ugl#NQ!P$IbAYdNXbQJHb`r()4c&zB|=t^2jE|ncoA4L#M&jr4w8% z`AgvKbe!v%x6UsXU&v^pNSiO3T0TbOvr@n#P@Iv1S7r(*Ue&LyTqFWZ)5 zANCQcwqQ^G(8Q%M_gY{I}QZbxIkz7g1;|gD05sqd09t!d8mojHwmPP z>+B`kgB#DH$|l*ocf_2bMlDgAs2;5tEx1Cc06Y#OvLj$t_hGyJ6PqxmO}j`CY4G3k zw4WX}N8T@WI#LJJ;5SU0^?qNqKlX;@R^bGBRD&UeY68cpPiDdB#!p4b-JJ>}5!lET z8}FwH!FO$w@-e6vBn#bU5%$Y;T_yhVcPi#GoAd*NPO0LmiU{`@P5k9;(aOL!aKuX-QaP`~2arex~&+Jl$ldR;7wx);c z!_NCS)Txl6hro)HrZ-yrz=EI_++J;b3oen%s(sv7D6y`fnd!u)Pv_8C*vVq>AtNhm z0U#M}+~T11#BQFi$Q;>jHZ-D$F>!n3Tf*=Xiec{#h+ukM7(P>`GD;T_<3TD`NZO4A z=%Pg)c2=69Qb+vQE;OOiu0K6ubn!ZhGSIV@w$T6-Rjpm<#ds)pC3#7fUT@Kj6QYUL z`clz!lt2-kWf!8=ge5+hX~I3@20TAMcldZBZ#h54+yVdQ{}S-VW3y^$P)X_fK*Va; zM}9|g3qfjjI?CfH>H7Kv4YLlLQL2q)vsPDBLk5V=_47#3-Cv)t`tqNvsF?XT)3&Sk2V!Ky_k=c< zqR_+YpRZ@Z<$SN>wfk*W#E&__UI;u^#gcVPp238cms99A_2E^L)r!*%+1Zsbe(B^QUB`Wu>@w( zI=DrhsWK^)@5=%3v_CZZ{r$8t13C__9tnk)@fd1MmVqN7CaQm-^pcrR#mv#sNNE@( zI)(#6uIg&R*boi^biZMlSIs8kA+v^;l5%41ZsJl2kX0NwCUf;UH>Gd;j z7~IR0`X4@EzIiTfl$Q2J$JgTF*;h=M?Muhl3J0Q-}WsqJ~C+Yo8dXO zkZ*)t)ZEDPF}YtpaLb*Uf9H!7pp8^JLT30MO6J;#2xTJyhKVwW8D8JZ4t! zB0ssj*qImZFIFh*q+g|GiVguHB}C535(G5SFfcF<9XN^gyWag|vwz+!kTkpwr{djDO=S2s!*A(OQ~P7R5(_h34^EI0o=36d=#5 zDu)X-$AgKq;LSSYe^(j{n&i0B7ZJr{NQhDj0qsExn!cj6rDx9% z55GtlQxZSC$5mQhuGbo>VMKZ`8SvLktErAE!mnr#vw;v9{9nBbkzqle%~)K1)BRu3dC!n%P!3|+ zw1i0yGRQ<&`^gN^$rFBt=aU7fzJ&Nrf72((pI=VWda6r@aRqayVa7`wGlRq<3oMit zG`33(g&@RZp+y*>gH{eg{@&o*s1mN-DBN|PgQ`4#;!LOnpoo#m_kJ4SFrkI(^ZG+9 zJ66e8e+V!8@1axQjI=Pf;nur+c($f{_quqz4yCD;;2zvM%QlcsCkKbm`|Q&nj`5?a-St7;4nY)zc|amN zX^aB;0fw;1mGD8IcIWnX#GuNx|G62cuzf3o;Z-jbTV_6TvKT@BTi;07l zJNcf<$t4P7b?mzp- zVF!oiAsR~o^|8L>j>VWE;X>c}*0aHXuFW$t!$|AUerFL{)lIqTh8~2ZAnVK3&Fc`9 zl6P3fQB;Kokrl3bU@z|Q#+w{Dl2nVHtuw{4QLl-H8IzJ2Gdp(e9b6!@gLO1ph+ z+`@?Cyxt&PT4|OJ-STt?l+92=CURx?sJJv@rWUn#w#_y1 z-0=kA==(6ljz`OhvvAcAvc|mSR`FZHw#V=GjSO-rt0ercAI12?^k`x?8}}VDi}k&K zL80`I+ZEk73mUjE9Eaihd{VpXh3#-HX-#Bl=UTlrkYg288i$){s(?nA-P2_9zDPV6 z4@WQX`M&S}Em8Q;9zSuuwQH8R$_gd#o$!&~iIv(kQ+G%sVUc6w`<1F87DycSs&Vg~ zR%IfKg7u@heZ%fLcfh|HoY}Ksx(n6>2*+2vFX%VV?H!(RrOB!UzFw=ysql^JS1A=- z1k#^#aI{YI^n8pqcQJ5ERJd_GkGqTiex1UL-DXLl==I)Kjvr`W-MFt7L%9v4?6r)u zXIyYaW`$iI=wh_0XCk57?0!qh)*a9eQ~lyPtwvex;Y{& z{Mu31BtHrYNZXWFj=VUBhlc*-kHF{gk?TknLiJXXlYdF&qbC&Zk0e$TqJx2FFZipk zE3&!~Qe~1+iW>Oj=lk#8fQ>tClhZ6a9kk50EL8mZt6u#q=XryWeDsT9sRWcr%_<9zd%JL^_QV#6d)h&-RIIXZSw=>Dw&*Gzk>zq_6X?Zdl|`v ze1|#y=oZGnWsxifw-ZAopz|4(rNEAe&08?=rY8?)F?#V@?S;uciqmBnpY<_1|0jlau zg|rXeOh1AYb;!n%ENE*Q$3AB082~kbUiDzmRo;#@v0%t>)kK zooLV^p)5VxZ`hUXFlj!|PW`QDpF%1V7<`Atpt`L*9DPx2wllCy$F4VOguM}raP*3- z`8c&;&0+2L(oKNji9(=!Csi&vxS1ADevwFiQj~~F?NrSb{TEBD7GF#$MWv2tkuNXs zAA&JwtV%KggIMe4T{c{-T&W0$$wkA|?wEDoi8bVV<%5TB#sxD^4~>iRD2WI~ zA++?k9T;6r`TRUO0r`4)7{lO$M#&YAXM}Ep@_6ouMNw2WHB++_f}RXNfg)b`)mFBcBo3jQ1Cv89E#dH^j zi!bk|NT5jQw{Gv7-6G}qeZ%`rSAg{hA~}MOY1acx-0M@)_YKM|rg#sA+HAl>=?n5- z*%%z75UGpQ5>wAk-J9P(E&IGa^zGN2HwzrvecoK3i|I4`JXTvSc%Z&M%z?E%1mtvLSSUE9|_KBlmAvARFTy&oI)psyorg9HN`OIWfSB0Z0+n@*zc`4F1n=Za z{vCrDV7!&%YdBEK#Iw#Y7!B*zAA;O0o5>6}o)^_cvUs2YWw#y{wO(f`(|obk-0875 zRAmk-ix5W9oAi47k>C@vmn($-_?t%D1~O-97GWhvQYsgpLmWnd5c?Bur^>-t(x=nr zyJ^y0VXoU|3GL!#g<{!mGIP?{q}&BU;DxVNvOY*N)KODQ+YbZ$H`%=HZ>kl1^zw>7 zOn}tnqVrh%5498#@SKgJihy}k}l7jSd!j5kGm$IqAXtsGyi zKU%hSI-JOU9wzcjgm(%$3*}W`!PS-)6?m>4CeWjD2sZ&^R8xyz((vpHPfTkM#(6!| zCmCYo@UZN!)Y@AuG`ZVo&X@6SGM!6~w&htWAT8yg_V}R*N)EgPK^Qk%|>!nIOTx@JglgV^UBjY`c@PAr*T@eS=6P$be zSwITunlji&!J(8;bfQ+ieaOqSNU+pXm7bsJOX6sMd$ED*M>0rY$WovZX0y@kR*=qt zA9^E~N~e>WJx*M{%(%Cg{^fxG6igLSrE=ThqF@2yHOr=#C9E-hg||2P;!Ovp^WJ>l zE@Z`oRYx)yZ)b;Puofng#to^l-r$X$QngqPftsza2_#6i4K}?&zV%o5hee={_7gqo zj~Y^Eh&WGTOTobW{3jZUr39%$Srcy*@AvlcgFi!Z`m0QCj{|1ZLMT@xK_*%)7ALif z)y+2OxVRqtML_5^ANi6y8PEw}qNP@`>XuT~O8yu&t50gaEmV%sz1}hh73Nza*3iD9 z27Bi1uYR0o{ao&`*6FaX_a4pqXY7-QZ@&?&slYV5KkLEg1X9^VlyH-aIXlylI>|R( z@jQiD(P>%8gD3suAWYd`7hIA%47*@+Kix3bx_cGmTz;YJq-09av~I{x87HkjkMdoq zIda?;-uwE4c|JFIHk!v&YO{2kIsSP4d=Dj(kM8n($q}#aTFhqG zxU}k0@)0UX6`VMarC4l^*Hw=F)@92+QZR{MS!9jD{fD{_k!y-k@S*I{`73J0uu6dcRC&EK>lZX=9kA>CbL2a@~Fwm%jWDa>&aEpnY>(!~Ki z*FO*44Fh*!`94**xvGh&vK(WEyOU$dNxU=@5Tr_|mR)X7GK@o(c^_13S@dnR1Eklj zteT$hPqspIKi;m?qtd$~BaTiq1+B~`Grdf;2rpgROVVE4sBa|GxGY{X&MDsThkb}6 zb}`6|(z)6(TIxZ1nZwgi9UTT*iwiAijI8emie8&@E7WI9ooG0^KvgPNHDS`|$UIsXEZ)m5I{SmKA`tPp+bV17aaW?f`Ozk&>NGz6es~{n! z4J0Xu!(>ca_2c&@%pBEx(z2HMW&fROwF>ntyVVBEl97>-Sn4~J(e6kBnE$`Osl@F z;)A7)Eh4;UAe`m!zhCWLIsJs-sZWV7OJu48K75+|;xATeX>ws0BbcVDXt%r-)z91VKx3qObm-C5oIKa~I16xuGy$luw1oMwXNJ2$` zxbgo%k*alU2dh;qYD~oiMo3-o_N)bdzGw`Ra&JY1VK+Q16H||a4 zlcqGD*#hYG!=z?43k z+Wf(YY|Ylw0#88FE94Nw{R|xO6uowb$9Z-4Cj_ME6F4KyJ=3(EMs z3A1gn1q2}qI=MP!Fb==hc2c~cIxleqZ%`6}QP# zUVrErNvrgd>1s}oDd9J@dXSsO<)5xB-?X!D$M9tpqAHamXhYLfUsOpcnmoz8zPfDORiUyV~p|nqj?D&Ef`ewO(g9pn(LS+VB~7`)A^P)DtrsmvYx` z3ElW+)7nK?6-7pTm;QCG`zoojP_0~Y9BwdJc`C}ez-2`OrF>V|3(d>jQG0{U=lW5f zW9Y_2DOb+?@27LFS&JTjA$=xRU_J-JT36xM7#wLZIlRF4sGm5V*+08SDtF^eN1yq{ z{QdL2+-%rGwObtW1jbSr{R+Kb5Qe2we9in$W}!d5=U>I6(mNdQ4jFm!%o+B=5@*&| zt`nKsI3f-#HZEIQaHur8Cd%Y87S$!s2t~!jFhB0+kYPRVkE78MmcRw!hxVO>C8ea| zlSk@wmzFA02}uWk$~N@xxJT!g3d}}SKj>cnOQ_o`wBxf#g4+{iv_KRb)_X;7@5;}d z>81Jrg zydTEIyuA2*WPSgsQt2P2L>!F!{3`NX5P?QKY(+;yi_3qco+|-<7=Ri)`r&dV=m_*$ zB|;t}nTSxS`|F2pBuh;;$QE)9H~0{-CTwEW-{DE_k8?)ZwCC=Ah}BSRrm89&HRJ8g>6Ix&b!Uf=?bNIDwEQSRT~wb z*h`gfVn1bE7E9sD0L7~X0FBWIKaB+aEmUMQgX3w?@nFnHquwm4vq+lHr&2PG7kiHR zAOh1G#+TFSQ#Pb=23M-kxlS4>CcS+0RYNKQHrY7Wc8$X<2A|77QTv=Dhs*6!_b(`_ z{hBM1Ir>&EJ@(PkDAI+QKlHme6^u~+tb9$p z^G}(pZyCA}c$(GpfIVc(<|(|l=8wJla5){es5IG}BrH^D@TmT$8(a+<6ny7pLURBS zue~*OvDJXbZ**=I(#Vubdq`X5|5U=c@+jZd7Gt_c5_Z(`@M*jAXzr}wu(Mkgc zHFtNsWpO$2>&b&7e~&ZVO}vRZn=gBogON2wh6h8$q9bT_*r~f&Mh`LdP{f?ql+MW} zgBE9Ql;T%Jn&VwZM;~CK$G^&p4Ejx`Dgh?a3i6fNjTUKa?Kt5NQ*$})w`g^GCiSyf z&97^Bjj;$|k0l%__=T2zyWn{>y7K+#fi*nF<-lItZhc39CuQee9u=Ct+NK4EoKWds5z6}8d z-;g+bUf*#+lhtwim?3^P7`PkGQVRtwyrWS5@rp#?fpO-etjuT8naq8HZ%kB-2TR!4 zu%omnhZV)EJ_P1`c|V*kJk3{p^<_t3j-Sg|{->fn^ST$)J=W}Xc3~XRcPdKrp)o5e zt>OMe_>8kELTc&;cbP<+vP7p+9H#Ht`>WfxWR)_fl54e0#ry<(o>`MdI%LfPH=bp#-SXZn#ILiocz z6-cY(UkHvON3KGp;!namx(8D-2M=1J`d7l^&ryZOmPV2G!UZOc>L(d*W?Poln%mKB z*88NM-$mtI8CR?%MNvu8?sc}E^QLQaA*Delrt}21%|2cy*)VO4DF)Z7Fz1X|*!7@Jx`MH7h{pM@Ha0)|jluB|xN;GJH~6y9I*xooW>GIs6EU--9p`NH*H zvWUm|6osdXi5cr36HM2&cntDRm1E*<>C~C5KxN=WhsL&<%;ZZC${cYKNh@&XAyX8e#41p`POugMx7OLQl(+RCmZHBT+T|4)yDb5Ht6BnORLT#oLZ;pUV+Ve zaaFI=^T612ERk1UDe>*MKOQD#Qq@9*tsh8XHmAvEMx0oDCvBC~0&88KbNv`O6W$4)e`x;&=v@(Mg>T&sQ5v&v`@4h;ssNX;>64wfDSF1}f;Q$1~*FZ@Ut-dkt z`_p63G=S;8;0Xx@a$jFxJ}CKn720*SwOjLQ?D>iPJ#62;@-Atxfo>J+(|$U3*p8|9 zQ|lY>x{KvN@!Rq%v3W1#Q%(&H$&-sYU%X42W&weROF!51=Z>ImUS*4|EkAw6dXv$< zE%9i44|Fh^@^WchOouOe`?$=yPDUZ=XlIJxIlri!!;S_~j5KKEVu>-C!u6e<9Y!Sp zCUYm8$|E6vnwu8L5V!tB>A7I;_fgT8n@1KiINZuft_&G^kN3Q49d*PzlWTIl%Vb|9 z5d)rqa{kptAx(rjIe{phBZ0stcWfy{8VtYpU5Nyg%nUJ-r1{v66YP!}qbNupI)zUYfYLSXeOf8u+=fRF5Cy>GZt53+0$V z0DbyXe0LLpPNi%b^MicC4>{f21~zivN$~U`S|ax4+cGXW#O7N(VH_FC9RObv<1@sw8>U+qI~NwgaL zv5fkUe#{oszPMQ72P1J9>b6@=6@h+Xfb=9$5i|QbE`%JT{LXPq#6KemX)IfQRYHNQ zI`4HjSH#v!|83<_IVLpOuAROpnb~Q39$r((O9j8g-zmS0pC#gJ-A%^bqHni@e$jHn z@@%0D5D}Fe2x9jo*~ggY{NRxAxNp|_VSVlVVHFN&DEu;?W?+GDEcyd}$stpc%QY<` zq1imX+`e@T<>|(k(Z1V-Dl7QIf(oi1o-XV_{hw7)0{v>ZQJ)hhcV-M)ZJsq7gmJmH zrU=5j>(r#+Un>o%?BqYFq!rkQ@tF%cqad=l-BP)nEvoZAb@xytqFr7b*P2W*e#nB+ zZT~#LisX`wH}@o+toD_eyFF4?W=pPyojEp_tf|>y%$QUU%%`eYP%WQEn#!Oj#msy& z)5;PD;goiD3&pRS?q8p8dN|3OZl|lDnZ9=R3Q~Ixi^kNV#N6p7An-oE1kzm8llGu; z{pWII8RLY35~89w?!1Ji>(4U;+6@-hmI!j5=gmGH4lCDOPS^_liX|Q|A$PM1g*C>+ zB^cBL@U@2?SosB4A_YTHuJoEet6Bx_Ag z+jVa!s*y!yh?vj8Zn>s)Z2*2QWtz2G1LswbnpBA!2z|OhAzAx($d0o@xbJacg5-`+6vc;YJHfg#Z{&Bj4*R#qYCix{ zu6O<2+EQptW)1j3G7>O~WMNxJIp>@^9{$3T=2#TrMyRs3FV4_K>?w6JL!dg5$4y-o z>uK#_rX%JF)!&nV$8}o7tJHz;RCEjqc>iNpBm0OF@z2ji`yOpbXx4u?_xPeV@{STB z2-U0GSEQTE<<^E?%`nff|1~E%XZ?xZZ^|$#i50@{!{y<`ZPVkaz1{8dKw^VDz3_ z96Uu9p14!Ct0_dJa@%t6XKca9!2f+Lncl~Bv!nF^5p&FCGKceGv(s%Lc~W2A=j(&I z&2=F`v``lR_m*a|?LQ0I=fL83T8Do^&-P`MX<80ii|>tR+<%>~+0o&y!Dvyz;DIpr zJ|9t@LOY$;EPynA{3g+p^_Pv#DR&K58?R7$Bj?%JDLpSqR-_vVF-m1R85yTTpp{zl zq7(QQ-Q&|1Q0;QTX}Y=DehX-ZPF*jt5vb>KLZ*BiH_IqJXG;1LNxKl#QiDMm?bm3t zl3((^!TY?PCIdvlXTBcU9V9xdLmc}2fwn4R@fTzLMJe1bE*6Oe3B$C0Me+LnO7=^v z{)ac+KgV=!kDc5rC+hOkM(?5VRz`!|9MwoV#Vm{0;`}MaI981b3bP@Ld^WF6qtg+? zTR056O>hVywI3R_5GwJH9|-5JXu5Wk&ZqZ}nP_A%NE1P(Mb;5`9Cm<<t%lvc z2(txymslEv2siNuA$4Ums~3vV_r)q5j@&yQ0xbD-);hPgNWX!8w@c2ur}LQyX<#;A zo_1q-E}94qTVlndciy$Sq%UQJ(VpNO!+6+7)6o5=z#v4Q@V1nY!NV!CN;16`7eWEW zU_??!jpxOB9Y9PsD zo?8YxJy!Aj)cm>x96TPU!SajIV8p@YLIo^m7K1%urc|-8%qQ`0C4UY9t4dsH;*4#y4iLSOcZ-Jhvl|)nfrPh8v)$;ic_J|Ru|;g z@HB{rKY17Qe6hyjx?olDkq!b2*;M(qSu>_aQQ+ zb|wveyQ?X0St;dW74|w@)l81UhT1WW-m6^viF@-0`ZyJINvES~CvF)bn(Y&=(fVOB zpYMPIG6t9`F@zfuZs+T-K3`KtQIAwmu9xd?uiL%-?2dHR7}VVq8P{;FwQ&5 z?IPE7nm?Zgtro_JBsW^9yS+BoW0tabJ@PjiEa!UcnomSexA>lfC1Nq+nEpb5Y^S|u z^?APY#+D&jx4-Hx?pKRc&f4 z`+f&$O{K)?7JEaszlNf-YDBlizE?cHQ;dXjOG~21dZUs;P8yUd{JGg1e)8OlGW!cb zkiR2BS+!B?w6tc%pBTq!uX?$Yj(#S?=gB9K1ktwdAqT?RM2B`Q48E`F=1nzg$L0W4+{ zX{MfqvV#}*G#O{25ye7Ee4fu?hE%bSQy9=^q|V?lBIzYqVobS62TJzo*YQY45@Qe=>s@iruHF{VEhBll7zA zmdIcVa2PLUf;O$H%;2(SvDuJ99ZtNs_knKTFln5!T0U*YnO>^%BdCrnqO`;i1A!L- z$->n}YaaXgQt%H*AF^b|FYoIK+=$YwI0G_TtI zy2SWVw<1-F-$Q}-Z;-UkGWq&=)!ym%)ivEWJni{*lTfN zbGe>|OIc-(EY^^?iOPA0Z(H@4ZZL1Q$wZQHipxUtP9jcqlJedamub>5FNpXQpG z|9$VZ_gcSgsB5j0j{aZsi9)4i1c?`j?f2u}@ixXxwc=(q?v~$fb4rwr7v2 z3^G9|UlTBi@7=hq?i%cZRXggpIg%^g_>dr`BLOQmQ<;SX8I3Bv-B;Ih?3LLu(-Y_8 zI!)ra-fQK{QFo)@{_!5Ot|gd7*usL&KfLbY|GTcgePxS=o`~i0 zub9@VLPp+rle79UNjzw(02o9c%;;UN66szbv!z;;gHQY(!zz8DL!=FPaUyf(%SI}s zh7Pnb&e{!+J1YNQ@LRF#A&i z>w2ShbhVwAMqE`w|EH!1e6LR=;Ve)NMftdfPwr%T)H90Ng$O>!Xs1KtXu;y>9!}tZm)ZiV8Nq+1Kv9L*;^Vlh*($oi2oiCv`uYm z9aOZZ+qTKy-gjr-AmJ%n?*0y+-$iQRfk3;BQ3@a2P+l+k^N&a3@Vbp(x1iuHYz;_) za0yYU)S^HYD+)F3#&AK;`v-an$ULrZg(@j!iuuo?;Zm_@eo1@`3a^a+eY$PW^6efD zFk=BbGKF|^k&4Kr%u2nLO14BT08*b-*>}3O-*O488lg{LBvX%O*^s#;3^y2ZVM!m2ED_(d>Q97 zw3*n%S8Uw09p95Zcp5WX)VZglGcU1P%gSS%^NeG^y2U3oPcE?qQ&j_jj4*~DR@d%= zP8W#TcbF7#NbBIG*B8P0p>ObaH=N~noxbb$HO&l9WppvZ&CdIE$CJ57SIK^vq0tWy zP-qj%miPwhe^P{ z^XWGEevB%V(JhvyLHo7!u5x&>e5H^PxzcnSRSXVU`28E_%fmbQW8K!6`{T&;uSdHL zW{208d$G3qZCVThoZQc&X@XTzRVx@>VTKCkx}Y$*eI&?KGf^^-%r3fv4jNgMiO5gB zyASJ-0idJ$&2oH&sjcik7=^NB%WkVi!6}$VVjMJBWR$?@PoLp`QB-muf&A7$j%&Ih zkH0&Q+pk$L}|{wV1?d+Ngoz?ap6t3Aa4tf;W%k*Lpo_7jLW0haUp> zx}nxMA9OhG#|9-Jg6wHbOlfpkn7p~ZMI=@r31uHU5EnT>i_O%6Fp0i2Xz0gI8J&X~ z(neb(S}qSporz^QCWLbgmK;o$S-=9r{-RU~tr%1>{^}o)a&Yn(TJ5lS;0E7^K&Q7f z71-6>-_VaqJ*C~`_inKX+^WN*L%JkOBp>?g^F;Ae8XY43A|w#XJN|O3c<~@+=|A^V>uEdw5>2*VXKg za(^L>-RtOdxz1drtU}8^;etxiImS-OI|TlzW}~}5F)&fmFsZbCHz3laoDslU?(6s69yn)(|MMuR9BRq}9Y|cx2NOQOlNS8p{=M#heMy06Wp( ze^k*Vx_r;U*hZ2R_Q1i9napO7iM`=Tshb?d#|^_$A`ChW$riV-)(ofEtmAd4vUp)I zT!5y%pk0b^q^mz{Cdq$ib6>lr3jFrtz(PX!*Lj!i&NMfZs4Az5hR%0XW?9u36&=wGxU^jC05j*w2WpL*)(0xXYZ+!AGCi8zY1vfb*4D`S*R|;5`aF*bgL29q#zy2vwtF9p@c&AC8@Gs}wX@M8Xag zS1r!|shWFx5(%oK@BzhmXmDzj0wu^rA-n8@@;mQsJ;ul<65Gn8nz&_<#qL??N$h@N z=*W8>_;#jPyK$Q+s$#h9Iw+Q@P{`^kxJBNHbHS;awx(RRj=v zymB;tp_~Ed{b(Fl@*B8j^SgY8%fXh!7-_fH2m9Hx)#F;SLIlVTSB8tV@$GY>jh^P! zNZ;`9k-biWJVTv{29Dj0(J9lrrQH~rjc zppA=^ICJ7A*l3JKn+y+}ih!V>AcF9~#exeZ&$6OXlgj&2Ofo||X0-^4aPTJ8ia|u! zbQKS&3INhq5F%qJi>A;AMwgLU%mdo!e^)*$uF`AtRK7Xg+E@6XyAvP3f`iA2BjRB( z8)fwPfi%`=bP6k~uH=#niX{rwieS)2MZ(}|7-6H`7{NYFxHR+*Z2~9ii-<@u z-JB(g3XFO^?Sami0qtjtMV_~v^gDK}(6~)7wAICgU_u z3a@{69sfbeH5i|(Dh0Reuhz$eKM!7wZsS^|d$!(3DzD?UigPhc3q5xP5&qC&4?q*h z*wIiI1t8&%6`WAQ;Nk~x`Q2On{!3bV&pzWRo=X^+67DHd!n*qQ4T$P)>*j#4OFECc zYyz=ZDZuyx9=$+={<5$e*eHRl{w>r&BXFY;;~>IW6r~(iLSud~0=Ikj7yKPQwZ6By z3jCK*+=y~`4_*Yza5!kWR9@9Kvu382EKVKyRJsTEZ)~!c&RX?bOn*A>v&)T5a&*`p zkp{hs3}Nq1-uR`@c7?pgxqPMQpa};ZguHf&Lz};YYv#3;$`&@RM;6^qzkdty!UWSz zj6c2JsZJK#bODJ2D#S<2yVpgT;pth8@4of*-}vHe!yc#V!5PziPe0O8$ME<6@`oq? zYsXxbx2B30*$K9~ZME5KYNjGqd4GAp+paUYh3`keevbY7Xirk-XZc#UEZy_+a(NQ1 z)f`DH9DwDD1TDdZF?27~7=^^BDSMhFaRSMX(a*LPTqr1;7HHAVsWDxMgCGlnxo7o9 z!0hE>NgPNZ@+zea3!_a8x6Fon9Ab6&CoslYoy-)f888R56gZjo$>+2|sKXvl8lg`Z zrV#I^g>M@DLN>JznK$-jbTI-@BL;Dfpd*;MHTPpdZjNKO`71m9Sn{t2Xos&@coQmKz3B97289{e zdB~z-Z9Lu-id>~t-#{~n4F$1S&ZOP-9IbDkEm7s3U-yMhV&Som;*d#hOh#OdwN)q5 zB8irR1mU&~H*9sX>4G)yIKJGF_B@RS7hHk~_Gf>VF#5jyeAeyRXey>kGkgRCvgE?u zzrpCs!-_=&tZ8|dO=mWk&q!a3{={qoAg%_$I(XhS5S_7)15p`jIyuDLa61PP;p3ls zZL-P&jPC;SuiibDO3%fF_6EvhNK6KJkp)KX0$|{Wd0nx?4agyQ21RQ~2W+Kzhav=c zb(UoyM#VLhw`INS1x?(hIu=As)W@i0Ro+^4Y1D$Kp%lk=L@^|T>-s+2>%+!s|4vec+{V2CVIgCR^SsF0trX*rhe4E&`h*|0<67bOYXKh-)d;#-5_KgFNp*IoD&d@U_rp#r6!@#V7aIFx*wWKGP zzwdcUVZTz{i>A)7{1J)+#V8`#=|3$~13mg-Rt5I+J)OcH4Qa8&)@|AN_a$GMDQrDJ5w#+_!j*SCvD3+x}S`IjK5zTCOd)1P=p zc}mI}RFbNJT@{gVVN`2t%4vd+Qyj`9w{7o7U@nvX1ixk9pYQhDE@q?GM!&cC_18pw zeqrJw(S41&N*&miywiTxoG?pK{N$>$hcdQT>xkZO$08h!ZT!|N5Jv<*!}ouqJn$`~ zpxfYmdt{4z7Kg@?xBcQdb~Am$j#)(3$%Pn+|Dl75IfyWWvbOkhv92f5nu7S zFP{^H#Thg`u3u-Z>ktpHjjtarxaMjv|79)wOE%2j*HDvZT1y+1XILA~@GI8A)gCKU zV&n<#$vtnWcU;lfI8*`w9#ak%4xu{yWeySi%0KM$$P9&2DPSE1y~sunkf>$_U>6UL zJ`xq3pHoOl*nT4TeCXBTO{S@b;pBC<*E}X1f)NDP3x$*X*DF?kJdicW ze zkNR2uxvXpNx8W{Qo)_3j>h#=nIkhZ!*;=Q=e=u;moj;;Sk+?7V)yzTG?qt4r^o>5X z`jUjxV~(au9;3-{5*tfUJfk?IX>UsCK;5&W=HEa3(u#I!Ag1#e`2S! zIOtC}f#A{oy(hyEz&&YL5f8O`8df8QD^mq{6b-b@6Del4XY%me!nqX(a}9!sUI8(|%ar)= zPoCl;@h_4qvWZ%+I%mU!IR_=19Xw>>SYvR0P~hyhl|DfjFllz@yr0uqw^!Ci-;sgJ+=_QKl@ zwJ?4TD|KJ3w_g{-dKJJ61XZy8+#d^%mst$hjz%}PxndLGLm;8D7U5CyVB^mq`>BI; z6Gf8*MQ#BRMY#71^0EF~O;)#vZejC+vYqZD=e6^Z(dlEX88$XvOur-$1@UFXRDAX9 zPxpS)vI{CwyU&lKQ`as2^gE&Qh)rm>!bektnjtV=aGt)85#Q#!7Y|NI{`oN8Db%*rg_t zwvWNS#o$gM)dE!^i`TKki)HzW6v@K=Sl-$^jpwT3PpEdOK5;1)H|%uUSU}cJz34Z}2Bc50T&x+@-2}!ip@>k{EivxovogRe{TA zM{3a(M>fH9uR_Uxqgju6*S6g2dTC}H>IIw>4Xq$hHee%^lHshJUmTd>5pAf==4nxW_TkvZN|K_y;n^a}*&G z2)!>!OiPYp?4bjYfC(xO0%V#5Hj>%2)072N`fDRI1RvuTf`^I05a&CIQzO0_xS?Q$ zS)L*h(_iOv;{;Ibk}gD*yzvBkO%i>-30Fy_K$rqQB8foevey@Mbur{UtF z4gzu$eY8Rk2-^57$AKX?y#)en%rN|ZxMp4wsI&_NE|x#(egNs-&-%&n7%!Z zciKJLZ_QvS-HKErI7q!y$Z!VW9?h+=r~h97)QZ~jZx1#gf(1Ux{OKp2l=l<#XMRQY-I?6vY4bue+* zo`=7#=5-xM1iP-B{lz49-UUwtjW0vMicsL|m6eJ{yi!SwP+SGYAu{5*Akn)!m1L36 zdanoO1Pl0p4LW`{14%+lnpl65nZ~C4|Jj^R4fud<8Xg_RNigH%wqCAj6V7S;+n4^` zPajYCS2Q-0kL&IpG!_MU#pp6N@V2$CW^2LgzQ%LE{?oBAB(_LYYxil!U<{h@I>Lm3L8OmL}$=8dJX1dT$e z2pL}%#9+bAYk$1kqX?Yp_c9DNYDjD|^I*jASIGpT_LA(9b)s3c^3s^Pc8Lys*rvd; z7H(D$4>hWsPmrqd7G0%^v!C9_k>6S5Bg*UHGcaLA^5$x$0E%EjuqhW*;9y zP82Mqj;I;^rf#??4k5WjFw*WYf`RpB`D@78CV!o59TdX^fDnKLZvZ< zc^d3=wvC-vNdoD)hevmUO!&P$m3Rqn7_@<*aqn8k`)LfRS_s;Yp}%yWU=X?`MY2pJ zH}TPQE<897k^jL&Z{DoJSh;QCht75k*rHTt=m{{+A#I zZrqc#x3&0beJbF-7%^P zRIi7hoxwT$YhRR8{nsklqu}} z-Ff{S-Ae6SS8us?zkEh=cbXOt11Fs&<)DiGQK@IamZn6}vPG1e16uoJa!*%O_3qeij|1rGj zzl#3tUnhLs152Eh+rraUgbx>F)DcjC%vAVn!{>1i$vzXD?u3d|^0q*n20us_Sc59` zQeY5@u$v1@FuVKle%IyjwYY%v0YLEv-4XPlT?r*qPzIbPuxfw#;?K0 z6O{AmRgJ$H+B(@ z>Rj{A?A_hn-R~P@HSKgi3h@!7lSw9mX{J*8E?b^0ah1=srRpDMQzbLky#iL2TWPsw z6JcuoW8-G{Dghz3=}mFe7mH2b(zv$Keds5MZ2sI;cwPKx;r!f%;~;hz_mQw=2s^ zFBD{zr;$T~p8JePbSIQJ{4X6tF z;~QDfVP5;=@7{@O!H z`?B}4>4X=Npg_umFNt+hPK?r6km{e@isiv(r?%kVJes5^Bt)y5>%G?AT*1Gb*<8|Q zJ)~b8X6yIADr%0nGt=;!y>4Nay2tT9T2hQ}C(Y+-MCf8gaH`_rhB8pnJY)qGdgtSg zxDf-_Wwvar!xLB;sq6~WGy%3RLE?dH%(O`ZO(J?o0Z{S2T%wK;8X)7{tu%-LKaVFO z`R_%S!FbpG+FoD|)K$cWaZA`I@#G9SB(NqEZNQ5z>L0 zk)j%`DDH(>0(+%}^}pDh11M;1;y_&;zt|}jWsSe78(6VG0$-emoYEx-WqvDGq}hso zqnxoZqGXKR8@ODnp*&9S2gmUM)RRzZ2x%hOO8H?p~5VV_9FRZMM}lShd^i zn$>T#Ieez|?Q>Gm?lIBn1KflM#i)}(e)i$f^~$4kF)*kzb{z)Cm*wVXWqm6$yOash z^my8>+phie-p>T6YBTP6fwDL|&|pyU;UpSF4J$?J>Sp;L=CV#s+9X63!}#F)tvl2} z!>dKKew4Bc%F>I9o_7=q#Jk&Q;NSO!NAJ*ooRF^c_nu>Mml@tMWFLwus25{+&V6yq z=e!zkal0~B!gw%3eAL^HWcj&&Jw8%#YJum&`_-G}*W2FKVfCa%)L4j^H#P*3XwZ;s zTcp102f@F5$#^l5e%Atlosb8TI%MFpaK+|IT*DLr8lhm9HKDfj(gUiSG6I~*$*#}hi(5b5zjAVC!T zLQ;4*Zl;888}u|;GxddKMF+hUUNn&;vxsbLmO^yHRblG&fP$#i3|5Ox@ZXQactf*?HKKfyuNqqd4PP9yQ zpk>wjniw7Q5Z79|usdD7(QIf?>a3k^O_vkL3MwBpKs(pf8^_N1QSwBrbMb`2e*OpY zNq5nta3o_*Z;nY41`F5A2RA7HJ+pFKB;^u7mBA6t!eFbB>w={^CAwI+pa#9g0gkfn z=b3f;o*PZBz%j#bFna4{zhR;Bz(`e28YTA&TH)IcHmKh02LmlnWg&Id!KpAx^HEV1 zthvhZcEyjqo!gX$*RZ#6Geoz-jxlfMshlT75Cf(YX+Vg}PccVYNc!lVC?L|A$9GWG z@0ymv5j+dV>g|g8qGo*V9Gr@;7SKz~n1a~KH_k$zFC#tOFg+|XkZI15IxtW?+T?>Z z@#lZ0_F#T#dO|)M`n4*7bfk+5I#hI!ZO#^I?tea=A>V6#Ha_JDX16T?79skbNSV-# z@n|;Y3MCxTnjJHubcFc|8k)M|rd?0>4_6pA+Lx1h- zo2nG)`{~l&wwUX%lFQT8Y61sWqJq*7t44d>*DR|S&X`Es!?N(?H?`OgXBX?|bi zJ;*PGimUCwF$tY_363&wsZff(V;i0bQttHBYwFB;P3kK|8-Q#G5U>f!br=kW_!hh# ztdx~uCTQaA1Tg4YLGH3wQIsgZ~ z2WQ%f#u;HsB%{h$NP+^=>J10&6^ex1eCXONgSYn-ysveAd~;m7Rd)K%$9T~=9~6}%j^=F=({Eo*T)+Q$ zJl#5uWjXeB$3fF^QI$sE2MR7))is8$^r-lxqF-flFMo7B_>h`qV=wU)FHfv&=lID= z!Hm;Yp1%*{2yX~pWF~&S@e`kmhR{|uEhD&>xE|fF{icuZuHTK+%w(+gY(U+DoA7Vc zIUxpw1vJ7DB83n7%S2|H$2i%WzWmx=Vw@r{Q%UmQJ$qk&LnkZ_+WyJdmR*>Q`lU{T zU`0p|O&{l<2H&rv_eWvD;})%L^|^j*=)A=RG0$Y0SbM!XxwEPyl$j(Ri!COWRK(5KL#ZU(rs3uWhCo6 z+aY0icZxIwvgJ3CAkkphx3z)~owR-Y zd;Jv@8UP&(K<7BGJf=4uMM#bgAI@ai4%}de%S%&}!vU|(S@2e&kF=tUPmJkPl_rHR ziTe^V4n=qkry@7o#`p>?XvZZj%rb=XYdR}{62{5g_r>gG2)-VwP7Z;F$E%dpVrl`t^ZT|}U@=UDBh&=+S5oZgASjdo`sO-TbK;mih*rb};ibd} z!7Pb5%rq$9U4@PHm2dwviW1R?pb<-!)Zx@8HVlr7q=fZc6r(@$*7p`dON_@eFVfUE zV36_oY|rl6t`T5^r|e)jcxA*9{US_)_u&z<3sMIION%(F`)rK2=~x@d{%iDWj^)Y!Db~GflI5$OYgn|49WGh zbZl#xV6(&PLI(WcU?HcbRNN>s98i74wpxV^Q%Z>Eq6gt{fkQvybF@@RT#PhTRQVv2 z?a=EWfUT$pY{Se!OOl6MRe;kuHp@)|??Qrs`0E~e@tM^5oHQycRS@!@8lnP?9T>$a zgVj)x?fRhN;N@w1`w&?Kz0X8yDeKgSu}}=bD}B2HdjR`C>-6xebLZnt9w9A^;<9Q@ zjm&>{xBLE24w_v^T0sh2a0sOdSd&3N4e=!ubLBxYId9WL=&F;nb^%}`sXf~3b^AX5 zlmBBg`$ivjS8Ka|L?|E=5=)8yb;>Ng#rAz9v^L9MYNkFMaUQQ|xSq`hZt28S*5Aw-Ui*>Zqaqj)}u2S4b zW8qN|%kj_Wk7$;w*ZoJTd+|CMc3Vi!A=SKt5Gp5?kV6p?88)%ba)jA#$Zn_a0}#CK zQLnFTvm)tDHR&N+e%!D>fVmOykhu^STziJ0!49I)Y1_*k7kkga)JiAmG!ao~2=ZSh zfGUZQT;n*Rf?}j*c+{Mh4m0*927WD`9Ztaa!?+*1ZFX9@Ldtgr4bB!yoW;19MQARrS*|Lf|}$Xphy-F33V`aDYZ&VBv{t z8<$QMf0cZ)&y8b*2!;$%cma*FjuU!8$})KAFXp1dc|&M4x;B&YT|5M!vg5lSqRFVj z-kQM4K6SgpsadR$&(SK4y}(-HTq(mW(ft_a*X1{RD~dZKOcNN6tl>^$UM8fDM4#n> zI3lN*C)?GUC7S4{a`imJm#%nsiv2@Y3}+*E%>6`<=oSjP1lu9eF}U^nEbiyMiR`*; zPt9ruAu22esdzWL(YX zv=9-3lmJ2zd6V|YUAynO;A1O;zhpAS4E@4%HWb6>Q}OU4$yDd@Eb6fV&B0MMo?3z{ z{|Gce1TA|mo$z^xRv$L6BSFDI|zBa21ShVC{n6;!h3>=3GG zhB)7TtJCMV(G-+jP}e2zf06mJCwGyO?&w?Um6!N$x(szH@gIj8T*-{R_UG|DOZP&R zk0%D-nl-=M)WHf^8NPm`q%@($XOkkZH-{J*H?=~xZMZFlgMl!=(6dEY_d~WD@h>&R zrw3Cfc-L;Izvst@SV@6TNjZ+rag%f}Ag!y{i1EwqiDK9x)0>zv1Ay9wOzVJj>h+8t z_jaM$P!}bm3^_@mK<;;wt^xk*kIiBob+?(jGEW<-Qm_N~MO5{u);N=y@e!~OrC)a*{ z90<2<3UD7*MK@YUQZy%f6nAu(84wE?`WD-tYRA5BoabTrM9;eMbwlFx) zhvt)9syimO^?aNT)SLf$9dma++RSLSP?k|Qor8m%2B_dh>{4;{$}2w<#eA|Xwd}1j z0ELB4L~+Cj4sk)~dVB1iu(B9<5!|l9bW6e5A$qDIL{$*{i-3f5{GZN9Ul#_992B;Y z$0E)pYm5Z$8^ll6FdM&e)D1%82kR^3x*DF21eZ+$_hc%Jxb+9S#FfpNy zs`h==q0{p+rGne`?Sha(B7usrQIepZsQi({@o&{4>$}5zrZAzb$4d9dhb_ya|Bohu zNT6e5x_+P;-5UlHaq^AT+@BSSa1N3$`QSK);+(kz%pkjnNK&0d-%sQ;TjN!RQ6p>y zZTx|bVIo*P#ZbagV5wf`?~3249ChA1Jz5;uoKMd(M-D|jFwf>B3ZCc3wG5wC=|N^} z$_#K!dT6xSAtE4~_WQt@6^&mfL9=pB#NK{-3t%VCw%<UQ$>lmJE3F@(y$ z?&fH{>&&T?l$Fr|5HXvI8>4gANOu@1evyr=Fn&i1i$o^e!r0Neaz98dMq=>pe=O0u z50wpfxi89Xb_zYcZ~NQVOt-6|z_wY+)XIwutlX3fRFKpBVurS+*Z!Syn~5QQNT!QkvdstSiJ(1F!s)aI zd2Z}lvqpzSQ!&?M^g8_cDrhc}@l;a68ixg?+K+Y+osze5v7qyK=V7BR*No^#Df9AW zm>X1{BWU;UqBotfgB=MVbMi>zI4cUOstX+6`lp>){Q)1XM*bNetuwS0| zKcLob&`9f$ya(xPm`4LZrOX(W9$Lh+A)Z2Ec_%cFNJFuozWyRnqO73%5)@0w%s3+c z&S$E0&bKQW6K3cG*$G4PHcq5yQNYCH9*=R4yc;-uJ;kDkoH z2*c7-s@Nle$AN&>@HM$ul*do+=UC$8Jp2bSR$(Qz2yN1<_FnnmH|*Z%Noj#dL=rdu z<|$}57sv^Uh$%PV`(uK=F*%PMvPhj+f%$%z@W#W*AVu|0+sGax8ejNpk+$3TMRTQZ z|1MEcLmF@Yk%&Zz0rlEX`;}~~Iqmf5MJ7nrNBC~P`F)Js2~7%pwwB_vk%~*XdZn-S z7La#_IbnA8UwAt-6HQFGjrof4L1-lWxwoKn?VqGqg_ttl>o$V7gdPl3casU#I1IMa zl)=)&p(2SRmf3xU=dHXz}fF1O`1JYAAJs4O)Oj}B(;Avy7jrPZu=Lmw<``ZtF~#g$$G4~Jem z*sks9Pv6z1>HF8mbB>ictI)SMI3R$mL19MRbVbrdG)0fKmR8_KBK``D+a&gGKbHBg z^FEIMpFq^9-+*VgOCjMUUPWJTUH{Yi9BDKnFf7-Bn;>T73?Yls*J{gOxK7bcCaes5 z2W5ho9E;5a)(a)d;nuo(AN~CU9Y01!+mL=;?`Hm{L@vpI#D6*9J2|B0SVquA6GSH4 zo+u7LJzR}u{WElc;KM|V1JZ!usB>m-DQRGYj`GYN8=VL3H?7zCzjrD<3gGbeUWxW^ z^&dH~zx%%P2yfn=-ksVv#DJViBvMctw97_ereyf$b^~A6HGj3caHvJH6`wEXK8#P+ zKju6>NI#Yj)f6P=BOj)!_mS%ncCo?Z=nZ2~{_CLB06;y91D_rAZV@Lto&+1$Jecqgar}tN1W5l1a{l?%HvsK5QQF` z--#h+zbk4Q&R*%klssIGn}|#RTwbyM&kJxYgV&0p)nd!$Z24)>Mr8RjMa^s}mFT0* zcAn&zex7$RA}RQZ(#{#&=mD9QAaJ0Dnh*8B5(tNHQ_dP<4Nc*el$JKLCs^@|m_j!( zA)ayD{(71j^}ef|>pA?j5PJ~mdDq>`(sg#JW$>vcZjC!76A4fm1tiKKg-BFF@qqnG z>rSI+GS7(UjzT^(p%Hh70SX#u_kH(O5*Sp8GSVgt5U~zt{1QU1pNN8C zN;d9H`+v2u>yUEbtr7QFQ%z6YKIbs1+5ur?QM(vuxDm_>)L9!Tw1h7{e?d(%KP z?~hU2**}D$Bg9FQP9XTbKkwJzI?1fLwjXx`k!CFxI*~~rCH%NS1$$=7?q{G^|B0N7 zolNAt1OW*b3TU87L#`@=Y(!(lf|eG7t>Q(@X=}V)_V~mCy>BIGGBPx*EcMSQu+^V> zjJCJxUlS-*X>NKWY%oi6LYrL1Ih!jG_RdXB%c8U0hW8D+K@3x{-r|%pZ@d|n?*mHy zT(%1FzYpG+c>D9;z7uouv!J;F;R+5T>o`cSs{li`P!bm-LsJ#v2zUt89ZesRG7!R*unaDK z0KoQRkO|erPWNhW;dG6E3D@??#V;Ce=?7qc7w$MY6j-Z=YLENrAC31 zQR0X6SWO1_!e!4L7QerFHSjS%U9}Ow2ymU)uDfZ-E@``0+OpN^EUQLc`(GHk=uIiC zk}{;k;qsMe?2jNHkzD*4@1>z|pqKpbyDzQQnQ$&lNpmGUmegOlHU)mY;1xTM0$ft3 z1wTuH`Al4juU$XGOtQ?S7df{X9a1b17lk?0o~%WaMB~?4P3N2&OmFfb)XnuZ+8f3& z0xcw0_MvwdM*y50TvOLbdKA37f%utwHm4<|=xEdc?0f@W{o`a>Cw#jE4mx*c`%rC$=iwYa5eZYYKA zGwb}*T5L1O7bx(H{c`SRzvhr$Ku{SKQDPnLxm)PT%6w1;ccqwnWvg|Ql=M6COA_w9 z?fS$F;ln}$pZ!~#Gajrq4^0|{{yGrR7a(pFQ3%PwO0~b;kdHdoz<4U$Y$zT+D8Z{N-2HN6@h-ei3Z13V~br-&h1b}oxn|4{%>9t@c zQfEUFirA|`F_nrD-sez{EtN|Z9~b+4cAp#Cb|3q{@g5cNz8IG@Q!v!#)C8KYUv!?Q zp8Ca9!bm@Q3?46Pm`3N0@%fg_s@nPsAwV}1vLvz#>hNc zo`MiR!I;V}s+DspGqdMmEu@;cr+wG2!`*t>rk=-iZVvUspmRV6x**3a%crTPE{ zAHqZw=-cPt-bF*_?LfYfIL<~m`Ct9r81vvLfa5m@B@#C3HbW6IglfT?W~N_u z{!(3co)@az-fk2IE$=-X0J6;D)>KWo3U_`J-%MA-=S144nQS`aIQG!f__N3p1@$7~ z3{=Uni8CV&v}E<$06iV-uNG9&BwmIK)EMY28f!u>qHe@6(2EbMsuEG;bcU3fA4tF)hm=HWz{snOLUSjI zfDFyT;e2YIY*rT3KKdsGJB32{!$xWO-O0&`-5{42jLO;et(SMBtTHbt4a_Dn!)E#; zRxkko2IHqF&IT+_Xa%Xz4n_Ro+3A-I zeFS=2G`T|p=xprzrsgx)SX#Tzs^o-SDKl5+adhUBJJp0<$ z-Z=0oU{NFFTtId15ef-M;rf0SX|LscjE7K~xt#Qt%718Q6lPhP4=Es}#j(3=mlq_E zs9vB(Ao|l&6@c4O_v%E3t`ppFyv^=mNa~C?Cefo16{q)NDfC9nNLpBjSrt=#5}>K6 zdc@(?U`~3m`DK`Q+%;dS%@Mw1HM{?uRCZDgpu6P8sAik?X5iz`ai+H^CO-*)T zMY27i39RKWZv2z4PItaZ)^HxwpB%c-a8C4#ILvx0Hj7pFxK97N%EnSfg8CiyFBRrN zp<>bOUzj1G5J*(W^SN8eRac;NG08czp~RRW>c|Ehi7)ZIhTq%Kc-Q>9s*l1pMLpLSz_{O(*8Xi?GU#;bjRU zrY)K5nBp1fR-FT#5U?p4`>fyWzmUO;X=%K^rC#agX%Zn_8jK)RaTWVP z5ic~=FdBjaH=1g0vkfEQ{RG1{I|of%AjtPX&|h>x_j;L@vxVtKXUyTSJ#=p(m!XU> zYE4Y_h%ivh2;}7gxiaFOb&SRBG$P0e@v;9+*9W}t2E0z{$kMVRtJ$1|~ikku5tW)qY17r3xl1af-? zzu2z2ej8maw4gajATZFnl#P%oM(k<3+t_qs3xiByFqFK}EefIV)b$qta;4pw^Wpjj z8i|@52gm6vBfMA86SVjEMN1P9of0w+$EuxIL|!t(4gP&rJQ+Jl4A9BEKgHWFcwNE(S9x4K=ryd(J1j#DX_ZwR81;{+i6CTy~H%kz6)Jm4ptu%|Ch=y13i$KDu!66 z2|0ZpCV~Qkd7g|1H2<34u01sTeL!L9Wn-$~26*!4k;i56Q~}7ULi&m~f`J!X7z>3L zi8|;4adTUeTwEp*KhZGS1lElq_R^BI6qRqKuG2gKRt&?c>8$K#%347t6A12A$~g7+ zzQY9num@exiTK_Sg2mu?`aRh~$mC7deGzPw(y}N6Qw64>IMGKp{VT5O?A#oNMqFp- z1)J5>!kej_f9y4W%l)tD$hlXd1{bFPnM#qhz$t4f`m`gJd>%mJlq;s|BSL^-X}YvhRRM5C)5L3S^VR$T=<$q#PgEMugQr2~j3)JE;!TtpyQX)P%;d_G4ABnCcDdxg9Sb~qJoeWAa|Davz zWc4$Jh1M_sYoEyBfi>xcFrSpS8rM1uWH*oMDxz47D0w*{dh!tZpwmc;#{{b}&5gjw zP!|pGU_u7Aw4o&mO;9*FR@&WkOZ}a+|Mdcu)ojh{JXV8<-E5B#Ir!~4*M*lDg}5_} zBh5k-@*Bo?2t1dY&Bq8)B<5_U;gD$-`!18ex6oIE$LSBjg1;}D_ns1s{|k+$(Z0X) ziu6ixqhhjY^T_3BT3Z#Fp6w)I|uM8k_oj2e`>@HQHCLVQq?Kn|<+}QRG zMv%e<@Kx9Mr)Dzo;VIOQmOIVC&(h~RQe>3>90>R2-RCVGGFFLPmP$pXC{d*zWuBJ zZ2vWVR`SY5&j5fk*i#&03N>O0JLmMY_;K)u zFcu+2mgMeu_4WGOcmAFZZ|9nm0b2OpkMTeIwQP}3!eVZG!i+^nG_fGFx4534S0Xdh zsr})KuqnC28Sk9NYTLX#jAaZa!pAkW+zBZHOk|eF8aNuvbeFu_965L3 z4lSQa?>^7yhS2=DJV&L-X$6qpU%rIT=6OMQ!ioY1p%CGLp`r#2hR{_S)je;Ea%s6` z<*5>C*gx}l;Ehh7|IUyKzx6mu@xhqJg_!4wV#78`5vJ>DdU*y1EuSyyxAT@sBC$`y zthQSZ%;lpnR{fN^bszqnjlp4_l59#KHr@aj_i7c=?Vg_b2za2h*Q#fXTlb=i)+5^k zJ3~l9X`9587luO6WJ*dwp$5r>wlYcl@$I^CS12(A0Hp6TW_y7CaRxfSvCjs6>|JSrD3Y`=#*&W|g&K-cE>##@(v{K0mjEKnH?>UD1opWHVSUS`tRp!U z^kCi*m;w5bTb{uyGQ~Cf`nkD!$xR9%A(DmM-HHxHkv%;Dd6O|2w;>Vb`EVfUP`lxw z(UO4MVmT8$)uZ*FAffY>RzVA5rY<}Ee>uBjeY}UYkE=gd#qs2RXHw|25*9q}LWjMI z;5hkxT*~<3Cp}N6;Gyc@aCq1|%Ue8NxaluB3%QSv&9*)|tmtw2uRfgk`ZrDGrRXe2 z??pH+Amx=;E=MA=z!88sWS8+8La+`7GwlBON#p;#nSxIu5hYc0E-C#0P;TzO`m&G^iJfV5OU#Y$av)8Rn@a=Mr+$QISz`*d;N%Rw}?g&kDaSezY*|z3>6>;bt=x! zs22p zU2mDwdN#NH0b&|#covtzZ$usE5397!>rK5EH zBK)X{Ndz2Oa2KZ)ZI0fOWahe=XIV=DiRr9yg;jRWx|uQ=(mF~Dl4PJ++V0y%+*2(E zFEaxtHp;RpF(0cVgeSibG%|Ep3ti~(gQhRC{4%1{2r!C_aEClK{b29Vlx5F}*%4IN z)n*&HlKvoLFCZ~!zsnl*S^buQ8|dE@?eIsW5^C(EOJA!_*m~WV@wE2S{ye9<%~`;H zVJv|=K}XPL^hf&;4x+6D^MyI@HCDwV^9ba4x*1W9VuXM1`@Jd z#ZuvV6mMfR2+Rz61wI%;5Yo*vOd(CH@X<8_{R~(lnpqthYG#Ss1&v#sYApxlzi6fA zN{Wmqcp7*QJ8Ue~_t~~t?!LGaGw^zz7Ic>?rXk}#g@MKH=hk~)gt-?z5p3ikU}jK! zMUgJOm&ClI!QajS{bb1r(j`p8nso+z0t5;26Y3JHS;hLQWc&R%^@M(>MbUIYybYjp1${Z7#`*q32=ZA)Z3{No;xQ6;@2;D9rAUl~nCXnWAVi3$ z<@CyI^+;#FVUrS;wNzT_E`0Qd)n^uD+&qe@l+aCaNvVR(L2eZMjIuE}QOxH)vmSD3 zy%0%IWPzgD?Ici@ll8rb&a*za;)7f+hEQ#zmV|6yvoqX&FMP3duP}cMZhCZN*8dvV zUwY_TOoX<2D>oYZyOngl@8eY2%G2O|{6<<@%c#7ON1HklvPwae3M$QYfF=+|wu#Qw zk!c)maKGwd!rIIv!v%u+rOZ6b{f0}|^!gqW5G13LMCj&qrPH@V6%6|+JH&(?N-vf? zAl{9gzMO24{EKJKi_GeX3^oiM8?ov~R}Od8MWxPZi}AeE-=kH|lciVgTD9l?u*0Q| zfZ~x?m$B>7H`v;458u6Jq#SHmM!T!{^v9kp1f4p#fy)lW3m~W32Q=y!5hrj$b-2c4 zG#2~cV;Cfuk`RCCP4!W7fbB&{v47KbTIXBq{2BG_sBUsN#@}rd+(2{vcLSv<0&+&U z3DSrBiNjrMvA#R5)qo_n#D!~4q1fU800ImNa~-r4eeEgloPD|N*Lz%<&oI5reKZZt z^KS_M^azTs`Qd}XkiMO((GqY@M)IzIc#aysFZY!>9`6IHG@NsQtMO6+rpBb`n!&0p z`O)-Whb(V)#*6D($2K#&f;Pb|-_p1w3$BHf^6UNUW&PXcqeyFcX05QG)f)5kW}ffk z6}g0QDhm7`3v7+Jv;$4ZT4$A4gm)yV%*STn?OA}O|L4l?!9hD=|qotFW zFsh=X0ju=oAi>vEvYFpnTi31Ond16wdbTnyA%X;!kKj}vr3d^x6PB|u zjJHB14TeWypJp&_Rz^2P7_(iJzEqNKmebHW-$@bPQA{_702kJaNM2^86|1L}Y02E; zD4nn=l0=&KB>!D2rJ0z=e;&LgPdw&AwUS)^?IyqyWt z<7bV74%zWn^wrR00K`tQ1-+#xl7d5|J@kikGW(CU27=vj13$qlb%^qw8Sp5}W!Mef zZ;GBHQu0h z=ww=~qt~@xiPi?IXT84z;ezs;t=xN&S#0jMeI4ua^?Q#aL{j79^Re!`SYGYj-~BH; zA87@gU z`XI&iI2fZ$qz$mNB188HM^D{H%TrqkS6dn9nzbhy{|#Z%O&7bMsYxu45R-mz=dkMZ z^Ymr>378n?vO+Z{> z)V+99fp2G`y%qHuC2Yv|jE^1;l2CGk9(>Rk$Fn^wMsl&bq;{*0$s0#qf$pFhGvl}( zi%R-MvPQXg__&o89gIoT546KJrw>C=}_VOsC)Ss_PUFzy9!6yg-({o=42GEN;k;`o|(A2JJ-7Z*>-zmG7z5 zS|-EMzh@67fG_?By!~9kR?0>zyX TBoGvU4RHs+wN7#)=+0YIV)rSjS#lbe`MJ&aP}y{G;_n{g%m{21b+fVe%2$!?&b z>Je`AXS$ALfS9GoEWqe7B^2p5LV)`l0>{2!k}I>UnyJiu`VU0gnz&lNbP22@wL+-c z*^?%hKnCt%F54f3l)#ZuK&Mx`kCor%8NWDkd#K{B+rxfM}iFcrQ_xM%z)K5FA|Lk6`*TLtWjQZnb4;x zvUvi{v>wFf2g|hd`f|~*eXqZ_@6~4cTrd@g<2g52=D)XEK;K;YADrjs=UXJap4Xhy zz9!8la>nT!6e(32$N?mX3KR$sUZiWI<0?HuA(fopt4_o`*DO02A5v}e@#zbK7jAD) zj+Uj7L4?7Ep%^~G9UDCRXAGn;A5R^5QkVLwJeq@uiivCYx1wn;;`eSnop#+G;++hk zK$nAkYpY0*|DrZ&aKI<u?uZVwedHy{sgd#KC*bi`77-D41f+>7x}!7@$MJ<_k=G z+~9dw^}Tgo?)CK71U2YPqA)A6neT-a?krLH5cLxm9OqsfrtKAQG{iKIEd^oME z&_byY!%(x}6%QOFsSsFV!$`GOFAA7HWAhmBw3_CD{Xn~Z)(fwkx|h~}e%|5n1d!#DDyy->WCs(srnpHV6K$|P)Yh|FEPY%wSCmAF)Z}^ zJ0!has#j%IZ0u-1r08MD~mK?$q2mXHncV_&q8F-0bWmq=qt zy7|a~5>8wNmYg~J_HH4JfBxlYH!Aq!;PrqUck*NaVFf`{Bg*_MQ_{ATo~z1lah((~ zT*N(0_!fr!2ZO&4xyr2ledqR?xkfG7=tHY0N!t&#^bGsB!oGa6aLr1O3ZOlT@Q5G}3p)M7;#*nb7!PkX;Ur;83mS`GwS zmZHM+#g2^Wcf^?k#W&JH2mMJQJH~7a;fI(ArY2SQ--!>(%S!9>?qA~JD~ph0F`wW? zZx6!`Wp6e5ck7TfGP1oi`!L|s(391MUFOP^4%?1g-qZb*R)WP(heOzn@L@szg>P^WQ*4m{w>%j6|{MHg#%!vjL4x?)gYr4 zfD`I^3}5eV!dDI_r8UJ@J|Y+%g<0*prQ~yYB=(L!ebAB7(i6-iT<_-be9phr3*vk4 zU;i+Ali}pE0>)c4Wg^qtK@7BIAG1Cz6mfL0YKB+}(!8sfaGc{X4b9kvlM|nn&?*wr z(dHu44+5e`%+Bz#CndJ0p&jwKnbMvKcLh0H9s8&?&X6AHgGK@&S%OIgy|(Ay(50f! zVP@ljVN$C=l^l=T(CWG;kGaFidGUAw5{{KO6PGMb zjw#337#x&u8=|yI1SgAq(^Hi`f6v<16iu`2{*=G`D~XTk-*DCX-mhg@Rj@t>T-SXT zw8}Q+bR5}%(DY{p`+fdMyD5#;!3d&Lt`6wbp@uar&i%q@t}C&q`eoI<sUX0%zFEr%uUCnMaR%|> zxc^+WaXAJ{@4lK%-MzNg=`4m@9ZFQuHM$iV6_O)`F+HDJ8Dy9o9Ui%P1>4g;3L-@R7O_)s1D%(BvREFMc3wJ;Z+va9~ z8frJNvK8ZP2cKm_zh*>rU~+1QW0HLhp>gAjIBB)Z?QHw@)oF!_a}ovz@PQFEKJo`I z=$SC{(d7++H>U?moP{D(Np%URk6f9G6zM4T0|P8T`M0ZLJNE12hVb?pp`J1&*kron zsz#)(Qhg)P>~F`ztq5oi{Ubgl*i$_iR^7;v|w}HNYaYTF-6k z7O<38<+dZV;=wsvwWDG(Vl$CMsPw+FxY~oxrCY+)Hi-`?U5)hsW6nM0gBg1?T+LYJJqSo< zgnDJ)IM*`t=t57pO}v>cVwHd#v(K)&FHPd2Niv0#qqsrOLvdlNnP08Pbh>WVth*j^ zJB}asm7rab|HPogp0lxFW}Y)O;ENHAJB@j2{g8APfPu>XeqB$wJFuLuRFM6T9?Z9Z zi9U8kh*HDH$DbClS>0Lrx+{+X7-UjYW5{)7KrO|rmlP|>AyqYQSa?l@i1$53oY=i7 zBK|b`Rn|sI@e{4hf+5uO6kS6C9RZe{l{V|T;8y{fXZ;=wG9cg!qmcY4<88rmEV{-Hitf(g9!!q2Nm#58g*S|md{(>u&ZgJDeB04*(XGsSe3zwBY zjU+mCX!y1v>EAA#!R7NRFi{k#dFC@&U>Pmh44)p~YQiS2ONoRHAE-|D z-^&Yneu z+2#F{0!4pf7AbDB?x%pm8Z{UeB}OHpMJBBWkZoMdU_M&@NO)1pWe{WfApwta^m^3_ zGJKY|oxw_y``pm#4Sp3kJg8gskmN;WuPPMz71S%riSs8Nv>U;T7+J7>I{4rcbc+3rx$ zMlzQp#wBV&qM(qS`0n0O`^71>T>LM`avxQps1X-bD22qo=2OSZj~7Oz1;TvIowd$_ z{?~gELw~^s`W?It%Slevn9wq*%fUd#WGbuD0J4a(-gmc>w=lEv-?s%l*X3Tr-Jadu z!M5wSle=Ivvgv=&$OlHBkZX1M)5F6=9c6GF}iJySDkMDG-5 z(eY1ByD_P!=(KJ3GdFm%KzY#N9^G;0gjS7p5jifH_20hk#)Z+%ypV;b675}H2@H!| z^i3SULaUGRDGN84?JAK?T*fJk3LZABqVXxwxtr>uyGf}?p>p^N`pA%TN{k`>fKSn! z-$8@b&%xmEqC-lDq|fIJ)z-`$9AP3ZG9iOk!qgcZUPU;{e}6j$F|=U-g+A? zCU2w-FD~1_R3k4{4B2h1d`3;KrB4|3QDAzN9TTM{j4bvpY|||}kPYdlX8p@}c#OGz zd3mbZVFYpgeAb?C8V2vme*?+i(pNA+LMOjRcCSf^#>F6&(3k*jg*q~skAHhvZ@zwf zLl0NVEnjDtzEK{xuZlDkw8b0a6wa!y;P5vqIw+vrgVE2aHZI9Pu+oJ=97`ZZE0}%3 z1aN0$yeB(^{mVB|G2ir!N}CG7dBy+YRSDgN5plZG7W6(zOWNCC3X4czW}$Q2Htogo zvU{en0G9xD^cRY|BGi1R1GYhlkUMF>YTcIt;j0WxM2iCn^o#+U5qf6TFBcrSZAAXYAV{GFl4St$R_;!Sc977 zCPp|wc-eel06IWXwSOQgi3g(Nho1HC(}d*EV28#(g}b4QH(Ehj7v>bzBn=8%uZ@#_ z2J{82)Z36Q4I`GhYKrV@3oDo@M;ETcNarpcliW_c8wR?O9J9GKK7KFZG}N%)u>^)v8x;aM7LI=qOOVxQZ*7VxJE}H< zvaC8CX~Kqr3L6dHY=l+Mu4)f|qo`MBQSJFNZ=jNgH5n)(wd&ca)@j`;ZjiRmUojhR zEQ-^42Xvkc4K0fnORkBBKv^8dN5|~s`1%9he8zZ|Q=lW=JxFB!j$L&BrvG`3mJ}rg zhSM=Tj>o^2?p z^RM}=N1mOnaX5UyL#a8ylZq1K$-+XLX4>8X)2UpFg~xKNjZ@sCa71a={7#umz+1k~ z^QrmT|1Hks$No0~Q@^glG0d+9kAV{{b(Nmsn;y9?GdSN2-_?vLqQzNzMaf*cnWi^M z>e*D~VTaj-Ih2}f!TcY9F45b$b9~T0)Mka{4`u|5`^4|vT0*~@KRs9557`&prCo3g z+f2-&sbZ|%*!-mo0rwu+N^r551Xd^~e=9p*o^d1Fs|-44%2VTKC#s#FEZ0HEgxaX3 z5D7Aa4N6+EDTX0yKEfeYjZA6i&14lY18yl^Mjg*x@nBy4BCY{L{ zopgl;)b@Jr4as3;k`d=&x0WUZKuoL19>r)R&6;vXs2RkNi$bZ#C!@5wOVd}yXV2ZnZ^9_>v3HT+u6hUQfc z`tI558xsG%6?!xee-6xUpr-8wxBK{m5m_la?%1MQ*uR+5Mj5El!`Ds3rl@g0gdL_{ zvy$zoj8Y#VPa?s^_pm8}@av*bp^L*cism305-gu?=C5rsPCOLfeYqfFm@UJ=>=+Iu zVv7iOXytmtU67bWWLupcx2Ln*9HVayc=u-XzpPj3qZv0l*;F&Fv=H#yH9t~nnkqs7 z2VUm~>B^o7+monVRs$;SUb@;W*R1oG;f^EOkaS~yanbM`Vx~(-YK2hK`}?A>0@9oP zEMyJ<-vkGF9|C#Tnt2fPH1Nn1CvH@SPg0dSTJnaBn;Aw|*MIAUa7F1HRDT*6p1dQ? z8O1qD@ybHL+4))u95zxj=MRSoX&`{0#Iel5lFKAdG?CE~izXmrC&4(xd>%k#k5PuP z)+HaeG(iNSELh$M;wVjx3N(%u>>D>|L~X%~0Y-}nO#k6x$$^B?2T{`KiZ0S?;aajw zE1&Ih?kX@*_I{R@pUq9otYzrY1S<1#a|=}1!O^)fF>^ccr{_{2MD8wE+3Uxe>gD3| zGzeGjTOVdkPS4SI`_Cudbn15Q@BX%l#Yl=W_w{`;q(*gar)nE6MMlTl!CfeoDGWmx z^i-inq56%RbH`~8a8Q-JWZIYF748*E*Crx>&WYhogJs(x)V-F$`uhW4rNO)TOuE zYiEG5ZjI@Uu9B6^)>C`uXa8Y7zV64rTsP(%+$iY8glaH3VI-NS;sX&O=$>gI^euUH z`_IT`H4p5Pe~ZQc^!)sl_^)>4y`d$}OCTELRzn8^MUPGkpcnzJWEs6Jn3u@3l3Qy} zGo(!3-jsoDVT?Uw?BMUnblKBmSuR;|ma9Kug*e?I>6|Ow0kGPSb!xMo* zzCC)KdCmv6_o%43%ahQ8dvKxuF0smZJN((SbATpVL@+|)$CMo#@|{=P(8?` zUf2Ai`*P#0IV6BoQ496bE4vBSuF?!tnsq;USFL?G?q3wVJhQa(GvWDLfk6(l z&zdwJ`N-lhAqRBy=isR^3c5HE(KhG^+%1PA)c)|a+Nd*Zy=iKa;-dbB3@0YVX}18e z5be)zDrg?H5d-@@A}B3FRMZ2pkO=DKANk&h&J`wo{{;pmlC2&bZ}fEAX=WBYqlf|JS{7Sdq4$=@ke&H@BS!)?(5pbRht@67c%Yo^OCi zkrkemZfgiq7YUG2pWEO5h%@k#SXW zb@P(|8sfv)&ColHX?h+eY%t7DCF7w~Wr&<4DcD|DOPV5?KmYaxpWav)sMbYBgj(ES z1-7nO_m(I}Rg-hg>Q>${8fi(0j!=P~APts~!)|)K04lI{`+WO^ z40B>ukK7}f>p->KJSPqO_tH83(s%5%q1l&@39}F#S{R#8K4*K^lq7WdRjOKoUPp16*dsC0;=YFX^yEQN#@%+y`Ei@Jjm z!^jt4dV(%L(4PxaF4Beyj=k{L*3@(`m|`d62cvKJ;x0E!+Rbep_;z1=+4Hz@)oTA) zS62lImwJ0!{G&xmT=zx=r~xb$+m`7;D-DkKL+ zV{WOjfBuhpk18d2ctd-V!56t(oKB&7zyb2C2a~6NSn8Hj$tm0AwmsmXniu>mPXC%P zzU$6BjYLA9d;`SDZ1Wk%G$cZT)x!{P(yCeZ@CC_`ypAXV6dVsaKu5Sc^V>1=7C zdf!GPIPuX<&flP#1qWFHwUV9}vE0h~frFM=0p40^$Oi>M`xzqjz8t%=_}<3$36&OBlDTUjI?A+T_aM2 ziY!d=HdCvM7^C@s_XXvCgK&Jh&EWY#6sgcTEmMEroe0mIgjmZ{bR^gvA%Cy_>P2oP z1r5wWkyY)aFF_Y+u4x4oWTu_$s9h7!YGP+y98A)YG(Wxp+`c!2+b*MyR6sk)M$D{m zFMS7{rSg5zZ+j|y`FF3Kn!E3vZ_$2aik5Y;rfWL=2ixkN1Drq&p214dyZ>Vn$y!W0D=FU@@az!ww(q%h zI9;0tZKKazTL|VGUGtzNPKO2TM|{pa#TSYS_kDcT+sBa2LzS}+i#0Qdd=x zJU0D+FXbQGz2*rtq$EWlW=Ixailb%HgDBO|oK)4^H-pfLAOO2=93exJo5*L9qQE#w)q7yRY(+dahuv&{dMQzMdU4;b|BSgpUk@W!tn9!&-ayS$c!u;zoFZcZB?!)Q-?{oXYSZcy%p$|L*0YN&&c zab1eR#|gV40oVK63Wsm)&KOw{SLF`=$r@ZHsPK8KCQ}SGTEra>ecj3%7oU+Ls-%H| zr>*$IB)Pn%U@zclKU1c3+b0%55dpMy2QSNQ+w^s|_<%B4`Hn|Bo0Op}pdt-4vtCO@ zKvDE)Ksz@)VI-;l%sZ~J-^+8t;EMt2(;p!O;3gC#g<3!CNkW6tobL*FEy56Vj-E7r zX%X+3rsu{@+~v4h@?{uLeO?mDjQt)4!dG6FW5f4ww?DqmX(rsK22XLJYUbwFT)h~l z5+P+skyoyRbd)c~f=H|02z4A)o>!N_jcPiRd=;7k?>*QgfZzJiGpx*a~ZZNSaj*o6(3(6*! zWi49VXxKG81H;hCG?PxRR9u_nYXansD)Qozp?_ZuEf_gc%gEnvJYrDG{55= zg?iyeO-3!}iz&S!i7X7Le*6FaYIQ&BKltbQHo>&FIDGT5`bGc`ht?;)P3v*3ZSE(V z((RP^;~JdnsCc`+P0UKZ)D$-4VWZ9&5yWv3TUqcdfsw~?*Z&N1btZj$9Z`3&7Xl=eb$3G6&bT(;kd@k6}_p#lqe$J7m zPg=?Ad0ljPj04||i2N=py+Pb~p9wrxL?W&`B|Ps}?@2;j+%#tC9!k*jQjUz9_e3PJ z@k1OuvV{x`Q-O_t$#@0-21-n)f4*v&m8$4rr_lmn)hfe%#6&y8gdUd7A1t$s-955c z(u+WNAgcT0#|Q|F%1uy!WAnyJTOiv~vpT>svE264hKZj84At4_E*+cB3aW}QSIi%T zsoGO`l811y@XzsVJRSWdtm}WqKzWf`gcgyO*GP=pA_=gRbP!%DyoD8EPv*&*7GB2(`&w@qM~Fvio#$0Wy|rJY#E=@9$=}rnB0^HSvk%^{jLcQ$0O9 z&*w$zFCYGv%JB$(?l4kP%caD|DHJ-K^NZQ3AgI^uhsZ{-BH0Kcfq2^z1`qJ&HLz1w zeBQN*9WMO9R;^pn_wlDKGD&srPH(HryZqiPx;k0czgxp0$hRm(C1bUqFtMywA3k+6 zJ)6pHm2Gk#RLp^+T(k7$wy5>7vd`gF4NT`t%xYbA?uOcPuqvct$ZK%+5ZWyL4rkK^ z0fMi|7EQ7vDNjZzvsm;@p{K6KT4E#e?5K5z-8SIOmF|NsLTTd8RVSf}ztZNqDqnt~ zy$ScekHx25_J0BiaanbLb88wfQE@6x12rh>Bun8u=VBTb26hzHNpC9y1)?<0Y$t&W_*XS3!}&T-+dLQeqy)NqwkX!PIWfm2~}|=u{e7kVW^3T=2z26 zwZV!@H!&oPi>Ii>o#xPKe$vM2K)Q}7v`)I%OniTx9rnMo;S63a7WJlu`OrZ?PkAAp zZ3jQz{_XScABX3Jc0}xBQrX-Qdf*L0u}>j-{1@|* zjtRQa&_$aJ@kuyDw%v5(*Gl6ZxDbCEX3Zl1!*9m#foIt%LjKr#25F-*4*kj1BE1bqvRs6cD>9`PEc_(@myocT3W2#OL0TW3G@yz=>i#z*m zuPnaiNyV*t-OH5*wMy6i<~mn|@TsiQY0#|ZaAfJ;i)|0qdvbUFJM@hzHF6`fCZ<(vw>B)1{S-Kbw4q zwaK!sbE{IEd3J8|vpS`jCtX1V=4a=#>-I~uw`iSCcSnZ~@R-sYp~b~!1L>Y`#E1aB zQn%;$iNC8ZlFVwd<)H&Ulh`MR@T6`7rG!o$080$I^&XnEW!d@t{PCZPzV|be%0lj= z$|CQ7`&wG~bTTO|zNAj3KMBoT)DVe7rrb>%pEG% zgi%;yIuHZ@l{sC&vPC@e%9;w_q0GKpr+Qd??<&5zm3c^n`v!B~Y*6BEB4*`D*S$E? z&{L|5Vs1I}U(a`{v1J@7nagL;2z!#y#~yeAS$}AS`!vE5soH%@Iz|#{6w9(~i%~^F2mJ1`g&s9dUHJYeRE9 zt_v)N{;SrulwX>qT&#%@6f!*_zXd-UIYn{eK(LF286C@}ymR#ucZ-$WWdjV#%1U)ge)MKh9g>nUJ2YDiR_IIhOs?Y}#fWGflRcI$&8#Zbs#N&M2 zS<^z8uwUg%zv3L!5PZyz$o7I&U0mZE?GRb!@}KujOX_+*Z0B?tvRWYx`Rc;d;QRDP zMXFF49!B{!E5IKOc^L(Y@?kWgn}%1G?zc}CN{&SnR=_Blja2$X2xk8 za(XXf<(Z$h`a>t-YH&xv!<|wJs%57cGlmPW zvdE#EQ&{2xR_Ad>P25FoQO9Ak{0B7g|7ujlmzh@!>8f>moC-cB>W>%Rdrk`~-^gam~?}3NDLM5g2C{?I&>C#efBl>kNQqRRHvBhs>(>Fh>st$za z6)cJ-5H?=Df+oh zeR|1iu_t9Z}_%6la|3mpjbY*Zz!tV(3S2efj|> zDX+tYjYu^gc`yk8M*yr!xP+_WFAGEz(B1q#CU3mNoaf(1Q{v`AY;{Wny)T(okCQX_zta-Ggp!Ake?1)6nN5m%@^lxwvQAMMWI^t<)eBisG zx4>on|JwV?uQ-BlTigi=Zox9aCAd2T3+@)&-QC??f-UgzHX zd+YuM@6($vz1CT)YpT1dtIpYH*FH2UV==@yNezrPIYxvQi+IQMkqbRQ9hSDz+o3t~ z+x5#)CPl?O?{76jc$+Acs1uH69w0xvdWer<1UfpXDn^kADW-a@V%s z9M0>s6jL>D=4Lzyz2YS%k8hP0fa$*2&b8AbVS#tina*%LPx6Mal@zihwG!T64CU9DURKV4?GGAmd|Z9nEcnPPQz)M25!`8f6JyO7oM|* zVgjsQ7gT5TE)N-MpYcSZ3+*$71C;9*>vA!RDyeX4DTd1%xOL0cLN5^RsD69CkuH*M zK_e`UkHTQ~7zA(Sp*yK#*DJHmgbC$>MIf$dt`MX!uPs?FJ{ZbB9X9HW{l9i~w}^^Wd|zx&;8_!;oGouA?e-m~3xWZ4q5h=guRJYPue3`GGUMqIQo`%ZsEz3Bq|k zcOh6pzVo-6OzU_C%otS(1d35?EZvgU?ET*WiHo((%42Nvr9vA;I7H6E*+ytVFD!B@ ztrS(*u=%iEpf8!qWQd^mRcK2&f@LZEB)f>px5zlLFxPBY({|*M9Ot>QTx2ZoqCMJK zynjzm0zCa9QuAl$I3q%+Zo1pd9vCM*xwpqbW)OQU^$3ivQoe8yTdznw@RtXQI`GMS zt!3j!%`+WH<=oS^siJ~=@wz*OS}@=gSl-=rnJqv`Unql|NePn2wmb^YRqJ30v}kAH zD6fK))Rf`s7~xgQB~}3$H|JkL5y$tY`j67f4n(kw$XA0nDK)z5j&8Qy zT?Q>LJT{t6Bclibr3K-KA&LPW(&vm6#e>eXUkS@#T~|s<5tIcK09E1)u5`+sRYpQ+8DFW0`u(z%gJQ4Z<1!ZT&=;g z99`>%giuDDPYyeYen$Kc=P)Z`lkn1m_O>G8hN3;yA*+GiuacG1?}1XnE8clubetsX z3*@t}A>QlcejK-TQq%%MH%Ew@H$#NnO|(?+K6BgAy2lh{Ws~YU0@F>kY9tbgmfDy& zY1GItb$8W}hkBnClYTrPNbuqKYWG|`p^f$1%+2%h#x09X8#4&cNaiLA7kmQ>#4vGO zu#zrD!cMBVHw9*rNcCF^Ds_ex;u%082CK#H5pl$6PW`%qUQX?*@zd_HW_eIUQ4!Sw zM@0)98{@T}Znjd<3KjUA`v{F>lT0^z+Bql%4WiwW3IDrj zpL(eZpx6HGFs#Ft_&IWLZYyqxn$fgOlD*>CVXdA}mhLV~LaT=bf7$3{&A@P9IYQ8V zY4wM}VuiZ3r2L~gIrK8$-&(cB(XQ4khQM8`%hKzIo1CV*ZmYHAqpw$KYy?PMf3AwxS*#T8fA`r#)`2tF5Ccl0Z#J z#zJpQ`D~$XhQsGgm&fjyJ;s&SkOOk^jV_;72f1ucgY^bK$j2!k5A9w)_w|az!}Q^9 zPbclhjIW-jn$DUck}ATXv%+d>m9+J5WfqE)32n~|*dNap-Y4_Ubh#h!&j@=+jT9M=*61UH z@o}{MnTK^)QQ5Zol78ek%CmgPXps-Psz6Pv`<#4SKSf#SztzvKH3G_6`Emk|y#xdO@l7aZNJ z#syy6&*R`jNH<0VZ9|OPu0#O`vRnq`1Wy$rdU&qm(+8M3~3Nksk6plK(T0ouM zWGzvGvi(|;3q5}7P{gi=COfJcEfTjoq^$+M0fdTPYS(VaOAV5nn{AMfX}8Gpn~p<1 zp6_UAn%_yVNXX$c>fZ&Qh{!O)#vh>QGfZB%fW|yda=9*kPlq6>#Hz^?Am=KgEMNnJ z){IcYstSAzg#EArJer4sbxY@MAXW+v`_by7B?dCji;3^Bj4@ zkqTG2B%r_SkYYxJ4qE=L-Zj3*1APRXxm)oJGyT8u*mTD(07I^vcAP6pIE(9~xku<_ zzqlSy^szZ2s2U<9p_azmiRXOqtxO>gwdYqT(Srk9A&e!8+dhl@j-KSRgtS~oyG#o{ zk5h`@Z0<3WblYn0)Z2`Fc`&>{m*TuN-Ac!7sEJ&8Ki?vnW;QDtjy8|L@E-c=e^@Wd z{?+T`E^3#Ir$<6DD3(~N0L3OHw%$NGFPKK&)u=#+8=D?AD#k`!A+6UoeSG8gZ)l&x z7q6QuD2)Q&_q1dYguYZ*=Xk^h1f@Q^6}x@dvs^cb{7BvD#zaLU7Z0?4gv{%7j)q3Q z+j;}sK-EMFW(F_TRa}{mzWL=DBezO0nVJYfYfn=8G1Y3my_rb(F-1l+{@j4E<~|=% zmUxC^2jLP`eL9}DWrnwdwCD-Dt|)tu`S17bLYib8K#3jxzdA$69&ZDFF(#`SmNGRw zh1$YNZ;i4&8UNn8bD8F8-}v<$C9Q5@G|L+*A_#=06=Wi|7_Fnw%i7fzK@bSDc|WE4 z=)p}oyaJr=@@u^11j0DM*Zhf$0ry2s-A#iLpFfQ?Jo>B%tyIlC-F4$6#`+N~R#zqd}Q46r@zbH0UuT z6hS*;HscK#(7Sg7BMGFu7iUbm)ERb_F9EMla^fR7zDqH7wnVam?_jaWmQZj_ksb#r znJc$gQOC^!yWx@ieGk1X;=1>aFH4;V=>No{uh&tOj|6qNml(Iw`d|w<&DKZt7_Z{6 zx=V&RpNhSjVEODtevi*_-Qdj-$YuT#oO7hLX|5 zWp;9s;xp@YJ-0dSo;?{pM2NdG(HSKfwXD zasQNq`ww}hM}37}J&h85?M_KPW|Wze0U16e_3MGP^-A!vp~y{BxkKL5ib;Y&TKVWT z2L>{T`jLJKP3Pk*pIJy1TC26G_${|*j&N)mPz04#J>5)`b^q6Q$oeG!wH^k+ zKXz(wye$0g)Er*-AHm_?@VBDjL}VSn)%Xx!N|6>cpnN%wNW?@LjX^|IK$RatB4r?a zcvRcHc2ZJ4AO6Dxb*lxwLi%8{jZKEqK~yZvo@v6hSHA4j?e>@d!(9X!*mMkPaFSC8 zi~Z%DR%RZ&wO&Yr9+H+`NV7ZH8^Oz_k%-O98C5QBt_3QeqiSuMxQ^Es5&|ZjIl)|J zd`|2E&$7gY1%&K=m*?iN?|-38Am!j<#Mi&X^0#RdVIZX4U_+-<(dqF(DDN+_j_X|B zi&WK-({_tm?iO5^(TVXaL2pP8zPUy<-7p(#1yv9;-Ig|2V3C-@#a6+)k=%vnKtE}z z3T7O+Wo$T35diPN?6p4rv3Gfo48RwH#N=Vwzo@^N^obN9gt+9*bDS)B3JA4SIAxXI zb5AtXj68;cn40jzNQA-k9P%WHFs)0AT~P4p{iJ6%dT zrzy}}(!`OTCx~OccbBTvU{uXF`R0By_3Hn!xyxd?Et}{(`@9z%=eBN&3ev}`m$!7g zNF*$4pp~ENY;v74P!Kwqjvylp@GGqnQyjrboG=B-l&4Bm2%tsbrhv9j2hUGgEzgKf zJPvtp`;bTSA+qwp#zsWAh|c$e5-YNkBQQPj52ey!&-6hLElf-S_G-{ zXA)`>4(KRU7}V4^Sny-~G|6vkP6fMPtq<%K<-Hy*Za1@qCL4~m7}H_qn_z+3zX;=- z$9~*~mhx8{Qb7!8pX}))dz>)#ms@Y8X!7}XhF_i^yHz*3ZBIIDOFMnnS5&9{OW{=u zN7H8CBJyGc-;x@osep*oMq1l`vtEBapZHDcJ({R^@y+%)wJn)(G`(!8sWVZcv6GV9d)omd)pSEnZo>um+q|`- z-@9ZCozLs-!pGOWcgXhH+-H?tKQAIOci1Eu-eRk(6-fn0^Ef1@waOT@z7_sRnDTln zuXp$^qo8PO-AKBN#4=8Mw$h`P1&eP#5cv)7??{>~P7~eOOPzLSvs;Km*U_(K15YdI zMhRkY%NdH5WQ4|4mZ)q7LXt`wBS-mo9O=`a;Kt8Q2R%pgy4e;F{Qp8IEC4V$L>~F< zLNL*A(>upnb9AbI=ZGbvxFGg%oui@5Fkuk7T2xfTKxEPmr^3F%F?C*ZWUom$`Bf|W zv-;vafX4d{uUvrxcX+1TY`J2>!j7D?<+*r=R}!L&NC^{0UNxa4%R5N<)0wTWF}0n} z)6o1WUh9oDHvJgjIC{b_tG90-$s!>){G3SGD#~gk(>rOC!+?PE9S~?j`jQ8Gy z9|pp)I=cKG_hM{*R>v`KS89}yb-7Od#>&g`_C!|<53Pb3`9vDdhNP7x0XKDN5^Spt zhs;yfa28C$9luuZlZU;< zDfq42$I!*$dOJ%<>HDfXGfXYA2v2%UhB%BXA;;Oti9FH(S_US4@{^|~D~Fu>e4mj` zTW$N8S-7+bqJHd91hgJ_!l$?_ea}$Q*X)rhZ;5g8VshD@?LYmR)ZEpbne6haohLpw znE`(jLq^Vk8X2k;s>R;JKV05)87;)UYJ)wsHT5+_&S7$LU?tBvaA|O6yWL!*Y1}N; z7xbhpQ=yHHb3_5W;Q8KMgR&#Of+GMVFYrcL@XM(9%I~THPn9?W?QouEfeYLP=!XPx zizprMu&JI%WCZN?XBmYX<~v2&Tg{J2u9 zH9TNQ^WBIV>>x7$Lb}VPj8r=z|12~kU!oGRv}mj`V&5A}JESbbpO`?n8S3hMkB zPG8ZIRn$nj_C{PL%nE0)S1Mxlp-)nN{7?c zFlsgxKZC!?_S%-`&K-3>xKQO8{3th`EqpN=n*Gb`@mWN%j4JO zs{09-zj9Pd{GSN_yKQK0z>||v$52ehsnc24t^UBrQYQ?BOCH=IRjL;ZQW(f&$fTY0 zkjZR|whJvC-8*mnk@%-ePUJYAhb5iZXl6^UB=dDHk7s?fGvYiG(-hH9T~CO`AXIpi=#IxL-ic*Un&?U0bvNB*I) zdiwbW^0A}y_2o)+)9-QPdpfi3l6>(la`T3%_Pf&_00wv$8y7LU=fax8vfWXprfnKP zS0T(|*+g6?)x(|e-sxEQ@%HT6U*CA~ZoRgZ=yq^SbVR{ym~w^mTPl#}xkNuuRD#dBds3WsRY$s*Ck0XY%OJ<5_aEMZeR{P_ZKa)a5trzo z-*k0rTt*q+vHZ@eAPz)*-c9AHhA4nJ+$kuG0K9`aU{VsUC zIoNphYYEe80p?w+?{2fl%f*fE85vN&VlJ<}Q(H|#1L@opjd27In`lLIq)EigKZkf) zaQBU8%g3FSPk9)-&^JWhcO=WNE|ZBmaWTwPz_1d3P@4E59S12y_ZLTiLv&UXlA=;a zfXQ5iW(&axEW4RBFt)moT8!v1XE4yyd%uBE5(JkR5z=_&d6XW3ndsrI;pNmgzB4y@ zBjT5PWtHX*6K1Ps8-3qPFe1!+uHC`r=?0F(&-z9C4%|e3mxJ{d%+%T)ssVUf1-y4L-T-9Sy2nRBtA% zR?UatS#h|@PY@Og72{`w_o6Yl_-kg;_?Ym<4H&G5Bl}|QX|RGx2r(T6ff(sWqQAE5 zp0~Pscy0?H-26*fl?G7upU3Q!WtdQA|7EL1Pe0xjs6!rVVWZXKYQjCtd$~HwLmym!y6bB~CQU#J% z8)d@ow{ux$mK^aFu}G17Tum%*`aaLDP4ntYp}d9T{Fj=W`fKfM5d*e@sfH=2g?e=W zdFglnS_LysNEfdG;Mlz241|ULyONeW^-}y! zy$y{@IrKqppYeIj5xiljrR$!1Up8}ZXLPa5e6I8eb66B}pDtE+$+P~pUPRlvq;iSK z)6Pqf8W$}U1{5%?5r|681xbw1EOk%(tQXyiwgJT!_P^}%rrSljh%qpJ#YvApZ~SYk zWcr#nG$i@$YnS&FF^6TX#Us|bjn2_hx%Efg&WFRRl;QYxGcif+_V9Y$KYxg}yN7s( z11R6&6}lKplSI-bi;Ctc;EDusjP7T_II+oF6M#4 zU)(?GW>=DHWuPjlyL}NL>nUWuwJfvYg-ROup3844T1*Lg0!IxaShO38ErcwFxl9$t zVZOjKR>kB~I-Sp|fEcwhG53$~3k^-3WWctyUAI{mP+@Y9S9eHrReecfz};%#(KOHN zeRMuSE8U`rlCm<=$1x7AZ>0NTI8h-n=c38^%7hh7-_14DBkIZ=VTepejpO=?k)j7F zjJ&sWxE$qS#nUKv+m+TDA>*t#!{n0>ClrwsPywgQm5$e&i;3DC28Mcx%!AAs5;0LS zvyT_N{G-H)A%C326%+k6%`86nJF#tW4(1l3=ty!u-b1&oC26z|xd6x4M} z=<`=dj2k5aaFvOO^+v)?!ZH2KCRdcDI2QOy+);vr`2aukWuKvUGxIN}hsTozArf|l zDOHlzZ?OH09&?2C0oCkB&=>W0<+IHMqZEul)J-g@Y@NpYn`b&7IKRf%6udZsi+Gwz zdc%Mp^V}hHx|(>~%lmL2Vb=jUu3szVnXA9Oa>?gWg)kNiOvd5!Z29 zt9|!9pYkw3KL~ugaszxLYd3_i%sN{+w@88hKq}y$Itf+MXj=^gpb@AK)8WaIhyJFo z&<-O37XdPo(ay*wUu^w31#KbS{Tx$}!|wuLTyZkYyoi=?7C26RX5Ez6=k~ZBWW~#+ zGeZ8vv|l1U1GH?P=cmgV!KE5~2vy1$>Re?x>rbTce*Yfd)> z&$s?n20a^J&z7n;W)_Kd!B`f@!Z*PadDE|*o$CFL6j524>FRW2Z^7M(wD&8T&yk`k z6h0)83uujS*d;?0-^d7gaWX;T4K5?mjc4s{T91^E-Ep@%^~Qe7$|f)VXTh~S{F7sl zw>Tg)uL5buvML8?3ROPGz35su33j#8Paa|6&uAe(#h1x(2FH4q2pS|W7COBezs#KY z_;)>?`sJU^|Cq6v%5F7S>Evaoee<>Np%Q@-q@N!kp)M3T=3Qp6C$mE>{kg92J+by2#4VSyA`~>Y+AFI z;{g*W1guw&oF?`_{*EDfjxv9yHRer2#a&tA->uS;io-9G%6Z6vbg``OW>iw(O0_?Honj>zCpOsTx#Fv~C>D4DqzW3w$-S>MP`rWTLk3r}G z5A8E^C34x{Azb-0a{e;dRcGd?&VdO19G8iaBLx79Gsb<@Zh853!=s15`{wwvZz*+d zx%2fruQexN)#WHaZ-LsbBk?agXBZ5PjswCsb8N!WccZ*_qmoQn(#4?&!6xX7u5`Jf zXafjD8{z4wQ+%#fZ4amX;Taekx~YBlH;{byyT@t|`6v7B8F*GFB}7D5J%{Kw;Zf0N z-5*hxsanoD_I)?X>P|i#aey+r|4i0eMFxoLC8iDAmS2&mrn1T#|y{VPHD5ad*(qiX;`}h4vZe^8^6V4jyi;4h|Qz^Na2wJ2~=aB5n!2=hZ%= zWFr0Q`R4J=UAx`bthL!@xmBmxTC&`Tkk9#Ly`^b-F?y>-^%Q|kMbE}|g$8Gs4igPACZ@=m{-lcgVFl+$ ztiVwROHa-wsrdE#4D=@_G9_fRYE`PF-UdLaI-NC-adIW*E#0K|@9t~)pSXW}ksz#w zD|H_NI`8*>3Ld%${_Fyr0mI9GBvbgR_+V~x-(IQBY%75gPO#c;9x+?ZAn{uL3AoRoA~Gr&Upxd!rw~0+Re1D zcd}5Ta1|OWDjGTEeN>W*wMEI5f9ZfE5YrI``#0O4UUv2(0{cGiOzPM$4 z{S@#LeyBNGy%^-A!Jt8yyHsniT2sY1?>^MjB(jA>I(xMb}{oO?$F&g*Prt*biJ;W;C zAjO*G5FXkecnjn!{9ssXw$WTN>BvI|_~n;Y(CHIsi|y)h1U}PTk*QLo1H$F(TBuX# z9gA$UIS z7tW|dR5$qu{3YEi%_Z+bptgCJC!pWZC6#IgTOt`$4RCjZ7$?Xl8alO-K_EqYrMFxl z>c}7(nPg7}XwLU!zL!C6H_{l8j7pBnul~!*7V(`9j_tG;*(1FSHa7Cl&4QU0#)&)rcqw1}CzrqMvBGE@ zJUK2imjd%nq&NXdW<-nI^|^_J>eKqBzlL;Z^~jSrK}+y8VWYyGT@zk9q3~&`YS_KB zs2F2apXL!{ka*nfHuw_tTZ^W^Zk0YjO*HF{W}Bs}G1Eq$D&5vnKS}BksAiM(#zjOt z03{vImM-R)4-LKAR9j8-8jsCv2Aoy!y6m0)@%mcDqd$nJ?Zm0?5gk3(w%MVT$&=}` zDj5@FR_LB;+EJsHG$o6-nq$3(Q7#{GBiC`(^t^y}>Xs=O@bVQMpZVCB|0_EO`MqQ= zE3Lp*drs64>9D}PSY1V8fOLtSN&F5+31F$j~@+MZ02urfy~?_P>5#Lqg)N` zXeqQfSQK&J3(*)uvaDBR#i@(FWr{<_m{rC(B@c=xm%I?`@^ze-#XPpK&JLR$1->#> zNky_AGHSsS6@LUqGEvb;#!X?xRbw$|wO*_cUwUd_W}aZHVLDED8@GeKwbpwPP8Dvj zBL)jML~4taNw~$Ur=;E|$tpi*&c@#70*HRsQ~;h>W08YyljV)6vT_oD@{~ip=yDun zlIz~HYqj1nzN;DDviLJ0`nQu#FJE2AhD-IC=E0V+^NgAWE~`{hxbjev@?l4F@zyuR zrpTmhN4u*;3hkGT&EwAJ02be`1bQsf&5SlNw~W0aS1iu*cY0X2DCe1*UxWuNLhgRq zWZ_RYYMmjv#C=jXCW!x#F>$WNxJQj0rOqkyx%|lG;nw$g34ij;%l3NhHjP1(tjqiQ z5U9PO<_O;(5M8vTh;J*sO~T+$&Hfri-i?X$xD+m_yV4#k&+$}*E7Zva#(=zuO00W9 zPU@oc8n~$^7kw=<=={4qY^FVoN!VlP$zI<_1$!yL5>N60yI%G@D~>-dn@uRw2|8Ag z2G`@d!i5%CM22mTB05ao5|}VL<`s~!;ptHSJ#f<`gVyz zbQ%1jUA9Kn8TY|FTMy^Jz*0s$ICME8+dSNy1}9A9P-d*XHnkNHt8W_hLkVUw6TA9?U!n@(>djTs;QszEHe!# zfwb{$5{>Y493uM{cm@zh!K7?t&{B|`Az@1ndtB!B+7#x5tP6FCNtA>OM|qSH$MnmJhKk-PUa}V`fMv zeA|%uK<`3O9?IxY+{ntkBf=0DBR$gkL-Gqtcl`V zl;2Y|6LoS0`>~T~+)ZNSUoWmM(=S{6ZYLA!C=zx$QNyktWuI18UzGnbf9rNI_b2U; z<4#KW$6CyO!W%W9!YKMggS(qP0XI_p`FGUr*`m<&T>2^{$|w}_{=0=rY@c=M5M=$U zphna)Y@**}M}{>+n7-4J;#l{zfZS2QLoN-XT9l3DDK^!HEV zhGG3SiMiWI#f51!`v!&A^09Qu-)O4r`uyHM1xx)(XJB&bgDS_08RH8jqBHT1evvQ; z7BbqLRiR16gh#aZ{k4^eAR4XpJSa~6B(Sm~c7f|R&)}=%a78iTZc2pn9H!3N#2EdE z;1C%+N{^Oae2q2oE;>2Q1hpzg8c9sgNud8w%kUiIEGnDXzS|OY;=B6Pr2t5m8rxvR zX_kI}hs0)<##|d;XJXLXnN&&?y#yq)@`ir%!%u07ZAVg?_)NO!lU{!DXz z0n~K{*x@Tf%mP-~&TVv2C(@2Vr+My=ff&2a-w>3xS-EEfSH%*@%hp{;rLIL}f~me& z68xZNkzvz8e(yA08o|;V7-d5`)~@KP1c!#n+cM>-z1?ZjGuyDBlCBL@xQKo)^_J6h z1hrMKPT?&wsJrC|97&M;_Bi0m3}!jHH^U_!8ajx(%>nEfkeNUcD#|9EEvV$ay~IVO z6>-fa+Vs7SOmbo`W&BF*-=SLPkodRXs<9L3N5!vv2>l7Zv+!?MMa{I#YS^Ku+1=Dz z8>lX7eUNjMyDfJoNqSW0eA)Nw-~6soLEj36iyeSYeSSpG3z?xNc>dGp4e)A~Nosaq z@$v8$d8V%uM)9oKe+Ee8SWbqDd>1Q-Cd?Bn(n(vC5Gb}GA;GEdM7vep@5b;q_9<-s z;@H=_rXlUiMU@(KZU24hyP@qhegOUzc@&b4-(0CxsjGo61F@8v)gN?5o_=-xw=^`P zj(t7jPIhlLBPGqVt4dJp)0WN?+mLxQCB=Xo;C^XnFF7iZPYozuEv?6fyN(nAMNu}1 zEMyfgnzXifSE3X~gAa-{Qe}(Obm!U^6=qQZCg`jwz~EanEloxe1E5L3AOc)4;{ewt z{BzD4Ko*7tB76z}ho%kzY}D*&9B=>I@Xu~(3D8J6Id&Gv{_D2?x|@UOpY2uJuIEGkJ`0mHef0_f3wXuKsim@*rF+=~g|KEQ@{4XDj8OPK74=4Sf&d4ME%MqK? zTT%Rv0l3W00-m_F3_Oqx`A@IIKLfFr_y>iPcmHWFR3t?du%&#H zB}VyQz2*P_C1PSH>wiJ?|J extends State { if (!_connected) { return getScaffold( context, - Flex( - direction: Axis.vertical, - children: [ - Expanded( - child: Center( - child: Text(S.of(context).check_internet), - ), + Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Column( + children: [ + ImageLabel( + imagePath: 'assets/images/no_wifi.png', + label: "It looks like you're offline", + labelTextStyle: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + color: Theme.of(context).colorScheme.primary, + ), + sublabel: S.of(context).check_internet, + ), + ], ), - ], + ), ), ); } From 9e850865b308a4e06c0731cb1e0fb607eb827fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Sun, 25 Feb 2024 00:18:43 +0000 Subject: [PATCH 259/334] refactor: extract profile image component --- .../pages_layouts/general/general.dart | 33 +--------- .../general/widgets/profile_button.dart | 60 ++++-------------- .../common_widgets/widgets/profile_image.dart | 61 +++++++++++++++++++ uni/lib/view/home/home.dart | 4 +- uni/lib/view/profile/profile.dart | 5 +- .../profile/widgets/profile_overview.dart | 14 +---- 6 files changed, 79 insertions(+), 98 deletions(-) create mode 100644 uni/lib/view/common_widgets/widgets/profile_image.dart diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 40d1de245..d42be9676 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; @@ -67,34 +66,6 @@ abstract class GeneralPageViewState extends State { Widget getBody(BuildContext context); - Future buildProfileDecorationImage( - BuildContext context, { - bool forceRetrieval = false, - }) async { - final sessionProvider = - Provider.of(context, listen: false); - await sessionProvider.ensureInitialized(context); - final profilePictureFile = - await ProfileProvider.fetchOrGetCachedProfilePicture( - sessionProvider.state!, - forceRetrieval: forceRetrieval, - ); - return getProfileDecorationImage(profilePictureFile); - } - - /// Returns the current user image. - /// - /// If the image is not found / doesn't exist returns a generic placeholder. - DecorationImage getProfileDecorationImage(File? profilePicture) { - const fallbackPicture = AssetImage('assets/images/profile_placeholder.png'); - final image = - profilePicture == null ? fallbackPicture : FileImage(profilePicture); - - final result = - DecorationImage(fit: BoxFit.cover, image: image as ImageProvider); - return result; - } - Widget refreshState(BuildContext context, Widget child) { return RefreshIndicator( key: GlobalKey(), @@ -126,9 +97,7 @@ abstract class GeneralPageViewState extends State { AppTopNavbar? getTopNavbar(BuildContext context) { return AppTopNavbar( title: this.getTitle(), - rightButton: ProfileButton( - getProfileDecorationImage: getProfileDecorationImage, - ), + rightButton: const ProfileButton(), ); } } diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart index 98f5e3025..4c5c6a271 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/profile_button.dart @@ -1,58 +1,24 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:uni/model/providers/startup/profile_provider.dart'; -import 'package:uni/model/providers/startup/session_provider.dart'; import 'package:uni/utils/navigation_items.dart'; +import 'package:uni/view/common_widgets/widgets/profile_image.dart'; class ProfileButton extends StatelessWidget { - const ProfileButton({required this.getProfileDecorationImage, super.key}); - - final DecorationImage Function(File?) getProfileDecorationImage; - - Future buildProfileDecorationImage( - BuildContext context, { - bool forceRetrieval = false, - }) async { - final sessionProvider = - Provider.of(context, listen: false); - await sessionProvider.ensureInitialized(context); - final profilePictureFile = - await ProfileProvider.fetchOrGetCachedProfilePicture( - sessionProvider.state!, - forceRetrieval: forceRetrieval, - ); - return getProfileDecorationImage(profilePictureFile); - } + const ProfileButton({super.key}); @override Widget build(BuildContext context) { - return FutureBuilder( - future: buildProfileDecorationImage(context), - builder: ( - BuildContext context, - AsyncSnapshot decorationImage, - ) { - return IconButton( - style: IconButton.styleFrom( - padding: const EdgeInsets.all(5), - shape: const CircleBorder(), - ), - onPressed: () => { - Navigator.pushNamed( - context, - '/${NavigationItem.navProfile.route}', - ), - }, - icon: CircleAvatar( - radius: 20, - foregroundImage: decorationImage.data?.image, - backgroundImage: - const AssetImage('assets/images/profile_placeholder.png'), - ), - ); + return IconButton( + style: IconButton.styleFrom( + padding: const EdgeInsets.all(5), + shape: const CircleBorder(), + ), + onPressed: () => { + Navigator.pushNamed( + context, + '/${NavigationItem.navProfile.route}', + ), }, + icon: const ProfileImage(radius: 20), ); } } diff --git a/uni/lib/view/common_widgets/widgets/profile_image.dart b/uni/lib/view/common_widgets/widgets/profile_image.dart new file mode 100644 index 000000000..20bc277ce --- /dev/null +++ b/uni/lib/view/common_widgets/widgets/profile_image.dart @@ -0,0 +1,61 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:uni/model/providers/startup/profile_provider.dart'; +import 'package:uni/model/providers/startup/session_provider.dart'; + +class ProfileImage extends StatelessWidget { + const ProfileImage({ + required this.radius, + super.key, + }); + + final double radius; + + Future buildProfileDecorationImage( + BuildContext context, + ) async { + final sessionProvider = + Provider.of(context, listen: false); + await sessionProvider.ensureInitialized(context); + final profilePictureFile = + await ProfileProvider.fetchOrGetCachedProfilePicture( + sessionProvider.state!, + ); + return getProfileDecorationImage(profilePictureFile); + } + + /// Returns the current user image. + /// + /// If the image is not found / doesn't exist returns null. + DecorationImage? getProfileDecorationImage(File? profilePicture) { + if (profilePicture == null) { + return null; + } + + return DecorationImage( + fit: BoxFit.cover, + image: FileImage(profilePicture), + ); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: buildProfileDecorationImage(context), + builder: ( + BuildContext context, + AsyncSnapshot decorationImage, + ) { + return CircleAvatar( + radius: radius, + foregroundImage: decorationImage.data?.image, + // backgroundImage: + // const AssetImage('assets/images/profile_placeholder.png'), + backgroundColor: Colors.transparent, + ); + }, + ); + } +} diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index a994a5f8f..2f3ec9506 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -83,9 +83,7 @@ class HomePageViewState extends GeneralPageViewState { padding: EdgeInsets.symmetric(horizontal: 8), child: UniIcon(), ), - rightButton: ProfileButton( - getProfileDecorationImage: getProfileDecorationImage, - ), + rightButton: const ProfileButton(), ); } } diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 0fd3e272a..7e462f536 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -38,10 +38,7 @@ class ProfilePageViewState extends SecondaryPageViewState { children: [ const Padding(padding: EdgeInsets.all(5)), const Padding(padding: EdgeInsets.all(10)), - ProfileOverview( - profile: profile, - getProfileDecorationImage: getProfileDecorationImage, - ), + ProfileOverview(profile: profile), const Padding(padding: EdgeInsets.all(5)), ...courseWidgets, AccountInfoCard(), diff --git a/uni/lib/view/profile/widgets/profile_overview.dart b/uni/lib/view/profile/widgets/profile_overview.dart index b0ad87195..5cd1ec3b5 100644 --- a/uni/lib/view/profile/widgets/profile_overview.dart +++ b/uni/lib/view/profile/widgets/profile_overview.dart @@ -5,16 +5,15 @@ import 'package:provider/provider.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; +import 'package:uni/view/common_widgets/widgets/profile_image.dart'; class ProfileOverview extends StatelessWidget { const ProfileOverview({ required this.profile, - required this.getProfileDecorationImage, super.key, }); final Profile profile; - final DecorationImage Function(File?) getProfileDecorationImage; @override Widget build(BuildContext context) { @@ -27,16 +26,7 @@ class ProfileOverview extends StatelessWidget { Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Container( - width: 150, - height: 150, - decoration: BoxDecoration( - shape: BoxShape.circle, - image: profilePic.data != null - ? getProfileDecorationImage(profilePic.data) - : null, - ), - ), + const ProfileImage(radius: 75), const Padding(padding: EdgeInsets.all(8)), Text( profile.name, From 93109903d37be83af898cf9cfc759d73db69ac84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Sun, 25 Feb 2024 01:21:42 +0000 Subject: [PATCH 260/334] fix: ensure equivalent placeholder image behavior --- uni/lib/view/common_widgets/widgets/profile_image.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/uni/lib/view/common_widgets/widgets/profile_image.dart b/uni/lib/view/common_widgets/widgets/profile_image.dart index 20bc277ce..35b0d39f6 100644 --- a/uni/lib/view/common_widgets/widgets/profile_image.dart +++ b/uni/lib/view/common_widgets/widgets/profile_image.dart @@ -30,13 +30,13 @@ class ProfileImage extends StatelessWidget { /// /// If the image is not found / doesn't exist returns null. DecorationImage? getProfileDecorationImage(File? profilePicture) { - if (profilePicture == null) { - return null; - } + final image = profilePicture != null + ? FileImage(profilePicture) as ImageProvider + : const AssetImage('assets/images/profile_placeholder.png'); return DecorationImage( fit: BoxFit.cover, - image: FileImage(profilePicture), + image: image, ); } @@ -51,8 +51,6 @@ class ProfileImage extends StatelessWidget { return CircleAvatar( radius: radius, foregroundImage: decorationImage.data?.image, - // backgroundImage: - // const AssetImage('assets/images/profile_placeholder.png'), backgroundColor: Colors.transparent, ); }, From 090a531baab7434c7b82797c255b30a24f7fdab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Sun, 25 Feb 2024 01:22:04 +0000 Subject: [PATCH 261/334] fix: padding with settings icon in profile page --- uni/lib/view/profile/profile.dart | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/uni/lib/view/profile/profile.dart b/uni/lib/view/profile/profile.dart index 7e462f536..be6f22fdb 100644 --- a/uni/lib/view/profile/profile.dart +++ b/uni/lib/view/profile/profile.dart @@ -54,14 +54,11 @@ class ProfilePageViewState extends SecondaryPageViewState { @override Widget getTopRightButton(BuildContext context) { - return Container( - padding: const EdgeInsets.fromLTRB(0, 10, 8, 10), - child: IconButton( - icon: const Icon(Icons.settings), - onPressed: () => Navigator.pushNamed( - context, - '/${NavigationItem.navSettings.route}', - ), + return IconButton( + icon: const Icon(Icons.settings), + onPressed: () => Navigator.pushNamed( + context, + '/${NavigationItem.navSettings.route}', ), ); } From 168bba7e1efcc6391232e3aab82a98bf646191ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Sun, 25 Feb 2024 01:28:33 +0000 Subject: [PATCH 262/334] refactor: make top navbar const --- uni/lib/view/home/home.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/lib/view/home/home.dart b/uni/lib/view/home/home.dart index 2f3ec9506..15b3298ae 100644 --- a/uni/lib/view/home/home.dart +++ b/uni/lib/view/home/home.dart @@ -78,12 +78,12 @@ class HomePageViewState extends GeneralPageViewState { @override AppTopNavbar? getTopNavbar(BuildContext context) { - return AppTopNavbar( - leftButton: const Padding( + return const AppTopNavbar( + leftButton: Padding( padding: EdgeInsets.symmetric(horizontal: 8), child: UniIcon(), ), - rightButton: const ProfileButton(), + rightButton: ProfileButton(), ); } } From 774c04b6ef7ebeaadf5004662ae943269a2e7fb9 Mon Sep 17 00:00:00 2001 From: LuisDuarte1 Date: Sun, 25 Feb 2024 11:06:17 +0000 Subject: [PATCH 263/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index a22120ef8..bf28d8701 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.5+238 +1.8.0-beta.6+239 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index b14fe4f41..dcf44716c 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.5+238 +version: 1.8.0-beta.6+239 environment: From 221fcc8325ef4d0e14cdb9c464b9090638bceeaa Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sun, 25 Feb 2024 18:20:24 +0000 Subject: [PATCH 264/334] Minor design changes --- .../common_widgets/expanded_image_label.dart | 3 +-- .../pages_layouts/general/general.dart | 24 ++++++++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/uni/lib/view/common_widgets/expanded_image_label.dart b/uni/lib/view/common_widgets/expanded_image_label.dart index d2ffc4e07..d7ef8a3d5 100644 --- a/uni/lib/view/common_widgets/expanded_image_label.dart +++ b/uni/lib/view/common_widgets/expanded_image_label.dart @@ -24,12 +24,11 @@ class ImageLabel extends StatelessWidget { height: 300, width: 300, ), - const SizedBox(height: 10), Text( label, style: labelTextStyle, ), - if (sublabel.isNotEmpty) const SizedBox(height: 20), + if (sublabel.isNotEmpty) const SizedBox(height: 10), Text( sublabel, style: sublabelTextStyle, diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index 596d0e3ea..6e70b4987 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -60,20 +60,16 @@ abstract class GeneralPageViewState extends State { context, Center( child: Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Column( - children: [ - ImageLabel( - imagePath: 'assets/images/no_wifi.png', - label: "It looks like you're offline", - labelTextStyle: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 18, - color: Theme.of(context).colorScheme.primary, - ), - sublabel: S.of(context).check_internet, - ), - ], + padding: const EdgeInsets.symmetric(vertical: 35), + child: ImageLabel( + imagePath: 'assets/images/no_wifi.png', + label: "It looks like you're offline", + labelTextStyle: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + color: Theme.of(context).colorScheme.primary, + ), + sublabel: S.of(context).check_internet, ), ), ), From 27870209e3c539592c04b9a0781b0f093fa5446f Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sun, 25 Feb 2024 18:24:02 +0000 Subject: [PATCH 265/334] Lint fix --- uni/lib/view/common_widgets/pages_layouts/general/general.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index e5f14c5b4..a8d7fca8e 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:logger/logger.dart'; From 3dcdc693c1fc3593b097ece6fbd168166113d40e Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sun, 25 Feb 2024 18:30:31 +0000 Subject: [PATCH 266/334] Translating --- uni/lib/generated/intl/messages_en.dart | 2 ++ uni/lib/generated/intl/messages_pt_PT.dart | 2 ++ uni/lib/generated/l10n.dart | 10 ++++++++++ uni/lib/l10n/intl_en.arb | 2 ++ uni/lib/l10n/intl_pt_PT.arb | 2 ++ .../common_widgets/pages_layouts/general/general.dart | 2 +- 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index de7261274..25e361d91 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -196,6 +196,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("No files found"), "no_info": MessageLookupByLibrary.simpleMessage( "There is no information to display"), + "no_internet": MessageLookupByLibrary.simpleMessage( + "It looks like you\'re offline"), "no_library_info": MessageLookupByLibrary.simpleMessage( "No library occupation information available"), "no_link": diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 76da6d9ff..972ec1f70 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -198,6 +198,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Nenhum ficheiro encontrado"), "no_info": MessageLookupByLibrary.simpleMessage( "Não existem informações para apresentar"), + "no_internet": + MessageLookupByLibrary.simpleMessage("Parece que estás offline"), "no_library_info": MessageLookupByLibrary.simpleMessage("Sem informação de ocupação"), "no_link": MessageLookupByLibrary.simpleMessage( diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 30773b6a0..c25203be5 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -1148,6 +1148,16 @@ class S { ); } + /// `It looks like you're offline` + String get no_internet { + return Intl.message( + 'It looks like you\'re offline', + name: 'no_internet', + desc: '', + args: [], + ); + } + /// `No files found` String get no_files_found { return Intl.message( diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index bf8d2121c..b3767aeb1 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -224,6 +224,8 @@ "@of_month": {}, "no_link": "We couldn't open the link", "@no_link": {}, + "no_internet": "It looks like you're offline", + "@no_internet": {}, "no_files_found": "No files found", "@no_files_found": {}, "other_links": "Other links", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 56dbd3945..5be333ef6 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -224,6 +224,8 @@ "@no_selected_courses": {}, "no_selected_exams": "Não existem exames para apresentar", "@no_selected_exams": {}, + "no_internet": "Parece que estás offline", + "@no_internet": {}, "no_print_info": "Sem informação de saldo", "@no_print_info": {}, "no_library_info": "Sem informação de ocupação", diff --git a/uni/lib/view/common_widgets/pages_layouts/general/general.dart b/uni/lib/view/common_widgets/pages_layouts/general/general.dart index a8d7fca8e..5ea94a8ff 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/general.dart @@ -63,7 +63,7 @@ abstract class GeneralPageViewState extends State { padding: const EdgeInsets.symmetric(vertical: 35), child: ImageLabel( imagePath: 'assets/images/no_wifi.png', - label: "It looks like you're offline", + label: S.of(context).no_internet, labelTextStyle: TextStyle( fontWeight: FontWeight.bold, fontSize: 18, From 16a178d126ba313aa0f56eaefd8fbfe1703db0a6 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Mon, 26 Feb 2024 11:02:34 +0000 Subject: [PATCH 267/334] Change local_cafe icon to free_breakfast --- uni/lib/utils/navbar_items.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/lib/utils/navbar_items.dart b/uni/lib/utils/navbar_items.dart index e557aee6e..aeb77a45e 100644 --- a/uni/lib/utils/navbar_items.dart +++ b/uni/lib/utils/navbar_items.dart @@ -14,8 +14,8 @@ enum NavbarItem { NavigationItem.navAcademicPath, ), navRestaurants( - Icons.local_cafe_outlined, - Icons.local_cafe, + Icons.free_breakfast_outlined, + Icons.free_breakfast, NavigationItem.navRestaurants, ), navFaculty(Icons.domain_outlined, Icons.domain, NavigationItem.navFaculty), From 05d96198683441cb7156db08a7378b369022b699 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Mon, 26 Feb 2024 15:32:11 +0000 Subject: [PATCH 268/334] Minor bug --- uni/lib/view/course_unit_info/course_unit_info.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/lib/view/course_unit_info/course_unit_info.dart b/uni/lib/view/course_unit_info/course_unit_info.dart index d57b7f367..80c5ba0e3 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -4,6 +4,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/providers/lazy/course_units_info_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; +import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/page_title.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/course_unit_info/widgets/course_unit_classes.dart'; @@ -155,7 +156,6 @@ class CourseUnitDetailPageViewState } @override - String? getTitle() { - return null; - } + String? getTitle() => + S.of(context).nav_title(NavigationItem.navCourseUnits.route); } From ae1ad2e90e2451d851370a70af229f62f2499bab Mon Sep 17 00:00:00 2001 From: bdmendes Date: Mon, 26 Feb 2024 18:10:32 +0000 Subject: [PATCH 269/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index bf28d8701..1f3f0d1c0 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.6+239 +1.8.0-beta.7+240 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index dcf44716c..66bdb02ad 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.6+239 +version: 1.8.0-beta.7+240 environment: From 42d1a3e36fe2f4fa8ea6e38b562bf5d463008f9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Mon, 26 Feb 2024 14:25:25 +0000 Subject: [PATCH 270/334] build(deps): upgrade dependencies --- uni/pubspec.lock | 272 ++++++++++++++++++++++++----------------------- 1 file changed, 140 insertions(+), 132 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 27418c39d..98b792ff1 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "64.0.0" add_2_calendar: dependency: "direct main" description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "6.2.0" animated_stack_widget: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: "06a96f1249f38a00435b3b0c9a3246d934d7dbc8183fc7c9e56989860edb99d4" + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" url: "https://pub.dev" source: hosted - version: "3.4.4" + version: "3.4.10" args: dependency: transitive description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: asn1lib - sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd" + sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6 url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.5.2" async: dependency: transitive description: @@ -69,10 +69,10 @@ packages: dependency: "direct main" description: name: battery_plus - sha256: "0568fbba70697b8d0c34c1176faa2bc6d61c7fb211a2d2d64e493b91ff72d3f8" + sha256: ba605aeafd6609cb5f8020c609a51941803a5fb2b6a7576f7c7eeeb52d29e750 url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.0.3" battery_plus_platform_interface: dependency: transitive description: @@ -109,18 +109,18 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" build_runner: dependency: "direct dev" description: @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: build_runner_core - sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" url: "https://pub.dev" source: hosted - version: "7.2.11" + version: "7.3.0" built_collection: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: built_value - sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 + sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e url: "https://pub.dev" source: hosted - version: "8.6.3" + version: "8.9.1" cached_network_image: dependency: "direct main" description: @@ -197,10 +197,10 @@ packages: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.1" clock: dependency: transitive description: @@ -213,18 +213,18 @@ packages: dependency: transitive description: name: code_builder - sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.10.0" collection: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: @@ -253,10 +253,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.7.2" crypto: dependency: "direct main" description: @@ -285,26 +285,26 @@ packages: dependency: "direct main" description: name: currency_text_input_formatter - sha256: "5d8db755ccde76817f6f5fb33e65304d7b1db442e1ccff079fe235584c6b7d5a" + sha256: b60c298fec9f0e96dfad88d25d026a6bf43f4e2bb9c59218afd8de1e09f54a60 url: "https://pub.dev" source: hosted - version: "2.1.10" + version: "2.1.11" dart_style: dependency: transitive description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.4" dbus: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" diacritic: dependency: "direct main" description: @@ -357,10 +357,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" fixnum: dependency: transitive description: @@ -402,10 +402,10 @@ packages: dependency: "direct main" description: name: flutter_local_notifications - sha256: "501ed9d54f1c8c0535b7991bade36f9e7e3b45a2346401f03775c1ec7a3c06ae" + sha256: "401643a6ea9d8451365f2ec11145335bf130560cfde367bdf8f0be6d60f89479" url: "https://pub.dev" source: hosted - version: "15.1.2" + version: "15.1.3" flutter_local_notifications_linux: dependency: transitive description: @@ -447,18 +447,18 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "8afc9a6aa6d8e8063523192ba837149dbf3d377a37c0b0fc579149a1fbd4a619" + sha256: "5b24061317f850af858ef7151dadbb6eb77c1c449c954c7bb064e8a5e0e7d81f" url: "https://pub.dev" source: hosted - version: "0.6.18" + version: "0.6.20" flutter_svg: dependency: "direct main" description: name: flutter_svg - sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" url: "https://pub.dev" source: hosted - version: "2.0.9" + version: "2.0.10+1" flutter_test: dependency: "direct dev" description: flutter @@ -513,10 +513,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" http_multi_server: dependency: transitive description: @@ -537,10 +537,10 @@ packages: dependency: "direct main" description: name: image - sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d" + sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "4.1.7" intl: dependency: "direct main" description: @@ -609,10 +609,10 @@ packages: dependency: transitive description: name: markdown - sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd + sha256: "1b134d9f8ff2da15cb298efe6cd8b7d2a78958c1b00384ebcbdf13fe340a6c90" url: "https://pub.dev" source: hosted - version: "7.1.1" + version: "7.2.1" matcher: dependency: transitive description: @@ -641,10 +641,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -657,10 +657,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" mockito: dependency: "direct dev" description: @@ -722,10 +722,10 @@ packages: dependency: transitive description: name: package_info_plus - sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "5.0.1" package_info_plus_platform_interface: dependency: transitive description: @@ -762,18 +762,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" path_provider_linux: dependency: transitive description: @@ -786,10 +786,10 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: @@ -810,18 +810,18 @@ packages: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.2" platform: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.4" plausible_analytics: dependency: "direct main" description: @@ -834,18 +834,18 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.8" pointycastle: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.7.4" polylabel: dependency: transitive description: @@ -906,18 +906,18 @@ packages: dependency: transitive description: name: sentry - sha256: "89e426587b0879e53c46a0aae0eb312696d9d2d803ba14b252a65cc24b1416a2" + sha256: d2ee9c850d876d285f22e2e662f400ec2438df9939fe4acd5d780df9841794ce url: "https://pub.dev" source: hosted - version: "7.14.0" + version: "7.16.1" sentry_flutter: dependency: "direct main" description: name: sentry_flutter - sha256: fd089ee4e75a927be037c56815a0a54af5a519f52b803a5ffecb589bb36e2401 + sha256: "5b428c189c825f16fb14e9166529043f06b965d5b59bfc3a1415e39c082398c0" url: "https://pub.dev" source: hosted - version: "7.14.0" + version: "7.16.1" shared_preferences: dependency: "direct main" description: @@ -938,42 +938,42 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.5" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shelf: dependency: transitive description: @@ -1023,10 +1023,10 @@ packages: dependency: transitive description: name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" source_map_stack_trace: dependency: transitive description: @@ -1051,54 +1051,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" sqflite: dependency: "direct main" description: name: sqflite - sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" + sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6 url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.2" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 + sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5" url: "https://pub.dev" source: hosted - version: "2.5.0+2" + version: "2.5.3" sqflite_common_ffi: dependency: "direct dev" description: name: sqflite_common_ffi - sha256: "35d2fce1e971707c227cc4775cc017d5eafe06c2654c3435ebd5c3ad6c170f5f" + sha256: "754927d82de369a6b9e760fb60640aa81da650f35ffd468d5a992814d6022908" url: "https://pub.dev" source: hosted - version: "2.3.0+4" + version: "2.3.2+1" sqlite3: dependency: transitive description: name: sqlite3 - sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb + sha256: "072128763f1547e3e9b4735ce846bfd226d68019ccda54db4cd427b12dfdedc9" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.4.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1119,10 +1127,10 @@ packages: dependency: "direct main" description: name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.0+1" term_glyph: dependency: transitive description: @@ -1135,26 +1143,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.24.3" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "0.5.9" timelines: dependency: "direct main" description: @@ -1223,26 +1231,26 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 + sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.2.5" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" + sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.3.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 + sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.2.4" url_launcher_linux: dependency: transitive description: @@ -1263,18 +1271,18 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" + sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.3" url_launcher_windows: dependency: transitive description: @@ -1287,34 +1295,34 @@ packages: dependency: transitive description: name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "4.3.3" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43" + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.11+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7" + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.11+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26 + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.11+1" vector_math: dependency: transitive description: @@ -1335,10 +1343,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "13.0.0" watcher: dependency: transitive description: @@ -1351,10 +1359,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1375,10 +1383,10 @@ packages: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.2.0" wkt_parser: dependency: transitive description: @@ -1399,18 +1407,18 @@ packages: dependency: transitive description: name: xdg_directories - sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" xml: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.5.0" yaml: dependency: transitive description: @@ -1420,5 +1428,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.3 <4.0.0" - flutter: ">=3.13.7" + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" From 36d60ed16321fc9276d5cabb0c53cff64c30aae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Mon, 26 Feb 2024 18:50:58 +0000 Subject: [PATCH 271/334] fix: hidden exams showing in home page --- .../local_storage/preferences_controller.dart | 5 + uni/lib/view/home/widgets/exam_card.dart | 112 +++++++++++------- 2 files changed, 72 insertions(+), 45 deletions(-) diff --git a/uni/lib/controller/local_storage/preferences_controller.dart b/uni/lib/controller/local_storage/preferences_controller.dart index 69b2fe3bb..3a8ba7633 100644 --- a/uni/lib/controller/local_storage/preferences_controller.dart +++ b/uni/lib/controller/local_storage/preferences_controller.dart @@ -49,6 +49,10 @@ class PreferencesController { StreamController.broadcast(); static final onStatsToggle = _statsToggleStreamController.stream; + static final _hiddenExamsChangeStreamController = + StreamController>.broadcast(); + static final onHiddenExamsChange = _hiddenExamsChangeStreamController.stream; + /// Returns the last time the data with given key was updated. static DateTime? getLastDataClassUpdateTime(String dataKey) { final lastUpdateTime = prefs.getString(dataKey + _lastUpdateTimeKeySuffix); @@ -235,6 +239,7 @@ class PreferencesController { static Future saveHiddenExams(List newHiddenExams) async { await prefs.setStringList(_hiddenExams, newHiddenExams); + _hiddenExamsChangeStreamController.add(newHiddenExams); } static List getHiddenExams() { diff --git a/uni/lib/view/home/widgets/exam_card.dart b/uni/lib/view/home/widgets/exam_card.dart index 9e7e89473..e41da412f 100644 --- a/uni/lib/view/home/widgets/exam_card.dart +++ b/uni/lib/view/home/widgets/exam_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; +import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/exam.dart'; import 'package:uni/model/providers/lazy/exam_provider.dart'; @@ -36,59 +37,80 @@ class ExamCard extends GenericCard { @override Widget buildCardContent(BuildContext context) { - return LazyConsumer>( - builder: (context, allExams) { - final nextExams = getPrimaryExams( - allExams, - ); - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - NextExamsWidget(exams: nextExams), - if (nextExams.length < maxExamsToDisplay && - allExams.length > nextExams.length) - Column( - children: [ - Container( - margin: const EdgeInsets.only( - right: 80, - left: 80, - top: 7, - bottom: 7, - ), - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: Theme.of(context).dividerColor, + return StreamBuilder( + stream: PreferencesController.onHiddenExamsChange, + initialData: PreferencesController.getHiddenExams(), + builder: (context, snapshot) { + final hiddenExams = snapshot.data ?? []; + + return LazyConsumer>( + builder: (context, allExams) { + final visibleExams = + getVisibleExams(allExams, hiddenExams).toList(); + + final nextExams = getPrimaryExams( + visibleExams, + ); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + NextExamsWidget(exams: nextExams), + if (nextExams.length < maxExamsToDisplay && + visibleExams.length > nextExams.length) + Column( + children: [ + Container( + margin: const EdgeInsets.only( + right: 80, + left: 80, + top: 7, + bottom: 7, + ), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor, + ), + ), ), ), - ), - ), - RemainingExamsWidget( - exams: allExams - .where((exam) => !nextExams.contains(exam)) - .take(maxExamsToDisplay - nextExams.length) - .toList(), + RemainingExamsWidget( + exams: visibleExams + .where((exam) => !nextExams.contains(exam)) + .take(maxExamsToDisplay - nextExams.length) + .toList(), + ), + ], ), - ], - ), - ], + ], + ); + }, + hasContent: (allExams) => + getVisibleExams(allExams, hiddenExams).isNotEmpty, + onNullContent: Center( + child: Text( + S.of(context).no_selected_exams, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + contentLoadingWidget: const ExamCardShimmer(), ); }, - hasContent: (allExams) => allExams.isNotEmpty, - onNullContent: Center( - child: Text( - S.of(context).no_selected_exams, - style: Theme.of(context).textTheme.titleLarge, - ), - ), - contentLoadingWidget: const ExamCardShimmer(), ); } - List getPrimaryExams(List allExams) { - return allExams - .where((exam) => isSameDay(allExams[0].begin, exam.begin)) + Iterable getVisibleExams( + List allExams, + List hiddenExams, + ) { + final hiddenExamsSet = Set.from(hiddenExams); + return allExams.where((exam) => !hiddenExamsSet.contains(exam.id)); + } + + List getPrimaryExams(List visibleExams) { + return visibleExams + .where((exam) => isSameDay(visibleExams[0].begin, exam.begin)) .toList(); } From 69f216d3a6d0fdbc6311e4ea2f59b73499813c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Mon, 26 Feb 2024 14:57:13 +0000 Subject: [PATCH 272/334] fix: null ects on courses --- uni/lib/view/course_units/widgets/course_unit_card.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/course_units/widgets/course_unit_card.dart b/uni/lib/view/course_units/widgets/course_unit_card.dart index 73ee045e8..64e9f80e6 100644 --- a/uni/lib/view/course_units/widgets/course_unit_card.dart +++ b/uni/lib/view/course_units/widgets/course_unit_card.dart @@ -20,7 +20,10 @@ class CourseUnitCard extends GenericCard { padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), child: Row( children: [ - Text("${courseUnit.ects.toString().replaceAll('.0', '')} ECTS"), + if (courseUnit.ects != null) + Text("${courseUnit.ects.toString().replaceAll('.0', '')} ECTS") + else + const Text('--- ECTS'), const Spacer(), Text(courseUnit.grade ?? '-'), ], From 471057d277fb2e07fab8a1ab2f04063f6332d9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Mon, 26 Feb 2024 18:57:10 +0000 Subject: [PATCH 273/334] feat: remove placeholder for ECTS when ECTS is null --- uni/lib/view/course_units/widgets/course_unit_card.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/uni/lib/view/course_units/widgets/course_unit_card.dart b/uni/lib/view/course_units/widgets/course_unit_card.dart index 64e9f80e6..f88d31429 100644 --- a/uni/lib/view/course_units/widgets/course_unit_card.dart +++ b/uni/lib/view/course_units/widgets/course_unit_card.dart @@ -21,9 +21,7 @@ class CourseUnitCard extends GenericCard { child: Row( children: [ if (courseUnit.ects != null) - Text("${courseUnit.ects.toString().replaceAll('.0', '')} ECTS") - else - const Text('--- ECTS'), + Text("${courseUnit.ects.toString().replaceAll('.0', '')} ECTS"), const Spacer(), Text(courseUnit.grade ?? '-'), ], From 43957308508fb33d0ec9e10c12a25335b019cb37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Mon, 26 Feb 2024 14:25:25 +0000 Subject: [PATCH 274/334] build(deps): upgrade dependencies --- uni/pubspec.lock | 272 ++++++++++++++++++++++++----------------------- 1 file changed, 140 insertions(+), 132 deletions(-) diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 27418c39d..98b792ff1 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "64.0.0" add_2_calendar: dependency: "direct main" description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "6.2.0" animated_stack_widget: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: "06a96f1249f38a00435b3b0c9a3246d934d7dbc8183fc7c9e56989860edb99d4" + sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d" url: "https://pub.dev" source: hosted - version: "3.4.4" + version: "3.4.10" args: dependency: transitive description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: asn1lib - sha256: "21afe4333076c02877d14f4a89df111e658a6d466cbfc802eb705eb91bd5adfd" + sha256: c9c85fedbe2188b95133cbe960e16f5f448860f7133330e272edbbca5893ddc6 url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.5.2" async: dependency: transitive description: @@ -69,10 +69,10 @@ packages: dependency: "direct main" description: name: battery_plus - sha256: "0568fbba70697b8d0c34c1176faa2bc6d61c7fb211a2d2d64e493b91ff72d3f8" + sha256: ba605aeafd6609cb5f8020c609a51941803a5fb2b6a7576f7c7eeeb52d29e750 url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "5.0.3" battery_plus_platform_interface: dependency: transitive description: @@ -109,18 +109,18 @@ packages: dependency: transitive description: name: build_daemon - sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "4.0.1" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "64e12b0521812d1684b1917bc80945625391cb9bdd4312536b1d69dcb6133ed8" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.2" build_runner: dependency: "direct dev" description: @@ -133,10 +133,10 @@ packages: dependency: transitive description: name: build_runner_core - sha256: c9e32d21dd6626b5c163d48b037ce906bbe428bc23ab77bcd77bb21e593b6185 + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" url: "https://pub.dev" source: hosted - version: "7.2.11" + version: "7.3.0" built_collection: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: built_value - sha256: a8de5955205b4d1dbbbc267daddf2178bd737e4bab8987c04a500478c9651e74 + sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e url: "https://pub.dev" source: hosted - version: "8.6.3" + version: "8.9.1" cached_network_image: dependency: "direct main" description: @@ -197,10 +197,10 @@ packages: dependency: transitive description: name: cli_util - sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.4.1" clock: dependency: transitive description: @@ -213,18 +213,18 @@ packages: dependency: transitive description: name: code_builder - sha256: "1be9be30396d7e4c0db42c35ea6ccd7cc6a1e19916b5dc64d6ac216b5544d677" + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 url: "https://pub.dev" source: hosted - version: "4.7.0" + version: "4.10.0" collection: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: @@ -253,10 +253,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.7.2" crypto: dependency: "direct main" description: @@ -285,26 +285,26 @@ packages: dependency: "direct main" description: name: currency_text_input_formatter - sha256: "5d8db755ccde76817f6f5fb33e65304d7b1db442e1ccff079fe235584c6b7d5a" + sha256: b60c298fec9f0e96dfad88d25d026a6bf43f4e2bb9c59218afd8de1e09f54a60 url: "https://pub.dev" source: hosted - version: "2.1.10" + version: "2.1.11" dart_style: dependency: transitive description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.4" dbus: dependency: transitive description: name: dbus - sha256: "6f07cba3f7b3448d42d015bfd3d53fe12e5b36da2423f23838efc1d5fb31a263" + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" url: "https://pub.dev" source: hosted - version: "0.7.8" + version: "0.7.10" diacritic: dependency: "direct main" description: @@ -357,10 +357,10 @@ packages: dependency: transitive description: name: file - sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" url: "https://pub.dev" source: hosted - version: "6.1.4" + version: "7.0.0" fixnum: dependency: transitive description: @@ -402,10 +402,10 @@ packages: dependency: "direct main" description: name: flutter_local_notifications - sha256: "501ed9d54f1c8c0535b7991bade36f9e7e3b45a2346401f03775c1ec7a3c06ae" + sha256: "401643a6ea9d8451365f2ec11145335bf130560cfde367bdf8f0be6d60f89479" url: "https://pub.dev" source: hosted - version: "15.1.2" + version: "15.1.3" flutter_local_notifications_linux: dependency: transitive description: @@ -447,18 +447,18 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "8afc9a6aa6d8e8063523192ba837149dbf3d377a37c0b0fc579149a1fbd4a619" + sha256: "5b24061317f850af858ef7151dadbb6eb77c1c449c954c7bb064e8a5e0e7d81f" url: "https://pub.dev" source: hosted - version: "0.6.18" + version: "0.6.20" flutter_svg: dependency: "direct main" description: name: flutter_svg - sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" url: "https://pub.dev" source: hosted - version: "2.0.9" + version: "2.0.10+1" flutter_test: dependency: "direct dev" description: flutter @@ -513,10 +513,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" http_multi_server: dependency: transitive description: @@ -537,10 +537,10 @@ packages: dependency: "direct main" description: name: image - sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d" + sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" url: "https://pub.dev" source: hosted - version: "4.1.4" + version: "4.1.7" intl: dependency: "direct main" description: @@ -609,10 +609,10 @@ packages: dependency: transitive description: name: markdown - sha256: acf35edccc0463a9d7384e437c015a3535772e09714cf60e07eeef3a15870dcd + sha256: "1b134d9f8ff2da15cb298efe6cd8b7d2a78958c1b00384ebcbdf13fe340a6c90" url: "https://pub.dev" source: hosted - version: "7.1.1" + version: "7.2.1" matcher: dependency: transitive description: @@ -641,10 +641,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -657,10 +657,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" mockito: dependency: "direct dev" description: @@ -722,10 +722,10 @@ packages: dependency: transitive description: name: package_info_plus - sha256: "6ff267fcd9d48cb61c8df74a82680e8b82e940231bb5f68356672fde0397334a" + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" url: "https://pub.dev" source: hosted - version: "4.1.0" + version: "5.0.1" package_info_plus_platform_interface: dependency: transitive description: @@ -762,18 +762,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "6b8b19bd80da4f11ce91b2d1fb931f3006911477cec227cce23d3253d80df3f1" + sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.2" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" path_provider_linux: dependency: transitive description: @@ -786,10 +786,10 @@ packages: dependency: transitive description: name: path_provider_platform_interface - sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c" + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" path_provider_windows: dependency: transitive description: @@ -810,18 +810,18 @@ packages: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.2" platform: dependency: transitive description: name: platform - sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.4" plausible_analytics: dependency: "direct main" description: @@ -834,18 +834,18 @@ packages: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.8" pointycastle: dependency: transitive description: name: pointycastle - sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" url: "https://pub.dev" source: hosted - version: "3.7.3" + version: "3.7.4" polylabel: dependency: transitive description: @@ -906,18 +906,18 @@ packages: dependency: transitive description: name: sentry - sha256: "89e426587b0879e53c46a0aae0eb312696d9d2d803ba14b252a65cc24b1416a2" + sha256: d2ee9c850d876d285f22e2e662f400ec2438df9939fe4acd5d780df9841794ce url: "https://pub.dev" source: hosted - version: "7.14.0" + version: "7.16.1" sentry_flutter: dependency: "direct main" description: name: sentry_flutter - sha256: fd089ee4e75a927be037c56815a0a54af5a519f52b803a5ffecb589bb36e2401 + sha256: "5b428c189c825f16fb14e9166529043f06b965d5b59bfc3a1415e39c082398c0" url: "https://pub.dev" source: hosted - version: "7.14.0" + version: "7.16.1" shared_preferences: dependency: "direct main" description: @@ -938,42 +938,42 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.5" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" shelf: dependency: transitive description: @@ -1023,10 +1023,10 @@ packages: dependency: transitive description: name: source_gen - sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.5.0" source_map_stack_trace: dependency: transitive description: @@ -1051,54 +1051,62 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" sqflite: dependency: "direct main" description: name: sqflite - sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a" + sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6 url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.3.2" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6 + sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5" url: "https://pub.dev" source: hosted - version: "2.5.0+2" + version: "2.5.3" sqflite_common_ffi: dependency: "direct dev" description: name: sqflite_common_ffi - sha256: "35d2fce1e971707c227cc4775cc017d5eafe06c2654c3435ebd5c3ad6c170f5f" + sha256: "754927d82de369a6b9e760fb60640aa81da650f35ffd468d5a992814d6022908" url: "https://pub.dev" source: hosted - version: "2.3.0+4" + version: "2.3.2+1" sqlite3: dependency: transitive description: name: sqlite3 - sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb + sha256: "072128763f1547e3e9b4735ce846bfd226d68019ccda54db4cd427b12dfdedc9" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.4.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1119,10 +1127,10 @@ packages: dependency: "direct main" description: name: synchronized - sha256: "5fcbd27688af6082f5abd611af56ee575342c30e87541d0245f7ff99faa02c60" + sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "3.1.0+1" term_glyph: dependency: transitive description: @@ -1135,26 +1143,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.24.3" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "0.5.9" timelines: dependency: "direct main" description: @@ -1223,26 +1231,26 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 + sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" url: "https://pub.dev" source: hosted - version: "6.2.2" + version: "6.2.5" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" + sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.3.0" url_launcher_ios: dependency: transitive description: name: url_launcher_ios - sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 + sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.2.4" url_launcher_linux: dependency: transitive description: @@ -1263,18 +1271,18 @@ packages: dependency: transitive description: name: url_launcher_platform_interface - sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.3.2" url_launcher_web: dependency: transitive description: name: url_launcher_web - sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" + sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.3" url_launcher_windows: dependency: transitive description: @@ -1287,34 +1295,34 @@ packages: dependency: transitive description: name: uuid - sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 url: "https://pub.dev" source: hosted - version: "3.0.7" + version: "4.3.3" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43" + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.11+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7" + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.11+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26 + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" url: "https://pub.dev" source: hosted - version: "1.1.9+1" + version: "1.1.11+1" vector_math: dependency: transitive description: @@ -1335,10 +1343,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "13.0.0" watcher: dependency: transitive description: @@ -1351,10 +1359,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1375,10 +1383,10 @@ packages: dependency: transitive description: name: win32 - sha256: "350a11abd2d1d97e0cc7a28a81b781c08002aa2864d9e3f192ca0ffa18b06ed3" + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "5.0.9" + version: "5.2.0" wkt_parser: dependency: transitive description: @@ -1399,18 +1407,18 @@ packages: dependency: transitive description: name: xdg_directories - sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2" + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" xml: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.5.0" yaml: dependency: transitive description: @@ -1420,5 +1428,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.3 <4.0.0" - flutter: ">=3.13.7" + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" From a0d9387beb19ffbf723f31e98dc5f448dab8f41c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Mon, 26 Feb 2024 17:09:30 +0000 Subject: [PATCH 275/334] fix: dark background for hidden exams --- uni/lib/view/theme.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/theme.dart b/uni/lib/view/theme.dart index 72585db48..c2eefca59 100644 --- a/uni/lib/view/theme.dart +++ b/uni/lib/view/theme.dart @@ -38,7 +38,10 @@ ThemeData applicationLightTheme = ThemeData( onTertiary: Colors.black, ), primaryColor: darkRed, + cardColor: Colors.white, dividerColor: _lightGrey, + hintColor: _lightGrey, + indicatorColor: darkRed, primaryTextTheme: Typography().black.copyWith( headlineMedium: const TextStyle(color: _strongGrey), bodyLarge: const TextStyle(color: _strongGrey), @@ -62,8 +65,8 @@ ThemeData applicationDarkTheme = ThemeData( ), primaryColor: _lightGrey, cardColor: _mildBlack, - hintColor: _darkishBlack, dividerColor: _strongGrey, + hintColor: _darkishBlack, indicatorColor: _lightGrey, primaryTextTheme: Typography().white, iconTheme: const IconThemeData(color: _lightGrey), From ac2607832074032d29fe9b7522866a290911fafb Mon Sep 17 00:00:00 2001 From: LuisDuarte1 Date: Mon, 26 Feb 2024 19:35:02 +0000 Subject: [PATCH 276/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 1f3f0d1c0..0c7e3bae7 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.7+240 +1.8.0-beta.8+241 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 66bdb02ad..ce845d0a1 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.7+240 +version: 1.8.0-beta.8+241 environment: From 4e8108898e79d441651a3ebcbb14138a954049be Mon Sep 17 00:00:00 2001 From: LuisDuarte1 Date: Mon, 26 Feb 2024 19:55:53 +0000 Subject: [PATCH 277/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 0c7e3bae7..dfb7caa87 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.8+241 +1.8.0-beta.9+242 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index ce845d0a1..53d1ca92f 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.8+241 +version: 1.8.0-beta.9+242 environment: From c39a27e246a6f6f71257f69ad2401a152ee0500c Mon Sep 17 00:00:00 2001 From: limwa Date: Mon, 26 Feb 2024 21:26:19 +0000 Subject: [PATCH 278/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index dfb7caa87..9bac51740 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.9+242 +1.8.0-beta.10+243 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 53d1ca92f..927581f5e 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.9+242 +version: 1.8.0-beta.10+243 environment: From dbe3b7bc10314aa843cedd8896daa8d6010b6c26 Mon Sep 17 00:00:00 2001 From: limwa Date: Tue, 27 Feb 2024 16:45:50 +0000 Subject: [PATCH 279/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 9bac51740..96e34c37f 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.10+243 +1.8.0-beta.11+244 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 927581f5e..b6cdd83a3 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.10+243 +version: 1.8.0-beta.11+244 environment: From 45f2acc32f8964c0e1d6e12f8dc66024c4d34456 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Wed, 28 Feb 2024 14:11:33 +0000 Subject: [PATCH 280/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 96e34c37f..c73501dc2 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.11+244 +1.8.0-beta.12+245 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index b6cdd83a3..a14e263c4 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.11+244 +version: 1.8.0-beta.12+245 environment: From 1dd592cad0a1464c58d73a3548593b05550d0a0d Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 28 Feb 2024 14:52:29 +0000 Subject: [PATCH 281/334] Resize bottom navbar --- .../pages_layouts/general/widgets/bottom_navigation_bar.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart index a72ee955c..ab00be2d4 100644 --- a/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart +++ b/uni/lib/view/common_widgets/pages_layouts/general/widgets/bottom_navigation_bar.dart @@ -64,6 +64,8 @@ class AppBottomNavbar extends StatelessWidget { ? Theme.of(context).colorScheme.onSurface : Theme.of(context).colorScheme.secondary, unselectedItemColor: Theme.of(context).colorScheme.onSurface, + selectedFontSize: 0, + unselectedFontSize: 0, showSelectedLabels: false, showUnselectedLabels: false, ), From 1cca043f5a3ce0b2f9800251d9df1097e9f47101 Mon Sep 17 00:00:00 2001 From: Process-ing Date: Wed, 28 Feb 2024 15:13:46 +0000 Subject: [PATCH 282/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index c73501dc2..99cf547e1 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.12+245 +1.8.0-beta.13+246 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index a14e263c4..7155e79f4 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.12+245 +version: 1.8.0-beta.13+246 environment: From 90b41751b6f116b343c96a4af28a3652f0a1fa86 Mon Sep 17 00:00:00 2001 From: eduardagmagno <145236480+eduardagmagno@users.noreply.github.com> Date: Sun, 17 Dec 2023 00:16:13 +0000 Subject: [PATCH 283/334] UpdateTermsAndConditions.md In the License and Usage Restrictions, we need to add which API's track users data and what data they track, acording to the playstores terms. It is better fit for someone that knows this to add it to these terms. --- uni/assets/text/TermsAndConditions.md | 122 +++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 14 deletions(-) diff --git a/uni/assets/text/TermsAndConditions.md b/uni/assets/text/TermsAndConditions.md index 9d4beaf66..e19ad029f 100644 --- a/uni/assets/text/TermsAndConditions.md +++ b/uni/assets/text/TermsAndConditions.md @@ -4,50 +4,144 @@ \ **Terms and conditions** -These terms and conditions ("Terms", "Agreement") are an agreement between NIAEFEUP ("us", "we" or "our"), Núcleo de Informática da Associação de Estudantes da Universidade do Porto, and you ("User", "you" or "your"). This Agreement sets forth the general terms and conditions of your use of the uni mobile application and any of its products or services (collectively, "Mobile Application" or "Services"). +These terms and conditions ("Terms", "Agreement") are an agreement between NIAEFEUP ("us", "we" or " +our"), Núcleo de Informática da Associação de Estudantes da Universidade do Porto, and you (" +User", "you" or "your"). This Agreement sets forth the general terms and conditions of your use of +the Uni mobile application and any of its products or services (collectively, "Mobile Application" +or "Services"). \ \ **Accounts and membership** -To use the Mobile Application, you are required to hold a digital account provided by Universidade do Porto. We are in no way affiliated with this university. Any act performed by you through the Mobile Application is subject to the [Universidade do Porto's terms and conditions](https://sigarra.up.pt/up/pt/web_base.gera_pagina?p_pagina=termos%20e%20condicoes). -By using your faculty account in the Mobile Application, you are responsible for maintaining the security of your account and you are fully responsible for all activities that occur under the account and any other actions taken in connection with it. We will not be liable for any acts or omissions by you, including any damages of any kind incurred as a result of such acts or omissions. -Any interaction with your data from Universidade do Porto's platform is made directly through it, and is not collected by us in any way. -If you choose to select the option to keep you logged in, you understand that your sign-in details may be stored locally, but not in any way shared with us. These details are stored in the application data and we hold no responsibility for ensuring their concealment. +To use the Uni Mobile Application, you are required to hold a digital account provided by +Universidade +do Porto. We are in no way affiliated with this university. Any act performed by you through the +Mobile Application is subject to +the [Universidade do Porto's terms and conditions](https://sigarra.up.pt/up/pt/web_base.gera_pagina?p_pagina=termos%20e%20condicoes). +By using your faculty account in the Mobile Application, you are responsible for maintaining the +security of your account and you are fully responsible for all activities that occur under the +account and any other actions taken in connection with it. We will not be liable for any acts or +omissions by you, including any damages of any kind incurred as a result of such acts or omissions. +Any interaction with your data from Universidade do Porto's platform is made directly through it, +and is not collected by us in any way. +If you choose to select the option to keep you logged in, you understand that your sign-in details +may be stored locally, but not in any way shared with us. These details are stored in the +application data and we hold no responsibility for ensuring their concealment. +\ +\ +**Ownership of Content:** + +We do not own any data, information or material ("Content") that you submit in the Mobile +Application in the course of using the Service. You shall have sole responsibility for the accuracy, +quality, integrity, legality, reliability, appropriateness, and intellectual property ownership or +right to use of all submitted Content. +\ +\ +**Monitoring and Reviewing:** + +We may, at our discretion but with no obligation, monitor and review Content in the Mobile +Application submitted or created using our Services by you. +\ +\ +**License and Usage Restrictions:** + +You grant us permission to access, copy, distribute, store, transmit, reformat, display and perform +the Content of your user account solely as required for the purpose of providing the Services to +you. +Unless specifically permitted by you, your use of the Mobile Application does not grant us the +license to link, reproduce, adapt, modify, publish or distribute the Content created by you or +stored in your user account, for evaluating the effectiveness of existing product features, planning +new features, or measuring size and characteristics about specific end-users, or devices with a data +broker, for commercial, marketing or any similar purpose. Unless specifically permitted by you, your +use of the Mobile Application does not grant us the +license to use analytics tools, advertising networks, third-party SDKs, or other external vendors +whose code we have added to the Uni Mobile Application, to collect information such as user ID, +device ID, or profiles with Third-Party Data for the purpose of accessing necessary data to +implement +the Services we provide. We do not have the license to sell or share sensitive data accessed trough +API' permissions with the +finality of facilitating sales. +\ +\ +**Data Collection and Privacy:** + +Personal data of users is not collected for any purpose other than providing the Services to +Personal data of users is not collected for any other purpose, unless providing the Services to you, +except in certain circumstances described +in [Play Store’ Privacy policy](https://support.google.com/googleplay/android-developer/answer/10144311?visit_id=638365485539535125-3072678242&rd=1) +and [App Stores privacy policy](https://developer.apple.com/app-store/app-privacy-details/#user-tracking). +\ +\ +**All user privacy and data use is in compliance with Play Store and App Store Policies mentioned +above.** \ \ -**User content** +**Content Removal:** -We do not own any data, information or material ("Content") that you submit in the Mobile Application in the course of using the Service. You shall have sole responsibility for the accuracy, quality, integrity, legality, reliability, appropriateness, and intellectual property ownership or right to use of all submitted Content. We may, but have no obligation to, monitor and review Content in the Mobile Application submitted or created using our Services by you. Unless specifically permitted by you, your use of the Mobile Application does not grant us the license to use, reproduce, adapt, modify, publish or distribute the Content created by you or stored in your user account for commercial, marketing or any similar purpose. But you grant us permission to access, copy, distribute, store, transmit, reformat, display and perform the Content of your user account solely as required for the purpose of providing the Services to you. Without limiting any of those representations or warranties, we have the right, though not the obligation, to, in our own sole discretion, refuse or remove any Content that, in our reasonable opinion, violates any of our policies or is in any way harmful or objectionable. +Without limiting any of those representations or warranties, we have the right, though not the +obligation, to, in our own sole discretion, refuse or remove any Content that, in our reasonable +opinion, violates any of our policies or is in any way harmful or objectionable. \ \ **Backups** -We are not responsible for Content residing in the Mobile Application. In no event shall we be held liable for any loss of any Content. It is your sole responsibility to maintain appropriate backup of your Content. +We are not responsible for Content residing in the Mobile Application. In no event shall we be held +liable for any loss of any Content. It is your sole responsibility to maintain appropriate backup of +your Content. \ \ **Links to other mobile applications** -Although this Mobile Application may link to other mobile applications, we are not, directly or indirectly, implying any approval, association, sponsorship, endorsement, or affiliation with any linked mobile application, unless specifically stated herein. Some of the links in the Mobile Application may be "affiliate links". This means if you click on the link and purchase an item, NIAEFEUP will receive an affiliate commission. We are not responsible for examining or evaluating, and we do not warrant the offerings of, any businesses or individuals or the content of their mobile applications. We do not assume any responsibility or liability for the actions, products, services, and content of any other third-parties. You should carefully review the legal statements and other conditions of use of any mobile application which you access through a link from this Mobile Application. Your linking to any other off-site mobile applications is at your own risk. +Although this Mobile Application may link to other mobile applications, we are not, directly or +indirectly, implying any approval, association, sponsorship, endorsement, or affiliation with any +linked mobile application, unless specifically stated herein. Some of the links in the Mobile +Application may be "affiliate links". This means if you click on the link and purchase an item, +NIAEFEUP will receive an affiliate commission. We are not responsible for examining or evaluating, +and we do not warrant the offerings of, any businesses or individuals or the content of their mobile +applications. We do not assume any responsibility or liability for the actions, products, services, +and content of any other third-parties. You should carefully review the legal statements and other +conditions of use of any mobile application which you access through a link from this Mobile +Application. Your linking to any other off-site mobile applications is at your own risk. \ \ **Limitation of liability** -To the fullest extent permitted by applicable law, in no event will NIAEFEUP, its affiliates, officers, directors, employees, agents, suppliers or licensors be liable to any person for (a): any indirect, incidental, special, punitive, cover or consequential damages (including, without limitation, damages for lost profits, revenue, sales, goodwill, use of content, impact on business, business interruption, loss of anticipated savings, loss of business opportunity) however caused, under any theory of liability, including, without limitation, contract, tort, warranty, breach of statutory duty, negligence or otherwise, even if NIAEFEUP has been advised as to the possibility of such damages or could have foreseen such damages. To the maximum extent permitted by applicable law, the aggregate liability of NIAEFEUP and its affiliates, officers, employees, agents, suppliers and licensors, relating to the services will be limited to an amount greater of one dollar or any amounts actually paid in cash by you to NIAEFEUP for the prior one month period prior to the first event or occurrence giving rise to such liability. The limitations and exclusions also apply if this remedy does not fully compensate you for any losses or fails of its essential purpose. +To the fullest extent permitted by applicable law, in no event will NIAEFEUP, its affiliates, +officers, directors, employees, agents, suppliers or licensors be liable to any person for (a): any +indirect, incidental, special, punitive, cover or consequential damages (including, without +limitation, damages for lost profits, revenue, sales, goodwill, use of content, impact on business, +business interruption, loss of anticipated savings, loss of business opportunity) however caused, +under any theory of liability, including, without limitation, contract, tort, warranty, breach of +statutory duty, negligence or otherwise, even if NIAEFEUP has been advised as to the possibility of +such damages or could have foreseen such damages. To the maximum extent permitted by applicable law, +the aggregate liability of NIAEFEUP and its affiliates, officers, employees, agents, suppliers and +licensors, relating to the services will be limited to an amount greater of one dollar or any +amounts actually paid in cash by you to NIAEFEUP for the prior one month period prior to the first +event or occurrence giving rise to such liability. The limitations and exclusions also apply if this +remedy does not fully compensate you for any losses or fails of its essential purpose. \ \ **Changes and amendments** -We reserve the right to modify this Agreement or its policies relating to the Mobile Application or Services at any time, effective upon posting of an updated version of this Agreement in the Mobile Application. When we do, we will revise the updated date at the bottom of this page. Continued use of the Mobile Application after any such changes shall constitute your consent to such changes. Policy was created with [Website Policies](https://www.websitepolicies.com). +We reserve the right to modify this Agreement or its policies relating to the Mobile Application or +Services at any time, effective upon posting of an updated version of this Agreement in the Mobile +Application. When we do, we will revise the updated date at the bottom of this page. Continued use +of the Mobile Application after any such changes shall constitute your consent to such changes. +Policy was created with [Website Policies](https://www.websitepolicies.com). \ \ **Acceptance of these terms** -You acknowledge that you have read this Agreement and agree to all its terms and conditions. By using the Mobile Application or its Services you agree to be bound by this Agreement. If you do not agree to abide by the terms of this Agreement, you are not authorized to use or access the Mobile Application and its Services. +You acknowledge that you have read this Agreement and agree to all its terms and conditions. By +using the Mobile Application or its Services you agree to be bound by this Agreement. If you do not +agree to abide by the terms of this Agreement, you are not authorized to use or access the Mobile +Application and its Services. \ \ **Contacting us** -If you would like to contact us to understand more about this Agreement or wish to contact us concerning any matter relating to it, you may send an email to [ni@aefeup.pt](mailto:ni@aefeup.pt). +If you would like to contact us to understand more about this Agreement or wish to contact us +concerning any matter relating to it, you may send an email to [ni@aefeup.pt](mailto:ni@aefeup.pt). \ \ -This document was last updated on February 25, 2021 +This document was last updated on November 30, 2023 From 6af7f5919257541e7d998dd4de868296b307a410 Mon Sep 17 00:00:00 2001 From: Eduarda Magno Date: Sun, 17 Dec 2023 16:32:41 +0000 Subject: [PATCH 284/334] Edited suggestions T&C --- uni/assets/text/TermsAndConditions.md | 32 ++++++++++++--------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/uni/assets/text/TermsAndConditions.md b/uni/assets/text/TermsAndConditions.md index e19ad029f..b83543236 100644 --- a/uni/assets/text/TermsAndConditions.md +++ b/uni/assets/text/TermsAndConditions.md @@ -29,7 +29,7 @@ may be stored locally, but not in any way shared with us. These details are stor application data and we hold no responsibility for ensuring their concealment. \ \ -**Ownership of Content:** +**Ownership of Content** We do not own any data, information or material ("Content") that you submit in the Mobile Application in the course of using the Service. You shall have sole responsibility for the accuracy, @@ -37,10 +37,17 @@ quality, integrity, legality, reliability, appropriateness, and intellectual pro right to use of all submitted Content. \ \ -**Monitoring and Reviewing:** +**Data Usage, Monitoring and Privacy** We may, at our discretion but with no obligation, monitor and review Content in the Mobile Application submitted or created using our Services by you. +Personal data of users is not collected for any other purpose, unless providing the Services to you, +except in certain circumstances described +in [Play Store’ Privacy policy](https://support.google.com/googleplay/android-developer/answer/10144311?visit_id=638365485539535125-3072678242&rd=1) +and [App Stores privacy policy](https://developer.apple.com/app-store/app-privacy-details/#user-tracking). +\ +\ +**All user privacy and data use is in compliance with Play Store and App Store Policies.** \ \ **License and Usage Restrictions:** @@ -52,30 +59,19 @@ Unless specifically permitted by you, your use of the Mobile Application does no license to link, reproduce, adapt, modify, publish or distribute the Content created by you or stored in your user account, for evaluating the effectiveness of existing product features, planning new features, or measuring size and characteristics about specific end-users, or devices with a data -broker, for commercial, marketing or any similar purpose. Unless specifically permitted by you, your +broker, for commercial, marketing or any similar purpose. +Unless specifically permitted by you, your use of the Mobile Application does not grant us the license to use analytics tools, advertising networks, third-party SDKs, or other external vendors whose code we have added to the Uni Mobile Application, to collect information such as user ID, device ID, or profiles with Third-Party Data for the purpose of accessing necessary data to implement -the Services we provide. We do not have the license to sell or share sensitive data accessed trough +the Services we provide. As of the last update, we anonymously track page visits and widget clicks, which is shared with our self-hosted instance of [Plausible Analytics](https://plausible.niaefeup.pt/). +We do not have the license to sell or share sensitive data accessed trough API' permissions with the finality of facilitating sales. \ \ -**Data Collection and Privacy:** - -Personal data of users is not collected for any purpose other than providing the Services to -Personal data of users is not collected for any other purpose, unless providing the Services to you, -except in certain circumstances described -in [Play Store’ Privacy policy](https://support.google.com/googleplay/android-developer/answer/10144311?visit_id=638365485539535125-3072678242&rd=1) -and [App Stores privacy policy](https://developer.apple.com/app-store/app-privacy-details/#user-tracking). -\ -\ -**All user privacy and data use is in compliance with Play Store and App Store Policies mentioned -above.** -\ -\ **Content Removal:** Without limiting any of those representations or warranties, we have the right, though not the @@ -126,7 +122,7 @@ remedy does not fully compensate you for any losses or fails of its essential pu We reserve the right to modify this Agreement or its policies relating to the Mobile Application or Services at any time, effective upon posting of an updated version of this Agreement in the Mobile Application. When we do, we will revise the updated date at the bottom of this page. Continued use -of the Mobile Application after any such changes shall constitute your consent to such changes. +of the Mobile Application after any such changes will only constitute if when prompted, you consent to them. Otherwise, you will be logged out of the Mobile Application. Policy was created with [Website Policies](https://www.websitepolicies.com). \ \ From f221af25d5fc2167bc11ae2032f4826f2cc2f7da Mon Sep 17 00:00:00 2001 From: eduardagmagno Date: Thu, 28 Dec 2023 02:24:48 +0000 Subject: [PATCH 285/334] 2_Edit_Terms&Conditions --- uni/assets/text/TermsAndConditions.md | 161 +++++++++++++------------- 1 file changed, 78 insertions(+), 83 deletions(-) diff --git a/uni/assets/text/TermsAndConditions.md b/uni/assets/text/TermsAndConditions.md index b83543236..78aec6efa 100644 --- a/uni/assets/text/TermsAndConditions.md +++ b/uni/assets/text/TermsAndConditions.md @@ -1,143 +1,138 @@ -# App desenvolvida pelo NIAEFEUP. -# De estudantes, para estudantes. +# app desenvolvida pelo niaefeup. +# de estudantes, para estudantes. \ \ -**Terms and conditions** +**terms and conditions** -These terms and conditions ("Terms", "Agreement") are an agreement between NIAEFEUP ("us", "we" or " -our"), Núcleo de Informática da Associação de Estudantes da Universidade do Porto, and you (" -User", "you" or "your"). This Agreement sets forth the general terms and conditions of your use of -the Uni mobile application and any of its products or services (collectively, "Mobile Application" -or "Services"). +these terms and conditions ("terms", "agreement") are an agreement between niaefeup ("us", "we" or " +our"), núcleo de informática da associação de estudantes da universidade do porto, and you (" +user", "you" or "your"). this agreement sets forth the general terms and conditions of your use of +the uni mobile application and any of its products or services (collectively, "mobile application" +or "services"). \ \ -**Accounts and membership** +**accounts and membership** -To use the Uni Mobile Application, you are required to hold a digital account provided by -Universidade -do Porto. We are in no way affiliated with this university. Any act performed by you through the -Mobile Application is subject to -the [Universidade do Porto's terms and conditions](https://sigarra.up.pt/up/pt/web_base.gera_pagina?p_pagina=termos%20e%20condicoes). -By using your faculty account in the Mobile Application, you are responsible for maintaining the +to use the uni mobile application, you are required to hold a digital account provided by +universidade +do porto. we are in no way affiliated with this university. any act performed by you through the +mobile application is subject to +the [universidade do porto's terms and conditions](https://sigarra.up.pt/up/pt/web_base.gera_pagina?p_pagina=termos%20e%20condicoes). +by using your faculty account in the mobile application, you are responsible for maintaining the security of your account and you are fully responsible for all activities that occur under the -account and any other actions taken in connection with it. We will not be liable for any acts or +account and any other actions taken in connection with it. we will not be liable for any acts or omissions by you, including any damages of any kind incurred as a result of such acts or omissions. -Any interaction with your data from Universidade do Porto's platform is made directly through it, +any interaction with your data from universidade do porto's platform is made directly through it, and is not collected by us in any way. -If you choose to select the option to keep you logged in, you understand that your sign-in details -may be stored locally, but not in any way shared with us. These details are stored in the +if you choose to select the option to keep you logged in, you understand that your sign-in details +may be stored locally, but not in any way shared with us. these details are stored in the application data and we hold no responsibility for ensuring their concealment. \ \ -**Ownership of Content** +**ownership of content** -We do not own any data, information or material ("Content") that you submit in the Mobile -Application in the course of using the Service. You shall have sole responsibility for the accuracy, +we do not own any data, information or material ("content") that you submit in the mobile +application in the course of using the service. you shall have sole responsibility for the accuracy, quality, integrity, legality, reliability, appropriateness, and intellectual property ownership or -right to use of all submitted Content. +right to use of all submitted content. \ \ -**Data Usage, Monitoring and Privacy** +**data usage, monitoring and privacy** -We may, at our discretion but with no obligation, monitor and review Content in the Mobile -Application submitted or created using our Services by you. -Personal data of users is not collected for any other purpose, unless providing the Services to you, +we may, at our discretion but with no obligation, monitor and review content in the mobile +application submitted or created using our services by you. +personal data of users is not collected for any other purpose, unless providing the services to you, except in certain circumstances described -in [Play Store’ Privacy policy](https://support.google.com/googleplay/android-developer/answer/10144311?visit_id=638365485539535125-3072678242&rd=1) -and [App Stores privacy policy](https://developer.apple.com/app-store/app-privacy-details/#user-tracking). +in [play store’ privacy policy](https://support.google.com/googleplay/android-developer/answer/10144311?visit_id=638365485539535125-3072678242&rd=1) +and [app store's privacy policy](https://developer.apple.com/app-store/app-privacy-details/#user-tracking). \ \ -**All user privacy and data use is in compliance with Play Store and App Store Policies.** -\ -\ -**License and Usage Restrictions:** +**license and usage restrictions:** -You grant us permission to access, copy, distribute, store, transmit, reformat, display and perform -the Content of your user account solely as required for the purpose of providing the Services to +you grant us permission to access, copy, distribute, store, transmit, reformat, display and perform +the content of your user account solely as required for the purpose of providing the services to you. -Unless specifically permitted by you, your use of the Mobile Application does not grant us the -license to link, reproduce, adapt, modify, publish or distribute the Content created by you or +unless specifically permitted by you, your use of the mobile application does not grant us the +license to link, reproduce, adapt, modify, publish or distribute the content created by you or stored in your user account, for evaluating the effectiveness of existing product features, planning new features, or measuring size and characteristics about specific end-users, or devices with a data broker, for commercial, marketing or any similar purpose. -Unless specifically permitted by you, your -use of the Mobile Application does not grant us the -license to use analytics tools, advertising networks, third-party SDKs, or other external vendors -whose code we have added to the Uni Mobile Application, to collect information such as user ID, -device ID, or profiles with Third-Party Data for the purpose of accessing necessary data to +unless specifically permitted by you, your +use of the mobile application does not grant us the +license to use analytics tools, advertising networks, third-party sdks, or other external vendors +whose code we have added to the uni mobile application, to collect information such as user id, +device id, or profiles with third-party data for the purpose of accessing necessary data to implement -the Services we provide. As of the last update, we anonymously track page visits and widget clicks, which is shared with our self-hosted instance of [Plausible Analytics](https://plausible.niaefeup.pt/). -We do not have the license to sell or share sensitive data accessed trough -API' permissions with the -finality of facilitating sales. +the services we provide. we anonymously track page visits and widget clicks, which is shared with our self-hosted instance of [plausible analytics](https://plausible.niaefeup.pt/). +we do not have the license to sell or share sensitive data collected with the purpose of facilitating sales. \ \ -**Content Removal:** +**content removal:** -Without limiting any of those representations or warranties, we have the right, though not the -obligation, to, in our own sole discretion, refuse or remove any Content that, in our reasonable +without limiting any of those representations or warranties, we have the right, though not the +obligation, to, in our own sole discretion, refuse or remove any content that, in our reasonable opinion, violates any of our policies or is in any way harmful or objectionable. \ \ -**Backups** +**backups** -We are not responsible for Content residing in the Mobile Application. In no event shall we be held -liable for any loss of any Content. It is your sole responsibility to maintain appropriate backup of -your Content. +we are not responsible for content residing in the mobile application. in no event shall we be held +liable for any loss of any content. it is your sole responsibility to maintain appropriate backup of +your content. \ \ -**Links to other mobile applications** +**links to other mobile applications** -Although this Mobile Application may link to other mobile applications, we are not, directly or +although this mobile application may link to other mobile applications, we are not, directly or indirectly, implying any approval, association, sponsorship, endorsement, or affiliation with any -linked mobile application, unless specifically stated herein. Some of the links in the Mobile -Application may be "affiliate links". This means if you click on the link and purchase an item, -NIAEFEUP will receive an affiliate commission. We are not responsible for examining or evaluating, +linked mobile application, unless specifically stated herein. some of the links in the mobile +application may be "affiliate links". this means if you click on the link and purchase an item, +niaefeup will receive an affiliate commission. we are not responsible for examining or evaluating, and we do not warrant the offerings of, any businesses or individuals or the content of their mobile -applications. We do not assume any responsibility or liability for the actions, products, services, -and content of any other third-parties. You should carefully review the legal statements and other -conditions of use of any mobile application which you access through a link from this Mobile -Application. Your linking to any other off-site mobile applications is at your own risk. +applications. we do not assume any responsibility or liability for the actions, products, services, +and content of any other third-parties. you should carefully review the legal statements and other +conditions of use of any mobile application which you access through a link from this mobile +application. your linking to any other off-site mobile applications is at your own risk. \ \ -**Limitation of liability** +**limitation of liability** -To the fullest extent permitted by applicable law, in no event will NIAEFEUP, its affiliates, +to the fullest extent permitted by applicable law, in no event will niaefeup, its affiliates, officers, directors, employees, agents, suppliers or licensors be liable to any person for (a): any indirect, incidental, special, punitive, cover or consequential damages (including, without limitation, damages for lost profits, revenue, sales, goodwill, use of content, impact on business, business interruption, loss of anticipated savings, loss of business opportunity) however caused, under any theory of liability, including, without limitation, contract, tort, warranty, breach of -statutory duty, negligence or otherwise, even if NIAEFEUP has been advised as to the possibility of -such damages or could have foreseen such damages. To the maximum extent permitted by applicable law, -the aggregate liability of NIAEFEUP and its affiliates, officers, employees, agents, suppliers and +statutory duty, negligence or otherwise, even if niaefeup has been advised as to the possibility of +such damages or could have foreseen such damages. to the maximum extent permitted by applicable law, +the aggregate liability of niaefeup and its affiliates, officers, employees, agents, suppliers and licensors, relating to the services will be limited to an amount greater of one dollar or any -amounts actually paid in cash by you to NIAEFEUP for the prior one month period prior to the first -event or occurrence giving rise to such liability. The limitations and exclusions also apply if this +amounts actually paid in cash by you to niaefeup for the prior one month period prior to the first +event or occurrence giving rise to such liability. the limitations and exclusions also apply if this remedy does not fully compensate you for any losses or fails of its essential purpose. \ \ -**Changes and amendments** +**changes and amendments** -We reserve the right to modify this Agreement or its policies relating to the Mobile Application or -Services at any time, effective upon posting of an updated version of this Agreement in the Mobile -Application. When we do, we will revise the updated date at the bottom of this page. Continued use -of the Mobile Application after any such changes will only constitute if when prompted, you consent to them. Otherwise, you will be logged out of the Mobile Application. -Policy was created with [Website Policies](https://www.websitepolicies.com). +we reserve the right to modify this agreement or its policies relating to the mobile application or +services at any time, effective upon posting of an updated version of this agreement in the mobile +application. when we do, we will revise the updated date at the bottom of this page. continued use +of the mobile application after any such changes will only constitute if when prompted, you consent to them. otherwise, you will be logged out of the mobile application. +policy was created with [website policies](https://www.websitepolicies.com). \ \ -**Acceptance of these terms** +**acceptance of these terms** -You acknowledge that you have read this Agreement and agree to all its terms and conditions. By -using the Mobile Application or its Services you agree to be bound by this Agreement. If you do not -agree to abide by the terms of this Agreement, you are not authorized to use or access the Mobile -Application and its Services. +you acknowledge that you have read this agreement and agree to all its terms and conditions. by +using the mobile application or its services you agree to be bound by this agreement. if you do not +agree to abide by the terms of this agreement, you are not authorized to use or access the mobile +application and its services. \ \ -**Contacting us** +**contacting us** -If you would like to contact us to understand more about this Agreement or wish to contact us +if you would like to contact us to understand more about this agreement or wish to contact us concerning any matter relating to it, you may send an email to [ni@aefeup.pt](mailto:ni@aefeup.pt). \ \ -This document was last updated on November 30, 2023 +this document was last updated on november 30, 2023 From 2fdceb3edf06f0805031cd2dff0a8cf75e6b07fb Mon Sep 17 00:00:00 2001 From: eduardagmagno Date: Thu, 28 Dec 2023 03:12:50 +0000 Subject: [PATCH 286/334] 2_Edit_Terms&Conditions --- uni/assets/text/TermsAndConditions.md | 148 +++++++++++++------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/uni/assets/text/TermsAndConditions.md b/uni/assets/text/TermsAndConditions.md index 78aec6efa..0dc2d0646 100644 --- a/uni/assets/text/TermsAndConditions.md +++ b/uni/assets/text/TermsAndConditions.md @@ -1,138 +1,138 @@ -# app desenvolvida pelo niaefeup. -# de estudantes, para estudantes. +# App desenvolvida pelo niaefeup. +# De estudantes, para estudantes. \ \ -**terms and conditions** +**Terms and Conditions** -these terms and conditions ("terms", "agreement") are an agreement between niaefeup ("us", "we" or " -our"), núcleo de informática da associação de estudantes da universidade do porto, and you (" -user", "you" or "your"). this agreement sets forth the general terms and conditions of your use of -the uni mobile application and any of its products or services (collectively, "mobile application" -or "services"). +These terms and conditions ("Terms", "Agreement") are an agreement between NIAEFEUP ("us", "we" or " +our"), Núcleo de Informática da Associação de Estudantes da Universidade do Porto, and you (" +User", "you" or "your"). This Agreement sets forth the general terms and conditions of your use of +the Uni Mobile Application and any of its products or services (collectively, "Mobile Application" +or "Services"). \ \ -**accounts and membership** +**Accounts and Membership** -to use the uni mobile application, you are required to hold a digital account provided by -universidade -do porto. we are in no way affiliated with this university. any act performed by you through the +To use the Uni Mobile Application, you are required to hold a digital account provided by +Universidade +do Porto. We are in no way affiliated with this university. Any act performed by you through the mobile application is subject to -the [universidade do porto's terms and conditions](https://sigarra.up.pt/up/pt/web_base.gera_pagina?p_pagina=termos%20e%20condicoes). -by using your faculty account in the mobile application, you are responsible for maintaining the +the [Universidade do Porto's terms and conditions](https://sigarra.up.pt/up/pt/web_base.gera_pagina?p_pagina=termos%20e%20condicoes). +By using your faculty account in the Mobile Application, you are responsible for maintaining the security of your account and you are fully responsible for all activities that occur under the account and any other actions taken in connection with it. we will not be liable for any acts or omissions by you, including any damages of any kind incurred as a result of such acts or omissions. -any interaction with your data from universidade do porto's platform is made directly through it, +Any interaction with your data from Universidade do Porto's platform is made directly through it, and is not collected by us in any way. -if you choose to select the option to keep you logged in, you understand that your sign-in details -may be stored locally, but not in any way shared with us. these details are stored in the +If you choose to select the option to keep you logged in, you understand that your sign-in details +may be stored locally, but not in any way shared with us. These details are stored in the application data and we hold no responsibility for ensuring their concealment. \ \ -**ownership of content** +**Ownership of Content** -we do not own any data, information or material ("content") that you submit in the mobile -application in the course of using the service. you shall have sole responsibility for the accuracy, +We do not own any data, information or material ("Content") that you submit in the Mobile +Application in the course of using the Service.You shall have sole responsibility for the accuracy, quality, integrity, legality, reliability, appropriateness, and intellectual property ownership or -right to use of all submitted content. +right to use of all submitted Content. \ \ -**data usage, monitoring and privacy** +**Data usage, Monitoring and Privacy** -we may, at our discretion but with no obligation, monitor and review content in the mobile -application submitted or created using our services by you. -personal data of users is not collected for any other purpose, unless providing the services to you, +We may, at our discretion but with no obligation, monitor and review Content in the Mobile +Application submitted or created using our Services by you. +Personal data of users is not collected for any other purpose, unless providing the Services to you, except in certain circumstances described -in [play store’ privacy policy](https://support.google.com/googleplay/android-developer/answer/10144311?visit_id=638365485539535125-3072678242&rd=1) -and [app store's privacy policy](https://developer.apple.com/app-store/app-privacy-details/#user-tracking). +in [Play Store’ Privacy Policy](https://support.google.com/googleplay/android-developer/answer/10144311?visit_id=638365485539535125-3072678242&rd=1) +and [App Store's Privacy Policy](https://developer.apple.com/app-store/app-privacy-details/#user-tracking). \ \ -**license and usage restrictions:** +**License and Usage Restrictions:** -you grant us permission to access, copy, distribute, store, transmit, reformat, display and perform -the content of your user account solely as required for the purpose of providing the services to +You grant us permission to access, copy, distribute, store, transmit, reformat, display and perform +the Content of your user account solely as required for the purpose of providing the Services to you. -unless specifically permitted by you, your use of the mobile application does not grant us the -license to link, reproduce, adapt, modify, publish or distribute the content created by you or +Unless specifically permitted by you, your use of the Mobile Application does not grant us the +license to link, reproduce, adapt, modify, publish or distribute the Content created by you or stored in your user account, for evaluating the effectiveness of existing product features, planning new features, or measuring size and characteristics about specific end-users, or devices with a data broker, for commercial, marketing or any similar purpose. -unless specifically permitted by you, your -use of the mobile application does not grant us the -license to use analytics tools, advertising networks, third-party sdks, or other external vendors -whose code we have added to the uni mobile application, to collect information such as user id, -device id, or profiles with third-party data for the purpose of accessing necessary data to +Unless specifically permitted by you, your +use of the Mobile Application does not grant us the +license to use analytics tools, advertising networks, third-party SDK's, or other external vendors +whose code we have added to the Uni Mobile Application, to collect information such as user ID, +device ID, or profiles with Third-Party Data for the purpose of accessing necessary data to implement -the services we provide. we anonymously track page visits and widget clicks, which is shared with our self-hosted instance of [plausible analytics](https://plausible.niaefeup.pt/). +the Services we provide. We anonymously track page visits and widget clicks, which is shared with our self-hosted instance of [plausible analytics](https://plausible.niaefeup.pt/). we do not have the license to sell or share sensitive data collected with the purpose of facilitating sales. \ \ -**content removal:** +**Content Removal:** -without limiting any of those representations or warranties, we have the right, though not the +Without limiting any of those representations or warranties, we have the right, though not the obligation, to, in our own sole discretion, refuse or remove any content that, in our reasonable opinion, violates any of our policies or is in any way harmful or objectionable. \ \ -**backups** +**Backups** -we are not responsible for content residing in the mobile application. in no event shall we be held -liable for any loss of any content. it is your sole responsibility to maintain appropriate backup of -your content. +We are not responsible for Content residing in the Mobile Application. In no event shall we be held +liable for any loss of any Content. It is your sole responsibility to maintain appropriate backup of +your Content. \ \ -**links to other mobile applications** +**Links to Other Mobile Applications** -although this mobile application may link to other mobile applications, we are not, directly or +Although this Mobile Application may link to other mobile applications, we are not, directly or indirectly, implying any approval, association, sponsorship, endorsement, or affiliation with any -linked mobile application, unless specifically stated herein. some of the links in the mobile -application may be "affiliate links". this means if you click on the link and purchase an item, -niaefeup will receive an affiliate commission. we are not responsible for examining or evaluating, +linked mobile application, unless specifically stated herein. Some of the links in the Mobile +Application may be "affiliate links". This means if you click on the link and purchase an item, +NIAEFEUP will receive an affiliate commission. We are not responsible for examining or evaluating, and we do not warrant the offerings of, any businesses or individuals or the content of their mobile -applications. we do not assume any responsibility or liability for the actions, products, services, -and content of any other third-parties. you should carefully review the legal statements and other -conditions of use of any mobile application which you access through a link from this mobile -application. your linking to any other off-site mobile applications is at your own risk. +applications. We do not assume any responsibility or liability for the actions, products, Services, +and Content of any other Third-Parties. You should carefully review the legal statements and other +conditions of use of any Mobile Application which you access through a link from this Mobile +Application. Your linking to any other off-site Mobile Applications is at your own risk. \ \ -**limitation of liability** +**Limitation of Liability** -to the fullest extent permitted by applicable law, in no event will niaefeup, its affiliates, +To the fullest extent permitted by applicable law, in no event will NIAEFEUP, its affiliates, officers, directors, employees, agents, suppliers or licensors be liable to any person for (a): any indirect, incidental, special, punitive, cover or consequential damages (including, without limitation, damages for lost profits, revenue, sales, goodwill, use of content, impact on business, business interruption, loss of anticipated savings, loss of business opportunity) however caused, under any theory of liability, including, without limitation, contract, tort, warranty, breach of -statutory duty, negligence or otherwise, even if niaefeup has been advised as to the possibility of -such damages or could have foreseen such damages. to the maximum extent permitted by applicable law, -the aggregate liability of niaefeup and its affiliates, officers, employees, agents, suppliers and +statutory duty, negligence or otherwise, even if NIAEFEUP has been advised as to the possibility of +such damages or could have foreseen such damages. To the maximum extent permitted by applicable law, +the aggregate liability of NIAEFEUP and its affiliates, officers, employees, agents, suppliers and licensors, relating to the services will be limited to an amount greater of one dollar or any -amounts actually paid in cash by you to niaefeup for the prior one month period prior to the first -event or occurrence giving rise to such liability. the limitations and exclusions also apply if this +amounts actually paid in cash by you to NIAEFEUP for the prior one month period prior to the first +event or occurrence giving rise to such liability. The limitations and exclusions also apply if this remedy does not fully compensate you for any losses or fails of its essential purpose. \ \ -**changes and amendments** +**Changes and Amendments** -we reserve the right to modify this agreement or its policies relating to the mobile application or -services at any time, effective upon posting of an updated version of this agreement in the mobile -application. when we do, we will revise the updated date at the bottom of this page. continued use -of the mobile application after any such changes will only constitute if when prompted, you consent to them. otherwise, you will be logged out of the mobile application. -policy was created with [website policies](https://www.websitepolicies.com). +We reserve the right to modify this Agreement or its policies relating to the Mobile Application or +services at any time, effective upon posting of an updated version of this Agreement in the Mobile +Application. When we do, we will revise the updated date at the bottom of this page. Continued use +of the Mobile Application after any such changes will only constitute if when prompted, you consent to them. Otherwise, you will be logged out of the Mobile Application. +Policy was created with [Website Policies](https://www.websitepolicies.com). \ \ -**acceptance of these terms** +**Acceptance of These Terms** -you acknowledge that you have read this agreement and agree to all its terms and conditions. by -using the mobile application or its services you agree to be bound by this agreement. if you do not -agree to abide by the terms of this agreement, you are not authorized to use or access the mobile -application and its services. +You acknowledge that you have read this Agreement and agree to all its terms and conditions. By +using the Mobile Application or its Services you agree to be bound by this Agreement. If you do not +agree to abide by the terms of this Agreement, you are not authorized to use or access the Mobile +Application and its Services. \ \ -**contacting us** +**Contacting Us** -if you would like to contact us to understand more about this agreement or wish to contact us +If you would like to contact us to understand more about this Agreement or wish to contact us concerning any matter relating to it, you may send an email to [ni@aefeup.pt](mailto:ni@aefeup.pt). \ \ -this document was last updated on november 30, 2023 +This document was last updated on December 28, 2023 From 8e4e41db69474561c4a409b8333d326670615aca Mon Sep 17 00:00:00 2001 From: eduardagmagno Date: Mon, 26 Feb 2024 20:52:38 +0000 Subject: [PATCH 287/334] Update Terms & Conditions --- uni/assets/text/TermsAndConditions.md | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/uni/assets/text/TermsAndConditions.md b/uni/assets/text/TermsAndConditions.md index 0dc2d0646..197dbf30f 100644 --- a/uni/assets/text/TermsAndConditions.md +++ b/uni/assets/text/TermsAndConditions.md @@ -1,8 +1,10 @@ -# App desenvolvida pelo niaefeup. +# App desenvolvida pelo NIAEFEUP. + # De estudantes, para estudantes. + \ \ -**Terms and Conditions** +**Terms and Conditions** These terms and conditions ("Terms", "Agreement") are an agreement between NIAEFEUP ("us", "we" or " our"), Núcleo de Informática da Associação de Estudantes da Universidade do Porto, and you (" @@ -43,7 +45,7 @@ We may, at our discretion but with no obligation, monitor and review Content in Application submitted or created using our Services by you. Personal data of users is not collected for any other purpose, unless providing the Services to you, except in certain circumstances described -in [Play Store’ Privacy Policy](https://support.google.com/googleplay/android-developer/answer/10144311?visit_id=638365485539535125-3072678242&rd=1) +in [Play Store’s Privacy Policy](https://support.google.com/googleplay/android-developer/answer/10144311?visit_id=638365485539535125-3072678242&rd=1) and [App Store's Privacy Policy](https://developer.apple.com/app-store/app-privacy-details/#user-tracking). \ \ @@ -56,15 +58,17 @@ Unless specifically permitted by you, your use of the Mobile Application does no license to link, reproduce, adapt, modify, publish or distribute the Content created by you or stored in your user account, for evaluating the effectiveness of existing product features, planning new features, or measuring size and characteristics about specific end-users, or devices with a data -broker, for commercial, marketing or any similar purpose. +broker, for commercial, marketing or any similar purpose. Unless specifically permitted by you, your use of the Mobile Application does not grant us the -license to use analytics tools, advertising networks, third-party SDK's, or other external vendors +license to use analytics tools, advertising networks, third-party SDKs, or other external vendors whose code we have added to the Uni Mobile Application, to collect information such as user ID, device ID, or profiles with Third-Party Data for the purpose of accessing necessary data to implement -the Services we provide. We anonymously track page visits and widget clicks, which is shared with our self-hosted instance of [plausible analytics](https://plausible.niaefeup.pt/). -we do not have the license to sell or share sensitive data collected with the purpose of facilitating sales. +the Services we provide. We anonymously track page visits and widget clicks, which is shared with +our self-hosted instance of [plausible analytics](https://plausible.niaefeup.pt/). +we do not have the license to sell or share sensitive data collected with the purpose of +facilitating sales. \ \ **Content Removal:** @@ -117,8 +121,9 @@ remedy does not fully compensate you for any losses or fails of its essential pu We reserve the right to modify this Agreement or its policies relating to the Mobile Application or services at any time, effective upon posting of an updated version of this Agreement in the Mobile Application. When we do, we will revise the updated date at the bottom of this page. Continued use -of the Mobile Application after any such changes will only constitute if when prompted, you consent to them. Otherwise, you will be logged out of the Mobile Application. -Policy was created with [Website Policies](https://www.websitepolicies.com). +of the Mobile Application after any such changes will only constitute if, when prompted, you consent +to them. Otherwise, you will be logged out of the Mobile Application. +This policy was created with [Website Policies](https://www.websitepolicies.com). \ \ **Acceptance of These Terms** From 5f2cf402c3e11557a6cc11c1362da4c221278f3c Mon Sep 17 00:00:00 2001 From: Bruno Mendes <61701401+bdmendes@users.noreply.github.com> Date: Wed, 28 Feb 2024 14:20:07 +0000 Subject: [PATCH 288/334] Apply grammar suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Lima --- uni/assets/text/TermsAndConditions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/assets/text/TermsAndConditions.md b/uni/assets/text/TermsAndConditions.md index 197dbf30f..ed9294150 100644 --- a/uni/assets/text/TermsAndConditions.md +++ b/uni/assets/text/TermsAndConditions.md @@ -22,7 +22,7 @@ mobile application is subject to the [Universidade do Porto's terms and conditions](https://sigarra.up.pt/up/pt/web_base.gera_pagina?p_pagina=termos%20e%20condicoes). By using your faculty account in the Mobile Application, you are responsible for maintaining the security of your account and you are fully responsible for all activities that occur under the -account and any other actions taken in connection with it. we will not be liable for any acts or +account and any other actions taken in connection with it. We will not be liable for any acts or omissions by you, including any damages of any kind incurred as a result of such acts or omissions. Any interaction with your data from Universidade do Porto's platform is made directly through it, and is not collected by us in any way. @@ -34,7 +34,7 @@ application data and we hold no responsibility for ensuring their concealment. **Ownership of Content** We do not own any data, information or material ("Content") that you submit in the Mobile -Application in the course of using the Service.You shall have sole responsibility for the accuracy, +Application in the course of using the Service. You shall have sole responsibility for the accuracy, quality, integrity, legality, reliability, appropriateness, and intellectual property ownership or right to use of all submitted Content. \ @@ -140,4 +140,4 @@ If you would like to contact us to understand more about this Agreement or wish concerning any matter relating to it, you may send an email to [ni@aefeup.pt](mailto:ni@aefeup.pt). \ \ -This document was last updated on December 28, 2023 +This document was last updated on February 28, 2023. From 55baaf4d618b2a9d40c3f29acd04478ae27f3161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Wed, 28 Feb 2024 21:18:19 +0000 Subject: [PATCH 289/334] fix: different schedules in consecutive weeks --- .../schedule_fetcher/schedule_fetcher.dart | 53 +++++++++++++++++++ .../schedule_fetcher_api.dart | 30 +++++++---- .../controller/parsers/parser_schedule.dart | 29 ++++++---- uni/lib/view/home/widgets/schedule_card.dart | 17 +++--- uni/lib/view/schedule/schedule.dart | 27 ++++++++-- 5 files changed, 123 insertions(+), 33 deletions(-) diff --git a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher.dart b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher.dart index b9e6a8cce..f9aac5d57 100644 --- a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher.dart +++ b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher.dart @@ -1,3 +1,5 @@ +import 'package:logger/logger.dart'; +import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/session_dependant_fetcher.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/entities/profile.dart'; @@ -8,6 +10,57 @@ abstract class ScheduleFetcher extends SessionDependantFetcher { // Returns the user's lectures. Future> getLectures(Session session, Profile profile); + Tuple2 getWeekStartEndDates() { + var start = DateTime.now().copyWith( + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + ); + + final mappedWeekday = start.weekday % 7; + final weekStart = start.add(Duration(days: -mappedWeekday)); + + final end = start.add(const Duration(days: 6)); + + return Tuple2(weekStart, end); + } + + List> getBlocks(Tuple2 week) { + // A block starts on sunday and ends on a saturday. At most, there is 1 + // block per week. + final blocks = >[]; + + var start = week.item1; + final end = week.item2; + + while (true) { + // Sunday has weekday = 7, and `Datetime.saturday - start.weekday` would + // be equal to -1. To solve this, we use the modulo operator to map + // sunday to 0. + final mappedWeekday = start.weekday % 7; + final daysUntilSaturday = DateTime.saturday - mappedWeekday; + Logger().d('Start: $start; Days until saturday: $daysUntilSaturday'); + final nextSaturday = start.add(Duration(days: daysUntilSaturday)); + + if (nextSaturday.isAfter(end)) { + blocks.add(Tuple2(start, end)); + print(blocks); + return blocks; + } + + blocks.add(Tuple2(start, nextSaturday)); + start = nextSaturday.add(const Duration(days: 1)); + } + } + + String toSigarraDate(DateTime date) { + return date.year.toString().padLeft(4, '0') + + date.month.toString().padLeft(2, '0') + + date.day.toString().padLeft(2, '0'); + } + /// Returns [Dates]. Dates getDates() { var date = DateTime.now(); diff --git a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart index d50099ff5..ec338f0b2 100644 --- a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart +++ b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart @@ -1,4 +1,6 @@ import 'package:http/http.dart'; +import 'package:logger/logger.dart'; +import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/controller/parsers/parser_schedule.dart'; @@ -19,21 +21,27 @@ class ScheduleFetcherApi extends ScheduleFetcher { /// Fetches the user's lectures from the faculties' API. @override Future> getLectures(Session session, Profile profile) async { - final dates = getDates(); + final blocks = getBlocks(getWeekStartEndDates()); + final urls = getEndpoints(session); - final responses = []; + final responses = , Response>>[]; for (final url in urls) { - final response = await NetworkRouter.getWithCookies( - url, - { - 'pv_codigo': session.username, - 'pv_semana_ini': dates.beginWeek, - 'pv_semana_fim': dates.endWeek, - }, - session, + final res = blocks.map( + (block) => NetworkRouter.getWithCookies( + url, + { + 'pv_codigo': session.username, + 'pv_semana_ini': toSigarraDate(block.item1), + 'pv_semana_fim': toSigarraDate(block.item2), + }, + session, + ).then((value) => Tuple2(block, value)), ); - responses.add(response); + + responses.addAll(await Future.wait(res)); } + + Logger().d('Fetched $responses schedules'); return parseScheduleMultipleRequests(responses); } } diff --git a/uni/lib/controller/parsers/parser_schedule.dart b/uni/lib/controller/parsers/parser_schedule.dart index 0a950c7d0..573f746a9 100644 --- a/uni/lib/controller/parsers/parser_schedule.dart +++ b/uni/lib/controller/parsers/parser_schedule.dart @@ -2,15 +2,16 @@ import 'dart:convert'; import 'package:http/http.dart' as http; import 'package:http/http.dart'; +import 'package:logger/logger.dart'; +import 'package:tuple/tuple.dart'; import 'package:uni/model/entities/lecture.dart'; -import 'package:uni/model/entities/time_utilities.dart'; Future> parseScheduleMultipleRequests( - List responses, + List, Response>> responses, ) async { var lectures = []; for (final response in responses) { - lectures += await parseSchedule(response); + lectures += await parseSchedule(response.item2, response.item1); } return lectures; } @@ -19,7 +20,10 @@ Future> parseScheduleMultipleRequests( /// date. /// /// This function parses a JSON object. -Future> parseSchedule(http.Response response) async { +Future> parseSchedule( + http.Response response, + Tuple2 week, +) async { final lectures = {}; final json = jsonDecode(response.body) as Map; @@ -27,9 +31,14 @@ Future> parseSchedule(http.Response response) async { final schedule = json['horario'] as List; for (var lecture in schedule) { lecture = lecture as Map; - final day = ((lecture['dia'] as int) - 2) % - 7; // Api: monday = 2, Lecture.dart class: monday = 0 - final secBegin = lecture['hora_inicio'] as int; + + final startTime = week.item1.add( + Duration( + days: (lecture['dia'] as int) - 1, + seconds: lecture['hora_inicio'] as int, + ), + ); + final subject = lecture['ucurr_sigla'] as String; final typeClass = lecture['tipo'] as String; @@ -47,12 +56,10 @@ Future> parseSchedule(http.Response response) async { final classNumber = lecture['turma_sigla'] as String; final occurrId = lecture['ocorrencia_id'] as int; - final monday = DateTime.now().getClosestMonday(); - final lec = Lecture.fromApi( subject, typeClass, - monday.add(Duration(days: day, seconds: secBegin)), + startTime, blocks, room, teacher, @@ -60,6 +67,8 @@ Future> parseSchedule(http.Response response) async { occurrId, ); + Logger().d('On week $week, found $lec'); + lectures.add(lec); } diff --git a/uni/lib/view/home/widgets/schedule_card.dart b/uni/lib/view/home/widgets/schedule_card.dart index edac76541..ee7944ba3 100644 --- a/uni/lib/view/home/widgets/schedule_card.dart +++ b/uni/lib/view/home/widgets/schedule_card.dart @@ -56,15 +56,18 @@ class ScheduleCard extends GenericCard { final lecturesByDay = lectures .groupListsBy( - (lecture) => lecture.startTime.weekday, + (lecture) => lecture.startTime.copyWith( + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + ), ) .entries + .where((element) => element.key.isAfter(DateTime.now())) .toList() - .sortedBy((element) { - // Sort by day of the week, but next days come first - final dayDiff = element.key - DateTime.now().weekday; - return dayDiff >= 0 ? dayDiff - 7 : dayDiff; - }).toList(); + .sortedBy((element) => element.key) + .toList(); for (final dayLectures in lecturesByDay.sublist(0, min(2, lecturesByDay.length))) { @@ -85,7 +88,7 @@ class ScheduleCard extends GenericCard { rows.add( DateRectangle( date: Provider.of(context) - .getWeekdaysWithLocale()[(day - 1) % 7], + .getWeekdaysWithLocale()[(day.weekday - 1) % 7], ), ); diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index bccfe528a..c7c45a98a 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -70,6 +70,14 @@ class SchedulePageViewState extends State @override Widget build(BuildContext context) { + final startOfToday = DateTime.now().copyWith( + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + ); + final queryData = MediaQuery.of(context); return Column( children: [ @@ -82,12 +90,19 @@ class SchedulePageViewState extends State Expanded( child: TabBarView( controller: tabController, - children: Iterable.generate(5).map((day) { + children: Iterable.generate( + 5, + (int index) => index + 1 < startOfToday.weekday + ? startOfToday + .add(Duration(days: -startOfToday.weekday + index + 8)) + : startOfToday + .add(Duration(days: -startOfToday.weekday + index + 1)), + ).map((day) { final lectures = lecturesOfDay(widget.lectures, day); if (lectures.isEmpty) { - return emptyDayColumn(context, day); + return emptyDayColumn(context, day.weekday - 1); } else { - return dayColumnBuilder(day, lectures, context); + return dayColumnBuilder(day.weekday - 1, lectures, context); } }).toList(), ), @@ -144,10 +159,12 @@ class SchedulePageViewState extends State ); } - static List lecturesOfDay(List lectures, int day) { + static List lecturesOfDay(List lectures, DateTime day) { final filteredLectures = []; for (var i = 0; i < lectures.length; i++) { - if (lectures[i].startTime.weekday - 1 == day) { + if (lectures[i].startTime.year == day.year && + lectures[i].startTime.month == day.month && + lectures[i].startTime.day == day.day) { filteredLectures.add(lectures[i]); } } From a2191240110f74976fe3d3ce0e8109ac187de407 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Wed, 28 Feb 2024 22:51:34 +0000 Subject: [PATCH 290/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 99cf547e1..1488636b1 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.13+246 +1.8.0-beta.14+247 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 7155e79f4..b882dd7c5 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.13+246 +version: 1.8.0-beta.14+247 environment: From 1a21d7d9190414d1345f346eea36b6e0230c4db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Wed, 28 Feb 2024 23:05:13 +0000 Subject: [PATCH 291/334] feat: add week model --- uni/lib/model/entities/time/week.dart | 117 ++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 uni/lib/model/entities/time/week.dart diff --git a/uni/lib/model/entities/time/week.dart b/uni/lib/model/entities/time/week.dart new file mode 100644 index 000000000..13a2fd26a --- /dev/null +++ b/uni/lib/model/entities/time/week.dart @@ -0,0 +1,117 @@ +DateTime getStartOfDay(DateTime date) { + return date.copyWith( + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + ); +} + +/// A [Week] represents a period of 7 days. +class Week implements Comparable { + /// Creates a [Week] that starts the given [start] **date** (not datetime). + factory Week(DateTime start) { + final startAtMidnight = start.copyWith( + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + ); + + final end = startAtMidnight.add(const Duration(days: 7)); + + return Week._internal(startAtMidnight, end); + } + + // Recommended by https://dart.dev/language/constructors#factory-constructors + Week._internal(this.start, this.end); + + final DateTime start; + final DateTime end; + + /// Returns whether the given [date] is within this [Week]. + bool contains(DateTime date) { + // First check if is at the same time or after the start of the week. + // Then check if is before the (exclusive) end of the week. + return date.compareTo(start) >= 0 && date.isBefore(end); + } + + /// Returns the [Week] that starts at the end of this [Week]. + Week next() { + return Week._internal(end, end.add(const Duration(days: 7))); + } + + /// Returns the [Week] that ends at the start of this [Week]. + Week previous() { + return Week._internal(start.subtract(const Duration(days: 7)), start); + } + + /// Returns the [Week] that is [duration] before this week. + Week subtract(Duration duration) { + final normalizedDuration = Duration(days: duration.inDays); + return Week._internal( + start.subtract(normalizedDuration), + end.subtract(normalizedDuration), + ); + } + + /// Returns the [Week] that is [duration] after this week. + Week add(Duration duration) { + final normalizedDuration = Duration(days: duration.inDays); + return Week._internal( + start.add(normalizedDuration), + end.add(normalizedDuration), + ); + } + + /// Returns the [Week] that starts at the given [weekday], contained in this + /// [Week]. + /// + /// The values for [weekday] are according to [DateTime.weekday]. + Week shiftStartTo(int weekday) { + // For instance, if [weekday] is 1 and [start] is on weekday 3, + // the final offset in days should be 5, since the offset must not be + // negative (the start of the returned week must be contained in this week). + final offsetInDays = (weekday - start.weekday) % 7; + + return Week._internal( + start.add(Duration(days: offsetInDays)), + end.add(Duration(days: offsetInDays)), + ); + } + + /// Returns the [Week] that ends (exclusive) at the given [weekday], contained + /// in this [Week]. + /// + /// The values for [weekday] are according to [DateTime.weekday]. + Week shiftEndTo(int weekday) { + return shiftStartTo(weekday).previous(); + } + + /// Returns the [DateTime] at the start of the given [weekday]. + /// + /// The values for [weekday] are according to [DateTime.weekday]. + DateTime getWeekday(int weekday) { + return start.add(Duration(days: weekday - start.weekday)); + } + + Iterable get weekdays { + return Iterable.generate(7, (index) => getWeekday(index + 1)); + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + return other is Week && other.start == start; + } + + @override + int get hashCode => start.hashCode; + + @override + int compareTo(Week other) { + return start.compareTo(other.start); + } +} From 40e93d0fd4e77e05e3ee9c9373761ce3aae48f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 02:38:24 +0000 Subject: [PATCH 292/334] refactor: make code more readable --- .../schedule_fetcher/schedule_fetcher.dart | 92 ++++++----------- .../schedule_fetcher_api.dart | 21 ++-- .../schedule_fetcher_html.dart | 29 +++--- .../controller/parsers/parser_schedule.dart | 27 +++-- .../parsers/parser_schedule_html.dart | 41 +++++--- .../model/{entities => utils}/time/week.dart | 35 +++++-- uni/lib/model/utils/time/weekday_mapper.dart | 99 +++++++++++++++++++ uni/lib/model/utils/week_response.dart | 14 +++ uni/lib/view/home/widgets/schedule_card.dart | 17 ++-- uni/lib/view/schedule/schedule.dart | 31 +++--- 10 files changed, 255 insertions(+), 151 deletions(-) rename uni/lib/model/{entities => utils}/time/week.dart (78%) create mode 100644 uni/lib/model/utils/time/weekday_mapper.dart create mode 100644 uni/lib/model/utils/week_response.dart diff --git a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher.dart b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher.dart index f9aac5d57..b34d8a0fd 100644 --- a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher.dart +++ b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher.dart @@ -1,88 +1,52 @@ -import 'package:logger/logger.dart'; -import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/session_dependant_fetcher.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; +import 'package:uni/model/utils/time/week.dart'; /// Class for fetching the user's schedule. abstract class ScheduleFetcher extends SessionDependantFetcher { // Returns the user's lectures. Future> getLectures(Session session, Profile profile); - Tuple2 getWeekStartEndDates() { - var start = DateTime.now().copyWith( - hour: 0, - minute: 0, - second: 0, - millisecond: 0, - microsecond: 0, - ); + List getWeeks(DateTime now) { + final week = Week(start: now); - final mappedWeekday = start.weekday % 7; - final weekStart = start.add(Duration(days: -mappedWeekday)); + // In a 7-day period, there are at most 2 weeks. According to SIGARRA + // convention, weeks start on Sundays. + // Also, for `nextWeek`, we can't use `thisWeek.next()` because it could + // return a week that doesn't intersect `week` (if `week` starts on a + // Sunday). + final thisWeek = week.endingOn(DateTime.sunday); + final nextWeek = week.startingOn(DateTime.sunday); - final end = start.add(const Duration(days: 6)); - - return Tuple2(weekStart, end); - } - - List> getBlocks(Tuple2 week) { - // A block starts on sunday and ends on a saturday. At most, there is 1 - // block per week. - final blocks = >[]; - - var start = week.item1; - final end = week.item2; - - while (true) { - // Sunday has weekday = 7, and `Datetime.saturday - start.weekday` would - // be equal to -1. To solve this, we use the modulo operator to map - // sunday to 0. - final mappedWeekday = start.weekday % 7; - final daysUntilSaturday = DateTime.saturday - mappedWeekday; - Logger().d('Start: $start; Days until saturday: $daysUntilSaturday'); - final nextSaturday = start.add(Duration(days: daysUntilSaturday)); - - if (nextSaturday.isAfter(end)) { - blocks.add(Tuple2(start, end)); - print(blocks); - return blocks; - } - - blocks.add(Tuple2(start, nextSaturday)); - start = nextSaturday.add(const Duration(days: 1)); - } - } - - String toSigarraDate(DateTime date) { - return date.year.toString().padLeft(4, '0') + - date.month.toString().padLeft(2, '0') + - date.day.toString().padLeft(2, '0'); + return thisWeek == nextWeek ? [thisWeek] : [thisWeek, nextWeek]; } /// Returns [Dates]. - Dates getDates() { - var date = DateTime.now(); - - final beginWeek = date.year.toString().padLeft(4, '0') + - date.month.toString().padLeft(2, '0') + - date.day.toString().padLeft(2, '0'); - date = date.add(const Duration(days: 6)); - - final endWeek = date.year.toString().padLeft(4, '0') + - date.month.toString().padLeft(2, '0') + - date.day.toString().padLeft(2, '0'); + List getDates() { + final date = DateTime.now(); + final weeks = getWeeks(date); final lectiveYear = date.month < 8 ? date.year - 1 : date.year; - return Dates(beginWeek, endWeek, lectiveYear); + + return weeks.map((week) => Dates(week, lectiveYear)).toList(); } } /// Stores the start and end dates of the week and the current lective year. class Dates { - Dates(this.beginWeek, this.endWeek, this.lectiveYear); - final String beginWeek; - final String endWeek; + Dates(this.week, this.lectiveYear); + final Week week; final int lectiveYear; + + String _toSigarraDate(DateTime date) { + return date.year.toString().padLeft(4, '0') + + date.month.toString().padLeft(2, '0') + + date.day.toString().padLeft(2, '0'); + } + + String get asSigarraWeekStart => _toSigarraDate(week.start); + String get asSigarraWeekEnd => + _toSigarraDate(week.end.subtract(const Duration(days: 1))); } diff --git a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart index ec338f0b2..98b61dc63 100644 --- a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart +++ b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart @@ -1,12 +1,10 @@ -import 'package:http/http.dart'; -import 'package:logger/logger.dart'; -import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/controller/parsers/parser_schedule.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; +import 'package:uni/model/utils/week_response.dart'; /// Class for fetching the user's lectures from the faculties' API. class ScheduleFetcherApi extends ScheduleFetcher { @@ -21,27 +19,26 @@ class ScheduleFetcherApi extends ScheduleFetcher { /// Fetches the user's lectures from the faculties' API. @override Future> getLectures(Session session, Profile profile) async { - final blocks = getBlocks(getWeekStartEndDates()); + final dates = getDates(); final urls = getEndpoints(session); - final responses = , Response>>[]; + final responses = []; for (final url in urls) { - final res = blocks.map( - (block) => NetworkRouter.getWithCookies( + final futures = dates.map( + (date) => NetworkRouter.getWithCookies( url, { 'pv_codigo': session.username, - 'pv_semana_ini': toSigarraDate(block.item1), - 'pv_semana_fim': toSigarraDate(block.item2), + 'pv_semana_ini': date.asSigarraWeekStart, + 'pv_semana_fim': date.asSigarraWeekEnd, }, session, - ).then((value) => Tuple2(block, value)), + ).then((response) => WeekResponse(date.week, response)), ); - responses.addAll(await Future.wait(res)); + responses.addAll(await Future.wait(futures)); } - Logger().d('Fetched $responses schedules'); return parseScheduleMultipleRequests(responses); } } diff --git a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_html.dart b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_html.dart index e1117d81e..62f2ea83b 100644 --- a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_html.dart +++ b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_html.dart @@ -1,4 +1,3 @@ -import 'package:http/http.dart'; import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher.dart'; import 'package:uni/controller/networking/network_router.dart'; @@ -6,6 +5,7 @@ import 'package:uni/controller/parsers/parser_schedule_html.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; +import 'package:uni/model/utils/week_response.dart'; /// Class for fetching the user's lectures from the schedule's HTML page. class ScheduleFetcherHtml extends ScheduleFetcher { @@ -23,22 +23,27 @@ class ScheduleFetcherHtml extends ScheduleFetcher { final dates = getDates(); final baseUrls = NetworkRouter.getBaseUrlsFromSession(session); - final lectureResponses = >[]; + final lectureResponses = >[]; for (final baseUrl in baseUrls) { final url = '${baseUrl}hor_geral.estudantes_view'; for (final course in profile.courses) { - final response = await NetworkRouter.getWithCookies( - url, - { - 'pv_fest_id': course.festId.toString(), - 'pv_ano_lectivo': dates.lectiveYear.toString(), - 'p_semana_inicio': dates.beginWeek, - 'p_semana_fim': dates.endWeek, - }, - session, + final futures = dates.map( + (date) => NetworkRouter.getWithCookies( + url, + { + 'pv_fest_id': course.festId.toString(), + 'pv_ano_lectivo': date.lectiveYear.toString(), + 'p_semana_inicio': date.asSigarraWeekStart, + 'p_semana_fim': date.asSigarraWeekEnd, + }, + session, + ).then( + (response) => Tuple2(WeekResponse(date.week, response), baseUrl), + ), ); - lectureResponses.add(Tuple2(response, baseUrl)); + + lectureResponses.addAll(await Future.wait(futures)); } } diff --git a/uni/lib/controller/parsers/parser_schedule.dart b/uni/lib/controller/parsers/parser_schedule.dart index 573f746a9..ee819cd74 100644 --- a/uni/lib/controller/parsers/parser_schedule.dart +++ b/uni/lib/controller/parsers/parser_schedule.dart @@ -1,17 +1,17 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:http/http.dart'; -import 'package:logger/logger.dart'; -import 'package:tuple/tuple.dart'; import 'package:uni/model/entities/lecture.dart'; +import 'package:uni/model/utils/time/week.dart'; +import 'package:uni/model/utils/time/weekday_mapper.dart'; +import 'package:uni/model/utils/week_response.dart'; Future> parseScheduleMultipleRequests( - List, Response>> responses, + List responses, ) async { var lectures = []; for (final response in responses) { - lectures += await parseSchedule(response.item2, response.item1); + lectures += await parseSchedule(response.response, response.week); } return lectures; } @@ -22,7 +22,7 @@ Future> parseScheduleMultipleRequests( /// This function parses a JSON object. Future> parseSchedule( http.Response response, - Tuple2 week, + Week week, ) async { final lectures = {}; @@ -32,12 +32,13 @@ Future> parseSchedule( for (var lecture in schedule) { lecture = lecture as Map; - final startTime = week.item1.add( - Duration( - days: (lecture['dia'] as int) - 1, - seconds: lecture['hora_inicio'] as int, - ), - ); + final startTime = week + .getWeekday(WeekdayMapper.fromSigarraToDart.map(lecture['dia'] as int)) + .add( + Duration( + seconds: lecture['hora_inicio'] as int, + ), + ); final subject = lecture['ucurr_sigla'] as String; final typeClass = lecture['tipo'] as String; @@ -67,8 +68,6 @@ Future> parseSchedule( occurrId, ); - Logger().d('On week $week, found $lec'); - lectures.add(lec); } diff --git a/uni/lib/controller/parsers/parser_schedule_html.dart b/uni/lib/controller/parsers/parser_schedule_html.dart index 6843f749d..d80d69a27 100644 --- a/uni/lib/controller/parsers/parser_schedule_html.dart +++ b/uni/lib/controller/parsers/parser_schedule_html.dart @@ -2,21 +2,21 @@ import 'dart:async'; import 'package:html/dom.dart'; import 'package:html/parser.dart' show parse; -import 'package:http/http.dart' as http; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/entities/session.dart'; -import 'package:uni/model/entities/time_utilities.dart'; +import 'package:uni/model/utils/time/week.dart'; +import 'package:uni/model/utils/time/weekday_mapper.dart'; +import 'package:uni/model/utils/week_response.dart'; Future> getOverlappedClasses( Session session, Document document, String faculty, + Week week, ) async { final lecturesList = []; - final monday = DateTime.now().getClosestMonday(); - final overlappingClasses = document.querySelectorAll('.dados > tbody > .d'); for (final element in overlappingClasses) { final subject = element.querySelector('acronym > a')?.text; @@ -31,7 +31,11 @@ Future> getOverlappedClasses( .querySelector('.horario > tbody > tr:first-child') ?.children .indexWhere((element) => element.text == textDay); - final day = aDay != null ? aDay - 1 : 0; + final day = week.getWeekday( + aDay != null + ? WeekdayMapper.fromSigarraToDart.map(aDay) + : DateTime.monday, + ); final startTime = element.querySelector('td[headers=t3]')?.text; final room = element.querySelector('td[headers=t4] > a')?.text; final teacher = element.querySelector('td[headers=t5] > a')?.text; @@ -44,13 +48,13 @@ Future> getOverlappedClasses( 'Overlapping class $subject has invalid startTime', ); } - final fullStartTime = monday.add( + final fullStartTime = day.add( Duration( - days: day, hours: int.parse(startTimeList[0]), minutes: int.parse(startTimeList[1]), ), ); + final href = element.querySelector('td[headers=t6] > a')?.attributes['href']; @@ -63,8 +67,8 @@ Future> getOverlappedClasses( session, ); - final classLectures = - await getScheduleFromHtml(response, session, faculty); + final classLectures = await getScheduleFromHtml( + WeekResponse(week, response), session, faculty); lecturesList.add( classLectures @@ -79,7 +83,7 @@ Future> getOverlappedClasses( final lect = Lecture.fromHtml( subject!, typeClass!, - monday.add(Duration(days: day)), + day, startTime!, 0, room!, @@ -94,21 +98,26 @@ Future> getOverlappedClasses( return lecturesList; } +const fromParserToDart = WeekdayMapper.fromStartWeekdays( + fromStart: 0, + fromStartWeekday: DateTime.monday, + toStart: 1, + toStartWeekday: DateTime.monday, +); + /// Extracts the user's lectures from an HTTP [response] and sorts them by date. /// /// This function parses the schedule's HTML page. Future> getScheduleFromHtml( - http.Response response, + WeekResponse response, Session session, String faculty, ) async { - final document = parse(response.body); + final document = parse(response.response); var semana = [0, 0, 0, 0, 0, 0]; final lecturesList = []; - final monday = DateTime.now().getClosestMonday(); - document.querySelectorAll('.horario > tbody > tr').forEach((Element element) { if (element.getElementsByClassName('horas').isNotEmpty) { var day = 0; @@ -144,7 +153,7 @@ Future> getScheduleFromHtml( final lect = Lecture.fromHtml( subject, typeClass, - monday.add(Duration(days: day)), + response.week.getWeekday(fromParserToDart.map(day)), startTime, blocks, room ?? '', @@ -162,7 +171,7 @@ Future> getScheduleFromHtml( lecturesList ..addAll( - await getOverlappedClasses(session, document, faculty), + await getOverlappedClasses(session, document, faculty, response.week), ) ..sort((a, b) => a.compare(b)); diff --git a/uni/lib/model/entities/time/week.dart b/uni/lib/model/utils/time/week.dart similarity index 78% rename from uni/lib/model/entities/time/week.dart rename to uni/lib/model/utils/time/week.dart index 13a2fd26a..dd8951212 100644 --- a/uni/lib/model/entities/time/week.dart +++ b/uni/lib/model/utils/time/week.dart @@ -1,3 +1,5 @@ +import 'package:uni/model/utils/time/weekday_mapper.dart'; + DateTime getStartOfDay(DateTime date) { return date.copyWith( hour: 0, @@ -11,7 +13,9 @@ DateTime getStartOfDay(DateTime date) { /// A [Week] represents a period of 7 days. class Week implements Comparable { /// Creates a [Week] that starts the given [start] **date** (not datetime). - factory Week(DateTime start) { + factory Week({ + required DateTime start, + }) { final startAtMidnight = start.copyWith( hour: 0, minute: 0, @@ -26,10 +30,17 @@ class Week implements Comparable { } // Recommended by https://dart.dev/language/constructors#factory-constructors - Week._internal(this.start, this.end); + Week._internal(this.start, this.end) + : toDartWeekday = WeekdayMapper( + fromStart: 1, + fromMonday: start.weekday, + toStart: 1, + toMonday: DateTime.monday, + ); final DateTime start; final DateTime end; + final WeekdayMapper toDartWeekday; /// Returns whether the given [date] is within this [Week]. bool contains(DateTime date) { @@ -70,7 +81,7 @@ class Week implements Comparable { /// [Week]. /// /// The values for [weekday] are according to [DateTime.weekday]. - Week shiftStartTo(int weekday) { + Week startingOn(int weekday) { // For instance, if [weekday] is 1 and [start] is on weekday 3, // the final offset in days should be 5, since the offset must not be // negative (the start of the returned week must be contained in this week). @@ -86,15 +97,22 @@ class Week implements Comparable { /// in this [Week]. /// /// The values for [weekday] are according to [DateTime.weekday]. - Week shiftEndTo(int weekday) { - return shiftStartTo(weekday).previous(); + Week endingOn(int weekday) { + // For instance, if [weekday] is 1 and [end] is on weekday 3, + // the final offset in days should be 2. + final offsetInDays = (end.weekday - weekday) % 7; + + return Week._internal( + start.subtract(Duration(days: offsetInDays)), + end.subtract(Duration(days: offsetInDays)), + ); } /// Returns the [DateTime] at the start of the given [weekday]. /// /// The values for [weekday] are according to [DateTime.weekday]. DateTime getWeekday(int weekday) { - return start.add(Duration(days: weekday - start.weekday)); + return start.add(Duration(days: (weekday - start.weekday) % 7)); } Iterable get weekdays { @@ -114,4 +132,9 @@ class Week implements Comparable { int compareTo(Week other) { return start.compareTo(other.start); } + + @override + String toString() { + return 'Week{start: $start, end: $end}'; + } } diff --git a/uni/lib/model/utils/time/weekday_mapper.dart b/uni/lib/model/utils/time/weekday_mapper.dart new file mode 100644 index 000000000..724f4a29e --- /dev/null +++ b/uni/lib/model/utils/time/weekday_mapper.dart @@ -0,0 +1,99 @@ +/// A class that maps weekdays from one system to another. +/// +/// --- +/// # Explanation +/// +/// Consider the following systems: +/// +/// | System | Mon | Tue | Wed | Thu | Fri | Sat | Sun | +/// |--------|-----|-----|-----|-----|-----|-----|-----| +/// | A | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +/// | B | 0 | 1 | 2 | 3 | 4 | 5 | 6 | +/// | C | 1 | 2 | 3 | 4 | 5 | 6 | 0 | +/// | D | 5 | 6 | 0 | 1 | 2 | 3 | 4 | +/// | E | 1 | 2 | 3 | 4 | 5 | -1 | 0 | +/// +/// All of these systems are valid and used in different contexts. This class +/// allows for mapping weekdays from one system to another. +/// +/// As you can see, a system is defined by two parameters: the number of the +/// first day of the week and the number of Monday (can be another day as long +/// as it is the same across all systems). +class WeekdayMapper { + const WeekdayMapper({ + required int fromStart, + required int fromMonday, + required int toStart, + required int toMonday, + }) : _toMonday = toMonday, + _toStart = toStart, + _fromMonday = fromMonday, + _fromStart = fromStart; + + /// Creates a [WeekdayMapper]. + /// + /// [fromStartWeekday] and [toStartWeekday] are the weekdays that correspond + /// to the first day of the week in the `from` and `to` systems, respectively. + /// These values are according to [DateTime.weekday]. + const WeekdayMapper.fromStartWeekdays({ + required int fromStart, + required int fromStartWeekday, + required int toStart, + required int toStartWeekday, + }) : this( + fromStart: fromStart, + fromMonday: (DateTime.monday - fromStartWeekday) % 7 + fromStart, + toStart: toStart, + toMonday: (DateTime.monday - toStartWeekday) % 7 + toStart, + ); + + static const fromSigarraToDart = WeekdayMapper.fromStartWeekdays( + fromStart: 1, + fromStartWeekday: DateTime.sunday, + toStart: 1, + toStartWeekday: DateTime.monday, + ); + + final int _fromStart; + final int _fromMonday; + + final int _toStart; + final int _toMonday; + + WeekdayMapper then(WeekdayMapper other) { + return WeekdayMapper( + fromStart: _fromStart, + fromMonday: _fromMonday, + toStart: other._toStart, + toMonday: other._toMonday, + ); + } + + int map(int fromWeekday) { + final mondayOffset = _toMonday - _fromMonday; + + // The algorithm is as follows: + // + // 1. Find the 0-based index of `fromWeekday` in the `from` system, by + // subtracting `fromStart` from it. At this point, you're working with a + // system that starts at 0 and ends at 6. You don't know which day is + // Monday. + // 2. Add the offset between the `from` and `to` Monday. Since we assume + // that the `to` system starts at 0, we can simply add the difference + // between the `from` and `to` Monday. This difference is `mondayOffset`. + // By adding this positive difference, we're now working with a system that + // is [mondayOffset, 6 + mondayOffset]. To go back to the 0-based system, + // we take the modulo 7 of the day. + // 3. At this point, we're working with a 0-based system that has been + // shifted by `mondayOffset`. We can now add `toStart` to get the final + // result. + final fromWeekdayZeroBased = fromWeekday - _fromStart; + final toWeekdayZeroBased = (fromWeekdayZeroBased + mondayOffset) % 7; + final toWeekday = toWeekdayZeroBased + _toStart; + + return toWeekday; + + // In case you're wondering, like me, the compact formula is: + // (fromWeekday - fromStart + toMonday - fromMonday) % 7 + toStart + } +} diff --git a/uni/lib/model/utils/week_response.dart b/uni/lib/model/utils/week_response.dart new file mode 100644 index 000000000..6f4219005 --- /dev/null +++ b/uni/lib/model/utils/week_response.dart @@ -0,0 +1,14 @@ +import 'package:http/http.dart' as http; +import 'package:uni/model/utils/time/week.dart'; + +class WeekResponse { + WeekResponse(this.week, this.response); + + final Week week; + final http.Response response; + + @override + String toString() { + return 'WeekResponse{week: $week, response: $response}'; + } +} diff --git a/uni/lib/view/home/widgets/schedule_card.dart b/uni/lib/view/home/widgets/schedule_card.dart index ee7944ba3..411051471 100644 --- a/uni/lib/view/home/widgets/schedule_card.dart +++ b/uni/lib/view/home/widgets/schedule_card.dart @@ -6,6 +6,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/providers/lazy/lecture_provider.dart'; +import 'package:uni/model/utils/time/week.dart'; import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/date_rectangle.dart'; import 'package:uni/view/common_widgets/generic_card.dart'; @@ -52,21 +53,19 @@ class ScheduleCard extends GenericCard { } List getScheduleRows(BuildContext context, List lectures) { + final now = DateTime.now(); + final week = Week(start: now); + final rows = []; final lecturesByDay = lectures + .where((lecture) => week.contains(lecture.startTime)) .groupListsBy( - (lecture) => lecture.startTime.copyWith( - hour: 0, - minute: 0, - second: 0, - millisecond: 0, - ), + (lecture) => lecture.startTime.weekday, ) .entries - .where((element) => element.key.isAfter(DateTime.now())) .toList() - .sortedBy((element) => element.key) + .sortedBy((element) => week.getWeekday(element.key)) .toList(); for (final dayLectures @@ -88,7 +87,7 @@ class ScheduleCard extends GenericCard { rows.add( DateRectangle( date: Provider.of(context) - .getWeekdaysWithLocale()[(day.weekday - 1) % 7], + .getWeekdaysWithLocale()[(day - 1) % 7], ), ); diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index c7c45a98a..b53d6a869 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -3,6 +3,7 @@ import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/providers/lazy/lecture_provider.dart'; +import 'package:uni/model/utils/time/week.dart'; import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; @@ -49,6 +50,7 @@ class SchedulePageView extends StatefulWidget { class SchedulePageViewState extends State with TickerProviderStateMixin { TabController? tabController; + late Week currentWeek; @override void initState() { @@ -57,7 +59,11 @@ class SchedulePageViewState extends State vsync: this, length: 5, ); - final weekDay = DateTime.now().weekday; + + final now = DateTime.now(); + currentWeek = Week(start: now); + + final weekDay = now.weekday; final offset = (weekDay > 5) ? 0 : (weekDay - 1) % 5; tabController?.animateTo(tabController!.index + offset); } @@ -70,14 +76,6 @@ class SchedulePageViewState extends State @override Widget build(BuildContext context) { - final startOfToday = DateTime.now().copyWith( - hour: 0, - minute: 0, - second: 0, - millisecond: 0, - microsecond: 0, - ); - final queryData = MediaQuery.of(context); return Column( children: [ @@ -92,11 +90,7 @@ class SchedulePageViewState extends State controller: tabController, children: Iterable.generate( 5, - (int index) => index + 1 < startOfToday.weekday - ? startOfToday - .add(Duration(days: -startOfToday.weekday + index + 8)) - : startOfToday - .add(Duration(days: -startOfToday.weekday + index + 1)), + (int index) => currentWeek.getWeekday(index + 1), ).map((day) { final lectures = lecturesOfDay(widget.lectures, day); if (lectures.isEmpty) { @@ -162,10 +156,11 @@ class SchedulePageViewState extends State static List lecturesOfDay(List lectures, DateTime day) { final filteredLectures = []; for (var i = 0; i < lectures.length; i++) { - if (lectures[i].startTime.year == day.year && - lectures[i].startTime.month == day.month && - lectures[i].startTime.day == day.day) { - filteredLectures.add(lectures[i]); + final lecture = lectures[i]; + if (lecture.startTime.day == day.day && + lecture.startTime.month == day.month && + lecture.startTime.year == day.year) { + filteredLectures.add(lecture); } } return filteredLectures; From 40d8adf72234d42e6a198002d11a50da515e85aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 02:39:01 +0000 Subject: [PATCH 293/334] style: add trailing comma --- uni/lib/controller/parsers/parser_schedule_html.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/uni/lib/controller/parsers/parser_schedule_html.dart b/uni/lib/controller/parsers/parser_schedule_html.dart index d80d69a27..2ab259b54 100644 --- a/uni/lib/controller/parsers/parser_schedule_html.dart +++ b/uni/lib/controller/parsers/parser_schedule_html.dart @@ -68,7 +68,10 @@ Future> getOverlappedClasses( ); final classLectures = await getScheduleFromHtml( - WeekResponse(week, response), session, faculty); + WeekResponse(week, response), + session, + faculty, + ); lecturesList.add( classLectures From 833fd5f147f2062e9898397b9a3ffd7f4d4db1ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 08:19:42 +0000 Subject: [PATCH 294/334] fix: use response body for parsing --- uni/lib/controller/parsers/parser_schedule_html.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/controller/parsers/parser_schedule_html.dart b/uni/lib/controller/parsers/parser_schedule_html.dart index 2ab259b54..916b7196b 100644 --- a/uni/lib/controller/parsers/parser_schedule_html.dart +++ b/uni/lib/controller/parsers/parser_schedule_html.dart @@ -116,7 +116,7 @@ Future> getScheduleFromHtml( Session session, String faculty, ) async { - final document = parse(response.response); + final document = parse(response.response.body); var semana = [0, 0, 0, 0, 0, 0]; final lecturesList = []; From b01df48deabeabd8d28509c208e1f43fbfb46c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 08:37:30 +0000 Subject: [PATCH 295/334] test: regenerate mocks --- .../src/exams2_page_test.mocks.dart | 420 ++++++++++++++++++ .../src/exams_page_test.mocks.dart | 50 ++- .../src/schedule_page_test.mocks.dart | 89 ++-- .../providers/exams_provider_test.mocks.dart | 84 ++-- .../lecture_provider_test.mocks.dart | 177 ++++---- 5 files changed, 665 insertions(+), 155 deletions(-) create mode 100644 uni/test/mocks/integration/src/exams2_page_test.mocks.dart diff --git a/uni/test/mocks/integration/src/exams2_page_test.mocks.dart b/uni/test/mocks/integration/src/exams2_page_test.mocks.dart new file mode 100644 index 000000000..2720f3dd6 --- /dev/null +++ b/uni/test/mocks/integration/src/exams2_page_test.mocks.dart @@ -0,0 +1,420 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in uni/test/integration/src/exams2_page_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; +import 'dart:convert' as _i4; +import 'dart:typed_data' as _i6; + +import 'package:http/http.dart' as _i2; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i5; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeResponse_0 extends _i1.SmartFake implements _i2.Response { + _FakeResponse_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeStreamedResponse_1 extends _i1.SmartFake + implements _i2.StreamedResponse { + _FakeStreamedResponse_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +/// A class which mocks [Client]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockClient extends _i1.Mock implements _i2.Client { + @override + _i3.Future<_i2.Response> head( + Uri? url, { + Map? headers, + }) => + (super.noSuchMethod( + Invocation.method( + #head, + [url], + {#headers: headers}, + ), + returnValue: _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #head, + [url], + {#headers: headers}, + ), + )), + returnValueForMissingStub: + _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #head, + [url], + {#headers: headers}, + ), + )), + ) as _i3.Future<_i2.Response>); + + @override + _i3.Future<_i2.Response> get( + Uri? url, { + Map? headers, + }) => + (super.noSuchMethod( + Invocation.method( + #get, + [url], + {#headers: headers}, + ), + returnValue: _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #get, + [url], + {#headers: headers}, + ), + )), + returnValueForMissingStub: + _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #get, + [url], + {#headers: headers}, + ), + )), + ) as _i3.Future<_i2.Response>); + + @override + _i3.Future<_i2.Response> post( + Uri? url, { + Map? headers, + Object? body, + _i4.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #post, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + returnValue: _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #post, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + returnValueForMissingStub: + _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #post, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + ) as _i3.Future<_i2.Response>); + + @override + _i3.Future<_i2.Response> put( + Uri? url, { + Map? headers, + Object? body, + _i4.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #put, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + returnValue: _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #put, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + returnValueForMissingStub: + _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #put, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + ) as _i3.Future<_i2.Response>); + + @override + _i3.Future<_i2.Response> patch( + Uri? url, { + Map? headers, + Object? body, + _i4.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #patch, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + returnValue: _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #patch, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + returnValueForMissingStub: + _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #patch, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + ) as _i3.Future<_i2.Response>); + + @override + _i3.Future<_i2.Response> delete( + Uri? url, { + Map? headers, + Object? body, + _i4.Encoding? encoding, + }) => + (super.noSuchMethod( + Invocation.method( + #delete, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + returnValue: _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #delete, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + returnValueForMissingStub: + _i3.Future<_i2.Response>.value(_FakeResponse_0( + this, + Invocation.method( + #delete, + [url], + { + #headers: headers, + #body: body, + #encoding: encoding, + }, + ), + )), + ) as _i3.Future<_i2.Response>); + + @override + _i3.Future read( + Uri? url, { + Map? headers, + }) => + (super.noSuchMethod( + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + returnValue: _i3.Future.value(_i5.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), + returnValueForMissingStub: + _i3.Future.value(_i5.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), + ) as _i3.Future); + + @override + _i3.Future<_i6.Uint8List> readBytes( + Uri? url, { + Map? headers, + }) => + (super.noSuchMethod( + Invocation.method( + #readBytes, + [url], + {#headers: headers}, + ), + returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + returnValueForMissingStub: + _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + ) as _i3.Future<_i6.Uint8List>); + + @override + _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => + (super.noSuchMethod( + Invocation.method( + #send, + [request], + ), + returnValue: + _i3.Future<_i2.StreamedResponse>.value(_FakeStreamedResponse_1( + this, + Invocation.method( + #send, + [request], + ), + )), + returnValueForMissingStub: + _i3.Future<_i2.StreamedResponse>.value(_FakeStreamedResponse_1( + this, + Invocation.method( + #send, + [request], + ), + )), + ) as _i3.Future<_i2.StreamedResponse>); + + @override + void close() => super.noSuchMethod( + Invocation.method( + #close, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [Response]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockResponse extends _i1.Mock implements _i2.Response { + @override + _i6.Uint8List get bodyBytes => (super.noSuchMethod( + Invocation.getter(#bodyBytes), + returnValue: _i6.Uint8List(0), + returnValueForMissingStub: _i6.Uint8List(0), + ) as _i6.Uint8List); + + @override + String get body => (super.noSuchMethod( + Invocation.getter(#body), + returnValue: _i5.dummyValue( + this, + Invocation.getter(#body), + ), + returnValueForMissingStub: _i5.dummyValue( + this, + Invocation.getter(#body), + ), + ) as String); + + @override + int get statusCode => (super.noSuchMethod( + Invocation.getter(#statusCode), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); + + @override + Map get headers => (super.noSuchMethod( + Invocation.getter(#headers), + returnValue: {}, + returnValueForMissingStub: {}, + ) as Map); + + @override + bool get isRedirect => (super.noSuchMethod( + Invocation.getter(#isRedirect), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get persistentConnection => (super.noSuchMethod( + Invocation.getter(#persistentConnection), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); +} diff --git a/uni/test/mocks/integration/src/exams_page_test.mocks.dart b/uni/test/mocks/integration/src/exams_page_test.mocks.dart index 40a40fedc..2d6896d46 100644 --- a/uni/test/mocks/integration/src/exams_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/exams_page_test.mocks.dart @@ -1,14 +1,15 @@ -// Mocks generated by Mockito 5.4.3-wip from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in uni/test/integration/src/exams_page_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; import 'dart:convert' as _i4; -import 'dart:typed_data' as _i5; +import 'dart:typed_data' as _i6; import 'package:http/http.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i5; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -295,12 +296,27 @@ class MockClient extends _i1.Mock implements _i2.Client { [url], {#headers: headers}, ), - returnValue: _i3.Future.value(''), - returnValueForMissingStub: _i3.Future.value(''), + returnValue: _i3.Future.value(_i5.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), + returnValueForMissingStub: + _i3.Future.value(_i5.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), ) as _i3.Future); @override - _i3.Future<_i5.Uint8List> readBytes( + _i3.Future<_i6.Uint8List> readBytes( Uri? url, { Map? headers, }) => @@ -310,10 +326,10 @@ class MockClient extends _i1.Mock implements _i2.Client { [url], {#headers: headers}, ), - returnValue: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), + returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), returnValueForMissingStub: - _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), - ) as _i3.Future<_i5.Uint8List>); + _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + ) as _i3.Future<_i6.Uint8List>); @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => @@ -355,17 +371,23 @@ class MockClient extends _i1.Mock implements _i2.Client { /// See the documentation for Mockito's code generation for more information. class MockResponse extends _i1.Mock implements _i2.Response { @override - _i5.Uint8List get bodyBytes => (super.noSuchMethod( + _i6.Uint8List get bodyBytes => (super.noSuchMethod( Invocation.getter(#bodyBytes), - returnValue: _i5.Uint8List(0), - returnValueForMissingStub: _i5.Uint8List(0), - ) as _i5.Uint8List); + returnValue: _i6.Uint8List(0), + returnValueForMissingStub: _i6.Uint8List(0), + ) as _i6.Uint8List); @override String get body => (super.noSuchMethod( Invocation.getter(#body), - returnValue: '', - returnValueForMissingStub: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#body), + ), + returnValueForMissingStub: _i5.dummyValue( + this, + Invocation.getter(#body), + ), ) as String); @override diff --git a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart index 5be25a598..638e98c48 100644 --- a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart @@ -1,20 +1,21 @@ -// Mocks generated by Mockito 5.4.3-wip from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in uni/test/integration/src/schedule_page_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; import 'dart:convert' as _i5; -import 'dart:typed_data' as _i6; -import 'dart:ui' as _i11; +import 'dart:typed_data' as _i7; +import 'dart:ui' as _i12; -import 'package:flutter/material.dart' as _i10; +import 'package:flutter/material.dart' as _i11; import 'package:http/http.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; import 'package:uni/model/entities/session.dart' as _i3; -import 'package:uni/model/providers/startup/session_provider.dart' as _i7; -import 'package:uni/model/providers/state_providers.dart' as _i9; -import 'package:uni/model/request_status.dart' as _i8; +import 'package:uni/model/providers/startup/session_provider.dart' as _i8; +import 'package:uni/model/providers/state_providers.dart' as _i10; +import 'package:uni/model/request_status.dart' as _i9; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -311,12 +312,27 @@ class MockClient extends _i1.Mock implements _i2.Client { [url], {#headers: headers}, ), - returnValue: _i4.Future.value(''), - returnValueForMissingStub: _i4.Future.value(''), + returnValue: _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), ) as _i4.Future); @override - _i4.Future<_i6.Uint8List> readBytes( + _i4.Future<_i7.Uint8List> readBytes( Uri? url, { Map? headers, }) => @@ -326,10 +342,10 @@ class MockClient extends _i1.Mock implements _i2.Client { [url], {#headers: headers}, ), - returnValue: _i4.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + returnValue: _i4.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), returnValueForMissingStub: - _i4.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), - ) as _i4.Future<_i6.Uint8List>); + _i4.Future<_i7.Uint8List>.value(_i7.Uint8List(0)), + ) as _i4.Future<_i7.Uint8List>); @override _i4.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => @@ -371,17 +387,23 @@ class MockClient extends _i1.Mock implements _i2.Client { /// See the documentation for Mockito's code generation for more information. class MockResponse extends _i1.Mock implements _i2.Response { @override - _i6.Uint8List get bodyBytes => (super.noSuchMethod( + _i7.Uint8List get bodyBytes => (super.noSuchMethod( Invocation.getter(#bodyBytes), - returnValue: _i6.Uint8List(0), - returnValueForMissingStub: _i6.Uint8List(0), - ) as _i6.Uint8List); + returnValue: _i7.Uint8List(0), + returnValueForMissingStub: _i7.Uint8List(0), + ) as _i7.Uint8List); @override String get body => (super.noSuchMethod( Invocation.getter(#body), - returnValue: '', - returnValueForMissingStub: '', + returnValue: _i6.dummyValue( + this, + Invocation.getter(#body), + ), + returnValueForMissingStub: _i6.dummyValue( + this, + Invocation.getter(#body), + ), ) as String); @override @@ -416,7 +438,7 @@ class MockResponse extends _i1.Mock implements _i2.Response { /// A class which mocks [SessionProvider]. /// /// See the documentation for Mockito's code generation for more information. -class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { +class MockSessionProvider extends _i1.Mock implements _i8.SessionProvider { @override bool get dependsOnSession => (super.noSuchMethod( Invocation.getter(#dependsOnSession), @@ -443,11 +465,11 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ); @override - _i8.RequestStatus get requestStatus => (super.noSuchMethod( + _i9.RequestStatus get requestStatus => (super.noSuchMethod( Invocation.getter(#requestStatus), - returnValue: _i8.RequestStatus.none, - returnValueForMissingStub: _i8.RequestStatus.none, - ) as _i8.RequestStatus); + returnValue: _i9.RequestStatus.none, + returnValueForMissingStub: _i9.RequestStatus.none, + ) as _i9.RequestStatus); @override bool get hasListeners => (super.noSuchMethod( @@ -457,7 +479,8 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ) as bool); @override - _i4.Future<_i3.Session> loadFromStorage(_i9.StateProviders? stateProviders) => + _i4.Future<_i3.Session> loadFromStorage( + _i10.StateProviders? stateProviders) => (super.noSuchMethod( Invocation.method( #loadFromStorage, @@ -480,7 +503,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ) as _i4.Future<_i3.Session>); @override - _i4.Future<_i3.Session> loadFromRemote(_i9.StateProviders? stateProviders) => + _i4.Future<_i3.Session> loadFromRemote(_i10.StateProviders? stateProviders) => (super.noSuchMethod( Invocation.method( #loadFromRemote, @@ -504,7 +527,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { @override _i4.Future postAuthentication( - _i10.BuildContext? context, + _i11.BuildContext? context, String? username, String? password, List? faculties, { @@ -526,9 +549,9 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ) as _i4.Future); @override - void updateState(_i3.Session? newState) => super.noSuchMethod( + void setState(_i3.Session? newState) => super.noSuchMethod( Invocation.method( - #updateState, + #setState, [newState], ), returnValueForMissingStub: null, @@ -544,7 +567,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ); @override - _i4.Future forceRefresh(_i10.BuildContext? context) => + _i4.Future forceRefresh(_i11.BuildContext? context) => (super.noSuchMethod( Invocation.method( #forceRefresh, @@ -555,7 +578,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ) as _i4.Future); @override - _i4.Future ensureInitialized(_i10.BuildContext? context) => + _i4.Future ensureInitialized(_i11.BuildContext? context) => (super.noSuchMethod( Invocation.method( #ensureInitialized, @@ -566,7 +589,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ) as _i4.Future); @override - void addListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #addListener, [listener], @@ -575,7 +598,7 @@ class MockSessionProvider extends _i1.Mock implements _i7.SessionProvider { ); @override - void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod( + void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( Invocation.method( #removeListener, [listener], diff --git a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart index 9f2e5d8ed..df42b853e 100644 --- a/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/exams_provider_test.mocks.dart @@ -1,17 +1,18 @@ -// Mocks generated by Mockito 5.4.3-wip from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in uni/test/unit/providers/exams_provider_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i3; import 'dart:convert' as _i4; -import 'dart:typed_data' as _i5; +import 'dart:typed_data' as _i6; import 'package:http/http.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; -import 'package:uni/controller/parsers/parser_exams.dart' as _i6; -import 'package:uni/model/entities/course.dart' as _i8; -import 'package:uni/model/entities/exam.dart' as _i7; +import 'package:mockito/src/dummies.dart' as _i5; +import 'package:uni/controller/parsers/parser_exams.dart' as _i7; +import 'package:uni/model/entities/course.dart' as _i9; +import 'package:uni/model/entities/exam.dart' as _i8; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -298,12 +299,27 @@ class MockClient extends _i1.Mock implements _i2.Client { [url], {#headers: headers}, ), - returnValue: _i3.Future.value(''), - returnValueForMissingStub: _i3.Future.value(''), + returnValue: _i3.Future.value(_i5.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), + returnValueForMissingStub: + _i3.Future.value(_i5.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), ) as _i3.Future); @override - _i3.Future<_i5.Uint8List> readBytes( + _i3.Future<_i6.Uint8List> readBytes( Uri? url, { Map? headers, }) => @@ -313,10 +329,10 @@ class MockClient extends _i1.Mock implements _i2.Client { [url], {#headers: headers}, ), - returnValue: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), + returnValue: _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), returnValueForMissingStub: - _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), - ) as _i3.Future<_i5.Uint8List>); + _i3.Future<_i6.Uint8List>.value(_i6.Uint8List(0)), + ) as _i3.Future<_i6.Uint8List>); @override _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => @@ -356,21 +372,33 @@ class MockClient extends _i1.Mock implements _i2.Client { /// A class which mocks [ParserExams]. /// /// See the documentation for Mockito's code generation for more information. -class MockParserExams extends _i1.Mock implements _i6.ParserExams { +class MockParserExams extends _i1.Mock implements _i7.ParserExams { @override String getExamSeasonAbbr(String? seasonStr) => (super.noSuchMethod( Invocation.method( #getExamSeasonAbbr, [seasonStr], ), - returnValue: '', - returnValueForMissingStub: '', + returnValue: _i5.dummyValue( + this, + Invocation.method( + #getExamSeasonAbbr, + [seasonStr], + ), + ), + returnValueForMissingStub: _i5.dummyValue( + this, + Invocation.method( + #getExamSeasonAbbr, + [seasonStr], + ), + ), ) as String); @override - _i3.Future> parseExams( + _i3.Future> parseExams( _i2.Response? response, - _i8.Course? course, + _i9.Course? course, ) => (super.noSuchMethod( Invocation.method( @@ -380,10 +408,10 @@ class MockParserExams extends _i1.Mock implements _i6.ParserExams { course, ], ), - returnValue: _i3.Future>.value(<_i7.Exam>{}), + returnValue: _i3.Future>.value(<_i8.Exam>{}), returnValueForMissingStub: - _i3.Future>.value(<_i7.Exam>{}), - ) as _i3.Future>); + _i3.Future>.value(<_i8.Exam>{}), + ) as _i3.Future>); } /// A class which mocks [Response]. @@ -391,17 +419,23 @@ class MockParserExams extends _i1.Mock implements _i6.ParserExams { /// See the documentation for Mockito's code generation for more information. class MockResponse extends _i1.Mock implements _i2.Response { @override - _i5.Uint8List get bodyBytes => (super.noSuchMethod( + _i6.Uint8List get bodyBytes => (super.noSuchMethod( Invocation.getter(#bodyBytes), - returnValue: _i5.Uint8List(0), - returnValueForMissingStub: _i5.Uint8List(0), - ) as _i5.Uint8List); + returnValue: _i6.Uint8List(0), + returnValueForMissingStub: _i6.Uint8List(0), + ) as _i6.Uint8List); @override String get body => (super.noSuchMethod( Invocation.getter(#body), - returnValue: '', - returnValueForMissingStub: '', + returnValue: _i5.dummyValue( + this, + Invocation.getter(#body), + ), + returnValueForMissingStub: _i5.dummyValue( + this, + Invocation.getter(#body), + ), ) as String); @override diff --git a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart index 49010fdd7..80287bc39 100644 --- a/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart +++ b/uni/test/mocks/unit/providers/lecture_provider_test.mocks.dart @@ -1,19 +1,21 @@ -// Mocks generated by Mockito 5.4.3-wip from annotations +// Mocks generated by Mockito 5.4.4 from annotations // in uni/test/unit/providers/lecture_provider_test.dart. // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; -import 'dart:convert' as _i8; -import 'dart:typed_data' as _i9; +import 'dart:convert' as _i9; +import 'dart:typed_data' as _i11; -import 'package:http/http.dart' as _i3; +import 'package:http/http.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i10; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher.dart' - as _i2; + as _i3; import 'package:uni/model/entities/lecture.dart' as _i5; import 'package:uni/model/entities/profile.dart' as _i7; import 'package:uni/model/entities/session.dart' as _i6; +import 'package:uni/model/utils/time/week.dart' as _i8; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -28,8 +30,8 @@ import 'package:uni/model/entities/session.dart' as _i6; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeDates_0 extends _i1.SmartFake implements _i2.Dates { - _FakeDates_0( +class _FakeResponse_0 extends _i1.SmartFake implements _i2.Response { + _FakeResponse_0( Object parent, Invocation parentInvocation, ) : super( @@ -38,19 +40,9 @@ class _FakeDates_0 extends _i1.SmartFake implements _i2.Dates { ); } -class _FakeResponse_1 extends _i1.SmartFake implements _i3.Response { - _FakeResponse_1( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeStreamedResponse_2 extends _i1.SmartFake - implements _i3.StreamedResponse { - _FakeStreamedResponse_2( +class _FakeStreamedResponse_1 extends _i1.SmartFake + implements _i2.StreamedResponse { + _FakeStreamedResponse_1( Object parent, Invocation parentInvocation, ) : super( @@ -62,7 +54,7 @@ class _FakeStreamedResponse_2 extends _i1.SmartFake /// A class which mocks [ScheduleFetcher]. /// /// See the documentation for Mockito's code generation for more information. -class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { +class MockScheduleFetcher extends _i1.Mock implements _i3.ScheduleFetcher { @override _i4.Future> getLectures( _i6.Session? session, @@ -82,26 +74,24 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { ) as _i4.Future>); @override - _i2.Dates getDates() => (super.noSuchMethod( + List<_i8.Week> getWeeks(DateTime? now) => (super.noSuchMethod( + Invocation.method( + #getWeeks, + [now], + ), + returnValue: <_i8.Week>[], + returnValueForMissingStub: <_i8.Week>[], + ) as List<_i8.Week>); + + @override + List<_i3.Dates> getDates() => (super.noSuchMethod( Invocation.method( #getDates, [], ), - returnValue: _FakeDates_0( - this, - Invocation.method( - #getDates, - [], - ), - ), - returnValueForMissingStub: _FakeDates_0( - this, - Invocation.method( - #getDates, - [], - ), - ), - ) as _i2.Dates); + returnValue: <_i3.Dates>[], + returnValueForMissingStub: <_i3.Dates>[], + ) as List<_i3.Dates>); @override List getEndpoints(_i6.Session? session) => (super.noSuchMethod( @@ -117,9 +107,9 @@ class MockScheduleFetcher extends _i1.Mock implements _i2.ScheduleFetcher { /// A class which mocks [Client]. /// /// See the documentation for Mockito's code generation for more information. -class MockClient extends _i1.Mock implements _i3.Client { +class MockClient extends _i1.Mock implements _i2.Client { @override - _i4.Future<_i3.Response> head( + _i4.Future<_i2.Response> head( Uri? url, { Map? headers, }) => @@ -129,7 +119,7 @@ class MockClient extends _i1.Mock implements _i3.Client { [url], {#headers: headers}, ), - returnValue: _i4.Future<_i3.Response>.value(_FakeResponse_1( + returnValue: _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #head, @@ -138,7 +128,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), returnValueForMissingStub: - _i4.Future<_i3.Response>.value(_FakeResponse_1( + _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #head, @@ -146,10 +136,10 @@ class MockClient extends _i1.Mock implements _i3.Client { {#headers: headers}, ), )), - ) as _i4.Future<_i3.Response>); + ) as _i4.Future<_i2.Response>); @override - _i4.Future<_i3.Response> get( + _i4.Future<_i2.Response> get( Uri? url, { Map? headers, }) => @@ -159,7 +149,7 @@ class MockClient extends _i1.Mock implements _i3.Client { [url], {#headers: headers}, ), - returnValue: _i4.Future<_i3.Response>.value(_FakeResponse_1( + returnValue: _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #get, @@ -168,7 +158,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), returnValueForMissingStub: - _i4.Future<_i3.Response>.value(_FakeResponse_1( + _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #get, @@ -176,14 +166,14 @@ class MockClient extends _i1.Mock implements _i3.Client { {#headers: headers}, ), )), - ) as _i4.Future<_i3.Response>); + ) as _i4.Future<_i2.Response>); @override - _i4.Future<_i3.Response> post( + _i4.Future<_i2.Response> post( Uri? url, { Map? headers, Object? body, - _i8.Encoding? encoding, + _i9.Encoding? encoding, }) => (super.noSuchMethod( Invocation.method( @@ -195,7 +185,7 @@ class MockClient extends _i1.Mock implements _i3.Client { #encoding: encoding, }, ), - returnValue: _i4.Future<_i3.Response>.value(_FakeResponse_1( + returnValue: _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #post, @@ -208,7 +198,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), returnValueForMissingStub: - _i4.Future<_i3.Response>.value(_FakeResponse_1( + _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #post, @@ -220,14 +210,14 @@ class MockClient extends _i1.Mock implements _i3.Client { }, ), )), - ) as _i4.Future<_i3.Response>); + ) as _i4.Future<_i2.Response>); @override - _i4.Future<_i3.Response> put( + _i4.Future<_i2.Response> put( Uri? url, { Map? headers, Object? body, - _i8.Encoding? encoding, + _i9.Encoding? encoding, }) => (super.noSuchMethod( Invocation.method( @@ -239,7 +229,7 @@ class MockClient extends _i1.Mock implements _i3.Client { #encoding: encoding, }, ), - returnValue: _i4.Future<_i3.Response>.value(_FakeResponse_1( + returnValue: _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #put, @@ -252,7 +242,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), returnValueForMissingStub: - _i4.Future<_i3.Response>.value(_FakeResponse_1( + _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #put, @@ -264,14 +254,14 @@ class MockClient extends _i1.Mock implements _i3.Client { }, ), )), - ) as _i4.Future<_i3.Response>); + ) as _i4.Future<_i2.Response>); @override - _i4.Future<_i3.Response> patch( + _i4.Future<_i2.Response> patch( Uri? url, { Map? headers, Object? body, - _i8.Encoding? encoding, + _i9.Encoding? encoding, }) => (super.noSuchMethod( Invocation.method( @@ -283,7 +273,7 @@ class MockClient extends _i1.Mock implements _i3.Client { #encoding: encoding, }, ), - returnValue: _i4.Future<_i3.Response>.value(_FakeResponse_1( + returnValue: _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #patch, @@ -296,7 +286,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), returnValueForMissingStub: - _i4.Future<_i3.Response>.value(_FakeResponse_1( + _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #patch, @@ -308,14 +298,14 @@ class MockClient extends _i1.Mock implements _i3.Client { }, ), )), - ) as _i4.Future<_i3.Response>); + ) as _i4.Future<_i2.Response>); @override - _i4.Future<_i3.Response> delete( + _i4.Future<_i2.Response> delete( Uri? url, { Map? headers, Object? body, - _i8.Encoding? encoding, + _i9.Encoding? encoding, }) => (super.noSuchMethod( Invocation.method( @@ -327,7 +317,7 @@ class MockClient extends _i1.Mock implements _i3.Client { #encoding: encoding, }, ), - returnValue: _i4.Future<_i3.Response>.value(_FakeResponse_1( + returnValue: _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #delete, @@ -340,7 +330,7 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), returnValueForMissingStub: - _i4.Future<_i3.Response>.value(_FakeResponse_1( + _i4.Future<_i2.Response>.value(_FakeResponse_0( this, Invocation.method( #delete, @@ -352,7 +342,7 @@ class MockClient extends _i1.Mock implements _i3.Client { }, ), )), - ) as _i4.Future<_i3.Response>); + ) as _i4.Future<_i2.Response>); @override _i4.Future read( @@ -365,12 +355,27 @@ class MockClient extends _i1.Mock implements _i3.Client { [url], {#headers: headers}, ), - returnValue: _i4.Future.value(''), - returnValueForMissingStub: _i4.Future.value(''), + returnValue: _i4.Future.value(_i10.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i10.dummyValue( + this, + Invocation.method( + #read, + [url], + {#headers: headers}, + ), + )), ) as _i4.Future); @override - _i4.Future<_i9.Uint8List> readBytes( + _i4.Future<_i11.Uint8List> readBytes( Uri? url, { Map? headers, }) => @@ -380,20 +385,20 @@ class MockClient extends _i1.Mock implements _i3.Client { [url], {#headers: headers}, ), - returnValue: _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), + returnValue: _i4.Future<_i11.Uint8List>.value(_i11.Uint8List(0)), returnValueForMissingStub: - _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), - ) as _i4.Future<_i9.Uint8List>); + _i4.Future<_i11.Uint8List>.value(_i11.Uint8List(0)), + ) as _i4.Future<_i11.Uint8List>); @override - _i4.Future<_i3.StreamedResponse> send(_i3.BaseRequest? request) => + _i4.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( Invocation.method( #send, [request], ), returnValue: - _i4.Future<_i3.StreamedResponse>.value(_FakeStreamedResponse_2( + _i4.Future<_i2.StreamedResponse>.value(_FakeStreamedResponse_1( this, Invocation.method( #send, @@ -401,14 +406,14 @@ class MockClient extends _i1.Mock implements _i3.Client { ), )), returnValueForMissingStub: - _i4.Future<_i3.StreamedResponse>.value(_FakeStreamedResponse_2( + _i4.Future<_i2.StreamedResponse>.value(_FakeStreamedResponse_1( this, Invocation.method( #send, [request], ), )), - ) as _i4.Future<_i3.StreamedResponse>); + ) as _i4.Future<_i2.StreamedResponse>); @override void close() => super.noSuchMethod( @@ -423,19 +428,25 @@ class MockClient extends _i1.Mock implements _i3.Client { /// A class which mocks [Response]. /// /// See the documentation for Mockito's code generation for more information. -class MockResponse extends _i1.Mock implements _i3.Response { +class MockResponse extends _i1.Mock implements _i2.Response { @override - _i9.Uint8List get bodyBytes => (super.noSuchMethod( + _i11.Uint8List get bodyBytes => (super.noSuchMethod( Invocation.getter(#bodyBytes), - returnValue: _i9.Uint8List(0), - returnValueForMissingStub: _i9.Uint8List(0), - ) as _i9.Uint8List); + returnValue: _i11.Uint8List(0), + returnValueForMissingStub: _i11.Uint8List(0), + ) as _i11.Uint8List); @override String get body => (super.noSuchMethod( Invocation.getter(#body), - returnValue: '', - returnValueForMissingStub: '', + returnValue: _i10.dummyValue( + this, + Invocation.getter(#body), + ), + returnValueForMissingStub: _i10.dummyValue( + this, + Invocation.getter(#body), + ), ) as String); @override From cb73616c76d602c9c1089a3be1d386ecc2069ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 10:25:33 +0000 Subject: [PATCH 296/334] test: fix some more tests --- uni/lib/view/schedule/schedule.dart | 11 +++++++---- uni/test/unit/view/Pages/schedule_page_view_test.dart | 4 ++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index b53d6a869..5738dd74e 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -12,7 +12,9 @@ import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/schedule/widgets/schedule_slot.dart'; class SchedulePage extends StatefulWidget { - const SchedulePage({super.key}); + const SchedulePage({super.key, this.now}); + + final DateTime? now; @override SchedulePageState createState() => SchedulePageState(); @@ -24,7 +26,7 @@ class SchedulePageState extends SecondaryPageViewState { return LazyConsumer>( builder: (context, lectures) => SchedulePageView(lectures), hasContent: (lectures) => lectures.isNotEmpty, - onNullContent: const SchedulePageView([]), + onNullContent: SchedulePageView(const [], now: widget.now), ); } @@ -39,9 +41,10 @@ class SchedulePageState extends SecondaryPageViewState { } class SchedulePageView extends StatefulWidget { - const SchedulePageView(this.lectures, {super.key}); + const SchedulePageView(this.lectures, {super.key, this.now}); final List lectures; + final DateTime? now; @override SchedulePageViewState createState() => SchedulePageViewState(); @@ -60,7 +63,7 @@ class SchedulePageViewState extends State length: 5, ); - final now = DateTime.now(); + final now = widget.now ?? DateTime.now(); currentWeek = Week(start: now); final weekDay = now.weekday; diff --git a/uni/test/unit/view/Pages/schedule_page_view_test.dart b/uni/test/unit/view/Pages/schedule_page_view_test.dart index 025d37a3d..0ae252ed7 100644 --- a/uni/test/unit/view/Pages/schedule_page_view_test.dart +++ b/uni/test/unit/view/Pages/schedule_page_view_test.dart @@ -12,6 +12,7 @@ void main() async { group('SchedulePage', () { const blocks = 4; const classNumber = 'MIEIC03'; + final now = DateTime(2021, 06, 05); final day0 = DateTime(2021, 06, 07); final day1 = DateTime(2021, 06, 08); final day2 = DateTime(2021, 06, 09); @@ -97,6 +98,7 @@ void main() async { (WidgetTester tester) async { final widget = SchedulePageView( [lecture1], + now: now, ); await tester.pumpWidget(testableWidget(widget, providers: [])); @@ -119,6 +121,7 @@ void main() async { (WidgetTester tester) async { final widget = SchedulePageView( [lecture1, lecture2], + now: now, ); await tester.pumpWidget(testableWidget(widget, providers: [])); await tester.pumpAndSettle(); @@ -149,6 +152,7 @@ void main() async { lecture5, lecture6, ], + now: now, ), ); From abebce4ba3b2473632cbb76e562c0ad030322094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 12:03:18 +0000 Subject: [PATCH 297/334] test: add tests for WeekdayMapper --- uni/lib/model/utils/time/weekday_mapper.dart | 10 ++- .../utils/time/weekday_mapper_test.dart | 69 +++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 uni/test/unit/models/utils/time/weekday_mapper_test.dart diff --git a/uni/lib/model/utils/time/weekday_mapper.dart b/uni/lib/model/utils/time/weekday_mapper.dart index 724f4a29e..068984b88 100644 --- a/uni/lib/model/utils/time/weekday_mapper.dart +++ b/uni/lib/model/utils/time/weekday_mapper.dart @@ -69,8 +69,15 @@ class WeekdayMapper { ); } + WeekdayMapper get inverse => WeekdayMapper( + fromStart: _toStart, + fromMonday: _toMonday, + toStart: _fromStart, + toMonday: _fromMonday, + ); + int map(int fromWeekday) { - final mondayOffset = _toMonday - _fromMonday; + final mondayOffset = (_toMonday - _toStart) - (_fromMonday - _fromStart); // The algorithm is as follows: // @@ -95,5 +102,6 @@ class WeekdayMapper { // In case you're wondering, like me, the compact formula is: // (fromWeekday - fromStart + toMonday - fromMonday) % 7 + toStart + // (fromWeekday + _toMonday - _toStart - _fromMonday) % 7 + _toStart } } diff --git a/uni/test/unit/models/utils/time/weekday_mapper_test.dart b/uni/test/unit/models/utils/time/weekday_mapper_test.dart new file mode 100644 index 000000000..d00a62550 --- /dev/null +++ b/uni/test/unit/models/utils/time/weekday_mapper_test.dart @@ -0,0 +1,69 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:uni/model/utils/time/weekday_mapper.dart'; + +void exhaustivelyTestWeekdayMapper( + int fromStart, + int fromMonday, + int toStart, + int toMonday, +) { + group( + 'When mapping from $fromStart..${fromStart + 6} (monday = $fromMonday)' + ' to $toStart..${toStart + 6} (monday = $toMonday)', () { + final mapper = WeekdayMapper( + fromStart: fromStart, + fromMonday: fromMonday, + toStart: toStart, + toMonday: toMonday, + ); + + final inverseMapper = mapper.inverse; + + final fromEnd = fromStart + 7; + final toEnd = toStart + 7; + + var fromWeekday = fromMonday; + var toWeekday = toMonday; + + for (var i = 0; i < 7; i++) { + group('[DateTime.weekday = ${i + 1}]', () { + test('fromWeekday = $fromWeekday should map to toWeekday = $toMonday', + () { + expect(mapper.map(fromWeekday), toWeekday); + }); + + test( + 'toWeekday = $toWeekday should inversely map to' + ' fromWeekday = $fromWeekday', () { + expect(inverseMapper.map(toWeekday), fromWeekday); + }); + }); + + fromWeekday = ++fromWeekday >= fromEnd ? fromStart : fromWeekday; + toWeekday = ++toWeekday >= toEnd ? toStart : toWeekday; + } + }); +} + +void main() { + group('WeekdayMapper', () { + // There are three main states for `fromStart` and `toStart`: + // - fromStart < toStart + // - fromStart = toStart + // - fromStart > toStart + + // The same goes for `fromMonday` and `toMonday`. + + exhaustivelyTestWeekdayMapper(2, 4, 3, 5); + exhaustivelyTestWeekdayMapper(2, 4, 3, 4); + exhaustivelyTestWeekdayMapper(2, 5, 3, 4); + + exhaustivelyTestWeekdayMapper(2, 4, 2, 5); + exhaustivelyTestWeekdayMapper(2, 4, 2, 4); + exhaustivelyTestWeekdayMapper(2, 5, 2, 4); + + exhaustivelyTestWeekdayMapper(3, 4, 2, 5); + exhaustivelyTestWeekdayMapper(3, 4, 2, 4); + exhaustivelyTestWeekdayMapper(3, 5, 2, 4); + }); +} From 2e8c72cae9d7ae5bd1abd7b72b9e36ff43c8882a Mon Sep 17 00:00:00 2001 From: Adriana Date: Fri, 22 Dec 2023 17:38:39 +0000 Subject: [PATCH 298/334] change uc link --- uni/lib/generated/intl/messages_all.dart | 22 +- uni/lib/generated/intl/messages_en.dart | 420 +++++++---------- uni/lib/generated/intl/messages_pt_PT.dart | 422 +++++++----------- uni/lib/generated/l10n.dart | 33 +- uni/lib/l10n/intl_en.arb | 2 + uni/lib/l10n/intl_pt_PT.arb | 2 + .../view/schedule/widgets/schedule_slot.dart | 28 +- 7 files changed, 374 insertions(+), 555 deletions(-) diff --git a/uni/lib/generated/intl/messages_all.dart b/uni/lib/generated/intl/messages_all.dart index b77f94db2..fb1bd2689 100644 --- a/uni/lib/generated/intl/messages_all.dart +++ b/uni/lib/generated/intl/messages_all.dart @@ -11,7 +11,6 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; import 'package:intl/src/intl_helpers.dart'; @@ -21,8 +20,8 @@ import 'messages_pt_PT.dart' as messages_pt_pt; typedef Future LibraryLoader(); Map _deferredLibraries = { - 'en': () => new SynchronousFuture(null), - 'pt_PT': () => new SynchronousFuture(null), + 'en': () => new Future.value(null), + 'pt_PT': () => new Future.value(null), }; MessageLookupByLibrary? _findExact(String localeName) { @@ -37,18 +36,19 @@ MessageLookupByLibrary? _findExact(String localeName) { } /// User programs should call this before using [localeName] for messages. -Future initializeMessages(String localeName) { +Future initializeMessages(String localeName) async { var availableLocale = Intl.verifiedLocale( - localeName, (locale) => _deferredLibraries[locale] != null, - onFailure: (_) => null); + localeName, + (locale) => _deferredLibraries[locale] != null, + onFailure: (_) => null); if (availableLocale == null) { - return new SynchronousFuture(false); + return new Future.value(false); } var lib = _deferredLibraries[availableLocale]; - lib == null ? new SynchronousFuture(false) : lib(); + await (lib == null ? new Future.value(false) : lib()); initializeInternalMessageLookup(() => new CompositeMessageLookup()); messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); - return new SynchronousFuture(true); + return new Future.value(true); } bool _messagesExistFor(String locale) { @@ -60,8 +60,8 @@ bool _messagesExistFor(String locale) { } MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { - var actualLocale = - Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); + var actualLocale = Intl.verifiedLocale(locale, _messagesExistFor, + onFailure: (_) => null); if (actualLocale == null) return null; return _findExact(actualLocale); } diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 25e361d91..fa9e7aeaf 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -7,8 +7,7 @@ // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes +// ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; @@ -20,268 +19,165 @@ typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'en'; - static String m0(time) => "last refresh at ${time}"; + static m0(time) => "last refresh at ${time}"; - static String m1(time) => - "${Intl.plural(time, zero: 'Refreshed ${time} minutes ago', one: 'Refreshed ${time} minute ago', other: 'Refreshed ${time} minutes ago')}"; + static m1(time) => "${Intl.plural(time, zero: 'Refreshed ${time} minutes ago', one: 'Refreshed ${time} minute ago', other: 'Refreshed ${time} minutes ago')}"; - static String m2(title) => "${Intl.select(title, { - 'horario': 'Schedule', - 'exames': 'Exams', - 'area': 'Personal Area', - 'cadeiras': 'Course Units', - 'autocarros': 'Buses', - 'locais': 'Places', - 'restaurantes': 'Restaurants', - 'calendario': 'Calendar', - 'biblioteca': 'Library', - 'percurso_academico': 'Academic Path', - 'transportes': 'Transports', - 'faculdade': 'Faculty', - 'other': 'Other', - })}"; + static m2(title) => "${Intl.select(title, {'horario': 'Schedule', 'exames': 'Exams', 'area': 'Personal Area', 'cadeiras': 'Course Units', 'autocarros': 'Buses', 'locais': 'Places', 'restaurantes': 'Restaurants', 'calendario': 'Calendar', 'biblioteca': 'Library', 'percurso_academico': 'Academic Path', 'transportes': 'Transports', 'faculdade': 'Faculty', 'other': 'Other', })}"; final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "about": MessageLookupByLibrary.simpleMessage("About us"), - "academic_services": - MessageLookupByLibrary.simpleMessage("Academic services"), - "account_card_title": - MessageLookupByLibrary.simpleMessage("Checking account"), - "add": MessageLookupByLibrary.simpleMessage("Add"), - "add_quota": MessageLookupByLibrary.simpleMessage("Add quota"), - "add_widget": MessageLookupByLibrary.simpleMessage("Add widget"), - "agree_terms": MessageLookupByLibrary.simpleMessage( - "By entering you confirm that you agree with these Terms and Conditions"), - "all_widgets_added": MessageLookupByLibrary.simpleMessage( - "All available widgets have already been added to your personal area!"), - "at_least_one_college": - MessageLookupByLibrary.simpleMessage("Select at least one college"), - "available_amount": - MessageLookupByLibrary.simpleMessage("Available amount"), - "average": MessageLookupByLibrary.simpleMessage("Average: "), - "balance": MessageLookupByLibrary.simpleMessage("Balance:"), - "banner_info": MessageLookupByLibrary.simpleMessage( - "We do now collect anonymous usage statistics in order to improve your experience. You can change it in settings."), - "bs_description": MessageLookupByLibrary.simpleMessage( - "Did you find any bugs in the application?\nDo you have any suggestions for the app?\nTell us so we can improve!"), - "bug_description": MessageLookupByLibrary.simpleMessage( - "Bug found, how to reproduce it, etc."), - "bus_error": - MessageLookupByLibrary.simpleMessage("Unable to get information"), - "bus_information": MessageLookupByLibrary.simpleMessage( - "Select the buses you want information about:"), - "buses_personalize": - MessageLookupByLibrary.simpleMessage("Personalize your buses here"), - "buses_text": MessageLookupByLibrary.simpleMessage( - "Favorite buses will be displayed in the favorites \'Bus\' widget. The remaining ones will only be displayed on the page."), - "cancel": MessageLookupByLibrary.simpleMessage("Cancel"), - "change": MessageLookupByLibrary.simpleMessage("Change"), - "change_prompt": MessageLookupByLibrary.simpleMessage( - "Do you want to change the password?"), - "check_internet": MessageLookupByLibrary.simpleMessage( - "Check your internet connection"), - "class_registration": - MessageLookupByLibrary.simpleMessage("Class Registration"), - "collect_usage_stats": - MessageLookupByLibrary.simpleMessage("Collect usage statistics"), - "college": MessageLookupByLibrary.simpleMessage("College: "), - "college_select": - MessageLookupByLibrary.simpleMessage("select your college(s)"), - "conclude": MessageLookupByLibrary.simpleMessage("Done"), - "configured_buses": - MessageLookupByLibrary.simpleMessage("Configured Buses"), - "confirm": MessageLookupByLibrary.simpleMessage("Confirm"), - "confirm_logout": MessageLookupByLibrary.simpleMessage( - "Do you really want to log out? Your local data will be deleted and you will have to log in again."), - "consent": MessageLookupByLibrary.simpleMessage( - "I consent to this information being reviewed by NIAEFEUP and may be deleted at my request."), - "contact": MessageLookupByLibrary.simpleMessage("Contact (optional)"), - "copy_center": MessageLookupByLibrary.simpleMessage("Copy center"), - "copy_center_building": MessageLookupByLibrary.simpleMessage( - "Floor -1 of building B | AEFEUP building"), - "course_class": MessageLookupByLibrary.simpleMessage("Classes"), - "course_info": MessageLookupByLibrary.simpleMessage("Info"), - "current_state": - MessageLookupByLibrary.simpleMessage("Current state: "), - "current_year": - MessageLookupByLibrary.simpleMessage("Current academic year: "), - "decrement": MessageLookupByLibrary.simpleMessage("Decrement 1,00€"), - "description": MessageLookupByLibrary.simpleMessage("Description"), - "desired_email": MessageLookupByLibrary.simpleMessage( - "Email where you want to be contacted"), - "dona_bia": MessageLookupByLibrary.simpleMessage( - "D. Beatriz\'s stationery store"), - "dona_bia_building": MessageLookupByLibrary.simpleMessage( - "Floor -1 of building B (B-142)"), - "download_error": - MessageLookupByLibrary.simpleMessage("Error downloading the file"), - "ects": MessageLookupByLibrary.simpleMessage("ECTS performed: "), - "edit_off": MessageLookupByLibrary.simpleMessage("Edit"), - "edit_on": MessageLookupByLibrary.simpleMessage("Finish editing"), - "empty_text": - MessageLookupByLibrary.simpleMessage("Please fill in this field"), - "exams_filter": - MessageLookupByLibrary.simpleMessage("Exams Filter Settings"), - "exit_confirm": - MessageLookupByLibrary.simpleMessage("Do you really want to exit?"), - "expired_password": - MessageLookupByLibrary.simpleMessage("Your password has expired"), - "failed_login": MessageLookupByLibrary.simpleMessage("Login failed"), - "fee_date": - MessageLookupByLibrary.simpleMessage("Deadline for next fee:"), - "fee_notification": - MessageLookupByLibrary.simpleMessage("Fee deadline"), - "files": MessageLookupByLibrary.simpleMessage("Files"), - "first_year_registration": MessageLookupByLibrary.simpleMessage( - "Year of first registration: "), - "floor": MessageLookupByLibrary.simpleMessage("Floor"), - "floors": MessageLookupByLibrary.simpleMessage("Floors"), - "forgot_password": - MessageLookupByLibrary.simpleMessage("Forgot password?"), - "generate_reference": - MessageLookupByLibrary.simpleMessage("Generate reference"), - "geral_registration": - MessageLookupByLibrary.simpleMessage("General Registration"), - "improvement_registration": - MessageLookupByLibrary.simpleMessage("Enrollment for Improvement"), - "increment": MessageLookupByLibrary.simpleMessage("Increment 1,00€"), - "invalid_credentials": - MessageLookupByLibrary.simpleMessage("Invalid credentials"), - "keep_login": MessageLookupByLibrary.simpleMessage("Stay signed in"), - "language": MessageLookupByLibrary.simpleMessage("Language"), - "last_refresh_time": m0, - "last_timestamp": m1, - "library_occupation": - MessageLookupByLibrary.simpleMessage("Library Occupation"), - "load_error": MessageLookupByLibrary.simpleMessage( - "Error loading the information"), - "loading_terms": MessageLookupByLibrary.simpleMessage( - "Loading Terms and Conditions..."), - "login": MessageLookupByLibrary.simpleMessage("Login"), - "logout": MessageLookupByLibrary.simpleMessage("Log out"), - "menus": MessageLookupByLibrary.simpleMessage("Menus"), - "min_value_reference": - MessageLookupByLibrary.simpleMessage("Minimum value: 1,00 €"), - "multimedia_center": - MessageLookupByLibrary.simpleMessage("Multimedia center"), - "nav_title": m2, - "news": MessageLookupByLibrary.simpleMessage("News"), - "no": MessageLookupByLibrary.simpleMessage("No"), - "no_app": MessageLookupByLibrary.simpleMessage( - "No app found to open the file"), - "no_bus": MessageLookupByLibrary.simpleMessage("Don\'t miss any bus!"), - "no_bus_stops": - MessageLookupByLibrary.simpleMessage("No configured stops"), - "no_class": MessageLookupByLibrary.simpleMessage( - "There are no classes to display"), - "no_classes": - MessageLookupByLibrary.simpleMessage("No classes to present"), - "no_classes_on": - MessageLookupByLibrary.simpleMessage("You don\'t have classes on"), - "no_college": MessageLookupByLibrary.simpleMessage("no college"), - "no_course_units": MessageLookupByLibrary.simpleMessage( - "No course units in the selected period"), - "no_data": MessageLookupByLibrary.simpleMessage( - "There is no data to show at this time"), - "no_date": MessageLookupByLibrary.simpleMessage("No date"), - "no_events": MessageLookupByLibrary.simpleMessage("No events found"), - "no_exams": MessageLookupByLibrary.simpleMessage( - "You have no exams scheduled\n"), - "no_exams_label": MessageLookupByLibrary.simpleMessage( - "Looks like you are on vacation!"), - "no_favorite_restaurants": - MessageLookupByLibrary.simpleMessage("No favorite restaurants"), - "no_files_found": - MessageLookupByLibrary.simpleMessage("No files found"), - "no_info": MessageLookupByLibrary.simpleMessage( - "There is no information to display"), - "no_internet": MessageLookupByLibrary.simpleMessage( - "It looks like you\'re offline"), - "no_library_info": MessageLookupByLibrary.simpleMessage( - "No library occupation information available"), - "no_link": - MessageLookupByLibrary.simpleMessage("We couldn\'t open the link"), - "no_menu_info": MessageLookupByLibrary.simpleMessage( - "There is no information available about meals"), - "no_menus": MessageLookupByLibrary.simpleMessage( - "There are no meals available"), - "no_name_course": - MessageLookupByLibrary.simpleMessage("Unnamed course"), - "no_places_info": MessageLookupByLibrary.simpleMessage( - "There is no information available about places"), - "no_print_info": MessageLookupByLibrary.simpleMessage( - "No print balance information"), - "no_references": MessageLookupByLibrary.simpleMessage( - "There are no references to pay"), - "no_results": MessageLookupByLibrary.simpleMessage("No match"), - "no_selected_courses": MessageLookupByLibrary.simpleMessage( - "There are no course units to display"), - "no_selected_exams": MessageLookupByLibrary.simpleMessage( - "There are no exams to present"), - "notifications": MessageLookupByLibrary.simpleMessage("Notifications"), - "occurrence_type": - MessageLookupByLibrary.simpleMessage("Type of occurrence"), - "of_month": MessageLookupByLibrary.simpleMessage("of"), - "open_error": - MessageLookupByLibrary.simpleMessage("Error opening the file"), - "other_links": MessageLookupByLibrary.simpleMessage("Other links"), - "pass_change_request": MessageLookupByLibrary.simpleMessage( - "For security reasons, passwords must be changed periodically."), - "password": MessageLookupByLibrary.simpleMessage("password"), - "pendent_references": - MessageLookupByLibrary.simpleMessage("Pending references"), - "permission_denied": - MessageLookupByLibrary.simpleMessage("Permission denied"), - "personal_assistance": - MessageLookupByLibrary.simpleMessage("Face-to-face assistance"), - "press_again": - MessageLookupByLibrary.simpleMessage("Press again to exit"), - "print": MessageLookupByLibrary.simpleMessage("Print"), - "prints": MessageLookupByLibrary.simpleMessage("Prints"), - "problem_id": MessageLookupByLibrary.simpleMessage( - "Brief identification of the problem"), - "reference_sigarra_help": MessageLookupByLibrary.simpleMessage( - "The generated reference data will appear in Sigarra, checking account.\nProfile > Checking Account"), - "reference_success": MessageLookupByLibrary.simpleMessage( - "Reference created successfully!"), - "remove": MessageLookupByLibrary.simpleMessage("Delete"), - "report_error": MessageLookupByLibrary.simpleMessage("Report error"), - "report_error_suggestion": - MessageLookupByLibrary.simpleMessage("Report error/suggestion"), - "restaurant_main_page": MessageLookupByLibrary.simpleMessage( - "Do you want to see your favorite restaurants in the main page?"), - "room": MessageLookupByLibrary.simpleMessage("Room"), - "school_calendar": - MessageLookupByLibrary.simpleMessage("School Calendar"), - "search": MessageLookupByLibrary.simpleMessage("Search"), - "semester": MessageLookupByLibrary.simpleMessage("Semester"), - "send": MessageLookupByLibrary.simpleMessage("Send"), - "sent_error": MessageLookupByLibrary.simpleMessage( - "An error occurred in sending"), - "settings": MessageLookupByLibrary.simpleMessage("Settings"), - "some_error": MessageLookupByLibrary.simpleMessage("Some error!"), - "stcp_stops": - MessageLookupByLibrary.simpleMessage("STCP - Upcoming Trips"), - "student_number": - MessageLookupByLibrary.simpleMessage("student number"), - "success": MessageLookupByLibrary.simpleMessage("Sent with success"), - "successful_open": - MessageLookupByLibrary.simpleMessage("File opened successfully"), - "tele_assistance": - MessageLookupByLibrary.simpleMessage("Telephone assistance"), - "tele_personal_assistance": MessageLookupByLibrary.simpleMessage( - "Face-to-face and telephone assistance"), - "telephone": MessageLookupByLibrary.simpleMessage("Telephone"), - "terms": MessageLookupByLibrary.simpleMessage("Terms and Conditions"), - "theme": MessageLookupByLibrary.simpleMessage("Theme"), - "title": MessageLookupByLibrary.simpleMessage("Title"), - "unavailable": MessageLookupByLibrary.simpleMessage("Unavailable"), - "valid_email": - MessageLookupByLibrary.simpleMessage("Please enter a valid email"), - "widget_prompt": MessageLookupByLibrary.simpleMessage( - "Choose a widget to add to your personal area:"), - "year": MessageLookupByLibrary.simpleMessage("Year"), - "yes": MessageLookupByLibrary.simpleMessage("Yes") - }; + static _notInlinedMessages(_) => { + "about" : MessageLookupByLibrary.simpleMessage("About us"), + "academic_services" : MessageLookupByLibrary.simpleMessage("Academic services"), + "account_card_title" : MessageLookupByLibrary.simpleMessage("Checking account"), + "add" : MessageLookupByLibrary.simpleMessage("Add"), + "add_quota" : MessageLookupByLibrary.simpleMessage("Add quota"), + "add_widget" : MessageLookupByLibrary.simpleMessage("Add widget"), + "agree_terms" : MessageLookupByLibrary.simpleMessage("By entering you confirm that you agree with these Terms and Conditions"), + "all_widgets_added" : MessageLookupByLibrary.simpleMessage("All available widgets have already been added to your personal area!"), + "at_least_one_college" : MessageLookupByLibrary.simpleMessage("Select at least one college"), + "available_amount" : MessageLookupByLibrary.simpleMessage("Available amount"), + "average" : MessageLookupByLibrary.simpleMessage("Average: "), + "balance" : MessageLookupByLibrary.simpleMessage("Balance:"), + "banner_info" : MessageLookupByLibrary.simpleMessage("We do now collect anonymous usage statistics in order to improve your experience. You can change it in settings."), + "bs_description" : MessageLookupByLibrary.simpleMessage("Did you find any bugs in the application?\nDo you have any suggestions for the app?\nTell us so we can improve!"), + "bug_description" : MessageLookupByLibrary.simpleMessage("Bug found, how to reproduce it, etc."), + "bus_error" : MessageLookupByLibrary.simpleMessage("Unable to get information"), + "bus_information" : MessageLookupByLibrary.simpleMessage("Select the buses you want information about:"), + "buses_personalize" : MessageLookupByLibrary.simpleMessage("Personalize your buses here"), + "buses_text" : MessageLookupByLibrary.simpleMessage("Favorite buses will be displayed in the favorites \'Bus\' widget. The remaining ones will only be displayed on the page."), + "cancel" : MessageLookupByLibrary.simpleMessage("Cancel"), + "change" : MessageLookupByLibrary.simpleMessage("Change"), + "change_prompt" : MessageLookupByLibrary.simpleMessage("Do you want to change the password?"), + "check_internet" : MessageLookupByLibrary.simpleMessage("Check your internet connection"), + "class_registration" : MessageLookupByLibrary.simpleMessage("Class Registration"), + "collect_usage_stats" : MessageLookupByLibrary.simpleMessage("Collect usage statistics"), + "college" : MessageLookupByLibrary.simpleMessage("College: "), + "college_select" : MessageLookupByLibrary.simpleMessage("select your college(s)"), + "conclude" : MessageLookupByLibrary.simpleMessage("Done"), + "configured_buses" : MessageLookupByLibrary.simpleMessage("Configured Buses"), + "confirm" : MessageLookupByLibrary.simpleMessage("Confirm"), + "confirm_logout" : MessageLookupByLibrary.simpleMessage("Do you really want to log out? Your local data will be deleted and you will have to log in again."), + "consent" : MessageLookupByLibrary.simpleMessage("I consent to this information being reviewed by NIAEFEUP and may be deleted at my request."), + "contact" : MessageLookupByLibrary.simpleMessage("Contact (optional)"), + "copy_center" : MessageLookupByLibrary.simpleMessage("Copy center"), + "copy_center_building" : MessageLookupByLibrary.simpleMessage("Floor -1 of building B | AEFEUP building"), + "course_class" : MessageLookupByLibrary.simpleMessage("Classes"), + "course_info" : MessageLookupByLibrary.simpleMessage("Info"), + "current_state" : MessageLookupByLibrary.simpleMessage("Current state: "), + "current_year" : MessageLookupByLibrary.simpleMessage("Current academic year: "), + "decrement" : MessageLookupByLibrary.simpleMessage("Decrement 1,00€"), + "description" : MessageLookupByLibrary.simpleMessage("Description"), + "desired_email" : MessageLookupByLibrary.simpleMessage("Email where you want to be contacted"), + "dona_bia" : MessageLookupByLibrary.simpleMessage("D. Beatriz\'s stationery store"), + "dona_bia_building" : MessageLookupByLibrary.simpleMessage("Floor -1 of building B (B-142)"), + "download_error" : MessageLookupByLibrary.simpleMessage("Error downloading the file"), + "ects" : MessageLookupByLibrary.simpleMessage("ECTS performed: "), + "edit_off" : MessageLookupByLibrary.simpleMessage("Edit"), + "edit_on" : MessageLookupByLibrary.simpleMessage("Finish editing"), + "empty_text" : MessageLookupByLibrary.simpleMessage("Please fill in this field"), + "exams_filter" : MessageLookupByLibrary.simpleMessage("Exams Filter Settings"), + "exit_confirm" : MessageLookupByLibrary.simpleMessage("Do you really want to exit?"), + "expired_password" : MessageLookupByLibrary.simpleMessage("Your password has expired"), + "failed_login" : MessageLookupByLibrary.simpleMessage("Login failed"), + "fee_date" : MessageLookupByLibrary.simpleMessage("Deadline for next fee:"), + "fee_notification" : MessageLookupByLibrary.simpleMessage("Fee deadline"), + "files" : MessageLookupByLibrary.simpleMessage("Files"), + "first_year_registration" : MessageLookupByLibrary.simpleMessage("Year of first registration: "), + "floor" : MessageLookupByLibrary.simpleMessage("Floor"), + "floors" : MessageLookupByLibrary.simpleMessage("Floors"), + "forgot_password" : MessageLookupByLibrary.simpleMessage("Forgot password?"), + "generate_reference" : MessageLookupByLibrary.simpleMessage("Generate reference"), + "geral_registration" : MessageLookupByLibrary.simpleMessage("General Registration"), + "improvement_registration" : MessageLookupByLibrary.simpleMessage("Enrollment for Improvement"), + "increment" : MessageLookupByLibrary.simpleMessage("Increment 1,00€"), + "invalid_credentials" : MessageLookupByLibrary.simpleMessage("Invalid credentials"), + "keep_login" : MessageLookupByLibrary.simpleMessage("Stay signed in"), + "language" : MessageLookupByLibrary.simpleMessage("Language"), + "last_refresh_time" : m0, + "last_timestamp" : m1, + "library_occupation" : MessageLookupByLibrary.simpleMessage("Library Occupation"), + "load_error" : MessageLookupByLibrary.simpleMessage("Error loading the information"), + "loading_terms" : MessageLookupByLibrary.simpleMessage("Loading Terms and Conditions..."), + "login" : MessageLookupByLibrary.simpleMessage("Login"), + "logout" : MessageLookupByLibrary.simpleMessage("Log out"), + "menus" : MessageLookupByLibrary.simpleMessage("Menus"), + "min_value_reference" : MessageLookupByLibrary.simpleMessage("Minimum value: 1,00 €"), + "multimedia_center" : MessageLookupByLibrary.simpleMessage("Multimedia center"), + "nav_title" : m2, + "news" : MessageLookupByLibrary.simpleMessage("News"), + "no" : MessageLookupByLibrary.simpleMessage("No"), + "no_app" : MessageLookupByLibrary.simpleMessage("No app found to open the file"), + "no_bus" : MessageLookupByLibrary.simpleMessage("Don\'t miss any bus!"), + "no_bus_stops" : MessageLookupByLibrary.simpleMessage("No configured stops"), + "no_class" : MessageLookupByLibrary.simpleMessage("There are no classes to display"), + "no_classes" : MessageLookupByLibrary.simpleMessage("No classes to present"), + "no_classes_on" : MessageLookupByLibrary.simpleMessage("You don\'t have classes on"), + "no_college" : MessageLookupByLibrary.simpleMessage("no college"), + "no_course_units" : MessageLookupByLibrary.simpleMessage("No course units in the selected period"), + "no_data" : MessageLookupByLibrary.simpleMessage("There is no data to show at this time"), + "no_date" : MessageLookupByLibrary.simpleMessage("No date"), + "no_events" : MessageLookupByLibrary.simpleMessage("No events found"), + "no_exams" : MessageLookupByLibrary.simpleMessage("You have no exams scheduled\n"), + "no_exams_label" : MessageLookupByLibrary.simpleMessage("Looks like you are on vacation!"), + "no_favorite_restaurants" : MessageLookupByLibrary.simpleMessage("No favorite restaurants"), + "no_files_found" : MessageLookupByLibrary.simpleMessage("No files found"), + "no_info" : MessageLookupByLibrary.simpleMessage("There is no information to display"), + "no_internet" : MessageLookupByLibrary.simpleMessage("It looks like you\'re offline"), + "no_library_info" : MessageLookupByLibrary.simpleMessage("No library occupation information available"), + "no_link" : MessageLookupByLibrary.simpleMessage("We couldn\'t open the link"), + "no_menu_info" : MessageLookupByLibrary.simpleMessage("There is no information available about meals"), + "no_menus" : MessageLookupByLibrary.simpleMessage("There are no meals available"), + "no_name_course" : MessageLookupByLibrary.simpleMessage("Unnamed course"), + "no_places_info" : MessageLookupByLibrary.simpleMessage("There is no information available about places"), + "no_print_info" : MessageLookupByLibrary.simpleMessage("No print balance information"), + "no_references" : MessageLookupByLibrary.simpleMessage("There are no references to pay"), + "no_results" : MessageLookupByLibrary.simpleMessage("No match"), + "no_selected_courses" : MessageLookupByLibrary.simpleMessage("There are no course units to display"), + "no_selected_exams" : MessageLookupByLibrary.simpleMessage("There are no exams to present"), + "notifications" : MessageLookupByLibrary.simpleMessage("Notifications"), + "occurrence_type" : MessageLookupByLibrary.simpleMessage("Type of occurrence"), + "of_month" : MessageLookupByLibrary.simpleMessage("of"), + "open_error" : MessageLookupByLibrary.simpleMessage("Error opening the file"), + "other_links" : MessageLookupByLibrary.simpleMessage("Other links"), + "pass_change_request" : MessageLookupByLibrary.simpleMessage("For security reasons, passwords must be changed periodically."), + "password" : MessageLookupByLibrary.simpleMessage("password"), + "pendent_references" : MessageLookupByLibrary.simpleMessage("Pending references"), + "permission_denied" : MessageLookupByLibrary.simpleMessage("Permission denied"), + "personal_assistance" : MessageLookupByLibrary.simpleMessage("Face-to-face assistance"), + "press_again" : MessageLookupByLibrary.simpleMessage("Press again to exit"), + "print" : MessageLookupByLibrary.simpleMessage("Print"), + "prints" : MessageLookupByLibrary.simpleMessage("Prints"), + "problem_id" : MessageLookupByLibrary.simpleMessage("Brief identification of the problem"), + "reference_sigarra_help" : MessageLookupByLibrary.simpleMessage("The generated reference data will appear in Sigarra, checking account.\nProfile > Checking Account"), + "reference_success" : MessageLookupByLibrary.simpleMessage("Reference created successfully!"), + "remove" : MessageLookupByLibrary.simpleMessage("Delete"), + "report_error" : MessageLookupByLibrary.simpleMessage("Report error"), + "report_error_suggestion" : MessageLookupByLibrary.simpleMessage("Report error/suggestion"), + "restaurant_main_page" : MessageLookupByLibrary.simpleMessage("Do you want to see your favorite restaurants in the main page?"), + "room" : MessageLookupByLibrary.simpleMessage("Room"), + "school_calendar" : MessageLookupByLibrary.simpleMessage("School Calendar"), + "search" : MessageLookupByLibrary.simpleMessage("Search"), + "semester" : MessageLookupByLibrary.simpleMessage("Semester"), + "send" : MessageLookupByLibrary.simpleMessage("Send"), + "sent_error" : MessageLookupByLibrary.simpleMessage("An error occurred in sending"), + "settings" : MessageLookupByLibrary.simpleMessage("Settings"), + "some_error" : MessageLookupByLibrary.simpleMessage("Some error!"), + "stcp_stops" : MessageLookupByLibrary.simpleMessage("STCP - Upcoming Trips"), + "student_number" : MessageLookupByLibrary.simpleMessage("student number"), + "success" : MessageLookupByLibrary.simpleMessage("Sent with success"), + "successful_open" : MessageLookupByLibrary.simpleMessage("File opened successfully"), + "tele_assistance" : MessageLookupByLibrary.simpleMessage("Telephone assistance"), + "tele_personal_assistance" : MessageLookupByLibrary.simpleMessage("Face-to-face and telephone assistance"), + "telephone" : MessageLookupByLibrary.simpleMessage("Telephone"), + "terms" : MessageLookupByLibrary.simpleMessage("Terms and Conditions"), + "theme" : MessageLookupByLibrary.simpleMessage("Theme"), + "title" : MessageLookupByLibrary.simpleMessage("Title"), + "uc_info" : MessageLookupByLibrary.simpleMessage("Open UC page in app"), + "unavailable" : MessageLookupByLibrary.simpleMessage("Unavailable"), + "valid_email" : MessageLookupByLibrary.simpleMessage("Please enter a valid email"), + "widget_prompt" : MessageLookupByLibrary.simpleMessage("Choose a widget to add to your personal area:"), + "year" : MessageLookupByLibrary.simpleMessage("Year"), + "yes" : MessageLookupByLibrary.simpleMessage("Yes") + }; } diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 972ec1f70..da5bd6883 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -7,8 +7,7 @@ // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes +// ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; @@ -20,270 +19,165 @@ typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'pt_PT'; - static String m0(time) => "última atualização às ${time}"; + static m0(time) => "última atualização às ${time}"; - static String m1(time) => - "${Intl.plural(time, zero: 'Atualizado há ${time} minutos', one: 'Atualizado há ${time} minuto', other: 'Atualizado há ${time} minutos')}"; + static m1(time) => "${Intl.plural(time, zero: 'Atualizado há ${time} minutos', one: 'Atualizado há ${time} minuto', other: 'Atualizado há ${time} minutos')}"; - static String m2(title) => "${Intl.select(title, { - 'horario': 'Horário', - 'exames': 'Exames', - 'area': 'Área Pessoal', - 'cadeiras': 'Cadeiras', - 'autocarros': 'Autocarros', - 'locais': 'Locais', - 'restaurantes': 'Restaurantes', - 'calendario': 'Calendário', - 'biblioteca': 'Biblioteca', - 'percurso_academico': 'Percurso Académico', - 'transportes': 'Transportes', - 'faculdade': 'Faculdade', - 'other': 'Outros', - })}"; + static m2(title) => "${Intl.select(title, {'horario': 'Horário', 'exames': 'Exames', 'area': 'Área Pessoal', 'cadeiras': 'Cadeiras', 'autocarros': 'Autocarros', 'locais': 'Locais', 'restaurantes': 'Restaurantes', 'calendario': 'Calendário', 'biblioteca': 'Biblioteca', 'percurso_academico': 'Percurso Académico', 'transportes': 'Transportes', 'faculdade': 'Faculdade', 'other': 'Outros', })}"; final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { - "about": MessageLookupByLibrary.simpleMessage("Sobre nós"), - "academic_services": - MessageLookupByLibrary.simpleMessage("Serviços académicos"), - "account_card_title": - MessageLookupByLibrary.simpleMessage("Conta Corrente"), - "add": MessageLookupByLibrary.simpleMessage("Adicionar"), - "add_quota": MessageLookupByLibrary.simpleMessage("Adicionar quota"), - "add_widget": MessageLookupByLibrary.simpleMessage("Adicionar widget"), - "agree_terms": MessageLookupByLibrary.simpleMessage( - "Ao entrares confirmas que concordas com estes Termos e Condições"), - "all_widgets_added": MessageLookupByLibrary.simpleMessage( - "Todos os widgets disponíveis já foram adicionados à tua área pessoal!"), - "at_least_one_college": MessageLookupByLibrary.simpleMessage( - "Seleciona pelo menos uma faculdade"), - "available_amount": - MessageLookupByLibrary.simpleMessage("Valor disponível"), - "average": MessageLookupByLibrary.simpleMessage("Média: "), - "balance": MessageLookupByLibrary.simpleMessage("Saldo:"), - "banner_info": MessageLookupByLibrary.simpleMessage( - "Agora recolhemos estatísticas de uso anónimas para melhorar a tua experiência. Podes alterá-lo nas definições."), - "bs_description": MessageLookupByLibrary.simpleMessage( - "Encontraste algum bug na aplicação?\nTens alguma sugestão para a app?\nConta-nos para que possamos melhorar!"), - "bug_description": MessageLookupByLibrary.simpleMessage( - "Bug encontrado, como o reproduzir, etc"), - "bus_error": MessageLookupByLibrary.simpleMessage( - "Não foi possível obter informação"), - "bus_information": MessageLookupByLibrary.simpleMessage( - "Seleciona os autocarros dos quais queres informação:"), - "buses_personalize": MessageLookupByLibrary.simpleMessage( - "Configura aqui os teus autocarros"), - "buses_text": MessageLookupByLibrary.simpleMessage( - "Os autocarros favoritos serão apresentados no widget \'Autocarros\' dos favoritos. Os restantes serão apresentados apenas na página."), - "cancel": MessageLookupByLibrary.simpleMessage("Cancelar"), - "change": MessageLookupByLibrary.simpleMessage("Alterar"), - "change_prompt": MessageLookupByLibrary.simpleMessage( - "Deseja alterar a palavra-passe?"), - "check_internet": MessageLookupByLibrary.simpleMessage( - "Verifica a tua ligação à internet"), - "class_registration": - MessageLookupByLibrary.simpleMessage("Inscrição de Turmas"), - "collect_usage_stats": MessageLookupByLibrary.simpleMessage( - "Partilhar estatísticas de uso"), - "college": MessageLookupByLibrary.simpleMessage("Faculdade: "), - "college_select": MessageLookupByLibrary.simpleMessage( - "seleciona a(s) tua(s) faculdade(s)"), - "conclude": MessageLookupByLibrary.simpleMessage("Concluído"), - "configured_buses": - MessageLookupByLibrary.simpleMessage("Autocarros Configurados"), - "confirm": MessageLookupByLibrary.simpleMessage("Confirmar"), - "confirm_logout": MessageLookupByLibrary.simpleMessage( - "Tens a certeza de que queres terminar sessão? Os teus dados locais serão apagados e terás de iniciar sessão novamente."), - "consent": MessageLookupByLibrary.simpleMessage( - "Consinto que esta informação seja revista pelo NIAEFEUP, podendo ser eliminada a meu pedido."), - "contact": MessageLookupByLibrary.simpleMessage("Contacto (opcional)"), - "copy_center": MessageLookupByLibrary.simpleMessage("Centro de cópias"), - "copy_center_building": MessageLookupByLibrary.simpleMessage( - "Piso -1 do edifício B | Edifício da AEFEUP"), - "course_class": MessageLookupByLibrary.simpleMessage("Turmas"), - "course_info": MessageLookupByLibrary.simpleMessage("Ficha"), - "current_state": MessageLookupByLibrary.simpleMessage("Estado atual: "), - "current_year": - MessageLookupByLibrary.simpleMessage("Ano curricular atual: "), - "decrement": MessageLookupByLibrary.simpleMessage("Decrementar 1,00€"), - "description": MessageLookupByLibrary.simpleMessage("Descrição"), - "desired_email": MessageLookupByLibrary.simpleMessage( - "Email em que desejas ser contactado"), - "dona_bia": - MessageLookupByLibrary.simpleMessage("Papelaria D. Beatriz"), - "dona_bia_building": MessageLookupByLibrary.simpleMessage( - "Piso -1 do edifício B (B-142)"), - "download_error": MessageLookupByLibrary.simpleMessage( - "Erro ao descarregar o ficheiro"), - "ects": MessageLookupByLibrary.simpleMessage("ECTS realizados: "), - "edit_off": MessageLookupByLibrary.simpleMessage("Editar"), - "edit_on": MessageLookupByLibrary.simpleMessage("Concluir edição"), - "empty_text": MessageLookupByLibrary.simpleMessage( - "Por favor preenche este campo"), - "exams_filter": - MessageLookupByLibrary.simpleMessage("Definições Filtro de Exames"), - "exit_confirm": MessageLookupByLibrary.simpleMessage( - "Tem a certeza de que pretende sair?"), - "expired_password": - MessageLookupByLibrary.simpleMessage("A tua palavra-passe expirou"), - "failed_login": MessageLookupByLibrary.simpleMessage("O login falhou"), - "fee_date": MessageLookupByLibrary.simpleMessage( - "Data limite próxima prestação:"), - "fee_notification": - MessageLookupByLibrary.simpleMessage("Data limite de propina"), - "files": MessageLookupByLibrary.simpleMessage("Ficheiros"), - "first_year_registration": - MessageLookupByLibrary.simpleMessage("Ano da primeira inscrição: "), - "floor": MessageLookupByLibrary.simpleMessage("Piso"), - "floors": MessageLookupByLibrary.simpleMessage("Pisos"), - "forgot_password": - MessageLookupByLibrary.simpleMessage("Esqueceu a palavra-passe?"), - "generate_reference": - MessageLookupByLibrary.simpleMessage("Gerar referência"), - "geral_registration": - MessageLookupByLibrary.simpleMessage("Inscrição Geral"), - "improvement_registration": - MessageLookupByLibrary.simpleMessage("Inscrição para Melhoria"), - "increment": MessageLookupByLibrary.simpleMessage("Incrementar 1,00€"), - "invalid_credentials": - MessageLookupByLibrary.simpleMessage("Credenciais inválidas"), - "keep_login": - MessageLookupByLibrary.simpleMessage("Manter sessão iniciada"), - "language": MessageLookupByLibrary.simpleMessage("Idioma"), - "last_refresh_time": m0, - "last_timestamp": m1, - "library_occupation": - MessageLookupByLibrary.simpleMessage("Ocupação da Biblioteca"), - "load_error": MessageLookupByLibrary.simpleMessage( - "Erro ao carregar a informação"), - "loading_terms": MessageLookupByLibrary.simpleMessage( - "Carregando os Termos e Condições..."), - "login": MessageLookupByLibrary.simpleMessage("Entrar"), - "logout": MessageLookupByLibrary.simpleMessage("Terminar sessão"), - "menus": MessageLookupByLibrary.simpleMessage("Ementas"), - "min_value_reference": - MessageLookupByLibrary.simpleMessage("Valor mínimo: 1,00 €"), - "multimedia_center": - MessageLookupByLibrary.simpleMessage("Centro de multimédia"), - "nav_title": m2, - "news": MessageLookupByLibrary.simpleMessage("Notícias"), - "no": MessageLookupByLibrary.simpleMessage("Não"), - "no_app": MessageLookupByLibrary.simpleMessage( - "Nenhuma aplicação encontrada para abrir o ficheiro"), - "no_bus": MessageLookupByLibrary.simpleMessage( - "Não percas nenhum autocarro!"), - "no_bus_stops": MessageLookupByLibrary.simpleMessage( - "Não existe nenhuma paragem configurada"), - "no_class": MessageLookupByLibrary.simpleMessage( - "Não existem turmas para apresentar"), - "no_classes": MessageLookupByLibrary.simpleMessage( - "Não existem aulas para apresentar"), - "no_classes_on": - MessageLookupByLibrary.simpleMessage("Não possui aulas à"), - "no_college": MessageLookupByLibrary.simpleMessage("sem faculdade"), - "no_course_units": MessageLookupByLibrary.simpleMessage( - "Sem cadeiras no período selecionado"), - "no_data": MessageLookupByLibrary.simpleMessage( - "Não há dados a mostrar neste momento"), - "no_date": MessageLookupByLibrary.simpleMessage("Sem data"), - "no_events": - MessageLookupByLibrary.simpleMessage("Nenhum evento encontrado"), - "no_exams": - MessageLookupByLibrary.simpleMessage("Não possui exames marcados"), - "no_exams_label": - MessageLookupByLibrary.simpleMessage("Parece que estás de férias!"), - "no_favorite_restaurants": - MessageLookupByLibrary.simpleMessage("Sem restaurantes favoritos"), - "no_files_found": - MessageLookupByLibrary.simpleMessage("Nenhum ficheiro encontrado"), - "no_info": MessageLookupByLibrary.simpleMessage( - "Não existem informações para apresentar"), - "no_internet": - MessageLookupByLibrary.simpleMessage("Parece que estás offline"), - "no_library_info": - MessageLookupByLibrary.simpleMessage("Sem informação de ocupação"), - "no_link": MessageLookupByLibrary.simpleMessage( - "Não conseguimos abrir o link"), - "no_menu_info": MessageLookupByLibrary.simpleMessage( - "Não há informação disponível sobre refeições"), - "no_menus": MessageLookupByLibrary.simpleMessage( - "Não há refeições disponíveis"), - "no_name_course": - MessageLookupByLibrary.simpleMessage("Curso sem nome"), - "no_places_info": MessageLookupByLibrary.simpleMessage( - "Não há informação disponível sobre locais"), - "no_print_info": - MessageLookupByLibrary.simpleMessage("Sem informação de saldo"), - "no_references": MessageLookupByLibrary.simpleMessage( - "Não existem referências a pagar"), - "no_results": MessageLookupByLibrary.simpleMessage("Sem resultados"), - "no_selected_courses": MessageLookupByLibrary.simpleMessage( - "Não existem cadeiras para apresentar"), - "no_selected_exams": MessageLookupByLibrary.simpleMessage( - "Não existem exames para apresentar"), - "notifications": MessageLookupByLibrary.simpleMessage("Notificações"), - "occurrence_type": - MessageLookupByLibrary.simpleMessage("Tipo de ocorrência"), - "of_month": MessageLookupByLibrary.simpleMessage("de"), - "open_error": - MessageLookupByLibrary.simpleMessage("Erro ao abrir o ficheiro"), - "other_links": MessageLookupByLibrary.simpleMessage("Outros links"), - "pass_change_request": MessageLookupByLibrary.simpleMessage( - "Por razões de segurança, as palavras-passe têm de ser alteradas periodicamente."), - "password": MessageLookupByLibrary.simpleMessage("palavra-passe"), - "pendent_references": - MessageLookupByLibrary.simpleMessage("Referências pendentes"), - "permission_denied": - MessageLookupByLibrary.simpleMessage("Sem permissão"), - "personal_assistance": - MessageLookupByLibrary.simpleMessage("Atendimento presencial"), - "press_again": MessageLookupByLibrary.simpleMessage( - "Pressione novamente para sair"), - "print": MessageLookupByLibrary.simpleMessage("Impressão"), - "prints": MessageLookupByLibrary.simpleMessage("Impressões"), - "problem_id": MessageLookupByLibrary.simpleMessage( - "Breve identificação do problema"), - "reference_sigarra_help": MessageLookupByLibrary.simpleMessage( - "Os dados da referência gerada aparecerão no Sigarra, conta corrente. Perfil > Conta Corrente"), - "reference_success": MessageLookupByLibrary.simpleMessage( - "Referência criada com sucesso!"), - "remove": MessageLookupByLibrary.simpleMessage("Remover"), - "report_error": MessageLookupByLibrary.simpleMessage("Reportar erro"), - "report_error_suggestion": - MessageLookupByLibrary.simpleMessage("Reportar erro/sugestão"), - "restaurant_main_page": MessageLookupByLibrary.simpleMessage( - "Queres ver os teus restaurantes favoritos na página principal?"), - "room": MessageLookupByLibrary.simpleMessage("Sala"), - "school_calendar": - MessageLookupByLibrary.simpleMessage("Calendário Escolar"), - "search": MessageLookupByLibrary.simpleMessage("Pesquisar"), - "semester": MessageLookupByLibrary.simpleMessage("Semestre"), - "send": MessageLookupByLibrary.simpleMessage("Enviar"), - "sent_error": - MessageLookupByLibrary.simpleMessage("Ocorreu um erro no envio"), - "settings": MessageLookupByLibrary.simpleMessage("Definições"), - "some_error": MessageLookupByLibrary.simpleMessage("Algum erro!"), - "stcp_stops": - MessageLookupByLibrary.simpleMessage("STCP - Próximas Viagens"), - "student_number": - MessageLookupByLibrary.simpleMessage("número de estudante"), - "success": MessageLookupByLibrary.simpleMessage("Enviado com sucesso"), - "successful_open": - MessageLookupByLibrary.simpleMessage("Ficheiro aberto com sucesso"), - "tele_assistance": - MessageLookupByLibrary.simpleMessage("Atendimento telefónico"), - "tele_personal_assistance": MessageLookupByLibrary.simpleMessage( - "Atendimento presencial e telefónico"), - "telephone": MessageLookupByLibrary.simpleMessage("Telefone"), - "terms": MessageLookupByLibrary.simpleMessage("Termos e Condições"), - "theme": MessageLookupByLibrary.simpleMessage("Tema"), - "title": MessageLookupByLibrary.simpleMessage("Título"), - "unavailable": MessageLookupByLibrary.simpleMessage("Indisponível"), - "valid_email": MessageLookupByLibrary.simpleMessage( - "Por favor insere um email válido"), - "widget_prompt": MessageLookupByLibrary.simpleMessage( - "Escolhe um widget para adicionares à tua área pessoal:"), - "year": MessageLookupByLibrary.simpleMessage("Ano"), - "yes": MessageLookupByLibrary.simpleMessage("Sim") - }; + static _notInlinedMessages(_) => { + "about" : MessageLookupByLibrary.simpleMessage("Sobre nós"), + "academic_services" : MessageLookupByLibrary.simpleMessage("Serviços académicos"), + "account_card_title" : MessageLookupByLibrary.simpleMessage("Conta Corrente"), + "add" : MessageLookupByLibrary.simpleMessage("Adicionar"), + "add_quota" : MessageLookupByLibrary.simpleMessage("Adicionar quota"), + "add_widget" : MessageLookupByLibrary.simpleMessage("Adicionar widget"), + "agree_terms" : MessageLookupByLibrary.simpleMessage("Ao entrares confirmas que concordas com estes Termos e Condições"), + "all_widgets_added" : MessageLookupByLibrary.simpleMessage("Todos os widgets disponíveis já foram adicionados à tua área pessoal!"), + "at_least_one_college" : MessageLookupByLibrary.simpleMessage("Seleciona pelo menos uma faculdade"), + "available_amount" : MessageLookupByLibrary.simpleMessage("Valor disponível"), + "average" : MessageLookupByLibrary.simpleMessage("Média: "), + "balance" : MessageLookupByLibrary.simpleMessage("Saldo:"), + "banner_info" : MessageLookupByLibrary.simpleMessage("Agora recolhemos estatísticas de uso anónimas para melhorar a tua experiência. Podes alterá-lo nas definições."), + "bs_description" : MessageLookupByLibrary.simpleMessage("Encontraste algum bug na aplicação?\nTens alguma sugestão para a app?\nConta-nos para que possamos melhorar!"), + "bug_description" : MessageLookupByLibrary.simpleMessage("Bug encontrado, como o reproduzir, etc"), + "bus_error" : MessageLookupByLibrary.simpleMessage("Não foi possível obter informação"), + "bus_information" : MessageLookupByLibrary.simpleMessage("Seleciona os autocarros dos quais queres informação:"), + "buses_personalize" : MessageLookupByLibrary.simpleMessage("Configura aqui os teus autocarros"), + "buses_text" : MessageLookupByLibrary.simpleMessage("Os autocarros favoritos serão apresentados no widget \'Autocarros\' dos favoritos. Os restantes serão apresentados apenas na página."), + "cancel" : MessageLookupByLibrary.simpleMessage("Cancelar"), + "change" : MessageLookupByLibrary.simpleMessage("Alterar"), + "change_prompt" : MessageLookupByLibrary.simpleMessage("Deseja alterar a palavra-passe?"), + "check_internet" : MessageLookupByLibrary.simpleMessage("Verifica a tua ligação à internet"), + "class_registration" : MessageLookupByLibrary.simpleMessage("Inscrição de Turmas"), + "collect_usage_stats" : MessageLookupByLibrary.simpleMessage("Partilhar estatísticas de uso"), + "college" : MessageLookupByLibrary.simpleMessage("Faculdade: "), + "college_select" : MessageLookupByLibrary.simpleMessage("seleciona a(s) tua(s) faculdade(s)"), + "conclude" : MessageLookupByLibrary.simpleMessage("Concluído"), + "configured_buses" : MessageLookupByLibrary.simpleMessage("Autocarros Configurados"), + "confirm" : MessageLookupByLibrary.simpleMessage("Confirmar"), + "confirm_logout" : MessageLookupByLibrary.simpleMessage("Tens a certeza de que queres terminar sessão? Os teus dados locais serão apagados e terás de iniciar sessão novamente."), + "consent" : MessageLookupByLibrary.simpleMessage("Consinto que esta informação seja revista pelo NIAEFEUP, podendo ser eliminada a meu pedido."), + "contact" : MessageLookupByLibrary.simpleMessage("Contacto (opcional)"), + "copy_center" : MessageLookupByLibrary.simpleMessage("Centro de cópias"), + "copy_center_building" : MessageLookupByLibrary.simpleMessage("Piso -1 do edifício B | Edifício da AEFEUP"), + "course_class" : MessageLookupByLibrary.simpleMessage("Turmas"), + "course_info" : MessageLookupByLibrary.simpleMessage("Ficha"), + "current_state" : MessageLookupByLibrary.simpleMessage("Estado atual: "), + "current_year" : MessageLookupByLibrary.simpleMessage("Ano curricular atual: "), + "decrement" : MessageLookupByLibrary.simpleMessage("Decrementar 1,00€"), + "description" : MessageLookupByLibrary.simpleMessage("Descrição"), + "desired_email" : MessageLookupByLibrary.simpleMessage("Email em que desejas ser contactado"), + "dona_bia" : MessageLookupByLibrary.simpleMessage("Papelaria D. Beatriz"), + "dona_bia_building" : MessageLookupByLibrary.simpleMessage("Piso -1 do edifício B (B-142)"), + "download_error" : MessageLookupByLibrary.simpleMessage("Erro ao descarregar o ficheiro"), + "ects" : MessageLookupByLibrary.simpleMessage("ECTS realizados: "), + "edit_off" : MessageLookupByLibrary.simpleMessage("Editar"), + "edit_on" : MessageLookupByLibrary.simpleMessage("Concluir edição"), + "empty_text" : MessageLookupByLibrary.simpleMessage("Por favor preenche este campo"), + "exams_filter" : MessageLookupByLibrary.simpleMessage("Definições Filtro de Exames"), + "exit_confirm" : MessageLookupByLibrary.simpleMessage("Tem a certeza de que pretende sair?"), + "expired_password" : MessageLookupByLibrary.simpleMessage("A tua palavra-passe expirou"), + "failed_login" : MessageLookupByLibrary.simpleMessage("O login falhou"), + "fee_date" : MessageLookupByLibrary.simpleMessage("Data limite próxima prestação:"), + "fee_notification" : MessageLookupByLibrary.simpleMessage("Data limite de propina"), + "files" : MessageLookupByLibrary.simpleMessage("Ficheiros"), + "first_year_registration" : MessageLookupByLibrary.simpleMessage("Ano da primeira inscrição: "), + "floor" : MessageLookupByLibrary.simpleMessage("Piso"), + "floors" : MessageLookupByLibrary.simpleMessage("Pisos"), + "forgot_password" : MessageLookupByLibrary.simpleMessage("Esqueceu a palavra-passe?"), + "generate_reference" : MessageLookupByLibrary.simpleMessage("Gerar referência"), + "geral_registration" : MessageLookupByLibrary.simpleMessage("Inscrição Geral"), + "improvement_registration" : MessageLookupByLibrary.simpleMessage("Inscrição para Melhoria"), + "increment" : MessageLookupByLibrary.simpleMessage("Incrementar 1,00€"), + "invalid_credentials" : MessageLookupByLibrary.simpleMessage("Credenciais inválidas"), + "keep_login" : MessageLookupByLibrary.simpleMessage("Manter sessão iniciada"), + "language" : MessageLookupByLibrary.simpleMessage("Idioma"), + "last_refresh_time" : m0, + "last_timestamp" : m1, + "library_occupation" : MessageLookupByLibrary.simpleMessage("Ocupação da Biblioteca"), + "load_error" : MessageLookupByLibrary.simpleMessage("Erro ao carregar a informação"), + "loading_terms" : MessageLookupByLibrary.simpleMessage("Carregando os Termos e Condições..."), + "login" : MessageLookupByLibrary.simpleMessage("Entrar"), + "logout" : MessageLookupByLibrary.simpleMessage("Terminar sessão"), + "menus" : MessageLookupByLibrary.simpleMessage("Ementas"), + "min_value_reference" : MessageLookupByLibrary.simpleMessage("Valor mínimo: 1,00 €"), + "multimedia_center" : MessageLookupByLibrary.simpleMessage("Centro de multimédia"), + "nav_title" : m2, + "news" : MessageLookupByLibrary.simpleMessage("Notícias"), + "no" : MessageLookupByLibrary.simpleMessage("Não"), + "no_app" : MessageLookupByLibrary.simpleMessage("Nenhuma aplicação encontrada para abrir o ficheiro"), + "no_bus" : MessageLookupByLibrary.simpleMessage("Não percas nenhum autocarro!"), + "no_bus_stops" : MessageLookupByLibrary.simpleMessage("Não existe nenhuma paragem configurada"), + "no_class" : MessageLookupByLibrary.simpleMessage("Não existem turmas para apresentar"), + "no_classes" : MessageLookupByLibrary.simpleMessage("Não existem aulas para apresentar"), + "no_classes_on" : MessageLookupByLibrary.simpleMessage("Não possui aulas à"), + "no_college" : MessageLookupByLibrary.simpleMessage("sem faculdade"), + "no_course_units" : MessageLookupByLibrary.simpleMessage("Sem cadeiras no período selecionado"), + "no_data" : MessageLookupByLibrary.simpleMessage("Não há dados a mostrar neste momento"), + "no_date" : MessageLookupByLibrary.simpleMessage("Sem data"), + "no_events" : MessageLookupByLibrary.simpleMessage("Nenhum evento encontrado"), + "no_exams" : MessageLookupByLibrary.simpleMessage("Não possui exames marcados"), + "no_exams_label" : MessageLookupByLibrary.simpleMessage("Parece que estás de férias!"), + "no_favorite_restaurants" : MessageLookupByLibrary.simpleMessage("Sem restaurantes favoritos"), + "no_files_found" : MessageLookupByLibrary.simpleMessage("Nenhum ficheiro encontrado"), + "no_info" : MessageLookupByLibrary.simpleMessage("Não existem informações para apresentar"), + "no_internet" : MessageLookupByLibrary.simpleMessage("Parece que estás offline"), + "no_library_info" : MessageLookupByLibrary.simpleMessage("Sem informação de ocupação"), + "no_link" : MessageLookupByLibrary.simpleMessage("Não conseguimos abrir o link"), + "no_menu_info" : MessageLookupByLibrary.simpleMessage("Não há informação disponível sobre refeições"), + "no_menus" : MessageLookupByLibrary.simpleMessage("Não há refeições disponíveis"), + "no_name_course" : MessageLookupByLibrary.simpleMessage("Curso sem nome"), + "no_places_info" : MessageLookupByLibrary.simpleMessage("Não há informação disponível sobre locais"), + "no_print_info" : MessageLookupByLibrary.simpleMessage("Sem informação de saldo"), + "no_references" : MessageLookupByLibrary.simpleMessage("Não existem referências a pagar"), + "no_results" : MessageLookupByLibrary.simpleMessage("Sem resultados"), + "no_selected_courses" : MessageLookupByLibrary.simpleMessage("Não existem cadeiras para apresentar"), + "no_selected_exams" : MessageLookupByLibrary.simpleMessage("Não existem exames para apresentar"), + "notifications" : MessageLookupByLibrary.simpleMessage("Notificações"), + "occurrence_type" : MessageLookupByLibrary.simpleMessage("Tipo de ocorrência"), + "of_month" : MessageLookupByLibrary.simpleMessage("de"), + "open_error" : MessageLookupByLibrary.simpleMessage("Erro ao abrir o ficheiro"), + "other_links" : MessageLookupByLibrary.simpleMessage("Outros links"), + "pass_change_request" : MessageLookupByLibrary.simpleMessage("Por razões de segurança, as palavras-passe têm de ser alteradas periodicamente."), + "password" : MessageLookupByLibrary.simpleMessage("palavra-passe"), + "pendent_references" : MessageLookupByLibrary.simpleMessage("Referências pendentes"), + "permission_denied" : MessageLookupByLibrary.simpleMessage("Sem permissão"), + "personal_assistance" : MessageLookupByLibrary.simpleMessage("Atendimento presencial"), + "press_again" : MessageLookupByLibrary.simpleMessage("Pressione novamente para sair"), + "print" : MessageLookupByLibrary.simpleMessage("Impressão"), + "prints" : MessageLookupByLibrary.simpleMessage("Impressões"), + "problem_id" : MessageLookupByLibrary.simpleMessage("Breve identificação do problema"), + "reference_sigarra_help" : MessageLookupByLibrary.simpleMessage("Os dados da referência gerada aparecerão no Sigarra, conta corrente. Perfil > Conta Corrente"), + "reference_success" : MessageLookupByLibrary.simpleMessage("Referência criada com sucesso!"), + "remove" : MessageLookupByLibrary.simpleMessage("Remover"), + "report_error" : MessageLookupByLibrary.simpleMessage("Reportar erro"), + "report_error_suggestion" : MessageLookupByLibrary.simpleMessage("Reportar erro/sugestão"), + "restaurant_main_page" : MessageLookupByLibrary.simpleMessage("Queres ver os teus restaurantes favoritos na página principal?"), + "room" : MessageLookupByLibrary.simpleMessage("Sala"), + "school_calendar" : MessageLookupByLibrary.simpleMessage("Calendário Escolar"), + "search" : MessageLookupByLibrary.simpleMessage("Pesquisar"), + "semester" : MessageLookupByLibrary.simpleMessage("Semestre"), + "send" : MessageLookupByLibrary.simpleMessage("Enviar"), + "sent_error" : MessageLookupByLibrary.simpleMessage("Ocorreu um erro no envio"), + "settings" : MessageLookupByLibrary.simpleMessage("Definições"), + "some_error" : MessageLookupByLibrary.simpleMessage("Algum erro!"), + "stcp_stops" : MessageLookupByLibrary.simpleMessage("STCP - Próximas Viagens"), + "student_number" : MessageLookupByLibrary.simpleMessage("número de estudante"), + "success" : MessageLookupByLibrary.simpleMessage("Enviado com sucesso"), + "successful_open" : MessageLookupByLibrary.simpleMessage("Ficheiro aberto com sucesso"), + "tele_assistance" : MessageLookupByLibrary.simpleMessage("Atendimento telefónico"), + "tele_personal_assistance" : MessageLookupByLibrary.simpleMessage("Atendimento presencial e telefónico"), + "telephone" : MessageLookupByLibrary.simpleMessage("Telefone"), + "terms" : MessageLookupByLibrary.simpleMessage("Termos e Condições"), + "theme" : MessageLookupByLibrary.simpleMessage("Tema"), + "title" : MessageLookupByLibrary.simpleMessage("Título"), + "uc_info" : MessageLookupByLibrary.simpleMessage("Abrir página da UC na app"), + "unavailable" : MessageLookupByLibrary.simpleMessage("Indisponível"), + "valid_email" : MessageLookupByLibrary.simpleMessage("Por favor insere um email válido"), + "widget_prompt" : MessageLookupByLibrary.simpleMessage("Escolhe um widget para adicionares à tua área pessoal:"), + "year" : MessageLookupByLibrary.simpleMessage("Ano"), + "yes" : MessageLookupByLibrary.simpleMessage("Sim") + }; } diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index c25203be5..96d92e0ad 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -10,7 +10,7 @@ import 'intl/messages_all.dart'; // ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars // ignore_for_file: join_return_with_assignment, prefer_final_in_for_each -// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes +// ignore_for_file: avoid_redundant_argument_values class S { S(); @@ -18,31 +18,28 @@ class S { static S? _current; static S get current { - assert(_current != null, - 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); + assert(_current != null, 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); return _current!; } - static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); + static const AppLocalizationDelegate delegate = + AppLocalizationDelegate(); static Future load(Locale locale) { - final name = (locale.countryCode?.isEmpty ?? false) - ? locale.languageCode - : locale.toString(); - final localeName = Intl.canonicalizedLocale(name); + final name = (locale.countryCode?.isEmpty ?? false) ? locale.languageCode : locale.toString(); + final localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((_) { Intl.defaultLocale = localeName; final instance = S(); S._current = instance; - + return instance; }); - } + } static S of(BuildContext context) { final instance = S.maybeOf(context); - assert(instance != null, - 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); + assert(instance != null, 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); return instance!; } @@ -1508,6 +1505,16 @@ class S { ); } + /// `Open UC page in app` + String get uc_info { + return Intl.message( + 'Open UC page in app', + name: 'uc_info', + desc: '', + args: [], + ); + } + /// `Unavailable` String get unavailable { return Intl.message( @@ -1604,4 +1611,4 @@ class AppLocalizationDelegate extends LocalizationsDelegate { } return false; } -} +} \ No newline at end of file diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index b3767aeb1..3cfcf8b35 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -296,6 +296,8 @@ "@terms": {}, "title": "Title", "@title": {}, + "uc_info": "Open UC page in app", + "@uc_info": {}, "unavailable": "Unavailable", "@unavailable": {}, "valid_email": "Please enter a valid email", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 5be333ef6..27aa67efa 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -296,6 +296,8 @@ "@terms": {}, "title": "Título", "@title": {}, + "uc_info": "Abrir página da UC na app", + "@uc_info": {}, "unavailable": "Indisponível", "@unavailable": {}, "valid_email": "Por favor insere um email válido", diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index 1f1ee234f..f8714c17a 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:provider/provider.dart'; import 'package:uni/controller/networking/network_router.dart'; -import 'package:uni/controller/networking/url_launcher.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/view/common_widgets/row_container.dart'; +import 'package:uni/view/course_unit_info/course_unit_info.dart'; class ScheduleSlot extends StatelessWidget { const ScheduleSlot({ @@ -16,6 +19,7 @@ class ScheduleSlot extends StatelessWidget { this.classNumber, super.key, }); + final String subject; final String rooms; final DateTime begin; @@ -104,6 +108,7 @@ class ScheduleSlot extends StatelessWidget { class SubjectButtonWidget extends StatelessWidget { const SubjectButtonWidget({required this.occurrId, super.key}); + final int occurrId; String toUcLink(int occurrId) { @@ -112,9 +117,18 @@ class SubjectButtonWidget extends StatelessWidget { 'UCURR_GERAL.FICHA_UC_VIEW?pv_ocorrencia_id=$occurrId'; } - Future _launchURL(BuildContext context) async { - final url = toUcLink(occurrId); - await launchUrlWithToast(context, url); + void _launchURL(BuildContext context) { + final courseUnits = Provider.of(context, listen: false) + .profile + .courseUnits; + final correspondingCourseUnit = + courseUnits.firstWhere((courseUnit) => courseUnit.occurrId == occurrId); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CourseUnitDetailPageView(correspondingCourseUnit), + ), + ); } @override @@ -131,7 +145,7 @@ class SubjectButtonWidget extends StatelessWidget { iconSize: 18, color: Colors.grey, alignment: Alignment.centerRight, - tooltip: 'Abrir página da UC no browser', + tooltip: S.of(context).uc_info, onPressed: () => _launchURL(context), ), ], @@ -145,6 +159,7 @@ class ScheduleTeacherClassInfoWidget extends StatelessWidget { this.classNumber, super.key, }); + final String? classNumber; final String teacher; @@ -160,6 +175,7 @@ class ScheduleTeacherClassInfoWidget extends StatelessWidget { class ScheduleTimeWidget extends StatelessWidget { const ScheduleTimeWidget({required this.begin, required this.end, super.key}); + final String begin; final String end; @@ -181,6 +197,7 @@ class ScheduleTimeTextField extends StatelessWidget { required this.context, super.key, }); + final String time; final BuildContext context; @@ -201,6 +218,7 @@ class TextFieldWidget extends StatelessWidget { required this.alignment, super.key, }); + final String text; final TextStyle? style; final TextAlign alignment; From 71adc76fc1647ea3068babb3a78cf7aea1614dc7 Mon Sep 17 00:00:00 2001 From: Adriana Date: Fri, 22 Dec 2023 17:59:10 +0000 Subject: [PATCH 299/334] changed --- uni/lib/generated/intl/messages_all.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/lib/generated/intl/messages_all.dart b/uni/lib/generated/intl/messages_all.dart index fb1bd2689..f40637340 100644 --- a/uni/lib/generated/intl/messages_all.dart +++ b/uni/lib/generated/intl/messages_all.dart @@ -20,8 +20,8 @@ import 'messages_pt_PT.dart' as messages_pt_pt; typedef Future LibraryLoader(); Map _deferredLibraries = { - 'en': () => new Future.value(null), - 'pt_PT': () => new Future.value(null), + 'en': () => new SynchronousFuture(null), + 'pt_PT': () => new SynchronousFuture(null), }; MessageLookupByLibrary? _findExact(String localeName) { From a245a4d12c16bb0e84863d8800b5d6060f37a8ae Mon Sep 17 00:00:00 2001 From: Adriana Date: Fri, 22 Dec 2023 18:14:59 +0000 Subject: [PATCH 300/334] formated everything --- uni/lib/generated/intl/messages_en.dart | 3 ++- uni/lib/generated/intl/messages_pt_PT.dart | 3 ++- uni/lib/generated/l10n.dart | 21 ++++++++++++--------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index fa9e7aeaf..30c13c224 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -21,7 +21,8 @@ class MessageLookup extends MessageLookupByLibrary { static m0(time) => "last refresh at ${time}"; - static m1(time) => "${Intl.plural(time, zero: 'Refreshed ${time} minutes ago', one: 'Refreshed ${time} minute ago', other: 'Refreshed ${time} minutes ago')}"; + static m1(time) => + "${Intl.plural(time, zero: 'Refreshed ${time} minutes ago', one: 'Refreshed ${time} minute ago', other: 'Refreshed ${time} minutes ago')}"; static m2(title) => "${Intl.select(title, {'horario': 'Schedule', 'exames': 'Exams', 'area': 'Personal Area', 'cadeiras': 'Course Units', 'autocarros': 'Buses', 'locais': 'Places', 'restaurantes': 'Restaurants', 'calendario': 'Calendar', 'biblioteca': 'Library', 'percurso_academico': 'Academic Path', 'transportes': 'Transports', 'faculdade': 'Faculty', 'other': 'Other', })}"; diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index da5bd6883..0a1bfec92 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -21,7 +21,8 @@ class MessageLookup extends MessageLookupByLibrary { static m0(time) => "última atualização às ${time}"; - static m1(time) => "${Intl.plural(time, zero: 'Atualizado há ${time} minutos', one: 'Atualizado há ${time} minuto', other: 'Atualizado há ${time} minutos')}"; + static m1(time) => + "${Intl.plural(time, zero: 'Atualizado há ${time} minutos', one: 'Atualizado há ${time} minuto', other: 'Atualizado há ${time} minutos')}"; static m2(title) => "${Intl.select(title, {'horario': 'Horário', 'exames': 'Exames', 'area': 'Área Pessoal', 'cadeiras': 'Cadeiras', 'autocarros': 'Autocarros', 'locais': 'Locais', 'restaurantes': 'Restaurantes', 'calendario': 'Calendário', 'biblioteca': 'Biblioteca', 'percurso_academico': 'Percurso Académico', 'transportes': 'Transportes', 'faculdade': 'Faculdade', 'other': 'Outros', })}"; diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 96d92e0ad..3be2a9846 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -18,28 +18,31 @@ class S { static S? _current; static S get current { - assert(_current != null, 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); + assert(_current != null, + 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); return _current!; } - static const AppLocalizationDelegate delegate = - AppLocalizationDelegate(); + static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); static Future load(Locale locale) { - final name = (locale.countryCode?.isEmpty ?? false) ? locale.languageCode : locale.toString(); - final localeName = Intl.canonicalizedLocale(name); + final name = (locale.countryCode?.isEmpty ?? false) + ? locale.languageCode + : locale.toString(); + final localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((_) { Intl.defaultLocale = localeName; final instance = S(); S._current = instance; - + return instance; }); - } + } static S of(BuildContext context) { final instance = S.maybeOf(context); - assert(instance != null, 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); + assert(instance != null, + 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); return instance!; } @@ -1611,4 +1614,4 @@ class AppLocalizationDelegate extends LocalizationsDelegate { } return false; } -} \ No newline at end of file +} From dea952f1bf8da1baf59ff2f00336d9e14265c7f7 Mon Sep 17 00:00:00 2001 From: Adriana Date: Fri, 22 Dec 2023 20:09:26 +0000 Subject: [PATCH 301/334] corrected the translations --- uni/lib/generated/intl/messages_all.dart | 4 ++-- uni/lib/generated/intl/messages_en.dart | 3 +-- uni/lib/generated/intl/messages_pt_PT.dart | 3 +-- uni/lib/generated/l10n.dart | 21 +++++++++------------ 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/uni/lib/generated/intl/messages_all.dart b/uni/lib/generated/intl/messages_all.dart index f40637340..fb1bd2689 100644 --- a/uni/lib/generated/intl/messages_all.dart +++ b/uni/lib/generated/intl/messages_all.dart @@ -20,8 +20,8 @@ import 'messages_pt_PT.dart' as messages_pt_pt; typedef Future LibraryLoader(); Map _deferredLibraries = { - 'en': () => new SynchronousFuture(null), - 'pt_PT': () => new SynchronousFuture(null), + 'en': () => new Future.value(null), + 'pt_PT': () => new Future.value(null), }; MessageLookupByLibrary? _findExact(String localeName) { diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 30c13c224..fa9e7aeaf 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -21,8 +21,7 @@ class MessageLookup extends MessageLookupByLibrary { static m0(time) => "last refresh at ${time}"; - static m1(time) => - "${Intl.plural(time, zero: 'Refreshed ${time} minutes ago', one: 'Refreshed ${time} minute ago', other: 'Refreshed ${time} minutes ago')}"; + static m1(time) => "${Intl.plural(time, zero: 'Refreshed ${time} minutes ago', one: 'Refreshed ${time} minute ago', other: 'Refreshed ${time} minutes ago')}"; static m2(title) => "${Intl.select(title, {'horario': 'Schedule', 'exames': 'Exams', 'area': 'Personal Area', 'cadeiras': 'Course Units', 'autocarros': 'Buses', 'locais': 'Places', 'restaurantes': 'Restaurants', 'calendario': 'Calendar', 'biblioteca': 'Library', 'percurso_academico': 'Academic Path', 'transportes': 'Transports', 'faculdade': 'Faculty', 'other': 'Other', })}"; diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 0a1bfec92..da5bd6883 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -21,8 +21,7 @@ class MessageLookup extends MessageLookupByLibrary { static m0(time) => "última atualização às ${time}"; - static m1(time) => - "${Intl.plural(time, zero: 'Atualizado há ${time} minutos', one: 'Atualizado há ${time} minuto', other: 'Atualizado há ${time} minutos')}"; + static m1(time) => "${Intl.plural(time, zero: 'Atualizado há ${time} minutos', one: 'Atualizado há ${time} minuto', other: 'Atualizado há ${time} minutos')}"; static m2(title) => "${Intl.select(title, {'horario': 'Horário', 'exames': 'Exames', 'area': 'Área Pessoal', 'cadeiras': 'Cadeiras', 'autocarros': 'Autocarros', 'locais': 'Locais', 'restaurantes': 'Restaurantes', 'calendario': 'Calendário', 'biblioteca': 'Biblioteca', 'percurso_academico': 'Percurso Académico', 'transportes': 'Transportes', 'faculdade': 'Faculdade', 'other': 'Outros', })}"; diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 3be2a9846..96d92e0ad 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -18,31 +18,28 @@ class S { static S? _current; static S get current { - assert(_current != null, - 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); + assert(_current != null, 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); return _current!; } - static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); + static const AppLocalizationDelegate delegate = + AppLocalizationDelegate(); static Future load(Locale locale) { - final name = (locale.countryCode?.isEmpty ?? false) - ? locale.languageCode - : locale.toString(); - final localeName = Intl.canonicalizedLocale(name); + final name = (locale.countryCode?.isEmpty ?? false) ? locale.languageCode : locale.toString(); + final localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((_) { Intl.defaultLocale = localeName; final instance = S(); S._current = instance; - + return instance; }); - } + } static S of(BuildContext context) { final instance = S.maybeOf(context); - assert(instance != null, - 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); + assert(instance != null, 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); return instance!; } @@ -1614,4 +1611,4 @@ class AppLocalizationDelegate extends LocalizationsDelegate { } return false; } -} +} \ No newline at end of file From 8662e0060c02227631a5583f5a4b0550db77a6e0 Mon Sep 17 00:00:00 2001 From: Adriana Date: Wed, 14 Feb 2024 15:08:18 +0000 Subject: [PATCH 302/334] Handling non registered ucs --- .../view/schedule/widgets/schedule_slot.dart | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index f8714c17a..9a1f7e238 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/generated/l10n.dart'; +import 'package:uni/model/entities/course_units/course_unit.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/view/common_widgets/row_container.dart'; import 'package:uni/view/course_unit_info/course_unit_info.dart'; @@ -110,29 +110,37 @@ class SubjectButtonWidget extends StatelessWidget { const SubjectButtonWidget({required this.occurrId, super.key}); final int occurrId; + CourseUnit correspondingCourseUnit(BuildContext context) { + final courseUnits = Provider.of(context, listen: false) + .profile + .courseUnits; + final notFound = + CourseUnit(abbreviation: 'NF', name: 'not found', occurrId: 0); + final correspondingCourseUnit = courseUnits.firstWhere( + (courseUnit) => courseUnit.occurrId == occurrId, + orElse: () => notFound, + ); - String toUcLink(int occurrId) { - const faculty = 'feup'; // should not be hardcoded - return '${NetworkRouter.getBaseUrl(faculty)}' - 'UCURR_GERAL.FICHA_UC_VIEW?pv_ocorrencia_id=$occurrId'; + return correspondingCourseUnit; } void _launchURL(BuildContext context) { - final courseUnits = Provider.of(context, listen: false) - .profile - .courseUnits; - final correspondingCourseUnit = - courseUnits.firstWhere((courseUnit) => courseUnit.occurrId == occurrId); + final correspondCourseUnit = correspondingCourseUnit(context); Navigator.push( context, MaterialPageRoute( - builder: (context) => CourseUnitDetailPageView(correspondingCourseUnit), + builder: (context) => CourseUnitDetailPageView(correspondCourseUnit), ), ); } @override Widget build(BuildContext context) { + if (correspondingCourseUnit(context).name == 'not found') { + return const Column( + mainAxisSize: MainAxisSize.min, + ); + } return Column( mainAxisSize: MainAxisSize.min, children: [ From 652c8006ed46ca2fa444b7bf9f40a117ba36a5fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Wed, 28 Feb 2024 18:13:03 +0000 Subject: [PATCH 303/334] Fix unit tests Co-authored-by: Adriana --- uni/lib/generated/intl/messages_all.dart | 9 +- uni/lib/generated/intl/messages_en.dart | 416 ++++++++++------- uni/lib/generated/intl/messages_pt_PT.dart | 419 +++++++++++------- uni/lib/generated/l10n.dart | 21 +- .../view/schedule/widgets/schedule_slot.dart | 5 +- uni/test/test_widget.dart | 5 + .../unit/view/Pages/exams_page_view_test.dart | 10 +- 7 files changed, 552 insertions(+), 333 deletions(-) diff --git a/uni/lib/generated/intl/messages_all.dart b/uni/lib/generated/intl/messages_all.dart index fb1bd2689..6b3ebeae5 100644 --- a/uni/lib/generated/intl/messages_all.dart +++ b/uni/lib/generated/intl/messages_all.dart @@ -38,9 +38,8 @@ MessageLookupByLibrary? _findExact(String localeName) { /// User programs should call this before using [localeName] for messages. Future initializeMessages(String localeName) async { var availableLocale = Intl.verifiedLocale( - localeName, - (locale) => _deferredLibraries[locale] != null, - onFailure: (_) => null); + localeName, (locale) => _deferredLibraries[locale] != null, + onFailure: (_) => null); if (availableLocale == null) { return new Future.value(false); } @@ -60,8 +59,8 @@ bool _messagesExistFor(String locale) { } MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { - var actualLocale = Intl.verifiedLocale(locale, _messagesExistFor, - onFailure: (_) => null); + var actualLocale = + Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); if (actualLocale == null) return null; return _findExact(actualLocale); } diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index fa9e7aeaf..3508c496d 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -21,163 +21,267 @@ class MessageLookup extends MessageLookupByLibrary { static m0(time) => "last refresh at ${time}"; - static m1(time) => "${Intl.plural(time, zero: 'Refreshed ${time} minutes ago', one: 'Refreshed ${time} minute ago', other: 'Refreshed ${time} minutes ago')}"; + static m1(time) => + "${Intl.plural(time, zero: 'Refreshed ${time} minutes ago', one: 'Refreshed ${time} minute ago', other: 'Refreshed ${time} minutes ago')}"; - static m2(title) => "${Intl.select(title, {'horario': 'Schedule', 'exames': 'Exams', 'area': 'Personal Area', 'cadeiras': 'Course Units', 'autocarros': 'Buses', 'locais': 'Places', 'restaurantes': 'Restaurants', 'calendario': 'Calendar', 'biblioteca': 'Library', 'percurso_academico': 'Academic Path', 'transportes': 'Transports', 'faculdade': 'Faculty', 'other': 'Other', })}"; + static m2(title) => "${Intl.select(title, { + 'horario': 'Schedule', + 'exames': 'Exams', + 'area': 'Personal Area', + 'cadeiras': 'Course Units', + 'autocarros': 'Buses', + 'locais': 'Places', + 'restaurantes': 'Restaurants', + 'calendario': 'Calendar', + 'biblioteca': 'Library', + 'percurso_academico': 'Academic Path', + 'transportes': 'Transports', + 'faculdade': 'Faculty', + 'other': 'Other', + })}"; final messages = _notInlinedMessages(_notInlinedMessages); - static _notInlinedMessages(_) => { - "about" : MessageLookupByLibrary.simpleMessage("About us"), - "academic_services" : MessageLookupByLibrary.simpleMessage("Academic services"), - "account_card_title" : MessageLookupByLibrary.simpleMessage("Checking account"), - "add" : MessageLookupByLibrary.simpleMessage("Add"), - "add_quota" : MessageLookupByLibrary.simpleMessage("Add quota"), - "add_widget" : MessageLookupByLibrary.simpleMessage("Add widget"), - "agree_terms" : MessageLookupByLibrary.simpleMessage("By entering you confirm that you agree with these Terms and Conditions"), - "all_widgets_added" : MessageLookupByLibrary.simpleMessage("All available widgets have already been added to your personal area!"), - "at_least_one_college" : MessageLookupByLibrary.simpleMessage("Select at least one college"), - "available_amount" : MessageLookupByLibrary.simpleMessage("Available amount"), - "average" : MessageLookupByLibrary.simpleMessage("Average: "), - "balance" : MessageLookupByLibrary.simpleMessage("Balance:"), - "banner_info" : MessageLookupByLibrary.simpleMessage("We do now collect anonymous usage statistics in order to improve your experience. You can change it in settings."), - "bs_description" : MessageLookupByLibrary.simpleMessage("Did you find any bugs in the application?\nDo you have any suggestions for the app?\nTell us so we can improve!"), - "bug_description" : MessageLookupByLibrary.simpleMessage("Bug found, how to reproduce it, etc."), - "bus_error" : MessageLookupByLibrary.simpleMessage("Unable to get information"), - "bus_information" : MessageLookupByLibrary.simpleMessage("Select the buses you want information about:"), - "buses_personalize" : MessageLookupByLibrary.simpleMessage("Personalize your buses here"), - "buses_text" : MessageLookupByLibrary.simpleMessage("Favorite buses will be displayed in the favorites \'Bus\' widget. The remaining ones will only be displayed on the page."), - "cancel" : MessageLookupByLibrary.simpleMessage("Cancel"), - "change" : MessageLookupByLibrary.simpleMessage("Change"), - "change_prompt" : MessageLookupByLibrary.simpleMessage("Do you want to change the password?"), - "check_internet" : MessageLookupByLibrary.simpleMessage("Check your internet connection"), - "class_registration" : MessageLookupByLibrary.simpleMessage("Class Registration"), - "collect_usage_stats" : MessageLookupByLibrary.simpleMessage("Collect usage statistics"), - "college" : MessageLookupByLibrary.simpleMessage("College: "), - "college_select" : MessageLookupByLibrary.simpleMessage("select your college(s)"), - "conclude" : MessageLookupByLibrary.simpleMessage("Done"), - "configured_buses" : MessageLookupByLibrary.simpleMessage("Configured Buses"), - "confirm" : MessageLookupByLibrary.simpleMessage("Confirm"), - "confirm_logout" : MessageLookupByLibrary.simpleMessage("Do you really want to log out? Your local data will be deleted and you will have to log in again."), - "consent" : MessageLookupByLibrary.simpleMessage("I consent to this information being reviewed by NIAEFEUP and may be deleted at my request."), - "contact" : MessageLookupByLibrary.simpleMessage("Contact (optional)"), - "copy_center" : MessageLookupByLibrary.simpleMessage("Copy center"), - "copy_center_building" : MessageLookupByLibrary.simpleMessage("Floor -1 of building B | AEFEUP building"), - "course_class" : MessageLookupByLibrary.simpleMessage("Classes"), - "course_info" : MessageLookupByLibrary.simpleMessage("Info"), - "current_state" : MessageLookupByLibrary.simpleMessage("Current state: "), - "current_year" : MessageLookupByLibrary.simpleMessage("Current academic year: "), - "decrement" : MessageLookupByLibrary.simpleMessage("Decrement 1,00€"), - "description" : MessageLookupByLibrary.simpleMessage("Description"), - "desired_email" : MessageLookupByLibrary.simpleMessage("Email where you want to be contacted"), - "dona_bia" : MessageLookupByLibrary.simpleMessage("D. Beatriz\'s stationery store"), - "dona_bia_building" : MessageLookupByLibrary.simpleMessage("Floor -1 of building B (B-142)"), - "download_error" : MessageLookupByLibrary.simpleMessage("Error downloading the file"), - "ects" : MessageLookupByLibrary.simpleMessage("ECTS performed: "), - "edit_off" : MessageLookupByLibrary.simpleMessage("Edit"), - "edit_on" : MessageLookupByLibrary.simpleMessage("Finish editing"), - "empty_text" : MessageLookupByLibrary.simpleMessage("Please fill in this field"), - "exams_filter" : MessageLookupByLibrary.simpleMessage("Exams Filter Settings"), - "exit_confirm" : MessageLookupByLibrary.simpleMessage("Do you really want to exit?"), - "expired_password" : MessageLookupByLibrary.simpleMessage("Your password has expired"), - "failed_login" : MessageLookupByLibrary.simpleMessage("Login failed"), - "fee_date" : MessageLookupByLibrary.simpleMessage("Deadline for next fee:"), - "fee_notification" : MessageLookupByLibrary.simpleMessage("Fee deadline"), - "files" : MessageLookupByLibrary.simpleMessage("Files"), - "first_year_registration" : MessageLookupByLibrary.simpleMessage("Year of first registration: "), - "floor" : MessageLookupByLibrary.simpleMessage("Floor"), - "floors" : MessageLookupByLibrary.simpleMessage("Floors"), - "forgot_password" : MessageLookupByLibrary.simpleMessage("Forgot password?"), - "generate_reference" : MessageLookupByLibrary.simpleMessage("Generate reference"), - "geral_registration" : MessageLookupByLibrary.simpleMessage("General Registration"), - "improvement_registration" : MessageLookupByLibrary.simpleMessage("Enrollment for Improvement"), - "increment" : MessageLookupByLibrary.simpleMessage("Increment 1,00€"), - "invalid_credentials" : MessageLookupByLibrary.simpleMessage("Invalid credentials"), - "keep_login" : MessageLookupByLibrary.simpleMessage("Stay signed in"), - "language" : MessageLookupByLibrary.simpleMessage("Language"), - "last_refresh_time" : m0, - "last_timestamp" : m1, - "library_occupation" : MessageLookupByLibrary.simpleMessage("Library Occupation"), - "load_error" : MessageLookupByLibrary.simpleMessage("Error loading the information"), - "loading_terms" : MessageLookupByLibrary.simpleMessage("Loading Terms and Conditions..."), - "login" : MessageLookupByLibrary.simpleMessage("Login"), - "logout" : MessageLookupByLibrary.simpleMessage("Log out"), - "menus" : MessageLookupByLibrary.simpleMessage("Menus"), - "min_value_reference" : MessageLookupByLibrary.simpleMessage("Minimum value: 1,00 €"), - "multimedia_center" : MessageLookupByLibrary.simpleMessage("Multimedia center"), - "nav_title" : m2, - "news" : MessageLookupByLibrary.simpleMessage("News"), - "no" : MessageLookupByLibrary.simpleMessage("No"), - "no_app" : MessageLookupByLibrary.simpleMessage("No app found to open the file"), - "no_bus" : MessageLookupByLibrary.simpleMessage("Don\'t miss any bus!"), - "no_bus_stops" : MessageLookupByLibrary.simpleMessage("No configured stops"), - "no_class" : MessageLookupByLibrary.simpleMessage("There are no classes to display"), - "no_classes" : MessageLookupByLibrary.simpleMessage("No classes to present"), - "no_classes_on" : MessageLookupByLibrary.simpleMessage("You don\'t have classes on"), - "no_college" : MessageLookupByLibrary.simpleMessage("no college"), - "no_course_units" : MessageLookupByLibrary.simpleMessage("No course units in the selected period"), - "no_data" : MessageLookupByLibrary.simpleMessage("There is no data to show at this time"), - "no_date" : MessageLookupByLibrary.simpleMessage("No date"), - "no_events" : MessageLookupByLibrary.simpleMessage("No events found"), - "no_exams" : MessageLookupByLibrary.simpleMessage("You have no exams scheduled\n"), - "no_exams_label" : MessageLookupByLibrary.simpleMessage("Looks like you are on vacation!"), - "no_favorite_restaurants" : MessageLookupByLibrary.simpleMessage("No favorite restaurants"), - "no_files_found" : MessageLookupByLibrary.simpleMessage("No files found"), - "no_info" : MessageLookupByLibrary.simpleMessage("There is no information to display"), - "no_internet" : MessageLookupByLibrary.simpleMessage("It looks like you\'re offline"), - "no_library_info" : MessageLookupByLibrary.simpleMessage("No library occupation information available"), - "no_link" : MessageLookupByLibrary.simpleMessage("We couldn\'t open the link"), - "no_menu_info" : MessageLookupByLibrary.simpleMessage("There is no information available about meals"), - "no_menus" : MessageLookupByLibrary.simpleMessage("There are no meals available"), - "no_name_course" : MessageLookupByLibrary.simpleMessage("Unnamed course"), - "no_places_info" : MessageLookupByLibrary.simpleMessage("There is no information available about places"), - "no_print_info" : MessageLookupByLibrary.simpleMessage("No print balance information"), - "no_references" : MessageLookupByLibrary.simpleMessage("There are no references to pay"), - "no_results" : MessageLookupByLibrary.simpleMessage("No match"), - "no_selected_courses" : MessageLookupByLibrary.simpleMessage("There are no course units to display"), - "no_selected_exams" : MessageLookupByLibrary.simpleMessage("There are no exams to present"), - "notifications" : MessageLookupByLibrary.simpleMessage("Notifications"), - "occurrence_type" : MessageLookupByLibrary.simpleMessage("Type of occurrence"), - "of_month" : MessageLookupByLibrary.simpleMessage("of"), - "open_error" : MessageLookupByLibrary.simpleMessage("Error opening the file"), - "other_links" : MessageLookupByLibrary.simpleMessage("Other links"), - "pass_change_request" : MessageLookupByLibrary.simpleMessage("For security reasons, passwords must be changed periodically."), - "password" : MessageLookupByLibrary.simpleMessage("password"), - "pendent_references" : MessageLookupByLibrary.simpleMessage("Pending references"), - "permission_denied" : MessageLookupByLibrary.simpleMessage("Permission denied"), - "personal_assistance" : MessageLookupByLibrary.simpleMessage("Face-to-face assistance"), - "press_again" : MessageLookupByLibrary.simpleMessage("Press again to exit"), - "print" : MessageLookupByLibrary.simpleMessage("Print"), - "prints" : MessageLookupByLibrary.simpleMessage("Prints"), - "problem_id" : MessageLookupByLibrary.simpleMessage("Brief identification of the problem"), - "reference_sigarra_help" : MessageLookupByLibrary.simpleMessage("The generated reference data will appear in Sigarra, checking account.\nProfile > Checking Account"), - "reference_success" : MessageLookupByLibrary.simpleMessage("Reference created successfully!"), - "remove" : MessageLookupByLibrary.simpleMessage("Delete"), - "report_error" : MessageLookupByLibrary.simpleMessage("Report error"), - "report_error_suggestion" : MessageLookupByLibrary.simpleMessage("Report error/suggestion"), - "restaurant_main_page" : MessageLookupByLibrary.simpleMessage("Do you want to see your favorite restaurants in the main page?"), - "room" : MessageLookupByLibrary.simpleMessage("Room"), - "school_calendar" : MessageLookupByLibrary.simpleMessage("School Calendar"), - "search" : MessageLookupByLibrary.simpleMessage("Search"), - "semester" : MessageLookupByLibrary.simpleMessage("Semester"), - "send" : MessageLookupByLibrary.simpleMessage("Send"), - "sent_error" : MessageLookupByLibrary.simpleMessage("An error occurred in sending"), - "settings" : MessageLookupByLibrary.simpleMessage("Settings"), - "some_error" : MessageLookupByLibrary.simpleMessage("Some error!"), - "stcp_stops" : MessageLookupByLibrary.simpleMessage("STCP - Upcoming Trips"), - "student_number" : MessageLookupByLibrary.simpleMessage("student number"), - "success" : MessageLookupByLibrary.simpleMessage("Sent with success"), - "successful_open" : MessageLookupByLibrary.simpleMessage("File opened successfully"), - "tele_assistance" : MessageLookupByLibrary.simpleMessage("Telephone assistance"), - "tele_personal_assistance" : MessageLookupByLibrary.simpleMessage("Face-to-face and telephone assistance"), - "telephone" : MessageLookupByLibrary.simpleMessage("Telephone"), - "terms" : MessageLookupByLibrary.simpleMessage("Terms and Conditions"), - "theme" : MessageLookupByLibrary.simpleMessage("Theme"), - "title" : MessageLookupByLibrary.simpleMessage("Title"), - "uc_info" : MessageLookupByLibrary.simpleMessage("Open UC page in app"), - "unavailable" : MessageLookupByLibrary.simpleMessage("Unavailable"), - "valid_email" : MessageLookupByLibrary.simpleMessage("Please enter a valid email"), - "widget_prompt" : MessageLookupByLibrary.simpleMessage("Choose a widget to add to your personal area:"), - "year" : MessageLookupByLibrary.simpleMessage("Year"), - "yes" : MessageLookupByLibrary.simpleMessage("Yes") - }; + static _notInlinedMessages(_) => { + "about": MessageLookupByLibrary.simpleMessage("About us"), + "academic_services": + MessageLookupByLibrary.simpleMessage("Academic services"), + "account_card_title": + MessageLookupByLibrary.simpleMessage("Checking account"), + "add": MessageLookupByLibrary.simpleMessage("Add"), + "add_quota": MessageLookupByLibrary.simpleMessage("Add quota"), + "add_widget": MessageLookupByLibrary.simpleMessage("Add widget"), + "agree_terms": MessageLookupByLibrary.simpleMessage( + "By entering you confirm that you agree with these Terms and Conditions"), + "all_widgets_added": MessageLookupByLibrary.simpleMessage( + "All available widgets have already been added to your personal area!"), + "at_least_one_college": + MessageLookupByLibrary.simpleMessage("Select at least one college"), + "available_amount": + MessageLookupByLibrary.simpleMessage("Available amount"), + "average": MessageLookupByLibrary.simpleMessage("Average: "), + "balance": MessageLookupByLibrary.simpleMessage("Balance:"), + "banner_info": MessageLookupByLibrary.simpleMessage( + "We do now collect anonymous usage statistics in order to improve your experience. You can change it in settings."), + "bs_description": MessageLookupByLibrary.simpleMessage( + "Did you find any bugs in the application?\nDo you have any suggestions for the app?\nTell us so we can improve!"), + "bug_description": MessageLookupByLibrary.simpleMessage( + "Bug found, how to reproduce it, etc."), + "bus_error": + MessageLookupByLibrary.simpleMessage("Unable to get information"), + "bus_information": MessageLookupByLibrary.simpleMessage( + "Select the buses you want information about:"), + "buses_personalize": + MessageLookupByLibrary.simpleMessage("Personalize your buses here"), + "buses_text": MessageLookupByLibrary.simpleMessage( + "Favorite buses will be displayed in the favorites \'Bus\' widget. The remaining ones will only be displayed on the page."), + "cancel": MessageLookupByLibrary.simpleMessage("Cancel"), + "change": MessageLookupByLibrary.simpleMessage("Change"), + "change_prompt": MessageLookupByLibrary.simpleMessage( + "Do you want to change the password?"), + "check_internet": MessageLookupByLibrary.simpleMessage( + "Check your internet connection"), + "class_registration": + MessageLookupByLibrary.simpleMessage("Class Registration"), + "collect_usage_stats": + MessageLookupByLibrary.simpleMessage("Collect usage statistics"), + "college": MessageLookupByLibrary.simpleMessage("College: "), + "college_select": + MessageLookupByLibrary.simpleMessage("select your college(s)"), + "conclude": MessageLookupByLibrary.simpleMessage("Done"), + "configured_buses": + MessageLookupByLibrary.simpleMessage("Configured Buses"), + "confirm": MessageLookupByLibrary.simpleMessage("Confirm"), + "confirm_logout": MessageLookupByLibrary.simpleMessage( + "Do you really want to log out? Your local data will be deleted and you will have to log in again."), + "consent": MessageLookupByLibrary.simpleMessage( + "I consent to this information being reviewed by NIAEFEUP and may be deleted at my request."), + "contact": MessageLookupByLibrary.simpleMessage("Contact (optional)"), + "copy_center": MessageLookupByLibrary.simpleMessage("Copy center"), + "copy_center_building": MessageLookupByLibrary.simpleMessage( + "Floor -1 of building B | AEFEUP building"), + "course_class": MessageLookupByLibrary.simpleMessage("Classes"), + "course_info": MessageLookupByLibrary.simpleMessage("Info"), + "current_state": + MessageLookupByLibrary.simpleMessage("Current state: "), + "current_year": + MessageLookupByLibrary.simpleMessage("Current academic year: "), + "decrement": MessageLookupByLibrary.simpleMessage("Decrement 1,00€"), + "description": MessageLookupByLibrary.simpleMessage("Description"), + "desired_email": MessageLookupByLibrary.simpleMessage( + "Email where you want to be contacted"), + "dona_bia": MessageLookupByLibrary.simpleMessage( + "D. Beatriz\'s stationery store"), + "dona_bia_building": MessageLookupByLibrary.simpleMessage( + "Floor -1 of building B (B-142)"), + "download_error": + MessageLookupByLibrary.simpleMessage("Error downloading the file"), + "ects": MessageLookupByLibrary.simpleMessage("ECTS performed: "), + "edit_off": MessageLookupByLibrary.simpleMessage("Edit"), + "edit_on": MessageLookupByLibrary.simpleMessage("Finish editing"), + "empty_text": + MessageLookupByLibrary.simpleMessage("Please fill in this field"), + "exams_filter": + MessageLookupByLibrary.simpleMessage("Exams Filter Settings"), + "exit_confirm": + MessageLookupByLibrary.simpleMessage("Do you really want to exit?"), + "expired_password": + MessageLookupByLibrary.simpleMessage("Your password has expired"), + "failed_login": MessageLookupByLibrary.simpleMessage("Login failed"), + "fee_date": + MessageLookupByLibrary.simpleMessage("Deadline for next fee:"), + "fee_notification": + MessageLookupByLibrary.simpleMessage("Fee deadline"), + "files": MessageLookupByLibrary.simpleMessage("Files"), + "first_year_registration": MessageLookupByLibrary.simpleMessage( + "Year of first registration: "), + "floor": MessageLookupByLibrary.simpleMessage("Floor"), + "floors": MessageLookupByLibrary.simpleMessage("Floors"), + "forgot_password": + MessageLookupByLibrary.simpleMessage("Forgot password?"), + "generate_reference": + MessageLookupByLibrary.simpleMessage("Generate reference"), + "geral_registration": + MessageLookupByLibrary.simpleMessage("General Registration"), + "improvement_registration": + MessageLookupByLibrary.simpleMessage("Enrollment for Improvement"), + "increment": MessageLookupByLibrary.simpleMessage("Increment 1,00€"), + "invalid_credentials": + MessageLookupByLibrary.simpleMessage("Invalid credentials"), + "keep_login": MessageLookupByLibrary.simpleMessage("Stay signed in"), + "language": MessageLookupByLibrary.simpleMessage("Language"), + "last_refresh_time": m0, + "last_timestamp": m1, + "library_occupation": + MessageLookupByLibrary.simpleMessage("Library Occupation"), + "load_error": MessageLookupByLibrary.simpleMessage( + "Error loading the information"), + "loading_terms": MessageLookupByLibrary.simpleMessage( + "Loading Terms and Conditions..."), + "login": MessageLookupByLibrary.simpleMessage("Login"), + "logout": MessageLookupByLibrary.simpleMessage("Log out"), + "menus": MessageLookupByLibrary.simpleMessage("Menus"), + "min_value_reference": + MessageLookupByLibrary.simpleMessage("Minimum value: 1,00 €"), + "multimedia_center": + MessageLookupByLibrary.simpleMessage("Multimedia center"), + "nav_title": m2, + "news": MessageLookupByLibrary.simpleMessage("News"), + "no": MessageLookupByLibrary.simpleMessage("No"), + "no_app": MessageLookupByLibrary.simpleMessage( + "No app found to open the file"), + "no_bus": MessageLookupByLibrary.simpleMessage("Don\'t miss any bus!"), + "no_bus_stops": + MessageLookupByLibrary.simpleMessage("No configured stops"), + "no_class": MessageLookupByLibrary.simpleMessage( + "There are no classes to display"), + "no_classes": + MessageLookupByLibrary.simpleMessage("No classes to present"), + "no_classes_on": + MessageLookupByLibrary.simpleMessage("You don\'t have classes on"), + "no_college": MessageLookupByLibrary.simpleMessage("no college"), + "no_course_units": MessageLookupByLibrary.simpleMessage( + "No course units in the selected period"), + "no_data": MessageLookupByLibrary.simpleMessage( + "There is no data to show at this time"), + "no_date": MessageLookupByLibrary.simpleMessage("No date"), + "no_events": MessageLookupByLibrary.simpleMessage("No events found"), + "no_exams": MessageLookupByLibrary.simpleMessage( + "You have no exams scheduled\n"), + "no_exams_label": MessageLookupByLibrary.simpleMessage( + "Looks like you are on vacation!"), + "no_favorite_restaurants": + MessageLookupByLibrary.simpleMessage("No favorite restaurants"), + "no_files_found": + MessageLookupByLibrary.simpleMessage("No files found"), + "no_info": MessageLookupByLibrary.simpleMessage( + "There is no information to display"), + "no_internet": MessageLookupByLibrary.simpleMessage( + "It looks like you\'re offline"), + "no_library_info": MessageLookupByLibrary.simpleMessage( + "No library occupation information available"), + "no_link": + MessageLookupByLibrary.simpleMessage("We couldn\'t open the link"), + "no_menu_info": MessageLookupByLibrary.simpleMessage( + "There is no information available about meals"), + "no_menus": MessageLookupByLibrary.simpleMessage( + "There are no meals available"), + "no_name_course": + MessageLookupByLibrary.simpleMessage("Unnamed course"), + "no_places_info": MessageLookupByLibrary.simpleMessage( + "There is no information available about places"), + "no_print_info": MessageLookupByLibrary.simpleMessage( + "No print balance information"), + "no_references": MessageLookupByLibrary.simpleMessage( + "There are no references to pay"), + "no_results": MessageLookupByLibrary.simpleMessage("No match"), + "no_selected_courses": MessageLookupByLibrary.simpleMessage( + "There are no course units to display"), + "no_selected_exams": MessageLookupByLibrary.simpleMessage( + "There are no exams to present"), + "notifications": MessageLookupByLibrary.simpleMessage("Notifications"), + "occurrence_type": + MessageLookupByLibrary.simpleMessage("Type of occurrence"), + "of_month": MessageLookupByLibrary.simpleMessage("of"), + "open_error": + MessageLookupByLibrary.simpleMessage("Error opening the file"), + "other_links": MessageLookupByLibrary.simpleMessage("Other links"), + "pass_change_request": MessageLookupByLibrary.simpleMessage( + "For security reasons, passwords must be changed periodically."), + "password": MessageLookupByLibrary.simpleMessage("password"), + "pendent_references": + MessageLookupByLibrary.simpleMessage("Pending references"), + "permission_denied": + MessageLookupByLibrary.simpleMessage("Permission denied"), + "personal_assistance": + MessageLookupByLibrary.simpleMessage("Face-to-face assistance"), + "press_again": + MessageLookupByLibrary.simpleMessage("Press again to exit"), + "print": MessageLookupByLibrary.simpleMessage("Print"), + "prints": MessageLookupByLibrary.simpleMessage("Prints"), + "problem_id": MessageLookupByLibrary.simpleMessage( + "Brief identification of the problem"), + "reference_sigarra_help": MessageLookupByLibrary.simpleMessage( + "The generated reference data will appear in Sigarra, checking account.\nProfile > Checking Account"), + "reference_success": MessageLookupByLibrary.simpleMessage( + "Reference created successfully!"), + "remove": MessageLookupByLibrary.simpleMessage("Delete"), + "report_error": MessageLookupByLibrary.simpleMessage("Report error"), + "report_error_suggestion": + MessageLookupByLibrary.simpleMessage("Report error/suggestion"), + "restaurant_main_page": MessageLookupByLibrary.simpleMessage( + "Do you want to see your favorite restaurants in the main page?"), + "room": MessageLookupByLibrary.simpleMessage("Room"), + "school_calendar": + MessageLookupByLibrary.simpleMessage("School Calendar"), + "search": MessageLookupByLibrary.simpleMessage("Search"), + "semester": MessageLookupByLibrary.simpleMessage("Semester"), + "send": MessageLookupByLibrary.simpleMessage("Send"), + "sent_error": MessageLookupByLibrary.simpleMessage( + "An error occurred in sending"), + "settings": MessageLookupByLibrary.simpleMessage("Settings"), + "some_error": MessageLookupByLibrary.simpleMessage("Some error!"), + "stcp_stops": + MessageLookupByLibrary.simpleMessage("STCP - Upcoming Trips"), + "student_number": + MessageLookupByLibrary.simpleMessage("student number"), + "success": MessageLookupByLibrary.simpleMessage("Sent with success"), + "successful_open": + MessageLookupByLibrary.simpleMessage("File opened successfully"), + "tele_assistance": + MessageLookupByLibrary.simpleMessage("Telephone assistance"), + "tele_personal_assistance": MessageLookupByLibrary.simpleMessage( + "Face-to-face and telephone assistance"), + "telephone": MessageLookupByLibrary.simpleMessage("Telephone"), + "terms": MessageLookupByLibrary.simpleMessage("Terms and Conditions"), + "theme": MessageLookupByLibrary.simpleMessage("Theme"), + "title": MessageLookupByLibrary.simpleMessage("Title"), + "uc_info": MessageLookupByLibrary.simpleMessage("Open UC page in app"), + "unavailable": MessageLookupByLibrary.simpleMessage("Unavailable"), + "valid_email": + MessageLookupByLibrary.simpleMessage("Please enter a valid email"), + "widget_prompt": MessageLookupByLibrary.simpleMessage( + "Choose a widget to add to your personal area:"), + "year": MessageLookupByLibrary.simpleMessage("Year"), + "yes": MessageLookupByLibrary.simpleMessage("Yes") + }; } diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index da5bd6883..64053ebfd 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -21,163 +21,270 @@ class MessageLookup extends MessageLookupByLibrary { static m0(time) => "última atualização às ${time}"; - static m1(time) => "${Intl.plural(time, zero: 'Atualizado há ${time} minutos', one: 'Atualizado há ${time} minuto', other: 'Atualizado há ${time} minutos')}"; + static m1(time) => + "${Intl.plural(time, zero: 'Atualizado há ${time} minutos', one: 'Atualizado há ${time} minuto', other: 'Atualizado há ${time} minutos')}"; - static m2(title) => "${Intl.select(title, {'horario': 'Horário', 'exames': 'Exames', 'area': 'Área Pessoal', 'cadeiras': 'Cadeiras', 'autocarros': 'Autocarros', 'locais': 'Locais', 'restaurantes': 'Restaurantes', 'calendario': 'Calendário', 'biblioteca': 'Biblioteca', 'percurso_academico': 'Percurso Académico', 'transportes': 'Transportes', 'faculdade': 'Faculdade', 'other': 'Outros', })}"; + static m2(title) => "${Intl.select(title, { + 'horario': 'Horário', + 'exames': 'Exames', + 'area': 'Área Pessoal', + 'cadeiras': 'Cadeiras', + 'autocarros': 'Autocarros', + 'locais': 'Locais', + 'restaurantes': 'Restaurantes', + 'calendario': 'Calendário', + 'biblioteca': 'Biblioteca', + 'percurso_academico': 'Percurso Académico', + 'transportes': 'Transportes', + 'faculdade': 'Faculdade', + 'other': 'Outros', + })}"; final messages = _notInlinedMessages(_notInlinedMessages); - static _notInlinedMessages(_) => { - "about" : MessageLookupByLibrary.simpleMessage("Sobre nós"), - "academic_services" : MessageLookupByLibrary.simpleMessage("Serviços académicos"), - "account_card_title" : MessageLookupByLibrary.simpleMessage("Conta Corrente"), - "add" : MessageLookupByLibrary.simpleMessage("Adicionar"), - "add_quota" : MessageLookupByLibrary.simpleMessage("Adicionar quota"), - "add_widget" : MessageLookupByLibrary.simpleMessage("Adicionar widget"), - "agree_terms" : MessageLookupByLibrary.simpleMessage("Ao entrares confirmas que concordas com estes Termos e Condições"), - "all_widgets_added" : MessageLookupByLibrary.simpleMessage("Todos os widgets disponíveis já foram adicionados à tua área pessoal!"), - "at_least_one_college" : MessageLookupByLibrary.simpleMessage("Seleciona pelo menos uma faculdade"), - "available_amount" : MessageLookupByLibrary.simpleMessage("Valor disponível"), - "average" : MessageLookupByLibrary.simpleMessage("Média: "), - "balance" : MessageLookupByLibrary.simpleMessage("Saldo:"), - "banner_info" : MessageLookupByLibrary.simpleMessage("Agora recolhemos estatísticas de uso anónimas para melhorar a tua experiência. Podes alterá-lo nas definições."), - "bs_description" : MessageLookupByLibrary.simpleMessage("Encontraste algum bug na aplicação?\nTens alguma sugestão para a app?\nConta-nos para que possamos melhorar!"), - "bug_description" : MessageLookupByLibrary.simpleMessage("Bug encontrado, como o reproduzir, etc"), - "bus_error" : MessageLookupByLibrary.simpleMessage("Não foi possível obter informação"), - "bus_information" : MessageLookupByLibrary.simpleMessage("Seleciona os autocarros dos quais queres informação:"), - "buses_personalize" : MessageLookupByLibrary.simpleMessage("Configura aqui os teus autocarros"), - "buses_text" : MessageLookupByLibrary.simpleMessage("Os autocarros favoritos serão apresentados no widget \'Autocarros\' dos favoritos. Os restantes serão apresentados apenas na página."), - "cancel" : MessageLookupByLibrary.simpleMessage("Cancelar"), - "change" : MessageLookupByLibrary.simpleMessage("Alterar"), - "change_prompt" : MessageLookupByLibrary.simpleMessage("Deseja alterar a palavra-passe?"), - "check_internet" : MessageLookupByLibrary.simpleMessage("Verifica a tua ligação à internet"), - "class_registration" : MessageLookupByLibrary.simpleMessage("Inscrição de Turmas"), - "collect_usage_stats" : MessageLookupByLibrary.simpleMessage("Partilhar estatísticas de uso"), - "college" : MessageLookupByLibrary.simpleMessage("Faculdade: "), - "college_select" : MessageLookupByLibrary.simpleMessage("seleciona a(s) tua(s) faculdade(s)"), - "conclude" : MessageLookupByLibrary.simpleMessage("Concluído"), - "configured_buses" : MessageLookupByLibrary.simpleMessage("Autocarros Configurados"), - "confirm" : MessageLookupByLibrary.simpleMessage("Confirmar"), - "confirm_logout" : MessageLookupByLibrary.simpleMessage("Tens a certeza de que queres terminar sessão? Os teus dados locais serão apagados e terás de iniciar sessão novamente."), - "consent" : MessageLookupByLibrary.simpleMessage("Consinto que esta informação seja revista pelo NIAEFEUP, podendo ser eliminada a meu pedido."), - "contact" : MessageLookupByLibrary.simpleMessage("Contacto (opcional)"), - "copy_center" : MessageLookupByLibrary.simpleMessage("Centro de cópias"), - "copy_center_building" : MessageLookupByLibrary.simpleMessage("Piso -1 do edifício B | Edifício da AEFEUP"), - "course_class" : MessageLookupByLibrary.simpleMessage("Turmas"), - "course_info" : MessageLookupByLibrary.simpleMessage("Ficha"), - "current_state" : MessageLookupByLibrary.simpleMessage("Estado atual: "), - "current_year" : MessageLookupByLibrary.simpleMessage("Ano curricular atual: "), - "decrement" : MessageLookupByLibrary.simpleMessage("Decrementar 1,00€"), - "description" : MessageLookupByLibrary.simpleMessage("Descrição"), - "desired_email" : MessageLookupByLibrary.simpleMessage("Email em que desejas ser contactado"), - "dona_bia" : MessageLookupByLibrary.simpleMessage("Papelaria D. Beatriz"), - "dona_bia_building" : MessageLookupByLibrary.simpleMessage("Piso -1 do edifício B (B-142)"), - "download_error" : MessageLookupByLibrary.simpleMessage("Erro ao descarregar o ficheiro"), - "ects" : MessageLookupByLibrary.simpleMessage("ECTS realizados: "), - "edit_off" : MessageLookupByLibrary.simpleMessage("Editar"), - "edit_on" : MessageLookupByLibrary.simpleMessage("Concluir edição"), - "empty_text" : MessageLookupByLibrary.simpleMessage("Por favor preenche este campo"), - "exams_filter" : MessageLookupByLibrary.simpleMessage("Definições Filtro de Exames"), - "exit_confirm" : MessageLookupByLibrary.simpleMessage("Tem a certeza de que pretende sair?"), - "expired_password" : MessageLookupByLibrary.simpleMessage("A tua palavra-passe expirou"), - "failed_login" : MessageLookupByLibrary.simpleMessage("O login falhou"), - "fee_date" : MessageLookupByLibrary.simpleMessage("Data limite próxima prestação:"), - "fee_notification" : MessageLookupByLibrary.simpleMessage("Data limite de propina"), - "files" : MessageLookupByLibrary.simpleMessage("Ficheiros"), - "first_year_registration" : MessageLookupByLibrary.simpleMessage("Ano da primeira inscrição: "), - "floor" : MessageLookupByLibrary.simpleMessage("Piso"), - "floors" : MessageLookupByLibrary.simpleMessage("Pisos"), - "forgot_password" : MessageLookupByLibrary.simpleMessage("Esqueceu a palavra-passe?"), - "generate_reference" : MessageLookupByLibrary.simpleMessage("Gerar referência"), - "geral_registration" : MessageLookupByLibrary.simpleMessage("Inscrição Geral"), - "improvement_registration" : MessageLookupByLibrary.simpleMessage("Inscrição para Melhoria"), - "increment" : MessageLookupByLibrary.simpleMessage("Incrementar 1,00€"), - "invalid_credentials" : MessageLookupByLibrary.simpleMessage("Credenciais inválidas"), - "keep_login" : MessageLookupByLibrary.simpleMessage("Manter sessão iniciada"), - "language" : MessageLookupByLibrary.simpleMessage("Idioma"), - "last_refresh_time" : m0, - "last_timestamp" : m1, - "library_occupation" : MessageLookupByLibrary.simpleMessage("Ocupação da Biblioteca"), - "load_error" : MessageLookupByLibrary.simpleMessage("Erro ao carregar a informação"), - "loading_terms" : MessageLookupByLibrary.simpleMessage("Carregando os Termos e Condições..."), - "login" : MessageLookupByLibrary.simpleMessage("Entrar"), - "logout" : MessageLookupByLibrary.simpleMessage("Terminar sessão"), - "menus" : MessageLookupByLibrary.simpleMessage("Ementas"), - "min_value_reference" : MessageLookupByLibrary.simpleMessage("Valor mínimo: 1,00 €"), - "multimedia_center" : MessageLookupByLibrary.simpleMessage("Centro de multimédia"), - "nav_title" : m2, - "news" : MessageLookupByLibrary.simpleMessage("Notícias"), - "no" : MessageLookupByLibrary.simpleMessage("Não"), - "no_app" : MessageLookupByLibrary.simpleMessage("Nenhuma aplicação encontrada para abrir o ficheiro"), - "no_bus" : MessageLookupByLibrary.simpleMessage("Não percas nenhum autocarro!"), - "no_bus_stops" : MessageLookupByLibrary.simpleMessage("Não existe nenhuma paragem configurada"), - "no_class" : MessageLookupByLibrary.simpleMessage("Não existem turmas para apresentar"), - "no_classes" : MessageLookupByLibrary.simpleMessage("Não existem aulas para apresentar"), - "no_classes_on" : MessageLookupByLibrary.simpleMessage("Não possui aulas à"), - "no_college" : MessageLookupByLibrary.simpleMessage("sem faculdade"), - "no_course_units" : MessageLookupByLibrary.simpleMessage("Sem cadeiras no período selecionado"), - "no_data" : MessageLookupByLibrary.simpleMessage("Não há dados a mostrar neste momento"), - "no_date" : MessageLookupByLibrary.simpleMessage("Sem data"), - "no_events" : MessageLookupByLibrary.simpleMessage("Nenhum evento encontrado"), - "no_exams" : MessageLookupByLibrary.simpleMessage("Não possui exames marcados"), - "no_exams_label" : MessageLookupByLibrary.simpleMessage("Parece que estás de férias!"), - "no_favorite_restaurants" : MessageLookupByLibrary.simpleMessage("Sem restaurantes favoritos"), - "no_files_found" : MessageLookupByLibrary.simpleMessage("Nenhum ficheiro encontrado"), - "no_info" : MessageLookupByLibrary.simpleMessage("Não existem informações para apresentar"), - "no_internet" : MessageLookupByLibrary.simpleMessage("Parece que estás offline"), - "no_library_info" : MessageLookupByLibrary.simpleMessage("Sem informação de ocupação"), - "no_link" : MessageLookupByLibrary.simpleMessage("Não conseguimos abrir o link"), - "no_menu_info" : MessageLookupByLibrary.simpleMessage("Não há informação disponível sobre refeições"), - "no_menus" : MessageLookupByLibrary.simpleMessage("Não há refeições disponíveis"), - "no_name_course" : MessageLookupByLibrary.simpleMessage("Curso sem nome"), - "no_places_info" : MessageLookupByLibrary.simpleMessage("Não há informação disponível sobre locais"), - "no_print_info" : MessageLookupByLibrary.simpleMessage("Sem informação de saldo"), - "no_references" : MessageLookupByLibrary.simpleMessage("Não existem referências a pagar"), - "no_results" : MessageLookupByLibrary.simpleMessage("Sem resultados"), - "no_selected_courses" : MessageLookupByLibrary.simpleMessage("Não existem cadeiras para apresentar"), - "no_selected_exams" : MessageLookupByLibrary.simpleMessage("Não existem exames para apresentar"), - "notifications" : MessageLookupByLibrary.simpleMessage("Notificações"), - "occurrence_type" : MessageLookupByLibrary.simpleMessage("Tipo de ocorrência"), - "of_month" : MessageLookupByLibrary.simpleMessage("de"), - "open_error" : MessageLookupByLibrary.simpleMessage("Erro ao abrir o ficheiro"), - "other_links" : MessageLookupByLibrary.simpleMessage("Outros links"), - "pass_change_request" : MessageLookupByLibrary.simpleMessage("Por razões de segurança, as palavras-passe têm de ser alteradas periodicamente."), - "password" : MessageLookupByLibrary.simpleMessage("palavra-passe"), - "pendent_references" : MessageLookupByLibrary.simpleMessage("Referências pendentes"), - "permission_denied" : MessageLookupByLibrary.simpleMessage("Sem permissão"), - "personal_assistance" : MessageLookupByLibrary.simpleMessage("Atendimento presencial"), - "press_again" : MessageLookupByLibrary.simpleMessage("Pressione novamente para sair"), - "print" : MessageLookupByLibrary.simpleMessage("Impressão"), - "prints" : MessageLookupByLibrary.simpleMessage("Impressões"), - "problem_id" : MessageLookupByLibrary.simpleMessage("Breve identificação do problema"), - "reference_sigarra_help" : MessageLookupByLibrary.simpleMessage("Os dados da referência gerada aparecerão no Sigarra, conta corrente. Perfil > Conta Corrente"), - "reference_success" : MessageLookupByLibrary.simpleMessage("Referência criada com sucesso!"), - "remove" : MessageLookupByLibrary.simpleMessage("Remover"), - "report_error" : MessageLookupByLibrary.simpleMessage("Reportar erro"), - "report_error_suggestion" : MessageLookupByLibrary.simpleMessage("Reportar erro/sugestão"), - "restaurant_main_page" : MessageLookupByLibrary.simpleMessage("Queres ver os teus restaurantes favoritos na página principal?"), - "room" : MessageLookupByLibrary.simpleMessage("Sala"), - "school_calendar" : MessageLookupByLibrary.simpleMessage("Calendário Escolar"), - "search" : MessageLookupByLibrary.simpleMessage("Pesquisar"), - "semester" : MessageLookupByLibrary.simpleMessage("Semestre"), - "send" : MessageLookupByLibrary.simpleMessage("Enviar"), - "sent_error" : MessageLookupByLibrary.simpleMessage("Ocorreu um erro no envio"), - "settings" : MessageLookupByLibrary.simpleMessage("Definições"), - "some_error" : MessageLookupByLibrary.simpleMessage("Algum erro!"), - "stcp_stops" : MessageLookupByLibrary.simpleMessage("STCP - Próximas Viagens"), - "student_number" : MessageLookupByLibrary.simpleMessage("número de estudante"), - "success" : MessageLookupByLibrary.simpleMessage("Enviado com sucesso"), - "successful_open" : MessageLookupByLibrary.simpleMessage("Ficheiro aberto com sucesso"), - "tele_assistance" : MessageLookupByLibrary.simpleMessage("Atendimento telefónico"), - "tele_personal_assistance" : MessageLookupByLibrary.simpleMessage("Atendimento presencial e telefónico"), - "telephone" : MessageLookupByLibrary.simpleMessage("Telefone"), - "terms" : MessageLookupByLibrary.simpleMessage("Termos e Condições"), - "theme" : MessageLookupByLibrary.simpleMessage("Tema"), - "title" : MessageLookupByLibrary.simpleMessage("Título"), - "uc_info" : MessageLookupByLibrary.simpleMessage("Abrir página da UC na app"), - "unavailable" : MessageLookupByLibrary.simpleMessage("Indisponível"), - "valid_email" : MessageLookupByLibrary.simpleMessage("Por favor insere um email válido"), - "widget_prompt" : MessageLookupByLibrary.simpleMessage("Escolhe um widget para adicionares à tua área pessoal:"), - "year" : MessageLookupByLibrary.simpleMessage("Ano"), - "yes" : MessageLookupByLibrary.simpleMessage("Sim") - }; + static _notInlinedMessages(_) => { + "about": MessageLookupByLibrary.simpleMessage("Sobre nós"), + "academic_services": + MessageLookupByLibrary.simpleMessage("Serviços académicos"), + "account_card_title": + MessageLookupByLibrary.simpleMessage("Conta Corrente"), + "add": MessageLookupByLibrary.simpleMessage("Adicionar"), + "add_quota": MessageLookupByLibrary.simpleMessage("Adicionar quota"), + "add_widget": MessageLookupByLibrary.simpleMessage("Adicionar widget"), + "agree_terms": MessageLookupByLibrary.simpleMessage( + "Ao entrares confirmas que concordas com estes Termos e Condições"), + "all_widgets_added": MessageLookupByLibrary.simpleMessage( + "Todos os widgets disponíveis já foram adicionados à tua área pessoal!"), + "at_least_one_college": MessageLookupByLibrary.simpleMessage( + "Seleciona pelo menos uma faculdade"), + "available_amount": + MessageLookupByLibrary.simpleMessage("Valor disponível"), + "average": MessageLookupByLibrary.simpleMessage("Média: "), + "balance": MessageLookupByLibrary.simpleMessage("Saldo:"), + "banner_info": MessageLookupByLibrary.simpleMessage( + "Agora recolhemos estatísticas de uso anónimas para melhorar a tua experiência. Podes alterá-lo nas definições."), + "bs_description": MessageLookupByLibrary.simpleMessage( + "Encontraste algum bug na aplicação?\nTens alguma sugestão para a app?\nConta-nos para que possamos melhorar!"), + "bug_description": MessageLookupByLibrary.simpleMessage( + "Bug encontrado, como o reproduzir, etc"), + "bus_error": MessageLookupByLibrary.simpleMessage( + "Não foi possível obter informação"), + "bus_information": MessageLookupByLibrary.simpleMessage( + "Seleciona os autocarros dos quais queres informação:"), + "buses_personalize": MessageLookupByLibrary.simpleMessage( + "Configura aqui os teus autocarros"), + "buses_text": MessageLookupByLibrary.simpleMessage( + "Os autocarros favoritos serão apresentados no widget \'Autocarros\' dos favoritos. Os restantes serão apresentados apenas na página."), + "cancel": MessageLookupByLibrary.simpleMessage("Cancelar"), + "change": MessageLookupByLibrary.simpleMessage("Alterar"), + "change_prompt": MessageLookupByLibrary.simpleMessage( + "Deseja alterar a palavra-passe?"), + "check_internet": MessageLookupByLibrary.simpleMessage( + "Verifica a tua ligação à internet"), + "class_registration": + MessageLookupByLibrary.simpleMessage("Inscrição de Turmas"), + "collect_usage_stats": MessageLookupByLibrary.simpleMessage( + "Partilhar estatísticas de uso"), + "college": MessageLookupByLibrary.simpleMessage("Faculdade: "), + "college_select": MessageLookupByLibrary.simpleMessage( + "seleciona a(s) tua(s) faculdade(s)"), + "conclude": MessageLookupByLibrary.simpleMessage("Concluído"), + "configured_buses": + MessageLookupByLibrary.simpleMessage("Autocarros Configurados"), + "confirm": MessageLookupByLibrary.simpleMessage("Confirmar"), + "confirm_logout": MessageLookupByLibrary.simpleMessage( + "Tens a certeza de que queres terminar sessão? Os teus dados locais serão apagados e terás de iniciar sessão novamente."), + "consent": MessageLookupByLibrary.simpleMessage( + "Consinto que esta informação seja revista pelo NIAEFEUP, podendo ser eliminada a meu pedido."), + "contact": MessageLookupByLibrary.simpleMessage("Contacto (opcional)"), + "copy_center": MessageLookupByLibrary.simpleMessage("Centro de cópias"), + "copy_center_building": MessageLookupByLibrary.simpleMessage( + "Piso -1 do edifício B | Edifício da AEFEUP"), + "course_class": MessageLookupByLibrary.simpleMessage("Turmas"), + "course_info": MessageLookupByLibrary.simpleMessage("Ficha"), + "current_state": MessageLookupByLibrary.simpleMessage("Estado atual: "), + "current_year": + MessageLookupByLibrary.simpleMessage("Ano curricular atual: "), + "decrement": MessageLookupByLibrary.simpleMessage("Decrementar 1,00€"), + "description": MessageLookupByLibrary.simpleMessage("Descrição"), + "desired_email": MessageLookupByLibrary.simpleMessage( + "Email em que desejas ser contactado"), + "dona_bia": + MessageLookupByLibrary.simpleMessage("Papelaria D. Beatriz"), + "dona_bia_building": MessageLookupByLibrary.simpleMessage( + "Piso -1 do edifício B (B-142)"), + "download_error": MessageLookupByLibrary.simpleMessage( + "Erro ao descarregar o ficheiro"), + "ects": MessageLookupByLibrary.simpleMessage("ECTS realizados: "), + "edit_off": MessageLookupByLibrary.simpleMessage("Editar"), + "edit_on": MessageLookupByLibrary.simpleMessage("Concluir edição"), + "empty_text": MessageLookupByLibrary.simpleMessage( + "Por favor preenche este campo"), + "exams_filter": + MessageLookupByLibrary.simpleMessage("Definições Filtro de Exames"), + "exit_confirm": MessageLookupByLibrary.simpleMessage( + "Tem a certeza de que pretende sair?"), + "expired_password": + MessageLookupByLibrary.simpleMessage("A tua palavra-passe expirou"), + "failed_login": MessageLookupByLibrary.simpleMessage("O login falhou"), + "fee_date": MessageLookupByLibrary.simpleMessage( + "Data limite próxima prestação:"), + "fee_notification": + MessageLookupByLibrary.simpleMessage("Data limite de propina"), + "files": MessageLookupByLibrary.simpleMessage("Ficheiros"), + "first_year_registration": + MessageLookupByLibrary.simpleMessage("Ano da primeira inscrição: "), + "floor": MessageLookupByLibrary.simpleMessage("Piso"), + "floors": MessageLookupByLibrary.simpleMessage("Pisos"), + "forgot_password": + MessageLookupByLibrary.simpleMessage("Esqueceu a palavra-passe?"), + "generate_reference": + MessageLookupByLibrary.simpleMessage("Gerar referência"), + "geral_registration": + MessageLookupByLibrary.simpleMessage("Inscrição Geral"), + "improvement_registration": + MessageLookupByLibrary.simpleMessage("Inscrição para Melhoria"), + "increment": MessageLookupByLibrary.simpleMessage("Incrementar 1,00€"), + "invalid_credentials": + MessageLookupByLibrary.simpleMessage("Credenciais inválidas"), + "keep_login": + MessageLookupByLibrary.simpleMessage("Manter sessão iniciada"), + "language": MessageLookupByLibrary.simpleMessage("Idioma"), + "last_refresh_time": m0, + "last_timestamp": m1, + "library_occupation": + MessageLookupByLibrary.simpleMessage("Ocupação da Biblioteca"), + "load_error": MessageLookupByLibrary.simpleMessage( + "Erro ao carregar a informação"), + "loading_terms": MessageLookupByLibrary.simpleMessage( + "Carregando os Termos e Condições..."), + "login": MessageLookupByLibrary.simpleMessage("Entrar"), + "logout": MessageLookupByLibrary.simpleMessage("Terminar sessão"), + "menus": MessageLookupByLibrary.simpleMessage("Ementas"), + "min_value_reference": + MessageLookupByLibrary.simpleMessage("Valor mínimo: 1,00 €"), + "multimedia_center": + MessageLookupByLibrary.simpleMessage("Centro de multimédia"), + "nav_title": m2, + "news": MessageLookupByLibrary.simpleMessage("Notícias"), + "no": MessageLookupByLibrary.simpleMessage("Não"), + "no_app": MessageLookupByLibrary.simpleMessage( + "Nenhuma aplicação encontrada para abrir o ficheiro"), + "no_bus": MessageLookupByLibrary.simpleMessage( + "Não percas nenhum autocarro!"), + "no_bus_stops": MessageLookupByLibrary.simpleMessage( + "Não existe nenhuma paragem configurada"), + "no_class": MessageLookupByLibrary.simpleMessage( + "Não existem turmas para apresentar"), + "no_classes": MessageLookupByLibrary.simpleMessage( + "Não existem aulas para apresentar"), + "no_classes_on": + MessageLookupByLibrary.simpleMessage("Não possui aulas à"), + "no_college": MessageLookupByLibrary.simpleMessage("sem faculdade"), + "no_course_units": MessageLookupByLibrary.simpleMessage( + "Sem cadeiras no período selecionado"), + "no_data": MessageLookupByLibrary.simpleMessage( + "Não há dados a mostrar neste momento"), + "no_date": MessageLookupByLibrary.simpleMessage("Sem data"), + "no_events": + MessageLookupByLibrary.simpleMessage("Nenhum evento encontrado"), + "no_exams": + MessageLookupByLibrary.simpleMessage("Não possui exames marcados"), + "no_exams_label": + MessageLookupByLibrary.simpleMessage("Parece que estás de férias!"), + "no_favorite_restaurants": + MessageLookupByLibrary.simpleMessage("Sem restaurantes favoritos"), + "no_files_found": + MessageLookupByLibrary.simpleMessage("Nenhum ficheiro encontrado"), + "no_info": MessageLookupByLibrary.simpleMessage( + "Não existem informações para apresentar"), + "no_internet": + MessageLookupByLibrary.simpleMessage("Parece que estás offline"), + "no_library_info": + MessageLookupByLibrary.simpleMessage("Sem informação de ocupação"), + "no_link": MessageLookupByLibrary.simpleMessage( + "Não conseguimos abrir o link"), + "no_menu_info": MessageLookupByLibrary.simpleMessage( + "Não há informação disponível sobre refeições"), + "no_menus": MessageLookupByLibrary.simpleMessage( + "Não há refeições disponíveis"), + "no_name_course": + MessageLookupByLibrary.simpleMessage("Curso sem nome"), + "no_places_info": MessageLookupByLibrary.simpleMessage( + "Não há informação disponível sobre locais"), + "no_print_info": + MessageLookupByLibrary.simpleMessage("Sem informação de saldo"), + "no_references": MessageLookupByLibrary.simpleMessage( + "Não existem referências a pagar"), + "no_results": MessageLookupByLibrary.simpleMessage("Sem resultados"), + "no_selected_courses": MessageLookupByLibrary.simpleMessage( + "Não existem cadeiras para apresentar"), + "no_selected_exams": MessageLookupByLibrary.simpleMessage( + "Não existem exames para apresentar"), + "notifications": MessageLookupByLibrary.simpleMessage("Notificações"), + "occurrence_type": + MessageLookupByLibrary.simpleMessage("Tipo de ocorrência"), + "of_month": MessageLookupByLibrary.simpleMessage("de"), + "open_error": + MessageLookupByLibrary.simpleMessage("Erro ao abrir o ficheiro"), + "other_links": MessageLookupByLibrary.simpleMessage("Outros links"), + "pass_change_request": MessageLookupByLibrary.simpleMessage( + "Por razões de segurança, as palavras-passe têm de ser alteradas periodicamente."), + "password": MessageLookupByLibrary.simpleMessage("palavra-passe"), + "pendent_references": + MessageLookupByLibrary.simpleMessage("Referências pendentes"), + "permission_denied": + MessageLookupByLibrary.simpleMessage("Sem permissão"), + "personal_assistance": + MessageLookupByLibrary.simpleMessage("Atendimento presencial"), + "press_again": MessageLookupByLibrary.simpleMessage( + "Pressione novamente para sair"), + "print": MessageLookupByLibrary.simpleMessage("Impressão"), + "prints": MessageLookupByLibrary.simpleMessage("Impressões"), + "problem_id": MessageLookupByLibrary.simpleMessage( + "Breve identificação do problema"), + "reference_sigarra_help": MessageLookupByLibrary.simpleMessage( + "Os dados da referência gerada aparecerão no Sigarra, conta corrente. Perfil > Conta Corrente"), + "reference_success": MessageLookupByLibrary.simpleMessage( + "Referência criada com sucesso!"), + "remove": MessageLookupByLibrary.simpleMessage("Remover"), + "report_error": MessageLookupByLibrary.simpleMessage("Reportar erro"), + "report_error_suggestion": + MessageLookupByLibrary.simpleMessage("Reportar erro/sugestão"), + "restaurant_main_page": MessageLookupByLibrary.simpleMessage( + "Queres ver os teus restaurantes favoritos na página principal?"), + "room": MessageLookupByLibrary.simpleMessage("Sala"), + "school_calendar": + MessageLookupByLibrary.simpleMessage("Calendário Escolar"), + "search": MessageLookupByLibrary.simpleMessage("Pesquisar"), + "semester": MessageLookupByLibrary.simpleMessage("Semestre"), + "send": MessageLookupByLibrary.simpleMessage("Enviar"), + "sent_error": + MessageLookupByLibrary.simpleMessage("Ocorreu um erro no envio"), + "settings": MessageLookupByLibrary.simpleMessage("Definições"), + "some_error": MessageLookupByLibrary.simpleMessage("Algum erro!"), + "stcp_stops": + MessageLookupByLibrary.simpleMessage("STCP - Próximas Viagens"), + "student_number": + MessageLookupByLibrary.simpleMessage("número de estudante"), + "success": MessageLookupByLibrary.simpleMessage("Enviado com sucesso"), + "successful_open": + MessageLookupByLibrary.simpleMessage("Ficheiro aberto com sucesso"), + "tele_assistance": + MessageLookupByLibrary.simpleMessage("Atendimento telefónico"), + "tele_personal_assistance": MessageLookupByLibrary.simpleMessage( + "Atendimento presencial e telefónico"), + "telephone": MessageLookupByLibrary.simpleMessage("Telefone"), + "terms": MessageLookupByLibrary.simpleMessage("Termos e Condições"), + "theme": MessageLookupByLibrary.simpleMessage("Tema"), + "title": MessageLookupByLibrary.simpleMessage("Título"), + "uc_info": + MessageLookupByLibrary.simpleMessage("Abrir página da UC na app"), + "unavailable": MessageLookupByLibrary.simpleMessage("Indisponível"), + "valid_email": MessageLookupByLibrary.simpleMessage( + "Por favor insere um email válido"), + "widget_prompt": MessageLookupByLibrary.simpleMessage( + "Escolhe um widget para adicionares à tua área pessoal:"), + "year": MessageLookupByLibrary.simpleMessage("Ano"), + "yes": MessageLookupByLibrary.simpleMessage("Sim") + }; } diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 96d92e0ad..3be2a9846 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -18,28 +18,31 @@ class S { static S? _current; static S get current { - assert(_current != null, 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); + assert(_current != null, + 'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.'); return _current!; } - static const AppLocalizationDelegate delegate = - AppLocalizationDelegate(); + static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); static Future load(Locale locale) { - final name = (locale.countryCode?.isEmpty ?? false) ? locale.languageCode : locale.toString(); - final localeName = Intl.canonicalizedLocale(name); + final name = (locale.countryCode?.isEmpty ?? false) + ? locale.languageCode + : locale.toString(); + final localeName = Intl.canonicalizedLocale(name); return initializeMessages(localeName).then((_) { Intl.defaultLocale = localeName; final instance = S(); S._current = instance; - + return instance; }); - } + } static S of(BuildContext context) { final instance = S.maybeOf(context); - assert(instance != null, 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); + assert(instance != null, + 'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?'); return instance!; } @@ -1611,4 +1614,4 @@ class AppLocalizationDelegate extends LocalizationsDelegate { } return false; } -} \ No newline at end of file +} diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index 9a1f7e238..c0c5c7e1c 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -111,9 +111,8 @@ class SubjectButtonWidget extends StatelessWidget { final int occurrId; CourseUnit correspondingCourseUnit(BuildContext context) { - final courseUnits = Provider.of(context, listen: false) - .profile - .courseUnits; + final courseUnits = + Provider.of(context, listen: false).state!.courseUnits; final notFound = CourseUnit(abbreviation: 'NF', name: 'not found', occurrId: 0); final correspondingCourseUnit = courseUnits.firstWhere( diff --git a/uni/test/test_widget.dart b/uni/test/test_widget.dart index 154b159b4..8021138fb 100644 --- a/uni/test/test_widget.dart +++ b/uni/test/test_widget.dart @@ -6,6 +6,8 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/app_locale.dart'; +import 'package:uni/model/entities/profile.dart'; +import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/view/locale_notifier.dart'; Future initTestEnvironment() async { @@ -23,6 +25,9 @@ Widget testableWidget( ChangeNotifierProvider( create: (_) => LocaleNotifier(AppLocale.pt), ), + ChangeNotifierProvider( + create: (_) => ProfileProvider()..setState(Profile()), + ), ...providers, ], child: _wrapWidget(widget), diff --git a/uni/test/unit/view/Pages/exams_page_view_test.dart b/uni/test/unit/view/Pages/exams_page_view_test.dart index a5b64eb77..72439fa55 100644 --- a/uni/test/unit/view/Pages/exams_page_view_test.dart +++ b/uni/test/unit/view/Pages/exams_page_view_test.dart @@ -50,7 +50,9 @@ void main() async { final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; await tester.pumpWidget(testableWidget(widget, providers: providers)); - await tester.pump(); + await tester.pumpAndSettle(); + + debugDumpApp(); expect(find.byKey(Key(firstExam.toString())), findsOneWidget); expect(find.byKey(Key('$firstExam-exam')), findsOneWidget); @@ -93,7 +95,7 @@ void main() async { final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; await tester.pumpWidget(testableWidget(widget, providers: providers)); - await tester.pump(); + await tester.pumpAndSettle(); expect( find.byKey(Key(examList.map((ex) => ex.toString()).join())), @@ -139,7 +141,7 @@ void main() async { final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; await tester.pumpWidget(testableWidget(widget, providers: providers)); - await tester.pump(); + await tester.pumpAndSettle(); expect(find.byKey(Key(firstExam.toString())), findsOneWidget); expect(find.byKey(Key(secondExam.toString())), findsOneWidget); @@ -208,7 +210,7 @@ void main() async { final providers = [ChangeNotifierProvider(create: (_) => examProvider)]; await tester.pumpWidget(testableWidget(widget, providers: providers)); - await tester.pump(); + await tester.pumpAndSettle(); expect(find.byKey(Key(firstDayKey)), findsOneWidget); expect(find.byKey(Key(secondDayKey)), findsOneWidget); From 182c0212c3a19be8072e03f04d7f0a34eae82144 Mon Sep 17 00:00:00 2001 From: Adriana Date: Wed, 28 Feb 2024 18:39:17 +0000 Subject: [PATCH 304/334] small changes --- uni/lib/l10n/intl_en.arb | 2 +- uni/lib/l10n/intl_pt_PT.arb | 2 +- uni/lib/view/schedule/widgets/schedule_slot.dart | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index 3cfcf8b35..e97e4101c 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -296,7 +296,7 @@ "@terms": {}, "title": "Title", "@title": {}, - "uc_info": "Open UC page in app", + "uc_info": "Open UC page", "@uc_info": {}, "unavailable": "Unavailable", "@unavailable": {}, diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index 27aa67efa..a342593f3 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -296,7 +296,7 @@ "@terms": {}, "title": "Título", "@title": {}, - "uc_info": "Abrir página da UC na app", + "uc_info": "Abrir página da UC", "@uc_info": {}, "unavailable": "Indisponível", "@unavailable": {}, diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index c0c5c7e1c..97efd5f9b 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -123,7 +123,7 @@ class SubjectButtonWidget extends StatelessWidget { return correspondingCourseUnit; } - void _launchURL(BuildContext context) { + void _launchUcPage(BuildContext context) { final correspondCourseUnit = correspondingCourseUnit(context); Navigator.push( context, @@ -153,7 +153,7 @@ class SubjectButtonWidget extends StatelessWidget { color: Colors.grey, alignment: Alignment.centerRight, tooltip: S.of(context).uc_info, - onPressed: () => _launchURL(context), + onPressed: () => _launchUcPage(context), ), ], ); From 2d0c6c27310446fa6f274240827a7e057efc6965 Mon Sep 17 00:00:00 2001 From: Adriana Date: Thu, 29 Feb 2024 12:35:07 +0000 Subject: [PATCH 305/334] Extract corresponding course unit to schedule slot --- .../view/schedule/widgets/schedule_slot.dart | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/uni/lib/view/schedule/widgets/schedule_slot.dart b/uni/lib/view/schedule/widgets/schedule_slot.dart index 97efd5f9b..48c55dcec 100644 --- a/uni/lib/view/schedule/widgets/schedule_slot.dart +++ b/uni/lib/view/schedule/widgets/schedule_slot.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; @@ -72,6 +73,8 @@ class ScheduleSlot extends StatelessWidget { rooms, style: Theme.of(context).textTheme.bodyMedium, ); + final courseUnit = _correspondingCourseUnit(context); + return [ ScheduleTimeWidget( begin: DateFormat('HH:mm').format(begin), @@ -84,9 +87,10 @@ class ScheduleSlot extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - SubjectButtonWidget( - occurrId: occurrId, - ), + if (courseUnit != null) + SubjectButtonWidget( + courseUnit: courseUnit, + ), subjectTextField, typeClassTextField, ], @@ -104,42 +108,31 @@ class ScheduleSlot extends StatelessWidget { roomTextField, ]; } -} -class SubjectButtonWidget extends StatelessWidget { - const SubjectButtonWidget({required this.occurrId, super.key}); - - final int occurrId; - CourseUnit correspondingCourseUnit(BuildContext context) { - final courseUnits = - Provider.of(context, listen: false).state!.courseUnits; - final notFound = - CourseUnit(abbreviation: 'NF', name: 'not found', occurrId: 0); - final correspondingCourseUnit = courseUnits.firstWhere( + CourseUnit? _correspondingCourseUnit(BuildContext context) { + final courseUnits = context.read().state!.courseUnits; + return courseUnits.firstWhereOrNull( (courseUnit) => courseUnit.occurrId == occurrId, - orElse: () => notFound, ); - - return correspondingCourseUnit; } +} + +class SubjectButtonWidget extends StatelessWidget { + const SubjectButtonWidget({required this.courseUnit, super.key}); + + final CourseUnit courseUnit; void _launchUcPage(BuildContext context) { - final correspondCourseUnit = correspondingCourseUnit(context); Navigator.push( context, MaterialPageRoute( - builder: (context) => CourseUnitDetailPageView(correspondCourseUnit), + builder: (context) => CourseUnitDetailPageView(courseUnit), ), ); } @override Widget build(BuildContext context) { - if (correspondingCourseUnit(context).name == 'not found') { - return const Column( - mainAxisSize: MainAxisSize.min, - ); - } return Column( mainAxisSize: MainAxisSize.min, children: [ From 80492e6cffddeeea12f20ebf845da4c453455433 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Thu, 29 Feb 2024 13:49:58 +0000 Subject: [PATCH 306/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 1488636b1..210803279 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.14+247 +1.8.0-beta.15+248 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index b882dd7c5..ba5cdbc32 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.14+247 +version: 1.8.0-beta.15+248 environment: From dee7e2096f21773f96d6d9f10fad6670779faa5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 14:42:49 +0000 Subject: [PATCH 307/334] docs: update weekday mapper docs and test structure --- uni/lib/model/utils/time/weekday_mapper.dart | 62 ++++++++++++------- .../utils/time/weekday_mapper_test.dart | 19 +++--- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/uni/lib/model/utils/time/weekday_mapper.dart b/uni/lib/model/utils/time/weekday_mapper.dart index 068984b88..3382ab0d7 100644 --- a/uni/lib/model/utils/time/weekday_mapper.dart +++ b/uni/lib/model/utils/time/weekday_mapper.dart @@ -77,31 +77,49 @@ class WeekdayMapper { ); int map(int fromWeekday) { - final mondayOffset = (_toMonday - _toStart) - (_fromMonday - _fromStart); - - // The algorithm is as follows: + // To find the resulting weekday, it goes like this: + // + // 1. The resulting weekday will be `toWeekdayZeroBased + toStart`. + // `toWeekdayZeroBased` corresponds to the resulting weekday in a system + // that is [0, 6]. By adding `toStart`, we are mapping it to the `to` + // system. + // + // 2. The `toWeekdayZeroBased` will be `toWeekdayZeroBasedUnbound % 7`. + // This operation is essential to return a value that is bound within the + // 7 weekdays that a week has. + // + // 3. The `toWeekdayZeroBasedUnbound` will be + // `fromWeekdayZeroBased + mondayIndexOffset`. `fromWeekdayZeroBased` + // corresponds to the provided weekday in a system that is [0, 6]. This + // can be obtained by performing the operation `fromWeekday - fromStart`. // - // 1. Find the 0-based index of `fromWeekday` in the `from` system, by - // subtracting `fromStart` from it. At this point, you're working with a - // system that starts at 0 and ends at 6. You don't know which day is - // Monday. - // 2. Add the offset between the `from` and `to` Monday. Since we assume - // that the `to` system starts at 0, we can simply add the difference - // between the `from` and `to` Monday. This difference is `mondayOffset`. - // By adding this positive difference, we're now working with a system that - // is [mondayOffset, 6 + mondayOffset]. To go back to the 0-based system, - // we take the modulo 7 of the day. - // 3. At this point, we're working with a 0-based system that has been - // shifted by `mondayOffset`. We can now add `toStart` to get the final - // result. - final fromWeekdayZeroBased = fromWeekday - _fromStart; - final toWeekdayZeroBased = (fromWeekdayZeroBased + mondayOffset) % 7; + // 4. `mondayIndexOffset` corresponds to the number of days that we need + // to advance a monday (or any other day) in the `from` system to get the + // corresponding weekday in the `to` system. This can be obtained by taking + // difference between `toMondayZeroBased` and `fromMondayZeroBased`. These + // two values can be obtained in the same fashion as `fromWeekdayZeroBased`. + // + // Taking these steps into account, we can derive the following formula: + // + // 1. toWeekdayZeroBased + toStart + // 2. toWeekdayZeroBasedUnbound % 7 + toStart + // 3. (fromWeekdayZeroBased + mondayIndexOffset) % 7 + toStart + // 4. (fromWeekday - fromStart + mondayIndexOffset) % 7 + toStart + // 5. (fromWeekday - fromStart + // + toMondayZeroBased - fromMondayZeroBased) % 7 + toStart + // 6. (fromWeekday - fromStart + // + (toMonday - toStart) - fromMondayZeroBased) % 7 + toStart + // 7. (fromWeekday - fromStart + // + (toMonday - toStart) - (fromMonday - fromStart)) % 7 + toStart + // 8. (fromWeekday - fromStart + // + (toMonday - toStart) - (fromMonday - fromStart)) % 7 + toStart + // 9. (fromWeekday - fromStart + // + toMonday - toStart - fromMonday + fromStart) % 7 + toStart + // 10. (fromWeekday + toMonday - toStart - fromMonday) % 7 + toStart + final toWeekdayZeroBased = + (fromWeekday + _toMonday - _toStart - _fromMonday) % 7; final toWeekday = toWeekdayZeroBased + _toStart; return toWeekday; - - // In case you're wondering, like me, the compact formula is: - // (fromWeekday - fromStart + toMonday - fromMonday) % 7 + toStart - // (fromWeekday + _toMonday - _toStart - _fromMonday) % 7 + _toStart } } diff --git a/uni/test/unit/models/utils/time/weekday_mapper_test.dart b/uni/test/unit/models/utils/time/weekday_mapper_test.dart index d00a62550..9504901c9 100644 --- a/uni/test/unit/models/utils/time/weekday_mapper_test.dart +++ b/uni/test/unit/models/utils/time/weekday_mapper_test.dart @@ -26,17 +26,16 @@ void exhaustivelyTestWeekdayMapper( var toWeekday = toMonday; for (var i = 0; i < 7; i++) { - group('[DateTime.weekday = ${i + 1}]', () { - test('fromWeekday = $fromWeekday should map to toWeekday = $toMonday', - () { - expect(mapper.map(fromWeekday), toWeekday); - }); + test( + '[DateTime.weekday = ${i + 1}] fromWeekday = $fromWeekday should' + ' map to toWeekday = $toMonday', () { + expect(mapper.map(fromWeekday), toWeekday); + }); - test( - 'toWeekday = $toWeekday should inversely map to' - ' fromWeekday = $fromWeekday', () { - expect(inverseMapper.map(toWeekday), fromWeekday); - }); + test( + '[DateTime.weekday = ${i + 1}] toWeekday = $toWeekday should' + ' inversely map to fromWeekday = $fromWeekday', () { + expect(inverseMapper.map(toWeekday), fromWeekday); }); fromWeekday = ++fromWeekday >= fromEnd ? fromStart : fromWeekday; From ebe3e405233dabd58bb7bb6cb43bdd26447447dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Wed, 28 Feb 2024 17:08:13 +0000 Subject: [PATCH 308/334] fix: display saturday classes on schedule --- uni/lib/view/schedule/schedule.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index bccfe528a..b5397ed14 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -55,10 +55,10 @@ class SchedulePageViewState extends State super.initState(); tabController = TabController( vsync: this, - length: 5, + length: 6, ); final weekDay = DateTime.now().weekday; - final offset = (weekDay > 5) ? 0 : (weekDay - 1) % 5; + final offset = (weekDay > 6) ? 0 : (weekDay - 1) % 6; tabController?.animateTo(tabController!.index + offset); } @@ -82,7 +82,7 @@ class SchedulePageViewState extends State Expanded( child: TabBarView( controller: tabController, - children: Iterable.generate(5).map((day) { + children: Iterable.generate(6).map((day) { final lectures = lecturesOfDay(widget.lectures, day); if (lectures.isEmpty) { return emptyDayColumn(context, day); @@ -100,7 +100,7 @@ class SchedulePageViewState extends State List createTabs(MediaQueryData queryData, BuildContext context) { final tabs = []; final workWeekDays = - context.read().getWeekdaysWithLocale().sublist(0, 5); + context.read().getWeekdaysWithLocale().sublist(0, 6); workWeekDays.asMap().forEach((index, day) { tabs.add( SizedBox( From fdbe879eea886afb0f4d855f8aa4b7298edafff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 02:53:11 +0000 Subject: [PATCH 309/334] fix: use correct determinants for weekend in pt_PT --- uni/lib/generated/intl/messages_en.dart | 2 ++ uni/lib/generated/intl/messages_pt_PT.dart | 2 ++ uni/lib/generated/l10n.dart | 10 ++++++++++ uni/lib/l10n/intl_en.arb | 2 ++ uni/lib/l10n/intl_pt_PT.arb | 2 ++ uni/lib/view/schedule/schedule.dart | 6 +++++- 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 3508c496d..c7aef77de 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -178,6 +178,8 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("No classes to present"), "no_classes_on": MessageLookupByLibrary.simpleMessage("You don\'t have classes on"), + "no_classes_on_weekend": + MessageLookupByLibrary.simpleMessage("You don\'t have classes on"), "no_college": MessageLookupByLibrary.simpleMessage("no college"), "no_course_units": MessageLookupByLibrary.simpleMessage( "No course units in the selected period"), diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 64053ebfd..b57f5d6dd 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -179,6 +179,8 @@ class MessageLookup extends MessageLookupByLibrary { "Não existem aulas para apresentar"), "no_classes_on": MessageLookupByLibrary.simpleMessage("Não possui aulas à"), + "no_classes_on_weekend": + MessageLookupByLibrary.simpleMessage("Não possui aulas ao"), "no_college": MessageLookupByLibrary.simpleMessage("sem faculdade"), "no_course_units": MessageLookupByLibrary.simpleMessage( "Sem cadeiras no período selecionado"), diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index 3be2a9846..fbc8cc307 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -928,6 +928,16 @@ class S { ); } + /// `You don't have classes on` + String get no_classes_on_weekend { + return Intl.message( + 'You don\'t have classes on', + name: 'no_classes_on_weekend', + desc: '', + args: [], + ); + } + /// `no college` String get no_college { return Intl.message( diff --git a/uni/lib/l10n/intl_en.arb b/uni/lib/l10n/intl_en.arb index e97e4101c..408136d18 100644 --- a/uni/lib/l10n/intl_en.arb +++ b/uni/lib/l10n/intl_en.arb @@ -180,6 +180,8 @@ "@no_classes": {}, "no_classes_on": "You don't have classes on", "@no_classes_on": {}, + "no_classes_on_weekend": "You don't have classes on", + "@no_classes_on_weekend": {}, "no_college": "no college", "@no_college": {}, "no_course_units": "No course units in the selected period", diff --git a/uni/lib/l10n/intl_pt_PT.arb b/uni/lib/l10n/intl_pt_PT.arb index a342593f3..012c4b201 100644 --- a/uni/lib/l10n/intl_pt_PT.arb +++ b/uni/lib/l10n/intl_pt_PT.arb @@ -190,6 +190,8 @@ "@no_classes": {}, "no_classes_on": "Não possui aulas à", "@no_classes_on": {}, + "no_classes_on_weekend": "Não possui aulas ao", + "@no_classes_on_weekend": {}, "no_college": "sem faculdade", "@no_college": {}, "no_course_units": "Sem cadeiras no período selecionado", diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index b5397ed14..bf70dc49a 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -158,10 +158,14 @@ class SchedulePageViewState extends State final weekday = Provider.of(context).getWeekdaysWithLocale()[day]; + final noClassesText = day >= DateTime.saturday - 1 + ? S.of(context).no_classes_on_weekend + : S.of(context).no_classes_on; + return Center( child: ImageLabel( imagePath: 'assets/images/schedule.png', - label: '${S.of(context).no_classes_on} $weekday.', + label: '$noClassesText $weekday.', labelTextStyle: const TextStyle(fontSize: 15), ), ); From a5ad0094a0614be6140bc98e33df8670e9ed9d75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 08:27:59 +0000 Subject: [PATCH 310/334] test: fix tests --- uni/test/unit/view/Pages/exams_page_view_test.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/uni/test/unit/view/Pages/exams_page_view_test.dart b/uni/test/unit/view/Pages/exams_page_view_test.dart index 72439fa55..b8d52f718 100644 --- a/uni/test/unit/view/Pages/exams_page_view_test.dart +++ b/uni/test/unit/view/Pages/exams_page_view_test.dart @@ -52,8 +52,6 @@ void main() async { await tester.pumpWidget(testableWidget(widget, providers: providers)); await tester.pumpAndSettle(); - debugDumpApp(); - expect(find.byKey(Key(firstExam.toString())), findsOneWidget); expect(find.byKey(Key('$firstExam-exam')), findsOneWidget); }); From e49a1be8e1045fc211240e97ffd1e5740c93cf0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 14:56:28 +0000 Subject: [PATCH 311/334] refactor: change response field on WeekResponse --- uni/lib/controller/parsers/parser_schedule.dart | 2 +- uni/lib/controller/parsers/parser_schedule_html.dart | 2 +- uni/lib/model/utils/week_response.dart | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/uni/lib/controller/parsers/parser_schedule.dart b/uni/lib/controller/parsers/parser_schedule.dart index ee819cd74..35dc7f12c 100644 --- a/uni/lib/controller/parsers/parser_schedule.dart +++ b/uni/lib/controller/parsers/parser_schedule.dart @@ -11,7 +11,7 @@ Future> parseScheduleMultipleRequests( ) async { var lectures = []; for (final response in responses) { - lectures += await parseSchedule(response.response, response.week); + lectures += await parseSchedule(response.innerResponse, response.week); } return lectures; } diff --git a/uni/lib/controller/parsers/parser_schedule_html.dart b/uni/lib/controller/parsers/parser_schedule_html.dart index 916b7196b..d9563aeb3 100644 --- a/uni/lib/controller/parsers/parser_schedule_html.dart +++ b/uni/lib/controller/parsers/parser_schedule_html.dart @@ -116,7 +116,7 @@ Future> getScheduleFromHtml( Session session, String faculty, ) async { - final document = parse(response.response.body); + final document = parse(response.innerResponse.body); var semana = [0, 0, 0, 0, 0, 0]; final lecturesList = []; diff --git a/uni/lib/model/utils/week_response.dart b/uni/lib/model/utils/week_response.dart index 6f4219005..38a97354d 100644 --- a/uni/lib/model/utils/week_response.dart +++ b/uni/lib/model/utils/week_response.dart @@ -2,13 +2,13 @@ import 'package:http/http.dart' as http; import 'package:uni/model/utils/time/week.dart'; class WeekResponse { - WeekResponse(this.week, this.response); + WeekResponse(this.week, this.innerResponse); final Week week; - final http.Response response; + final http.Response innerResponse; @override String toString() { - return 'WeekResponse{week: $week, response: $response}'; + return 'WeekResponse{week: $week, response: $innerResponse}'; } } From 0186fc4c1ebebfc3f74c5073451a975372f2cd99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 14:58:04 +0000 Subject: [PATCH 312/334] refactor: remove unused method --- uni/lib/model/utils/time/week.dart | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/uni/lib/model/utils/time/week.dart b/uni/lib/model/utils/time/week.dart index dd8951212..a38a4f85f 100644 --- a/uni/lib/model/utils/time/week.dart +++ b/uni/lib/model/utils/time/week.dart @@ -1,15 +1,5 @@ import 'package:uni/model/utils/time/weekday_mapper.dart'; -DateTime getStartOfDay(DateTime date) { - return date.copyWith( - hour: 0, - minute: 0, - second: 0, - millisecond: 0, - microsecond: 0, - ); -} - /// A [Week] represents a period of 7 days. class Week implements Comparable { /// Creates a [Week] that starts the given [start] **date** (not datetime). From 0396bcfd295739272a6f8f971db5a62732b8dac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 15:18:14 +0000 Subject: [PATCH 313/334] test: remove debugDumpApp --- uni/test/unit/view/Pages/exams_page_view_test.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/uni/test/unit/view/Pages/exams_page_view_test.dart b/uni/test/unit/view/Pages/exams_page_view_test.dart index 72439fa55..b8d52f718 100644 --- a/uni/test/unit/view/Pages/exams_page_view_test.dart +++ b/uni/test/unit/view/Pages/exams_page_view_test.dart @@ -52,8 +52,6 @@ void main() async { await tester.pumpWidget(testableWidget(widget, providers: providers)); await tester.pumpAndSettle(); - debugDumpApp(); - expect(find.byKey(Key(firstExam.toString())), findsOneWidget); expect(find.byKey(Key('$firstExam-exam')), findsOneWidget); }); From a5811c97ad674f661e9fcc5ae8931718d7ce7440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 15:31:26 +0000 Subject: [PATCH 314/334] test: add tests for week --- .../unit/models/utils/time/week_test.dart | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 uni/test/unit/models/utils/time/week_test.dart diff --git a/uni/test/unit/models/utils/time/week_test.dart b/uni/test/unit/models/utils/time/week_test.dart new file mode 100644 index 000000000..ba4f86e59 --- /dev/null +++ b/uni/test/unit/models/utils/time/week_test.dart @@ -0,0 +1,194 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:uni/model/utils/time/week.dart'; + +void main() { + group('Week', () { + final week1 = Week(start: DateTime(2024, 2, 29)); + final week2 = Week(start: DateTime(2024, 3, 3)); + + group('startingOn', () { + test('should return the same week if the starting day is the same', () { + expect(week1.startingOn(DateTime.thursday), week1); + expect(week2.startingOn(DateTime.sunday), week2); + }); + + group( + 'should return a week that starts in the given weekday, after the' + " current week's start and before the next one's", () { + test('week1, starting on Sunday', () { + expect( + week1.startingOn(DateTime.sunday).start.weekday, + DateTime.sunday, + ); + + expect( + week1.startingOn(DateTime.sunday).start.isAfter(week1.start), + true, + ); + + expect( + week1 + .startingOn(DateTime.sunday) + .start + .isBefore(week1.next().start), + true, + ); + }); + + test('week1, ending on Monday', () { + expect( + week1.startingOn(DateTime.monday).start.weekday, + DateTime.monday, + ); + + expect( + week1.startingOn(DateTime.monday).start.isAfter(week1.start), + true, + ); + + expect( + week1 + .startingOn(DateTime.monday) + .start + .isBefore(week1.next().start), + true, + ); + }); + + test('week2, ending on Monday', () { + expect( + week2.startingOn(DateTime.monday).start.weekday, + DateTime.monday, + ); + + expect( + week2.startingOn(DateTime.monday).start.isAfter(week2.start), + true, + ); + + expect( + week2 + .startingOn(DateTime.monday) + .start + .isBefore(week2.next().start), + true, + ); + }); + + test('week2, ending on Saturday', () { + expect( + week2.startingOn(DateTime.saturday).start.weekday, + DateTime.saturday, + ); + + expect( + week2.startingOn(DateTime.saturday).start.isAfter(week2.start), + true, + ); + + expect( + week2 + .startingOn(DateTime.saturday) + .start + .isBefore(week2.next().start), + true, + ); + }); + }); + }); + + group('endingOn', () { + test('should return the same week if the ending day is the same', () { + expect(week1.endingOn(DateTime.thursday), week1); + expect(week2.endingOn(DateTime.sunday), week2); + }); + + group( + 'should return a week that ends in the given weekday, before the' + " current week's end and after the previous one's", () { + test('week1, ending on Sunday', () { + expect( + week1.endingOn(DateTime.sunday).end.weekday, + DateTime.sunday, + ); + + expect( + week1.endingOn(DateTime.sunday).end.isBefore(week1.end), + true, + ); + + expect( + week1.endingOn(DateTime.sunday).end.isAfter(week1.previous().end), + true, + ); + }); + + test('week1, ending on Monday', () { + expect( + week1.endingOn(DateTime.monday).end.weekday, + DateTime.monday, + ); + + expect( + week1.endingOn(DateTime.monday).end.isBefore(week1.end), + true, + ); + + expect( + week1.endingOn(DateTime.monday).end.isAfter(week1.previous().end), + true, + ); + }); + + test('week2, ending on Monday', () { + expect( + week2.endingOn(DateTime.monday).end.weekday, + DateTime.monday, + ); + + expect( + week2.endingOn(DateTime.monday).end.isBefore(week2.end), + true, + ); + + expect( + week2.endingOn(DateTime.monday).end.isAfter(week1.previous().end), + true, + ); + }); + + test('week2, ending on Saturday', () { + expect( + week2.endingOn(DateTime.saturday).end.weekday, + DateTime.saturday, + ); + + expect( + week2.endingOn(DateTime.saturday).end.isBefore(week2.end), + true, + ); + + expect( + week2.endingOn(DateTime.saturday).end.isAfter(week1.previous().end), + true, + ); + }); + }); + }); + + group('getWeekday', () { + group( + 'should return a day within the same week, with the' + ' requested weekday', () { + for (var i = 0; i < 7; i++) { + test('[DateTime.weekday = ${i + 1}]', () { + final weekday = DateTime.monday + i; + final day = week1.getWeekday(weekday); + expect(day.weekday, weekday); + expect(week1.contains(day), true); + }); + } + }); + }); + }); +} From 600503b359d27ace13d155d886d950164d1a7d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 17:21:04 +0000 Subject: [PATCH 315/334] test: add tests for fromWeekdays constructor --- uni/lib/model/utils/time/week.dart | 2 +- uni/lib/model/utils/time/weekday_mapper.dart | 21 +++ .../utils/time/weekday_mapper_test.dart | 128 ++++++++++++++++-- 3 files changed, 140 insertions(+), 11 deletions(-) diff --git a/uni/lib/model/utils/time/week.dart b/uni/lib/model/utils/time/week.dart index a38a4f85f..0cf6e6244 100644 --- a/uni/lib/model/utils/time/week.dart +++ b/uni/lib/model/utils/time/week.dart @@ -125,6 +125,6 @@ class Week implements Comparable { @override String toString() { - return 'Week{start: $start, end: $end}'; + return 'Week(start: $start, end: $end)'; } } diff --git a/uni/lib/model/utils/time/weekday_mapper.dart b/uni/lib/model/utils/time/weekday_mapper.dart index 3382ab0d7..da9c7e210 100644 --- a/uni/lib/model/utils/time/weekday_mapper.dart +++ b/uni/lib/model/utils/time/weekday_mapper.dart @@ -122,4 +122,25 @@ class WeekdayMapper { return toWeekday; } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + return other is WeekdayMapper && + other._fromStart == _fromStart && + other._fromMonday == _fromMonday && + other._toStart == _toStart && + other._toMonday == _toMonday; + } + + @override + int get hashCode => + _fromStart.hashCode ^ + _fromMonday.hashCode ^ + _toStart.hashCode ^ + _toMonday.hashCode; + + @override + String toString() => 'WeekdayMapper(fromStart: $_fromStart, ' + 'fromMonday: $_fromMonday, toStart: $_toStart, toMonday: $_toMonday)'; } diff --git a/uni/test/unit/models/utils/time/weekday_mapper_test.dart b/uni/test/unit/models/utils/time/weekday_mapper_test.dart index 9504901c9..ab4eb52c8 100644 --- a/uni/test/unit/models/utils/time/weekday_mapper_test.dart +++ b/uni/test/unit/models/utils/time/weekday_mapper_test.dart @@ -44,25 +44,133 @@ void exhaustivelyTestWeekdayMapper( }); } +void ensureMapperEquivalenceByStartWeekdays( + int fromStart, + int fromStartWeekday, + int toStart, + int toStartWeekday, +) { + group( + 'When mapping from $fromStart..${fromStart + 6}' + ' (start.weekday = $fromStartWeekday to $toStart..${toStart + 6}' + ' (start.weekday = $toStartWeekday)', () { + test('should be correctly created', () { + final equivalent = WeekdayMapper( + fromStart: fromStart, + fromMonday: WeekdayMapper.fromStartWeekdays( + fromStart: 1, + fromStartWeekday: DateTime.monday, + toStart: fromStart, + toStartWeekday: fromStartWeekday, + ).map(DateTime.monday), + toStart: toStart, + toMonday: WeekdayMapper.fromStartWeekdays( + fromStart: 1, + fromStartWeekday: DateTime.monday, + toStart: toStart, + toStartWeekday: toStartWeekday, + ).map(DateTime.monday), + ); + + final mapper = WeekdayMapper.fromStartWeekdays( + fromStart: fromStart, + fromStartWeekday: fromStartWeekday, + toStart: toStart, + toStartWeekday: toStartWeekday, + ); + + expect(mapper, equivalent); + }); + }); +} + void main() { group('WeekdayMapper', () { + group('map', () { + // There are three main states for `fromStart` and `toStart`: + // - fromStart < toStart + // - fromStart = toStart + // - fromStart > toStart + + // The same goes for `fromMonday` and `toMonday`. + + exhaustivelyTestWeekdayMapper(2, 4, 3, 5); + exhaustivelyTestWeekdayMapper(2, 4, 3, 4); + exhaustivelyTestWeekdayMapper(2, 5, 3, 4); + + exhaustivelyTestWeekdayMapper(2, 4, 2, 5); + exhaustivelyTestWeekdayMapper(2, 4, 2, 4); + exhaustivelyTestWeekdayMapper(2, 5, 2, 4); + + exhaustivelyTestWeekdayMapper(3, 4, 2, 5); + exhaustivelyTestWeekdayMapper(3, 4, 2, 4); + exhaustivelyTestWeekdayMapper(3, 5, 2, 4); + }); + }); + + group('constructor fromStartWeekdays', () { // There are three main states for `fromStart` and `toStart`: // - fromStart < toStart // - fromStart = toStart // - fromStart > toStart - // The same goes for `fromMonday` and `toMonday`. + // The same goes for `fromStartWeekday` and `toStartWeekday`. - exhaustivelyTestWeekdayMapper(2, 4, 3, 5); - exhaustivelyTestWeekdayMapper(2, 4, 3, 4); - exhaustivelyTestWeekdayMapper(2, 5, 3, 4); + ensureMapperEquivalenceByStartWeekdays( + 2, + DateTime.tuesday, + 3, + DateTime.thursday, + ); + ensureMapperEquivalenceByStartWeekdays( + 2, + DateTime.wednesday, + 3, + DateTime.wednesday, + ); + ensureMapperEquivalenceByStartWeekdays( + 2, + DateTime.thursday, + 3, + DateTime.tuesday, + ); - exhaustivelyTestWeekdayMapper(2, 4, 2, 5); - exhaustivelyTestWeekdayMapper(2, 4, 2, 4); - exhaustivelyTestWeekdayMapper(2, 5, 2, 4); + ensureMapperEquivalenceByStartWeekdays( + 2, + DateTime.tuesday, + 2, + DateTime.thursday, + ); + ensureMapperEquivalenceByStartWeekdays( + 2, + DateTime.wednesday, + 2, + DateTime.wednesday, + ); + ensureMapperEquivalenceByStartWeekdays( + 2, + DateTime.thursday, + 2, + DateTime.tuesday, + ); - exhaustivelyTestWeekdayMapper(3, 4, 2, 5); - exhaustivelyTestWeekdayMapper(3, 4, 2, 4); - exhaustivelyTestWeekdayMapper(3, 5, 2, 4); + ensureMapperEquivalenceByStartWeekdays( + 3, + DateTime.tuesday, + 2, + DateTime.thursday, + ); + ensureMapperEquivalenceByStartWeekdays( + 3, + DateTime.wednesday, + 2, + DateTime.wednesday, + ); + ensureMapperEquivalenceByStartWeekdays( + 3, + DateTime.thursday, + 2, + DateTime.tuesday, + ); }); } From c4a43d9dfdbe22a89cef71fe3031252193f01c57 Mon Sep 17 00:00:00 2001 From: limwa Date: Thu, 29 Feb 2024 18:44:09 +0000 Subject: [PATCH 316/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 210803279..4d63bda09 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.15+248 +1.8.0-beta.16+249 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index ba5cdbc32..b9df706fd 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.15+248 +version: 1.8.0-beta.16+249 environment: From 86efdfa7a7470b65a8b32f9cf8b511159b3116ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Thu, 29 Feb 2024 18:51:23 +0000 Subject: [PATCH 317/334] style: format schedule.dart --- uni/lib/view/schedule/schedule.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index 1db70546f..404dddaea 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -62,7 +62,7 @@ class SchedulePageViewState extends State vsync: this, length: 6, ); - + final now = widget.now ?? DateTime.now(); currentWeek = Week(start: now); From 3659ce2abc4cece9be65ca15cff1641b56b150f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 1 Mar 2024 11:33:43 +0000 Subject: [PATCH 318/334] refactor: get current datetime in a higher ancestor --- uni/lib/view/schedule/schedule.dart | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index 404dddaea..2140095a5 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -23,10 +23,12 @@ class SchedulePage extends StatefulWidget { class SchedulePageState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { + final now = widget.now ?? DateTime.now(); + return LazyConsumer>( - builder: (context, lectures) => SchedulePageView(lectures), + builder: (context, lectures) => SchedulePageView(lectures, now: now), hasContent: (lectures) => lectures.isNotEmpty, - onNullContent: SchedulePageView(const [], now: widget.now), + onNullContent: SchedulePageView(const [], now: now), ); } @@ -41,10 +43,10 @@ class SchedulePageState extends SecondaryPageViewState { } class SchedulePageView extends StatefulWidget { - const SchedulePageView(this.lectures, {super.key, this.now}); + const SchedulePageView(this.lectures, {required this.now, super.key}); final List lectures; - final DateTime? now; + final DateTime now; @override SchedulePageViewState createState() => SchedulePageViewState(); @@ -63,10 +65,9 @@ class SchedulePageViewState extends State length: 6, ); - final now = widget.now ?? DateTime.now(); - currentWeek = Week(start: now); + currentWeek = Week(start: widget.now); - final weekDay = now.weekday; + final weekDay = widget.now.weekday; final offset = (weekDay > 6) ? 0 : (weekDay - 1) % 6; tabController?.animateTo(tabController!.index + offset); } From 93fed15e9233457e5b47eef1ca427f04adf796a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 1 Mar 2024 11:38:24 +0000 Subject: [PATCH 319/334] refactor: use weekday mapper to determine indices --- uni/lib/model/utils/time/weekday_mapper.dart | 7 +++++++ uni/lib/view/schedule/schedule.dart | 11 +++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/uni/lib/model/utils/time/weekday_mapper.dart b/uni/lib/model/utils/time/weekday_mapper.dart index da9c7e210..615a9602d 100644 --- a/uni/lib/model/utils/time/weekday_mapper.dart +++ b/uni/lib/model/utils/time/weekday_mapper.dart @@ -54,6 +54,13 @@ class WeekdayMapper { toStartWeekday: DateTime.monday, ); + static const fromDartToIndex = WeekdayMapper.fromStartWeekdays( + fromStart: 1, + fromStartWeekday: DateTime.monday, + toStart: 0, + toStartWeekday: DateTime.monday, + ); + final int _fromStart; final int _fromMonday; diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index 2140095a5..abc48989d 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -4,6 +4,7 @@ import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/providers/lazy/lecture_provider.dart'; import 'package:uni/model/utils/time/week.dart'; +import 'package:uni/model/utils/time/weekday_mapper.dart'; import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/expanded_image_label.dart'; import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; @@ -92,15 +93,13 @@ class SchedulePageViewState extends State Expanded( child: TabBarView( controller: tabController, - children: Iterable.generate( - 6, - (int index) => currentWeek.getWeekday(index + 1), - ).map((day) { + children: currentWeek.weekdays.take(6).map((day) { final lectures = lecturesOfDay(widget.lectures, day); + final index = WeekdayMapper.fromDartToIndex.map(day.weekday); if (lectures.isEmpty) { - return emptyDayColumn(context, day.weekday - 1); + return emptyDayColumn(context, index); } else { - return dayColumnBuilder(day.weekday - 1, lectures, context); + return dayColumnBuilder(index, lectures, context); } }).toList(), ), From e1ecb4cf29a77620f33005436a88238d3f9d9e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 1 Mar 2024 11:46:35 +0000 Subject: [PATCH 320/334] refactor: use records instead of WeekResponse --- .../schedule_fetcher/schedule_fetcher_api.dart | 7 ++++--- .../schedule_fetcher/schedule_fetcher_html.dart | 7 ++++--- uni/lib/controller/parsers/parser_schedule.dart | 7 +++---- .../controller/parsers/parser_schedule_html.dart | 13 +++++++------ uni/lib/model/utils/week_response.dart | 14 -------------- 5 files changed, 18 insertions(+), 30 deletions(-) delete mode 100644 uni/lib/model/utils/week_response.dart diff --git a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart index 98b61dc63..e0892fcce 100644 --- a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart +++ b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart @@ -1,10 +1,11 @@ +import 'package:http/http.dart' as http; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/controller/parsers/parser_schedule.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; -import 'package:uni/model/utils/week_response.dart'; +import 'package:uni/model/utils/time/week.dart'; /// Class for fetching the user's lectures from the faculties' API. class ScheduleFetcherApi extends ScheduleFetcher { @@ -22,7 +23,7 @@ class ScheduleFetcherApi extends ScheduleFetcher { final dates = getDates(); final urls = getEndpoints(session); - final responses = []; + final responses = <(Week, http.Response)>[]; for (final url in urls) { final futures = dates.map( (date) => NetworkRouter.getWithCookies( @@ -33,7 +34,7 @@ class ScheduleFetcherApi extends ScheduleFetcher { 'pv_semana_fim': date.asSigarraWeekEnd, }, session, - ).then((response) => WeekResponse(date.week, response)), + ).then((response) => (date.week, response)), ); responses.addAll(await Future.wait(futures)); diff --git a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_html.dart b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_html.dart index 62f2ea83b..6ca596dd1 100644 --- a/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_html.dart +++ b/uni/lib/controller/fetchers/schedule_fetcher/schedule_fetcher_html.dart @@ -1,3 +1,4 @@ +import 'package:http/http.dart' as http; import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher.dart'; import 'package:uni/controller/networking/network_router.dart'; @@ -5,7 +6,7 @@ import 'package:uni/controller/parsers/parser_schedule_html.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/entities/profile.dart'; import 'package:uni/model/entities/session.dart'; -import 'package:uni/model/utils/week_response.dart'; +import 'package:uni/model/utils/time/week.dart'; /// Class for fetching the user's lectures from the schedule's HTML page. class ScheduleFetcherHtml extends ScheduleFetcher { @@ -23,7 +24,7 @@ class ScheduleFetcherHtml extends ScheduleFetcher { final dates = getDates(); final baseUrls = NetworkRouter.getBaseUrlsFromSession(session); - final lectureResponses = >[]; + final lectureResponses = >[]; for (final baseUrl in baseUrls) { final url = '${baseUrl}hor_geral.estudantes_view'; @@ -39,7 +40,7 @@ class ScheduleFetcherHtml extends ScheduleFetcher { }, session, ).then( - (response) => Tuple2(WeekResponse(date.week, response), baseUrl), + (response) => Tuple2((date.week, response), baseUrl), ), ); diff --git a/uni/lib/controller/parsers/parser_schedule.dart b/uni/lib/controller/parsers/parser_schedule.dart index 35dc7f12c..7bedade65 100644 --- a/uni/lib/controller/parsers/parser_schedule.dart +++ b/uni/lib/controller/parsers/parser_schedule.dart @@ -4,14 +4,13 @@ import 'package:http/http.dart' as http; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/utils/time/week.dart'; import 'package:uni/model/utils/time/weekday_mapper.dart'; -import 'package:uni/model/utils/week_response.dart'; Future> parseScheduleMultipleRequests( - List responses, + List<(Week, http.Response)> responsesPerWeeks, ) async { var lectures = []; - for (final response in responses) { - lectures += await parseSchedule(response.innerResponse, response.week); + for (final (week, response) in responsesPerWeeks) { + lectures += await parseSchedule(response, week); } return lectures; } diff --git a/uni/lib/controller/parsers/parser_schedule_html.dart b/uni/lib/controller/parsers/parser_schedule_html.dart index d9563aeb3..7cdd00b42 100644 --- a/uni/lib/controller/parsers/parser_schedule_html.dart +++ b/uni/lib/controller/parsers/parser_schedule_html.dart @@ -2,12 +2,12 @@ import 'dart:async'; import 'package:html/dom.dart'; import 'package:html/parser.dart' show parse; +import 'package:http/http.dart' as http; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/model/entities/lecture.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/utils/time/week.dart'; import 'package:uni/model/utils/time/weekday_mapper.dart'; -import 'package:uni/model/utils/week_response.dart'; Future> getOverlappedClasses( Session session, @@ -68,7 +68,7 @@ Future> getOverlappedClasses( ); final classLectures = await getScheduleFromHtml( - WeekResponse(week, response), + (week, response), session, faculty, ); @@ -112,11 +112,12 @@ const fromParserToDart = WeekdayMapper.fromStartWeekdays( /// /// This function parses the schedule's HTML page. Future> getScheduleFromHtml( - WeekResponse response, + (Week, http.Response) responsePerWeek, Session session, String faculty, ) async { - final document = parse(response.innerResponse.body); + final (week, response) = responsePerWeek; + final document = parse(response.body); var semana = [0, 0, 0, 0, 0, 0]; final lecturesList = []; @@ -156,7 +157,7 @@ Future> getScheduleFromHtml( final lect = Lecture.fromHtml( subject, typeClass, - response.week.getWeekday(fromParserToDart.map(day)), + week.getWeekday(fromParserToDart.map(day)), startTime, blocks, room ?? '', @@ -174,7 +175,7 @@ Future> getScheduleFromHtml( lecturesList ..addAll( - await getOverlappedClasses(session, document, faculty, response.week), + await getOverlappedClasses(session, document, faculty, week), ) ..sort((a, b) => a.compare(b)); diff --git a/uni/lib/model/utils/week_response.dart b/uni/lib/model/utils/week_response.dart deleted file mode 100644 index 38a97354d..000000000 --- a/uni/lib/model/utils/week_response.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:http/http.dart' as http; -import 'package:uni/model/utils/time/week.dart'; - -class WeekResponse { - WeekResponse(this.week, this.innerResponse); - - final Week week; - final http.Response innerResponse; - - @override - String toString() { - return 'WeekResponse{week: $week, response: $innerResponse}'; - } -} From 3d62af985ed6a472c9a1faf347f11bea7bb45ca5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 1 Mar 2024 14:06:18 +0000 Subject: [PATCH 321/334] docs: update documentation to match variable names --- uni/lib/controller/parsers/parser_schedule_html.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/uni/lib/controller/parsers/parser_schedule_html.dart b/uni/lib/controller/parsers/parser_schedule_html.dart index 7cdd00b42..61767ff6e 100644 --- a/uni/lib/controller/parsers/parser_schedule_html.dart +++ b/uni/lib/controller/parsers/parser_schedule_html.dart @@ -108,7 +108,8 @@ const fromParserToDart = WeekdayMapper.fromStartWeekdays( toStartWeekday: DateTime.monday, ); -/// Extracts the user's lectures from an HTTP [response] and sorts them by date. +/// Extracts the user's lectures from a Week-HTTP pair in [responsePerWeek] and +/// sorts them by date. /// /// This function parses the schedule's HTML page. Future> getScheduleFromHtml( From bb0f2a00147bf0d2a4303d8241e605ec58e684ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 1 Mar 2024 14:09:54 +0000 Subject: [PATCH 322/334] refactor: use common weekday mappers --- .../parsers/parser_schedule_html.dart | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/uni/lib/controller/parsers/parser_schedule_html.dart b/uni/lib/controller/parsers/parser_schedule_html.dart index 61767ff6e..4de3e04fd 100644 --- a/uni/lib/controller/parsers/parser_schedule_html.dart +++ b/uni/lib/controller/parsers/parser_schedule_html.dart @@ -101,13 +101,6 @@ Future> getOverlappedClasses( return lecturesList; } -const fromParserToDart = WeekdayMapper.fromStartWeekdays( - fromStart: 0, - fromStartWeekday: DateTime.monday, - toStart: 1, - toStartWeekday: DateTime.monday, -); - /// Extracts the user's lectures from a Week-HTTP pair in [responsePerWeek] and /// sorts them by date. /// @@ -125,14 +118,14 @@ Future> getScheduleFromHtml( document.querySelectorAll('.horario > tbody > tr').forEach((Element element) { if (element.getElementsByClassName('horas').isNotEmpty) { - var day = 0; + var dayIndex = 0; final children = element.children; for (var i = 1; i < children.length; i++) { - for (var d = day; d < semana.length; d++) { + for (var d = dayIndex; d < semana.length; d++) { if (semana[d] == 0) { break; } - day++; + dayIndex++; } final clsName = children[i].className; if (clsName == 'TE' || clsName == 'TP' || clsName == 'PL') { @@ -153,12 +146,14 @@ Future> getScheduleFromHtml( final blocks = int.parse(children[i].attributes['rowspan']!); final startTime = children[0].text.substring(0, 5); - semana[day] += blocks; + semana[dayIndex] += blocks; final lect = Lecture.fromHtml( subject, typeClass, - week.getWeekday(fromParserToDart.map(day)), + week.getWeekday( + WeekdayMapper.fromDartToIndex.inverse.map(dayIndex), + ), startTime, blocks, room ?? '', @@ -168,7 +163,7 @@ Future> getScheduleFromHtml( ); lecturesList.add(lect); } - day++; + dayIndex++; } semana = semana.expand((i) => [if ((i - 1) < 0) 0 else i - 1]).toList(); } From 7eae0df69eb8e3fe55a45fb1420db0689e0350fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 1 Mar 2024 13:52:11 +0000 Subject: [PATCH 323/334] refactor: now argument in schedule constructor --- .../controller/parsers/parser_schedule_html.dart | 2 +- uni/lib/main.dart | 2 +- uni/lib/view/schedule/schedule.dart | 13 +++++++------ uni/test/integration/src/schedule_page_test.dart | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/uni/lib/controller/parsers/parser_schedule_html.dart b/uni/lib/controller/parsers/parser_schedule_html.dart index 4de3e04fd..f632698b3 100644 --- a/uni/lib/controller/parsers/parser_schedule_html.dart +++ b/uni/lib/controller/parsers/parser_schedule_html.dart @@ -107,7 +107,7 @@ Future> getOverlappedClasses( /// This function parses the schedule's HTML page. Future> getScheduleFromHtml( (Week, http.Response) responsePerWeek, - Session session, + Session session,q String faculty, ) async { final (week, response) = responsePerWeek; diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 21dfe91fd..854cf9755 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -240,7 +240,7 @@ class ApplicationState extends State { ), '/${NavigationItem.navSchedule.route}': PageTransition.makePageTransition( - page: const SchedulePage(), + page: SchedulePage(), settings: settings, ), '/${NavigationItem.navExams.route}': diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index abc48989d..a55aef9c4 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -13,9 +13,9 @@ import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/schedule/widgets/schedule_slot.dart'; class SchedulePage extends StatefulWidget { - const SchedulePage({super.key, this.now}); + SchedulePage({super.key, DateTime? now}) : now = now ?? DateTime.now(); - final DateTime? now; + final DateTime now; @override SchedulePageState createState() => SchedulePageState(); @@ -24,12 +24,13 @@ class SchedulePage extends StatefulWidget { class SchedulePageState extends SecondaryPageViewState { @override Widget getBody(BuildContext context) { - final now = widget.now ?? DateTime.now(); - return LazyConsumer>( - builder: (context, lectures) => SchedulePageView(lectures, now: now), + builder: (context, lectures) => SchedulePageView( + lectures, + now: widget.now, + ), hasContent: (lectures) => lectures.isNotEmpty, - onNullContent: SchedulePageView(const [], now: now), + onNullContent: SchedulePageView(const [], now: widget.now), ); } diff --git a/uni/test/integration/src/schedule_page_test.dart b/uni/test/integration/src/schedule_page_test.dart index 080616ffa..0630b566e 100644 --- a/uni/test/integration/src/schedule_page_test.dart +++ b/uni/test/integration/src/schedule_page_test.dart @@ -55,7 +55,7 @@ void main() async { Session(username: 'up1234', cookies: 'cookie', faculties: ['feup']), ); - const widget = SchedulePage(); + final widget = SchedulePage(); final providers = [ ChangeNotifierProvider(create: (_) => scheduleProvider), From c8fa63c5be2da90d5e26af7f7c72aaf4c4799db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 1 Mar 2024 14:32:45 +0000 Subject: [PATCH 324/334] tests: scroll schedule until tabs are visible --- uni/lib/controller/parsers/parser_schedule_html.dart | 2 +- uni/test/integration/src/schedule_page_test.dart | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/uni/lib/controller/parsers/parser_schedule_html.dart b/uni/lib/controller/parsers/parser_schedule_html.dart index f632698b3..4de3e04fd 100644 --- a/uni/lib/controller/parsers/parser_schedule_html.dart +++ b/uni/lib/controller/parsers/parser_schedule_html.dart @@ -107,7 +107,7 @@ Future> getOverlappedClasses( /// This function parses the schedule's HTML page. Future> getScheduleFromHtml( (Week, http.Response) responsePerWeek, - Session session,q + Session session, String faculty, ) async { final (week, response) = responsePerWeek; diff --git a/uni/test/integration/src/schedule_page_test.dart b/uni/test/integration/src/schedule_page_test.dart index 0630b566e..07a7c8a2e 100644 --- a/uni/test/integration/src/schedule_page_test.dart +++ b/uni/test/integration/src/schedule_page_test.dart @@ -81,17 +81,22 @@ void main() async { await tester.pumpAndSettle(); + await tester.ensureVisible(find.byKey(const Key('schedule-page-tab-2'))); await tester.tap(find.byKey(const Key('schedule-page-tab-2'))); await tester.pumpAndSettle(); + await tester.ensureVisible(find.byKey(const Key('schedule-page-tab-1'))); await tester.tap(find.byKey(const Key('schedule-page-tab-1'))); await tester.pumpAndSettle(); + await tester.ensureVisible(find.byKey(const Key('schedule-page-tab-0'))); await tester.tap(find.byKey(const Key('schedule-page-tab-0'))); await tester.pumpAndSettle(); testScheduleSlot('ASSO', '11:00', '13:00', 'EaD', 'TP', 'DRP'); + await tester.ensureVisible(find.byKey(const Key('schedule-page-tab-2'))); await tester.tap(find.byKey(const Key('schedule-page-tab-2'))); await tester.pumpAndSettle(); + await tester.ensureVisible(find.byKey(const Key('schedule-page-tab-3'))); await tester.tap(find.byKey(const Key('schedule-page-tab-3'))); await tester.pumpAndSettle(); From 136f19ebbc456484f10010a423c9f4b72e9fb540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 1 Mar 2024 18:36:08 +0000 Subject: [PATCH 325/334] refactor: remove toDartWeekdays from Week --- uni/lib/model/utils/time/week.dart | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/uni/lib/model/utils/time/week.dart b/uni/lib/model/utils/time/week.dart index 0cf6e6244..02f8efe26 100644 --- a/uni/lib/model/utils/time/week.dart +++ b/uni/lib/model/utils/time/week.dart @@ -1,5 +1,3 @@ -import 'package:uni/model/utils/time/weekday_mapper.dart'; - /// A [Week] represents a period of 7 days. class Week implements Comparable { /// Creates a [Week] that starts the given [start] **date** (not datetime). @@ -20,17 +18,10 @@ class Week implements Comparable { } // Recommended by https://dart.dev/language/constructors#factory-constructors - Week._internal(this.start, this.end) - : toDartWeekday = WeekdayMapper( - fromStart: 1, - fromMonday: start.weekday, - toStart: 1, - toMonday: DateTime.monday, - ); + Week._internal(this.start, this.end); final DateTime start; final DateTime end; - final WeekdayMapper toDartWeekday; /// Returns whether the given [date] is within this [Week]. bool contains(DateTime date) { From 5bd8dd0a7b7f2788a8010045d1faef43afff9ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Lima?= Date: Fri, 1 Mar 2024 19:52:27 +0000 Subject: [PATCH 326/334] refactor: build current week at the widget level --- uni/lib/view/schedule/schedule.dart | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/uni/lib/view/schedule/schedule.dart b/uni/lib/view/schedule/schedule.dart index a55aef9c4..878d1e9b7 100644 --- a/uni/lib/view/schedule/schedule.dart +++ b/uni/lib/view/schedule/schedule.dart @@ -45,10 +45,11 @@ class SchedulePageState extends SecondaryPageViewState { } class SchedulePageView extends StatefulWidget { - const SchedulePageView(this.lectures, {required this.now, super.key}); + SchedulePageView(this.lectures, {required DateTime now, super.key}) + : currentWeek = Week(start: now); final List lectures; - final DateTime now; + final Week currentWeek; @override SchedulePageViewState createState() => SchedulePageViewState(); @@ -57,7 +58,6 @@ class SchedulePageView extends StatefulWidget { class SchedulePageViewState extends State with TickerProviderStateMixin { TabController? tabController; - late Week currentWeek; @override void initState() { @@ -67,9 +67,7 @@ class SchedulePageViewState extends State length: 6, ); - currentWeek = Week(start: widget.now); - - final weekDay = widget.now.weekday; + final weekDay = widget.currentWeek.start.weekday; final offset = (weekDay > 6) ? 0 : (weekDay - 1) % 6; tabController?.animateTo(tabController!.index + offset); } @@ -94,7 +92,7 @@ class SchedulePageViewState extends State Expanded( child: TabBarView( controller: tabController, - children: currentWeek.weekdays.take(6).map((day) { + children: widget.currentWeek.weekdays.take(6).map((day) { final lectures = lecturesOfDay(widget.lectures, day); final index = WeekdayMapper.fromDartToIndex.map(day.weekday); if (lectures.isEmpty) { From 90a93fa9616b27cc3f31079a52f6b0bf3b34f7a2 Mon Sep 17 00:00:00 2001 From: limwa Date: Fri, 1 Mar 2024 21:54:56 +0000 Subject: [PATCH 327/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 4d63bda09..c695e9688 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.16+249 +1.8.0-beta.17+250 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index b9df706fd..71d18e89c 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.16+249 +version: 1.8.0-beta.17+250 environment: From d82e5a2d9ee6c3148e6ea247c6acdaa30e167856 Mon Sep 17 00:00:00 2001 From: limwa Date: Sat, 2 Mar 2024 01:42:27 +0000 Subject: [PATCH 328/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index c695e9688..1fc0d9ab7 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.17+250 +1.8.0-beta.18+251 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 71d18e89c..92598751d 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.17+250 +version: 1.8.0-beta.18+251 environment: From 380e044db9a8b1d63eaa3b8a047c3cb423b68904 Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Tue, 27 Feb 2024 11:28:19 +0000 Subject: [PATCH 329/334] Automatic faculty Selection --- .../fetchers/faculties_fetcher.dart | 33 +++++++++++++++++++ .../controller/networking/network_router.dart | 7 +--- uni/lib/model/entities/session.dart | 11 +++++-- 3 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 uni/lib/controller/fetchers/faculties_fetcher.dart diff --git a/uni/lib/controller/fetchers/faculties_fetcher.dart b/uni/lib/controller/fetchers/faculties_fetcher.dart new file mode 100644 index 000000000..7c0701a7a --- /dev/null +++ b/uni/lib/controller/fetchers/faculties_fetcher.dart @@ -0,0 +1,33 @@ +import 'package:html/parser.dart'; +import 'package:uni/controller/networking/network_router.dart'; +import 'package:uni/model/entities/session.dart'; + +Future> getStudentFaculties(Session session) async { + final registerFaculties = []; + + final query = {'pct_codigo': session.username}; + const baseUrl = + 'https://sigarra.up.pt/up/pt/vld_entidades_geral.entidade_pagina'; + + final pctRequest = + await NetworkRouter.getWithCookies(baseUrl, query, session); + + final pctDocument = parse(pctRequest.body); + + final list = + pctDocument.querySelectorAll('#conteudoinner>ul a').map((e) => e.text); + + //user is enrolled in only one faculty + if (list.isEmpty) { + final singleFaculty = pctDocument.querySelector('a')!.attributes['href']; + final regex2 = RegExp(r'.*\/([a-z]+)\/.*'); + final faculty = regex2.firstMatch(singleFaculty!)?.group(1)?.toUpperCase(); + registerFaculties.add(faculty!); + } else { + final regex = RegExp(r'.*\(([A-Z]+)\)'); + for (final element in list) { + registerFaculties.add(regex.firstMatch(element)!.group(1)!); + } + } + return registerFaculties; +} diff --git a/uni/lib/controller/networking/network_router.dart b/uni/lib/controller/networking/network_router.dart index 3f6594777..c284258df 100644 --- a/uni/lib/controller/networking/network_router.dart +++ b/uni/lib/controller/networking/network_router.dart @@ -72,14 +72,9 @@ class NetworkRouter { persistentSession: persistentSession, ); - if (session == null) { - Logger().e('Login failed: user not authenticated'); - return null; - } - Logger().i('Login successful'); _lastLoginTime = DateTime.now(); - _cachedSession = session; + _cachedSession = await session; return session; }, diff --git a/uni/lib/model/entities/session.dart b/uni/lib/model/entities/session.dart index 636c65a8b..65b929566 100644 --- a/uni/lib/model/entities/session.dart +++ b/uni/lib/model/entities/session.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:http/http.dart' as http; +import 'package:uni/controller/fetchers/faculties_fetcher.dart'; import 'package:uni/controller/networking/network_router.dart'; /// Stores information about a user session. @@ -19,21 +20,25 @@ class Session { /// Creates a new Session instance from an HTTP response. /// Returns null if the authentication failed. - static Session? fromLogin( + static Future fromLogin( http.Response response, List faculties, { required bool persistentSession, - }) { + }) async { final responseBody = json.decode(response.body) as Map; if (!(responseBody['authenticated'] as bool)) { return null; } - return Session( + final session = Session( faculties: faculties, username: responseBody['codigo'] as String, cookies: NetworkRouter.extractCookies(response.headers), ); + + session.faculties = await getStudentFaculties(session); + + return session; } } From 5869196020fb723600aca2dc7b28efd5adb1b4b7 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Mon, 4 Mar 2024 11:41:09 +0000 Subject: [PATCH 330/334] Improve faculties fetcher readability --- .../fetchers/faculties_fetcher.dart | 46 +++---- uni/lib/generated/intl/messages_all.dart | 13 +- uni/lib/generated/intl/messages_en.dart | 13 +- uni/lib/generated/intl/messages_pt_PT.dart | 14 +-- uni/lib/generated/l10n.dart | 6 +- uni/pubspec.lock | 112 +++++++++--------- 6 files changed, 103 insertions(+), 101 deletions(-) diff --git a/uni/lib/controller/fetchers/faculties_fetcher.dart b/uni/lib/controller/fetchers/faculties_fetcher.dart index 7c0701a7a..754cefefd 100644 --- a/uni/lib/controller/fetchers/faculties_fetcher.dart +++ b/uni/lib/controller/fetchers/faculties_fetcher.dart @@ -3,31 +3,31 @@ import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/model/entities/session.dart'; Future> getStudentFaculties(Session session) async { - final registerFaculties = []; + final response = await NetworkRouter.getWithCookies( + 'https://sigarra.up.pt/up/pt/vld_entidades_geral.entidade_pagina', + {'pct_codigo': session.username}, + session, + ); - final query = {'pct_codigo': session.username}; - const baseUrl = - 'https://sigarra.up.pt/up/pt/vld_entidades_geral.entidade_pagina'; + final document = parse(response.body); - final pctRequest = - await NetworkRouter.getWithCookies(baseUrl, query, session); + final facultiesList = + document.querySelectorAll('#conteudoinner>ul a').map((e) => e.text); - final pctDocument = parse(pctRequest.body); - - final list = - pctDocument.querySelectorAll('#conteudoinner>ul a').map((e) => e.text); - - //user is enrolled in only one faculty - if (list.isEmpty) { - final singleFaculty = pctDocument.querySelector('a')!.attributes['href']; - final regex2 = RegExp(r'.*\/([a-z]+)\/.*'); - final faculty = regex2.firstMatch(singleFaculty!)?.group(1)?.toUpperCase(); - registerFaculties.add(faculty!); - } else { - final regex = RegExp(r'.*\(([A-Z]+)\)'); - for (final element in list) { - registerFaculties.add(regex.firstMatch(element)!.group(1)!); - } + if (facultiesList.isEmpty) { + // The user is enrolled in a single faculty, + // and the selection page is skipped. + // We can extract the faculty from any anchor. + final singleFaculty = document.querySelector('a')!.attributes['href']; + final regex = RegExp(r'.*\/([a-z]+)\/.*'); + final faculty = regex.firstMatch(singleFaculty!)?.group(1)?.toUpperCase(); + return [faculty!.toLowerCase()]; } - return registerFaculties; + + // We extract the faculties from the list. + // An example list is (201906166 (FEUP), 201906166 (FCUP)). + final regex = RegExp(r'.*\(([A-Z]+)\)'); + return facultiesList + .map((e) => regex.firstMatch(e)!.group(1)!.toLowerCase()) + .toList(); } diff --git a/uni/lib/generated/intl/messages_all.dart b/uni/lib/generated/intl/messages_all.dart index 6b3ebeae5..b77f94db2 100644 --- a/uni/lib/generated/intl/messages_all.dart +++ b/uni/lib/generated/intl/messages_all.dart @@ -11,6 +11,7 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; import 'package:intl/src/intl_helpers.dart'; @@ -20,8 +21,8 @@ import 'messages_pt_PT.dart' as messages_pt_pt; typedef Future LibraryLoader(); Map _deferredLibraries = { - 'en': () => new Future.value(null), - 'pt_PT': () => new Future.value(null), + 'en': () => new SynchronousFuture(null), + 'pt_PT': () => new SynchronousFuture(null), }; MessageLookupByLibrary? _findExact(String localeName) { @@ -36,18 +37,18 @@ MessageLookupByLibrary? _findExact(String localeName) { } /// User programs should call this before using [localeName] for messages. -Future initializeMessages(String localeName) async { +Future initializeMessages(String localeName) { var availableLocale = Intl.verifiedLocale( localeName, (locale) => _deferredLibraries[locale] != null, onFailure: (_) => null); if (availableLocale == null) { - return new Future.value(false); + return new SynchronousFuture(false); } var lib = _deferredLibraries[availableLocale]; - await (lib == null ? new Future.value(false) : lib()); + lib == null ? new SynchronousFuture(false) : lib(); initializeInternalMessageLookup(() => new CompositeMessageLookup()); messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); - return new Future.value(true); + return new SynchronousFuture(true); } bool _messagesExistFor(String locale) { diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index c7aef77de..0c9633353 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -7,7 +7,8 @@ // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; @@ -19,12 +20,12 @@ typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'en'; - static m0(time) => "last refresh at ${time}"; + static String m0(time) => "last refresh at ${time}"; - static m1(time) => + static String m1(time) => "${Intl.plural(time, zero: 'Refreshed ${time} minutes ago', one: 'Refreshed ${time} minute ago', other: 'Refreshed ${time} minutes ago')}"; - static m2(title) => "${Intl.select(title, { + static String m2(title) => "${Intl.select(title, { 'horario': 'Schedule', 'exames': 'Exams', 'area': 'Personal Area', @@ -41,7 +42,7 @@ class MessageLookup extends MessageLookupByLibrary { })}"; final messages = _notInlinedMessages(_notInlinedMessages); - static _notInlinedMessages(_) => { + static Map _notInlinedMessages(_) => { "about": MessageLookupByLibrary.simpleMessage("About us"), "academic_services": MessageLookupByLibrary.simpleMessage("Academic services"), @@ -277,7 +278,7 @@ class MessageLookup extends MessageLookupByLibrary { "terms": MessageLookupByLibrary.simpleMessage("Terms and Conditions"), "theme": MessageLookupByLibrary.simpleMessage("Theme"), "title": MessageLookupByLibrary.simpleMessage("Title"), - "uc_info": MessageLookupByLibrary.simpleMessage("Open UC page in app"), + "uc_info": MessageLookupByLibrary.simpleMessage("Open UC page"), "unavailable": MessageLookupByLibrary.simpleMessage("Unavailable"), "valid_email": MessageLookupByLibrary.simpleMessage("Please enter a valid email"), diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index b57f5d6dd..07e64dbed 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -7,7 +7,8 @@ // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names +// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes +// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; @@ -19,12 +20,12 @@ typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'pt_PT'; - static m0(time) => "última atualização às ${time}"; + static String m0(time) => "última atualização às ${time}"; - static m1(time) => + static String m1(time) => "${Intl.plural(time, zero: 'Atualizado há ${time} minutos', one: 'Atualizado há ${time} minuto', other: 'Atualizado há ${time} minutos')}"; - static m2(title) => "${Intl.select(title, { + static String m2(title) => "${Intl.select(title, { 'horario': 'Horário', 'exames': 'Exames', 'area': 'Área Pessoal', @@ -41,7 +42,7 @@ class MessageLookup extends MessageLookupByLibrary { })}"; final messages = _notInlinedMessages(_notInlinedMessages); - static _notInlinedMessages(_) => { + static Map _notInlinedMessages(_) => { "about": MessageLookupByLibrary.simpleMessage("Sobre nós"), "academic_services": MessageLookupByLibrary.simpleMessage("Serviços académicos"), @@ -279,8 +280,7 @@ class MessageLookup extends MessageLookupByLibrary { "terms": MessageLookupByLibrary.simpleMessage("Termos e Condições"), "theme": MessageLookupByLibrary.simpleMessage("Tema"), "title": MessageLookupByLibrary.simpleMessage("Título"), - "uc_info": - MessageLookupByLibrary.simpleMessage("Abrir página da UC na app"), + "uc_info": MessageLookupByLibrary.simpleMessage("Abrir página da UC"), "unavailable": MessageLookupByLibrary.simpleMessage("Indisponível"), "valid_email": MessageLookupByLibrary.simpleMessage( "Por favor insere um email válido"), diff --git a/uni/lib/generated/l10n.dart b/uni/lib/generated/l10n.dart index fbc8cc307..16ea5a058 100644 --- a/uni/lib/generated/l10n.dart +++ b/uni/lib/generated/l10n.dart @@ -10,7 +10,7 @@ import 'intl/messages_all.dart'; // ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars // ignore_for_file: join_return_with_assignment, prefer_final_in_for_each -// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes class S { S(); @@ -1518,10 +1518,10 @@ class S { ); } - /// `Open UC page in app` + /// `Open UC page` String get uc_info { return Intl.message( - 'Open UC page in app', + 'Open UC page', name: 'uc_info', desc: '', args: [], diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 98b792ff1..543e2e90e 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a url: "https://pub.dev" source: hosted - version: "64.0.0" + version: "61.0.0" add_2_calendar: dependency: "direct main" description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "5.13.0" animated_stack_widget: dependency: transitive description: @@ -221,10 +221,10 @@ packages: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.2" connectivity_plus: dependency: "direct main" description: @@ -253,10 +253,10 @@ packages: dependency: transitive description: name: coverage - sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" + sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" url: "https://pub.dev" source: hosted - version: "1.7.2" + version: "1.6.4" crypto: dependency: "direct main" description: @@ -285,18 +285,18 @@ packages: dependency: "direct main" description: name: currency_text_input_formatter - sha256: b60c298fec9f0e96dfad88d25d026a6bf43f4e2bb9c59218afd8de1e09f54a60 + sha256: "5d8db755ccde76817f6f5fb33e65304d7b1db442e1ccff079fe235584c6b7d5a" url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.1.10" dart_style: dependency: transitive description: name: dart_style - sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.2" dbus: dependency: transitive description: @@ -447,10 +447,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "5b24061317f850af858ef7151dadbb6eb77c1c449c954c7bb064e8a5e0e7d81f" + sha256: "21b085a1c185e46701373866144ced56cfb7a0c33f63c916bb8fe2d0c1491278" url: "https://pub.dev" source: hosted - version: "0.6.20" + version: "0.6.19" flutter_svg: dependency: "direct main" description: @@ -513,10 +513,10 @@ packages: dependency: "direct main" description: name: http - sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -641,10 +641,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.9.1" mgrs_dart: dependency: transitive description: @@ -657,10 +657,10 @@ packages: dependency: transitive description: name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "1.0.4" mockito: dependency: "direct dev" description: @@ -722,10 +722,10 @@ packages: dependency: transitive description: name: package_info_plus - sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" + sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "4.2.0" package_info_plus_platform_interface: dependency: transitive description: @@ -810,10 +810,10 @@ packages: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "5.4.0" platform: dependency: transitive description: @@ -962,10 +962,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.1" shared_preferences_windows: dependency: transitive description: @@ -1079,34 +1079,34 @@ packages: dependency: "direct dev" description: name: sqflite_common_ffi - sha256: "754927d82de369a6b9e760fb60640aa81da650f35ffd468d5a992814d6022908" + sha256: "35d2fce1e971707c227cc4775cc017d5eafe06c2654c3435ebd5c3ad6c170f5f" url: "https://pub.dev" source: hosted - version: "2.3.2+1" + version: "2.3.0+4" sqlite3: dependency: transitive description: name: sqlite3 - sha256: "072128763f1547e3e9b4735ce846bfd226d68019ccda54db4cd427b12dfdedc9" + sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.1.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.11.0" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" stream_transform: dependency: transitive description: @@ -1143,26 +1143,26 @@ packages: dependency: "direct dev" description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.24.3" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.0" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.5.3" timelines: dependency: "direct main" description: @@ -1231,10 +1231,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" + sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c url: "https://pub.dev" source: hosted - version: "6.2.5" + version: "6.2.4" url_launcher_android: dependency: transitive description: @@ -1279,10 +1279,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b + sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.2.0" url_launcher_windows: dependency: transitive description: @@ -1295,10 +1295,10 @@ packages: dependency: transitive description: name: uuid - sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 + sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f" url: "https://pub.dev" source: hosted - version: "4.3.3" + version: "4.2.2" vector_graphics: dependency: transitive description: @@ -1343,10 +1343,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "11.10.0" watcher: dependency: transitive description: @@ -1359,10 +1359,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -1383,10 +1383,10 @@ packages: dependency: transitive description: name: win32 - sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" + sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 url: "https://pub.dev" source: hosted - version: "5.2.0" + version: "5.1.1" wkt_parser: dependency: transitive description: @@ -1415,10 +1415,10 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.3.0" yaml: dependency: transitive description: @@ -1428,5 +1428,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" + dart: ">=3.1.3 <4.0.0" + flutter: ">=3.13.7" From b32263ea4cc4f22f9ac066b828f38ec89ead5375 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Mon, 4 Mar 2024 12:15:50 +0000 Subject: [PATCH 331/334] Fix faculties circular dependency; remove selection UI; print on profile --- .../controller/networking/network_router.dart | 13 +- uni/lib/generated/intl/messages_all.dart | 13 +- uni/lib/generated/intl/messages_en.dart | 13 +- uni/lib/generated/intl/messages_pt_PT.dart | 14 +-- uni/lib/model/entities/session.dart | 11 +- .../providers/startup/session_provider.dart | 21 +++- uni/lib/view/login/login.dart | 14 --- .../login/widgets/faculties_multiselect.dart | 78 ------------ .../widgets/faculties_selection_form.dart | 92 -------------- uni/lib/view/login/widgets/inputs.dart | 10 -- .../profile/widgets/profile_overview.dart | 9 ++ uni/pubspec.lock | 112 +++++++++--------- 12 files changed, 115 insertions(+), 285 deletions(-) delete mode 100644 uni/lib/view/login/widgets/faculties_multiselect.dart delete mode 100644 uni/lib/view/login/widgets/faculties_selection_form.dart diff --git a/uni/lib/controller/networking/network_router.dart b/uni/lib/controller/networking/network_router.dart index c284258df..3ff18efb9 100644 --- a/uni/lib/controller/networking/network_router.dart +++ b/uni/lib/controller/networking/network_router.dart @@ -36,20 +36,22 @@ class NetworkRouter { static Session? _cachedSession; /// Performs a login using the Sigarra API, - /// returning an authenticated [Session] on the given [faculties] with the + /// returning an authenticated [Session] with the /// given username [username] and password [password] if successful. static Future login( String username, String password, List faculties, { required bool persistentSession, + bool ignoreCached = false, }) async { return _loginLock.synchronized( () async { if (_lastLoginTime != null && DateTime.now().difference(_lastLoginTime!) < const Duration(minutes: 1) && - _cachedSession != null) { + _cachedSession != null && + !ignoreCached) { Logger().d('Login request ignored due to recent login'); return _cachedSession; } @@ -72,9 +74,14 @@ class NetworkRouter { persistentSession: persistentSession, ); + if (session == null) { + Logger().e('Login failed: user not authenticated'); + return null; + } + Logger().i('Login successful'); _lastLoginTime = DateTime.now(); - _cachedSession = await session; + _cachedSession = session; return session; }, diff --git a/uni/lib/generated/intl/messages_all.dart b/uni/lib/generated/intl/messages_all.dart index b77f94db2..6b3ebeae5 100644 --- a/uni/lib/generated/intl/messages_all.dart +++ b/uni/lib/generated/intl/messages_all.dart @@ -11,7 +11,6 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; import 'package:intl/src/intl_helpers.dart'; @@ -21,8 +20,8 @@ import 'messages_pt_PT.dart' as messages_pt_pt; typedef Future LibraryLoader(); Map _deferredLibraries = { - 'en': () => new SynchronousFuture(null), - 'pt_PT': () => new SynchronousFuture(null), + 'en': () => new Future.value(null), + 'pt_PT': () => new Future.value(null), }; MessageLookupByLibrary? _findExact(String localeName) { @@ -37,18 +36,18 @@ MessageLookupByLibrary? _findExact(String localeName) { } /// User programs should call this before using [localeName] for messages. -Future initializeMessages(String localeName) { +Future initializeMessages(String localeName) async { var availableLocale = Intl.verifiedLocale( localeName, (locale) => _deferredLibraries[locale] != null, onFailure: (_) => null); if (availableLocale == null) { - return new SynchronousFuture(false); + return new Future.value(false); } var lib = _deferredLibraries[availableLocale]; - lib == null ? new SynchronousFuture(false) : lib(); + await (lib == null ? new Future.value(false) : lib()); initializeInternalMessageLookup(() => new CompositeMessageLookup()); messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); - return new SynchronousFuture(true); + return new Future.value(true); } bool _messagesExistFor(String locale) { diff --git a/uni/lib/generated/intl/messages_en.dart b/uni/lib/generated/intl/messages_en.dart index 0c9633353..c7aef77de 100644 --- a/uni/lib/generated/intl/messages_en.dart +++ b/uni/lib/generated/intl/messages_en.dart @@ -7,8 +7,7 @@ // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes +// ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; @@ -20,12 +19,12 @@ typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'en'; - static String m0(time) => "last refresh at ${time}"; + static m0(time) => "last refresh at ${time}"; - static String m1(time) => + static m1(time) => "${Intl.plural(time, zero: 'Refreshed ${time} minutes ago', one: 'Refreshed ${time} minute ago', other: 'Refreshed ${time} minutes ago')}"; - static String m2(title) => "${Intl.select(title, { + static m2(title) => "${Intl.select(title, { 'horario': 'Schedule', 'exames': 'Exams', 'area': 'Personal Area', @@ -42,7 +41,7 @@ class MessageLookup extends MessageLookupByLibrary { })}"; final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { + static _notInlinedMessages(_) => { "about": MessageLookupByLibrary.simpleMessage("About us"), "academic_services": MessageLookupByLibrary.simpleMessage("Academic services"), @@ -278,7 +277,7 @@ class MessageLookup extends MessageLookupByLibrary { "terms": MessageLookupByLibrary.simpleMessage("Terms and Conditions"), "theme": MessageLookupByLibrary.simpleMessage("Theme"), "title": MessageLookupByLibrary.simpleMessage("Title"), - "uc_info": MessageLookupByLibrary.simpleMessage("Open UC page"), + "uc_info": MessageLookupByLibrary.simpleMessage("Open UC page in app"), "unavailable": MessageLookupByLibrary.simpleMessage("Unavailable"), "valid_email": MessageLookupByLibrary.simpleMessage("Please enter a valid email"), diff --git a/uni/lib/generated/intl/messages_pt_PT.dart b/uni/lib/generated/intl/messages_pt_PT.dart index 07e64dbed..b57f5d6dd 100644 --- a/uni/lib/generated/intl/messages_pt_PT.dart +++ b/uni/lib/generated/intl/messages_pt_PT.dart @@ -7,8 +7,7 @@ // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases -// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes -// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes +// ignore_for_file:unused_import, file_names import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; @@ -20,12 +19,12 @@ typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'pt_PT'; - static String m0(time) => "última atualização às ${time}"; + static m0(time) => "última atualização às ${time}"; - static String m1(time) => + static m1(time) => "${Intl.plural(time, zero: 'Atualizado há ${time} minutos', one: 'Atualizado há ${time} minuto', other: 'Atualizado há ${time} minutos')}"; - static String m2(title) => "${Intl.select(title, { + static m2(title) => "${Intl.select(title, { 'horario': 'Horário', 'exames': 'Exames', 'area': 'Área Pessoal', @@ -42,7 +41,7 @@ class MessageLookup extends MessageLookupByLibrary { })}"; final messages = _notInlinedMessages(_notInlinedMessages); - static Map _notInlinedMessages(_) => { + static _notInlinedMessages(_) => { "about": MessageLookupByLibrary.simpleMessage("Sobre nós"), "academic_services": MessageLookupByLibrary.simpleMessage("Serviços académicos"), @@ -280,7 +279,8 @@ class MessageLookup extends MessageLookupByLibrary { "terms": MessageLookupByLibrary.simpleMessage("Termos e Condições"), "theme": MessageLookupByLibrary.simpleMessage("Tema"), "title": MessageLookupByLibrary.simpleMessage("Título"), - "uc_info": MessageLookupByLibrary.simpleMessage("Abrir página da UC"), + "uc_info": + MessageLookupByLibrary.simpleMessage("Abrir página da UC na app"), "unavailable": MessageLookupByLibrary.simpleMessage("Indisponível"), "valid_email": MessageLookupByLibrary.simpleMessage( "Por favor insere um email válido"), diff --git a/uni/lib/model/entities/session.dart b/uni/lib/model/entities/session.dart index 65b929566..636c65a8b 100644 --- a/uni/lib/model/entities/session.dart +++ b/uni/lib/model/entities/session.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:http/http.dart' as http; -import 'package:uni/controller/fetchers/faculties_fetcher.dart'; import 'package:uni/controller/networking/network_router.dart'; /// Stores information about a user session. @@ -20,25 +19,21 @@ class Session { /// Creates a new Session instance from an HTTP response. /// Returns null if the authentication failed. - static Future fromLogin( + static Session? fromLogin( http.Response response, List faculties, { required bool persistentSession, - }) async { + }) { final responseBody = json.decode(response.body) as Map; if (!(responseBody['authenticated'] as bool)) { return null; } - final session = Session( + return Session( faculties: faculties, username: responseBody['codigo'] as String, cookies: NetworkRouter.extractCookies(response.headers), ); - - session.faculties = await getStudentFaculties(session); - - return session; } } diff --git a/uni/lib/model/providers/startup/session_provider.dart b/uni/lib/model/providers/startup/session_provider.dart index e5cce690b..037392a37 100644 --- a/uni/lib/model/providers/startup/session_provider.dart +++ b/uni/lib/model/providers/startup/session_provider.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:uni/controller/background_workers/notifications.dart'; +import 'package:uni/controller/fetchers/faculties_fetcher.dart'; import 'package:uni/controller/fetchers/terms_and_conditions_fetcher.dart'; import 'package:uni/controller/local_storage/preferences_controller.dart'; import 'package:uni/controller/networking/network_router.dart'; @@ -47,27 +48,41 @@ class SessionProvider extends StateProviderNotifier { Future postAuthentication( BuildContext context, String username, - String password, - List faculties, { + String password, { required bool persistentSession, }) async { final locale = Provider.of(context, listen: false).getLocale(); Session? session; + List faculties; + try { + // We need to login to fetch the faculties, so perform a temporary login. + final tempSession = await NetworkRouter.login( + username, + password, + ['feup'], + persistentSession: false, + ignoreCached: true, + ); + faculties = await getStudentFaculties(tempSession!); + + // Now get the session with the correct faculties. session = await NetworkRouter.login( username, password, faculties, persistentSession: persistentSession, + ignoreCached: true, ); } catch (e) { throw InternetStatusException(locale); } if (session == null) { + // Get the fail reason. final responseHtml = - await NetworkRouter.loginInSigarra(username, password, faculties); + await NetworkRouter.loginInSigarra(username, password, ['feup']); if (isPasswordExpired(responseHtml) && context.mounted) { throw ExpiredCredentialsException(); diff --git a/uni/lib/view/login/login.dart b/uni/lib/view/login/login.dart index c1451e9f5..39f44c081 100644 --- a/uni/lib/view/login/login.dart +++ b/uni/lib/view/login/login.dart @@ -25,10 +25,6 @@ class LoginPageView extends StatefulWidget { /// Manages the 'login section' view. class LoginPageViewState extends State { - List faculties = [ - 'feup', - ]; // May choose more than one faculty in the dropdown. - static final FocusNode usernameFocus = FocusNode(); static final FocusNode passwordFocus = FocusNode(); @@ -58,7 +54,6 @@ class LoginPageViewState extends State { context, user, pass, - faculties, persistentSession: _keepSignedIn, ); if (context.mounted) { @@ -95,14 +90,6 @@ class LoginPageViewState extends State { } } - /// Updates the list of faculties - /// based on the options the user selected (used as a callback) - void setFaculties(List faculties) { - setState(() { - this.faculties = faculties; - }); - } - /// Tracks if the user wants to keep signed in (has a /// checkmark on the button). void _setKeepSignedIn({bool? value}) { @@ -215,7 +202,6 @@ class LoginPageViewState extends State { child: SingleChildScrollView( child: Column( children: [ - createFacultyInput(context, faculties, setFaculties), Padding( padding: EdgeInsets.only(bottom: queryData.size.height / 35), ), diff --git a/uni/lib/view/login/widgets/faculties_multiselect.dart b/uni/lib/view/login/widgets/faculties_multiselect.dart deleted file mode 100644 index 4188d558f..000000000 --- a/uni/lib/view/login/widgets/faculties_multiselect.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/view/login/widgets/faculties_selection_form.dart'; - -class FacultiesMultiselect extends StatelessWidget { - const FacultiesMultiselect( - this.selectedFaculties, - this.setFaculties, { - super.key, - }); - final List selectedFaculties; - final void Function(List) setFaculties; - - @override - Widget build(BuildContext context) { - const textColor = Color.fromARGB(255, 0xfa, 0xfa, 0xfa); - - return TextButton( - style: TextButton.styleFrom( - textStyle: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.w300, - color: textColor, - ), - ), - onPressed: () { - showDialog( - context: context, - builder: (BuildContext context) { - return FacultiesSelectionForm( - List.from(selectedFaculties), - setFaculties, - ); - }, - ); - }, - child: _createButtonContent(context), - ); - } - - Widget _createButtonContent(BuildContext context) { - return Container( - padding: const EdgeInsets.fromLTRB(5, 0, 5, 7), - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide( - color: Colors.white, - ), - ), - ), - child: Row( - children: [ - Expanded( - child: Text( - _facultiesListText(context), - style: const TextStyle(color: Colors.white), - ), - ), - const Icon( - Icons.arrow_drop_down, - color: Colors.white, - ), - ], - ), - ); - } - - String _facultiesListText(BuildContext context) { - if (selectedFaculties.isEmpty) { - return S.of(context).no_college; - } - final buffer = StringBuffer(); - for (final faculty in selectedFaculties) { - buffer.write('${faculty.toUpperCase()}, '); - } - return buffer.toString().substring(0, buffer.length - 2); - } -} diff --git a/uni/lib/view/login/widgets/faculties_selection_form.dart b/uni/lib/view/login/widgets/faculties_selection_form.dart deleted file mode 100644 index 58602cca7..000000000 --- a/uni/lib/view/login/widgets/faculties_selection_form.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/constants.dart' as constants; -import 'package:uni/view/common_widgets/toast_message.dart'; - -class FacultiesSelectionForm extends StatefulWidget { - const FacultiesSelectionForm( - this.selectedFaculties, - this.setFaculties, { - super.key, - }); - final List selectedFaculties; - final void Function(List) setFaculties; - - @override - State createState() => _FacultiesSelectionFormState(); -} - -class _FacultiesSelectionFormState extends State { - @override - Widget build(BuildContext context) { - return AlertDialog( - backgroundColor: const Color.fromARGB(255, 0x75, 0x17, 0x1e), - title: Text(S.of(context).college_select), - titleTextStyle: const TextStyle( - color: Color.fromARGB(255, 0xfa, 0xfa, 0xfa), - fontSize: 18, - ), - content: SizedBox( - height: 500, - width: 200, - child: createCheckList(context), - ), - actions: createActionButtons(context), - ); - } - - List createActionButtons(BuildContext context) { - return [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text( - S.of(context).cancel, - style: const TextStyle(color: Colors.white), - ), - ), - ElevatedButton( - style: ElevatedButton.styleFrom( - foregroundColor: Theme.of(context).primaryColor, - backgroundColor: Colors.white, - ), - onPressed: () { - if (widget.selectedFaculties.isEmpty) { - ToastMessage.warning( - context, - S.of(context).at_least_one_college, - ); - return; - } - Navigator.pop(context); - widget.setFaculties(widget.selectedFaculties); - }, - child: Text(S.of(context).confirm), - ), - ]; - } - - Widget createCheckList(BuildContext context) { - return ListView( - children: List.generate(constants.faculties.length, (i) { - final faculty = constants.faculties.elementAt(i); - return CheckboxListTile( - title: Text( - faculty.toUpperCase(), - style: const TextStyle(color: Colors.white, fontSize: 20), - ), - key: Key('FacultyCheck$faculty'), - value: widget.selectedFaculties.contains(faculty), - onChanged: (value) { - setState(() { - if (value != null && value) { - widget.selectedFaculties.add(faculty); - } else { - widget.selectedFaculties.remove(faculty); - } - }); - }, - ); - }), - ); - } -} diff --git a/uni/lib/view/login/widgets/inputs.dart b/uni/lib/view/login/widgets/inputs.dart index b926e1831..2f3d2b1f6 100644 --- a/uni/lib/view/login/widgets/inputs.dart +++ b/uni/lib/view/login/widgets/inputs.dart @@ -1,16 +1,6 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/view/about/widgets/terms_and_conditions.dart'; -import 'package:uni/view/login/widgets/faculties_multiselect.dart'; - -/// Creates the widget for the user to choose their faculty -Widget createFacultyInput( - BuildContext context, - List faculties, - void Function(List) setFaculties, -) { - return FacultiesMultiselect(faculties, setFaculties); -} /// Creates the widget for the username input. Widget createUsernameInput( diff --git a/uni/lib/view/profile/widgets/profile_overview.dart b/uni/lib/view/profile/widgets/profile_overview.dart index 5cd1ec3b5..51a6703cf 100644 --- a/uni/lib/view/profile/widgets/profile_overview.dart +++ b/uni/lib/view/profile/widgets/profile_overview.dart @@ -45,6 +45,15 @@ class ProfileOverview extends StatelessWidget { fontWeight: FontWeight.w300, ), ), + const Padding(padding: EdgeInsets.all(5)), + Text( + session.faculties.map((e) => e.toUpperCase()).toList().join(', '), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w300, + ), + ), ], ), ); diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 543e2e90e..98b792ff1 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "61.0.0" + version: "64.0.0" add_2_calendar: dependency: "direct main" description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: analyzer - sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "5.13.0" + version: "6.2.0" animated_stack_widget: dependency: transitive description: @@ -221,10 +221,10 @@ packages: dependency: "direct main" description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: @@ -253,10 +253,10 @@ packages: dependency: transitive description: name: coverage - sha256: "595a29b55ce82d53398e1bcc2cba525d7bd7c59faeb2d2540e9d42c390cfeeeb" + sha256: "8acabb8306b57a409bf4c83522065672ee13179297a6bb0cb9ead73948df7c76" url: "https://pub.dev" source: hosted - version: "1.6.4" + version: "1.7.2" crypto: dependency: "direct main" description: @@ -285,18 +285,18 @@ packages: dependency: "direct main" description: name: currency_text_input_formatter - sha256: "5d8db755ccde76817f6f5fb33e65304d7b1db442e1ccff079fe235584c6b7d5a" + sha256: b60c298fec9f0e96dfad88d25d026a6bf43f4e2bb9c59218afd8de1e09f54a60 url: "https://pub.dev" source: hosted - version: "2.1.10" + version: "2.1.11" dart_style: dependency: transitive description: name: dart_style - sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.3.4" dbus: dependency: transitive description: @@ -447,10 +447,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "21b085a1c185e46701373866144ced56cfb7a0c33f63c916bb8fe2d0c1491278" + sha256: "5b24061317f850af858ef7151dadbb6eb77c1c449c954c7bb064e8a5e0e7d81f" url: "https://pub.dev" source: hosted - version: "0.6.19" + version: "0.6.20" flutter_svg: dependency: "direct main" description: @@ -513,10 +513,10 @@ packages: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: a2bbf9d017fcced29139daa8ed2bba4ece450ab222871df93ca9eec6f80c34ba url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" http_multi_server: dependency: transitive description: @@ -641,10 +641,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" mgrs_dart: dependency: transitive description: @@ -657,10 +657,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" mockito: dependency: "direct dev" description: @@ -722,10 +722,10 @@ packages: dependency: transitive description: name: package_info_plus - sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017" + sha256: "88bc797f44a94814f2213db1c9bd5badebafdfb8290ca9f78d4b9ee2a3db4d79" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "5.0.1" package_info_plus_platform_interface: dependency: transitive description: @@ -810,10 +810,10 @@ packages: dependency: transitive description: name: petitparser - sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 url: "https://pub.dev" source: hosted - version: "5.4.0" + version: "6.0.2" platform: dependency: transitive description: @@ -962,10 +962,10 @@ packages: dependency: transitive description: name: shared_preferences_web - sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_windows: dependency: transitive description: @@ -1079,34 +1079,34 @@ packages: dependency: "direct dev" description: name: sqflite_common_ffi - sha256: "35d2fce1e971707c227cc4775cc017d5eafe06c2654c3435ebd5c3ad6c170f5f" + sha256: "754927d82de369a6b9e760fb60640aa81da650f35ffd468d5a992814d6022908" url: "https://pub.dev" source: hosted - version: "2.3.0+4" + version: "2.3.2+1" sqlite3: dependency: transitive description: name: sqlite3 - sha256: db65233e6b99e99b2548932f55a987961bc06d82a31a0665451fa0b4fff4c3fb + sha256: "072128763f1547e3e9b4735ce846bfd226d68019ccda54db4cd427b12dfdedc9" url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.4.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -1143,26 +1143,26 @@ packages: dependency: "direct dev" description: name: test - sha256: "13b41f318e2a5751c3169137103b60c584297353d4b1761b66029bae6411fe46" + sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f url: "https://pub.dev" source: hosted - version: "1.24.3" + version: "1.24.9" test_api: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" test_core: dependency: transitive description: name: test_core - sha256: "99806e9e6d95c7b059b7a0fc08f07fc53fabe54a829497f0d9676299f1e8637e" + sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a url: "https://pub.dev" source: hosted - version: "0.5.3" + version: "0.5.9" timelines: dependency: "direct main" description: @@ -1231,10 +1231,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: c512655380d241a337521703af62d2c122bf7b77a46ff7dd750092aa9433499c + sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" url: "https://pub.dev" source: hosted - version: "6.2.4" + version: "6.2.5" url_launcher_android: dependency: transitive description: @@ -1279,10 +1279,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2" + sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b url: "https://pub.dev" source: hosted - version: "2.2.0" + version: "2.2.3" url_launcher_windows: dependency: transitive description: @@ -1295,10 +1295,10 @@ packages: dependency: transitive description: name: uuid - sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f" + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 url: "https://pub.dev" source: hosted - version: "4.2.2" + version: "4.3.3" vector_graphics: dependency: transitive description: @@ -1343,10 +1343,10 @@ packages: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "13.0.0" watcher: dependency: transitive description: @@ -1359,10 +1359,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1383,10 +1383,10 @@ packages: dependency: transitive description: name: win32 - sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574 + sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.2.0" wkt_parser: dependency: transitive description: @@ -1415,10 +1415,10 @@ packages: dependency: transitive description: name: xml - sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.5.0" yaml: dependency: transitive description: @@ -1428,5 +1428,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.1.3 <4.0.0" - flutter: ">=3.13.7" + dart: ">=3.2.0 <4.0.0" + flutter: ">=3.16.0" From df9cc4d07f97f8c574250480d7982582b2c59d0d Mon Sep 17 00:00:00 2001 From: rubuy-74 Date: Mon, 4 Mar 2024 14:08:22 +0000 Subject: [PATCH 332/334] Resolve test problems --- uni/test/mocks/integration/src/schedule_page_test.mocks.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart index 638e98c48..65e5988a5 100644 --- a/uni/test/mocks/integration/src/schedule_page_test.mocks.dart +++ b/uni/test/mocks/integration/src/schedule_page_test.mocks.dart @@ -529,8 +529,7 @@ class MockSessionProvider extends _i1.Mock implements _i8.SessionProvider { _i4.Future postAuthentication( _i11.BuildContext? context, String? username, - String? password, - List? faculties, { + String? password, { required bool? persistentSession, }) => (super.noSuchMethod( @@ -540,7 +539,6 @@ class MockSessionProvider extends _i1.Mock implements _i8.SessionProvider { context, username, password, - faculties, ], {#persistentSession: persistentSession}, ), From fcdabb1ced50adfff98c81385e79616d7010c668 Mon Sep 17 00:00:00 2001 From: Rubem Neto <88210776+rubuy-74@users.noreply.github.com> Date: Mon, 4 Mar 2024 14:22:54 +0000 Subject: [PATCH 333/334] Rewrite parsing method via review comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: André Lima --- uni/lib/controller/fetchers/faculties_fetcher.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uni/lib/controller/fetchers/faculties_fetcher.dart b/uni/lib/controller/fetchers/faculties_fetcher.dart index 754cefefd..f27c896bb 100644 --- a/uni/lib/controller/fetchers/faculties_fetcher.dart +++ b/uni/lib/controller/fetchers/faculties_fetcher.dart @@ -18,10 +18,10 @@ Future> getStudentFaculties(Session session) async { // The user is enrolled in a single faculty, // and the selection page is skipped. // We can extract the faculty from any anchor. - final singleFaculty = document.querySelector('a')!.attributes['href']; - final regex = RegExp(r'.*\/([a-z]+)\/.*'); - final faculty = regex.firstMatch(singleFaculty!)?.group(1)?.toUpperCase(); - return [faculty!.toLowerCase()]; + final singleFaculty = document.querySelector('a')!.attributes['href']!; + final uri = Uri.parse(singleFaculty); + final faculty = uri.pathSegments[0]; + return [faculty.toLowerCase()]; } // We extract the faculties from the list. From ff3e731c5f68f71befcada1db65e2fc45749a2be Mon Sep 17 00:00:00 2001 From: limwa Date: Mon, 4 Mar 2024 14:47:42 +0000 Subject: [PATCH 334/334] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 1fc0d9ab7..1e25189a6 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.8.0-beta.18+251 +1.8.0-beta.19+252 diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 92598751d..664f629a7 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: "none" # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.8.0-beta.18+251 +version: 1.8.0-beta.19+252 environment: