From 2c2e1f96604f7219a2793e881ee2cd22848f72fc Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 21 Feb 2024 23:33:59 +0000 Subject: [PATCH 01/17] First setup and initial tries --- .../course_units_info_fetcher.dart | 18 +++- .../parsers/parser_course_unit_info.dart | 10 +++ .../model/entities/course_units/sheet.dart | 4 + .../lazy/course_units_info_provider.dart | 10 +-- .../widgets/course_unit_sheet.dart | 88 +++---------------- 5 files changed, 48 insertions(+), 82 deletions(-) create mode 100644 uni/lib/model/entities/course_units/sheet.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 1c54029e3..2ed0abba8 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 @@ -6,6 +6,7 @@ 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_sheet.dart'; import 'package:uni/model/entities/session.dart'; +import 'package:uni/model/entities/course_units/sheet.dart'; class CourseUnitsInfoFetcher implements SessionDependantFetcher { @override @@ -27,6 +28,19 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { return parseCourseUnitSheet(response); } + Future fetchSheet( + Session session, + int occurId, + ) async { + final url = '${getEndpoints(session)[0]}mob_ucurr_geral.perfil'; + final response = await NetworkRouter.getWithCookies( + url, + {'pv_ocorrencia_id': occurId.toString()}, + session, + ); + return parseSheet(response); + } + Future> fetchCourseUnitFiles( Session session, int occurId, @@ -34,9 +48,7 @@ class CourseUnitsInfoFetcher implements SessionDependantFetcher { final url = '${getEndpoints(session)[0]}mob_ucurr_geral.conteudos'; final response = await NetworkRouter.getWithCookies( url, - { - 'pv_ocorrencia_id': occurId.toString(), - }, + {'pv_ocorrencia_id': occurId.toString()}, session, ); return parseFiles(response, session); diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index ad8751b19..c61cb259c 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -4,6 +4,7 @@ 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/sheet.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'; @@ -42,6 +43,15 @@ Future> parseFiles( return dirs; } +Future parseSheet(http.Response response) async { + final json = jsonDecode(response.body) as Map; + final sheet = {}; + for (final item in json.entries) { + sheet[item.key] = item.value; + } + return Sheet(sheet); +} + Future parseCourseUnitSheet(http.Response response) async { final document = parse(response.body); final titles = document.querySelectorAll('#conteudoinner h3'); diff --git a/uni/lib/model/entities/course_units/sheet.dart b/uni/lib/model/entities/course_units/sheet.dart new file mode 100644 index 000000000..cdcd8193e --- /dev/null +++ b/uni/lib/model/entities/course_units/sheet.dart @@ -0,0 +1,4 @@ +class Sheet { + Sheet(this.sections); + Map sections; +} 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..1c3c527d2 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -9,8 +9,9 @@ import 'package:uni/model/entities/course_units/course_unit_sheet.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/entities/course_units/sheet.dart'; -typedef SheetsMap = Map; +typedef SheetsMap = Map; typedef ClassesMap = Map>; typedef FilesMap = Map>; @@ -25,7 +26,7 @@ class CourseUnitsInfoProvider initialState: Tuple3({}, {}, {}), ); - UnmodifiableMapView get courseUnitsSheets => + UnmodifiableMapView get courseUnitsSheets => UnmodifiableMapView(state!.item1); UnmodifiableMapView> @@ -38,9 +39,8 @@ class CourseUnitsInfoProvider CourseUnit courseUnit, Session session, ) async { - state!.item1[courseUnit] = await CourseUnitsInfoFetcher() - .fetchCourseUnitSheet(session, courseUnit.occurrId); - notifyListeners(); + state!.item1[courseUnit] = + await CourseUnitsInfoFetcher().fetchSheet(session, courseUnit.occurrId); } Future fetchCourseUnitClasses( diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index e113055f3..98fae880f 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -4,11 +4,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:html/dom.dart' as dom; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; +import 'package:uni/model/entities/course_units/sheet.dart'; + import 'package:uni/view/course_unit_info/widgets/course_unit_info_card.dart'; class CourseUnitSheetView extends StatelessWidget { const CourseUnitSheetView(this.courseUnitSheet, {super.key}); - final CourseUnitSheet courseUnitSheet; + final Sheet courseUnitSheet; @override Widget build(BuildContext context) { @@ -24,83 +26,21 @@ class CourseUnitSheetView extends StatelessWidget { CourseUnitInfoCard _buildCard( String sectionTitle, - String sectionContent, + dynamic sectionContent, ) { return CourseUnitInfoCard( sectionTitle, - HtmlWidget( - sectionContent, - customWidgetBuilder: (element) { - if (element.className == 'informa' || element.className == 'limpar') { - return Container(); - } - if (element.localName == 'table') { - try { - element = _preprocessTable(element); - final tBody = element.children - .firstWhere((element) => element.localName == 'tbody'); - final rows = tBody.children; - return Padding( - padding: const EdgeInsets.only(bottom: 10), - child: Table( - border: TableBorder.all(), - children: rows - .map( - (e) => TableRow( - children: e.children - .sublist(0, min(4, e.children.length)) - .map( - (e) => TableCell( - child: Padding( - padding: const EdgeInsets.all(8), - child: HtmlWidget( - e.outerHtml, - ), - ), - ), - ) - .toList(), - ), - ) - .toList(), - ), - ); - } catch (e) { - return null; - } - } - return null; - }, + Container( + child: sectionContent is String || sectionContent is int + ? Text(sectionContent.toString()) + : sectionContent is List + ? Column( + children: sectionContent + .map((item) => Text(item.toString())) + .toList(), + ) + : sectionContent as Widget?, ), ); } - - dom.Element _preprocessTable(dom.Element tableElement) { - final processedTable = tableElement.clone(true); - final tBody = tableElement.children - .firstWhere((element) => element.localName == 'tbody'); - final rows = tBody.children; - - for (var i = 0; i < rows.length; i++) { - for (var j = 0; j < rows[i].children.length; j++) { - final cell = rows[i].children[j]; - if (cell.attributes['rowspan'] != null) { - final rowSpan = int.parse(cell.attributes['rowspan']!); - if (rowSpan <= 1) { - continue; - } - processedTable.children[0].children[i].children[j].innerHtml = ''; - for (var k = 1; k < rowSpan; k++) { - try { - processedTable.children[0].children[i + k].children - .insert(j, cell.clone(true)); - } catch (_) { - continue; - } - } - } - } - } - return processedTable; - } } From 94fe851cd476997eef9ec549eedb3eed24d422c6 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sun, 10 Mar 2024 16:54:40 +0000 Subject: [PATCH 02/17] Starting page redesign --- .../common_widgets/generic_expandable.dart | 34 +++ .../widgets/course_unit_info_card.dart | 23 +- .../widgets/course_unit_sheet.dart | 44 ++-- uni/pubspec.lock | 236 +++++++++++++++++- uni/pubspec.yaml | 3 +- 5 files changed, 294 insertions(+), 46 deletions(-) create mode 100644 uni/lib/view/common_widgets/generic_expandable.dart diff --git a/uni/lib/view/common_widgets/generic_expandable.dart b/uni/lib/view/common_widgets/generic_expandable.dart new file mode 100644 index 000000000..f19fb047b --- /dev/null +++ b/uni/lib/view/common_widgets/generic_expandable.dart @@ -0,0 +1,34 @@ +import 'package:expandable/expandable.dart'; +import 'package:flutter/material.dart'; + +abstract class GenericExpandable extends StatelessWidget { + const GenericExpandable( + {super.key, required this.title, required this.content}); + + final String title; + final Widget content; + + @override + Widget build(BuildContext context) { + return ExpandablePanel( + header: Align( + alignment: Alignment.centerLeft, + child: Text(title, style: const TextStyle(fontSize: 20)), + ), + collapsed: ShaderMask( + shaderCallback: (bounds) => const LinearGradient( + colors: [Colors.black, Colors.transparent], + stops: [0.7, 1.0], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ).createShader(bounds), + blendMode: BlendMode.dstIn, + child: LimitedBox( + maxHeight: 100, + child: content, + ), + ), + expanded: content, + ); + } +} diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_info_card.dart b/uni/lib/view/course_unit_info/widgets/course_unit_info_card.dart index b79eb7a06..e5552046e 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_info_card.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_info_card.dart @@ -1,22 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:uni/view/common_widgets/generic_expandable.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; -class CourseUnitInfoCard extends GenericExpansionCard { - const CourseUnitInfoCard(this.sectionTitle, this.content, {super.key}) +class CourseUnitInfoCard extends GenericExpandable { + CourseUnitInfoCard(this.sectionTitle, this.info, {super.key}) : super( - cardMargin: const EdgeInsets.only(bottom: 10), - smallTitle: true, + title: normalizeTitle(sectionTitle), + content: info, ); final String sectionTitle; - final Widget content; - - @override - Widget buildCardContent(BuildContext context) { - return Container(padding: const EdgeInsets.only(top: 10), child: content); - } + final Widget info; +} - @override - String getTitle(BuildContext context) { - return sectionTitle; - } +String normalizeTitle(String sectionTitle) { + return sectionTitle[0].toUpperCase() + sectionTitle.substring(1); } diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 98fae880f..467c86179 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -1,6 +1,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:html/dom.dart' as dom; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; @@ -15,32 +16,33 @@ class CourseUnitSheetView extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - padding: const EdgeInsets.only(left: 10, right: 10), - child: ListView( - children: courseUnitSheet.sections.entries - .map((e) => _buildCard(e.key, e.value)) - .toList(), - ), - ); + padding: const EdgeInsets.only(left: 10, right: 10), + child: Padding( + padding: const EdgeInsets.all(20), + child: ListView( + children: courseUnitSheet.sections.entries + .map((e) => _buildCard(e.key, e.value)) + .toList(), + ), + )); } - CourseUnitInfoCard _buildCard( + Widget _buildCard( String sectionTitle, dynamic sectionContent, ) { - return CourseUnitInfoCard( - sectionTitle, - Container( - child: sectionContent is String || sectionContent is int - ? Text(sectionContent.toString()) - : sectionContent is List - ? Column( - children: sectionContent - .map((item) => Text(item.toString())) - .toList(), - ) - : sectionContent as Widget?, - ), + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column(children: [ + const Opacity( + opacity: 0.25, + child: Divider(color: Colors.grey), + ), + CourseUnitInfoCard( + sectionTitle, + HtmlWidget(sectionContent.toString()), + ), + ]), ); } } diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 98b792ff1..673dc3837 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -65,6 +65,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + audio_session: + dependency: transitive + description: + name: audio_session + sha256: "6fdf255ed3af86535c96452c33ecff1245990bb25a605bfb1958661ccc3d467f" + url: "https://pub.dev" + source: hosted + version: "0.1.18" battery_plus: dependency: "direct main" description: @@ -193,6 +201,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + chewie: + dependency: transitive + description: + name: chewie + sha256: "8bc4ac4cf3f316e50a25958c0f5eb9bb12cf7e8308bb1d74a43b230da2cfc144" + url: "https://pub.dev" + source: hosted + version: "1.7.5" cli_util: dependency: transitive description: @@ -329,6 +345,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.3" + expandable: + dependency: "direct main" + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.dev" + source: hosted + version: "5.0.1" expansion_tile_card: dependency: "direct main" description: @@ -469,6 +493,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_widget_from_html: + dependency: "direct main" + description: + name: flutter_widget_from_html + sha256: "22c911b6ccf82b83e0c457d987bac4e703440fea0fc88dab24f4dfe995a5f33f" + url: "https://pub.dev" + source: hosted + version: "0.14.11" flutter_widget_from_html_core: dependency: "direct main" description: @@ -485,6 +517,54 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.0" + fwfh_cached_network_image: + dependency: transitive + description: + name: fwfh_cached_network_image + sha256: "952aea958a5fda7d616cc297ba4bc08427e381459e75526fa375d6d8345630d3" + url: "https://pub.dev" + source: hosted + version: "0.14.2" + fwfh_chewie: + dependency: transitive + description: + name: fwfh_chewie + sha256: bbb036cd322ab77dc0edd34cbbf76181681f5e414987ece38745dc4f3d7408ed + url: "https://pub.dev" + source: hosted + version: "0.14.7" + fwfh_just_audio: + dependency: transitive + description: + name: fwfh_just_audio + sha256: "4962bc59cf8bbb0a77a55ff56a7b925612b0d8263bc2ede3636b9c86113cb493" + url: "https://pub.dev" + source: hosted + version: "0.14.2" + fwfh_svg: + dependency: transitive + description: + name: fwfh_svg + sha256: "3fd83926b7245d287f133a437ef430befd99d3b00ba8c600f26cc324af281f72" + url: "https://pub.dev" + source: hosted + version: "0.8.1" + fwfh_url_launcher: + dependency: transitive + description: + name: fwfh_url_launcher + sha256: "2a526c9819f74b4106ba2fba4dac79f0082deecd8d2c7011cd0471cb710e3eff" + url: "https://pub.dev" + source: hosted + version: "0.9.0+4" + fwfh_webview: + dependency: transitive + description: + name: fwfh_webview + sha256: b828bb5ddd4361a866cdb8f1b0de4f3348f332915ecf2f4215ba17e46c656adc + url: "https://pub.dev" + source: hosted + version: "0.14.8" glob: dependency: transitive description: @@ -573,6 +653,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.8.1" + just_audio: + dependency: transitive + description: + name: just_audio + sha256: b607cd1a43bac03d85c3aaee00448ff4a589ef2a77104e3d409889ff079bf823 + url: "https://pub.dev" + source: hosted + version: "0.9.36" + just_audio_platform_interface: + dependency: transitive + description: + name: just_audio_platform_interface + sha256: c3dee0014248c97c91fe6299edb73dc4d6c6930a2f4f713579cd692d9e47f4a1 + url: "https://pub.dev" + source: hosted + version: "4.2.2" + just_audio_web: + dependency: transitive + description: + name: just_audio_web + sha256: "134356b0fe3d898293102b33b5fd618831ffdc72bb7a1b726140abdf22772b70" + url: "https://pub.dev" + source: hosted + version: "0.4.9" latlong2: dependency: "direct main" description: @@ -581,6 +685,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" lists: dependency: transitive description: @@ -617,18 +745,18 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" material_design_icons_flutter: dependency: "direct main" description: @@ -641,10 +769,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" mgrs_dart: dependency: transitive description: @@ -738,10 +866,10 @@ packages: dependency: "direct main" description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" path_parsing: dependency: transitive description: @@ -1339,6 +1467,46 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.0" + video_player: + dependency: transitive + description: + name: video_player + sha256: afc65f4b8bcb2c188f64a591f84fb471f4f2e19fc607c65fd8d2f8fedb3dec23 + url: "https://pub.dev" + source: hosted + version: "2.8.3" + video_player_android: + dependency: transitive + description: + name: video_player_android + sha256: "4dd9b8b86d70d65eecf3dcabfcdfbb9c9115d244d022654aba49a00336d540c2" + url: "https://pub.dev" + source: hosted + version: "2.4.12" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6" + url: "https://pub.dev" + source: hosted + version: "6.2.2" + video_player_web: + dependency: transitive + description: + name: video_player_web + sha256: "8e9cb7fe94e49490e67bbc15149691792b58a0ade31b32e3f3688d104a0e057b" + url: "https://pub.dev" + source: hosted + version: "2.2.0" vm_service: dependency: transitive description: @@ -1347,6 +1515,22 @@ packages: url: "https://pub.dev" source: hosted version: "13.0.0" + wakelock_plus: + dependency: transitive + description: + name: wakelock_plus + sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d + url: "https://pub.dev" + source: hosted + version: "1.1.4" + wakelock_plus_platform_interface: + dependency: transitive + description: + name: wakelock_plus_platform_interface + sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385" + url: "https://pub.dev" + source: hosted + version: "1.1.0" watcher: dependency: transitive description: @@ -1379,6 +1563,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + webview_flutter: + dependency: transitive + description: + name: webview_flutter + sha256: "25e1b6e839e8cbfbd708abc6f85ed09d1727e24e08e08c6b8590d7c65c9a8932" + url: "https://pub.dev" + source: hosted + version: "4.7.0" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: "3e5f4e9d818086b0d01a66fb1ff9cc72ab0cc58c71980e3d3661c5685ea0efb0" + url: "https://pub.dev" + source: hosted + version: "3.15.0" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d + url: "https://pub.dev" + source: hosted + version: "2.10.0" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: "9bf168bccdf179ce90450b5f37e36fe263f591c9338828d6bf09b6f8d0f57f86" + url: "https://pub.dev" + source: hosted + version: "3.12.0" win32: dependency: transitive description: @@ -1428,5 +1644,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 927581f5e..a60262ac1 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -26,6 +26,7 @@ dependencies: diacritic: ^0.1.5 email_validator: ^2.0.1 encrypt: ^5.0.3 + expandable: ^5.0.1 expansion_tile_card: ^3.0.0 flutter: sdk: flutter @@ -38,7 +39,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.11 + flutter_widget_from_html: ^0.14.11 html: ^0.15.0 http: ^1.1.0 image: ^4.1.4 From 2c726dd8391be035d782a80ab79c2fc8a7be0390 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Mon, 18 Mar 2024 18:38:07 +0000 Subject: [PATCH 03/17] Implementing fetch --- .../parsers/parser_course_unit_info.dart | 42 ++++++++++++++++-- .../model/entities/course_units/sheet.dart | 44 ++++++++++++++++++- .../course_unit_info/course_unit_info.dart | 3 +- 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index c61cb259c..993c7adc2 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -45,11 +45,45 @@ Future> parseFiles( Future parseSheet(http.Response response) async { final json = jsonDecode(response.body) as Map; - final sheet = {}; - for (final item in json.entries) { - sheet[item.key] = item.value; + final professors = getCourseUnitProfessors(json['ds'] as List); + + json['responsabilidades'].forEach((dynamic element) { + final professor = Professor.fromJson(element as Map); + if (professors.contains(professor)) { + professors[professors.indexWhere((element) => element == professor)] + .regent = true; + } else { + professors.add(professor); + } + }); + + return Sheet( + professors: professors, + content: json['conteudo'].toString(), + evaluation: json['for_avaliacao'].toString(), + ); +} + +List getCourseUnitProfessors(List ds) { + final professors = []; + for (final map in ds) { + for (final docente in map['docentes'] as Iterable) { + final professor = Professor( + code: docente['doc_codigo'].toString(), + name: docente['nome'].toString(), + classes: [map['tipo'].toString()], + regent: false, + ); + if (professors.contains(professor)) { + professors[professors.indexWhere((element) => element == professor)] + .classes + .add(map['tipo'].toString()); + } else { + professors.add(professor); + } + } } - return Sheet(sheet); + return professors; } Future parseCourseUnitSheet(http.Response response) async { diff --git a/uni/lib/model/entities/course_units/sheet.dart b/uni/lib/model/entities/course_units/sheet.dart index cdcd8193e..69a3e4545 100644 --- a/uni/lib/model/entities/course_units/sheet.dart +++ b/uni/lib/model/entities/course_units/sheet.dart @@ -1,4 +1,44 @@ class Sheet { - Sheet(this.sections); - Map sections; + Sheet({ + required this.professors, + required this.content, + required this.evaluation, + }); + List professors; + String content; + String evaluation; +} + +class Professor { + Professor({ + required this.code, + required this.name, + required this.classes, + required this.regent, + }); + + factory Professor.fromJson(Map json) { + return Professor( + code: json['code'].toString(), + name: json['name'].toString(), + classes: [], + regent: true, + ); + } + + String code; + String name; + List classes; + bool regent; + + @override + bool operator ==(Object other) { + if (other is Professor) { + return other.code == code; + } + return false; + } + + @override + int get hashCode => code.hashCode; } 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 80c5ba0e3..781572c94 100644 --- a/uni/lib/view/course_unit_info/course_unit_info.dart +++ b/uni/lib/view/course_unit_info/course_unit_info.dart @@ -108,8 +108,7 @@ class CourseUnitDetailPageViewState final sheet = context .read() .courseUnitsSheets[widget.courseUnit]; - - if (sheet == null || sheet.sections.isEmpty) { + if (sheet == null) { return Center( child: Text( S.of(context).no_info, From b878ad1b4767d4b52e49d7c509ee612f735eb83f Mon Sep 17 00:00:00 2001 From: DGoiana Date: Mon, 18 Mar 2024 23:56:28 +0000 Subject: [PATCH 04/17] Fetching regents pictures --- .../parsers/parser_course_unit_info.dart | 13 ++--- .../model/entities/course_units/sheet.dart | 16 +++++-- .../widgets/course_unit_sheet.dart | 48 +++++++++++++++---- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 993c7adc2..6f1081ade 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -46,21 +46,19 @@ Future> parseFiles( Future parseSheet(http.Response response) async { final json = jsonDecode(response.body) as Map; final professors = getCourseUnitProfessors(json['ds'] as List); + final regents = []; json['responsabilidades'].forEach((dynamic element) { - final professor = Professor.fromJson(element as Map); - if (professors.contains(professor)) { - professors[professors.indexWhere((element) => element == professor)] - .regent = true; - } else { - professors.add(professor); - } + regents.add(Professor.fromJson(element as Map)); }); + print(regents); + return Sheet( professors: professors, content: json['conteudo'].toString(), evaluation: json['for_avaliacao'].toString(), + regents: regents, ); } @@ -72,7 +70,6 @@ List getCourseUnitProfessors(List ds) { code: docente['doc_codigo'].toString(), name: docente['nome'].toString(), classes: [map['tipo'].toString()], - regent: false, ); if (professors.contains(professor)) { professors[professors.indexWhere((element) => element == professor)] diff --git a/uni/lib/model/entities/course_units/sheet.dart b/uni/lib/model/entities/course_units/sheet.dart index 69a3e4545..a477f6ac2 100644 --- a/uni/lib/model/entities/course_units/sheet.dart +++ b/uni/lib/model/entities/course_units/sheet.dart @@ -1,10 +1,17 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + class Sheet { Sheet({ required this.professors, required this.content, required this.evaluation, + required this.regents, }); List professors; + List regents; String content; String evaluation; } @@ -14,22 +21,21 @@ class Professor { required this.code, required this.name, required this.classes, - required this.regent, + this.picture, }); factory Professor.fromJson(Map json) { return Professor( - code: json['code'].toString(), - name: json['name'].toString(), + code: json['codigo'].toString(), + name: json['nome'].toString(), classes: [], - regent: true, ); } + File? picture; String code; String name; List classes; - bool regent; @override bool operator ==(Object other) { diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 467c86179..6d3c3d7cf 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -1,11 +1,16 @@ +import 'dart:io'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:html/dom.dart' as dom; +import 'package:provider/provider.dart'; +import 'package:uni/model/providers/startup/session_provider.dart'; +import 'package:uni/model/providers/state_providers.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/course_units/sheet.dart'; +import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/view/course_unit_info/widgets/course_unit_info_card.dart'; @@ -15,16 +20,41 @@ class CourseUnitSheetView extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.only(left: 10, right: 10), - child: Padding( - padding: const EdgeInsets.all(20), - child: ListView( - children: courseUnitSheet.sections.entries - .map((e) => _buildCard(e.key, e.value)) - .toList(), + final session = context.read().state!; + + courseUnitSheet.regents.forEach((element) async { + element.picture = await ProfileProvider.fetchOrGetCachedProfilePicture( + session, + studentNumber: int.parse(element.code), + ); + }); + + return FutureBuilder( + builder: (BuildContext context, AsyncSnapshot> snapshot) { + return Container( + padding: const EdgeInsets.only(left: 20, right: 20), + child: Column( + children: [ + SizedBox( + height: 100, + width: double.infinity, + child: Row( + children: [ + ...(snapshot.data ?? []).map((regent) { + return CircleAvatar( + radius: 40, + backgroundImage: FileImage(regent.picture!), + ); + }), + ], + ), + ), + ], ), - )); + ); + }, + future: Future.value(courseUnitSheet.regents), + ); } Widget _buildCard( From a57216d8e8c980d03478a759ef2bb17191b4f0c9 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Tue, 19 Mar 2024 00:07:38 +0000 Subject: [PATCH 05/17] Handling fetch errors --- .../view/course_unit_info/widgets/course_unit_sheet.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 6d3c3d7cf..9bee8a241 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -43,7 +43,11 @@ class CourseUnitSheetView extends StatelessWidget { ...(snapshot.data ?? []).map((regent) { return CircleAvatar( radius: 40, - backgroundImage: FileImage(regent.picture!), + backgroundImage: regent.picture != null + ? FileImage(regent.picture!) as ImageProvider + : const AssetImage( + 'assets/images/profile_placeholder.png', + ), // Provide path to your default image asset ); }), ], From 84d36643f3ccc2f3e25861fe404248223859c966 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Tue, 19 Mar 2024 15:12:56 +0000 Subject: [PATCH 06/17] Fetching and presenting professors --- .../parsers/parser_course_unit_info.dart | 8 +- .../model/entities/course_units/sheet.dart | 3 +- .../widgets/course_unit_sheet.dart | 98 ++++++++++++++++--- 3 files changed, 92 insertions(+), 17 deletions(-) diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 6f1081ade..37421f6cd 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -1,3 +1,4 @@ +import 'dart:collection'; import 'dart:convert'; import 'package:html/parser.dart'; @@ -68,7 +69,7 @@ List getCourseUnitProfessors(List ds) { for (final docente in map['docentes'] as Iterable) { final professor = Professor( code: docente['doc_codigo'].toString(), - name: docente['nome'].toString(), + name: shortName(docente['nome'].toString()), classes: [map['tipo'].toString()], ); if (professors.contains(professor)) { @@ -160,3 +161,8 @@ String _htmlAfterElement(String body, String elementOuterHtml) { final index = body.indexOf(elementOuterHtml) + elementOuterHtml.length; return body.substring(index, body.indexOf('

', index)); } + +String shortName(String name) { + final splitName = name.split(' '); + return '${splitName.first} ${splitName.last}'; +} diff --git a/uni/lib/model/entities/course_units/sheet.dart b/uni/lib/model/entities/course_units/sheet.dart index a477f6ac2..152407e50 100644 --- a/uni/lib/model/entities/course_units/sheet.dart +++ b/uni/lib/model/entities/course_units/sheet.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:uni/controller/parsers/parser_course_unit_info.dart'; class Sheet { Sheet({ @@ -27,7 +28,7 @@ class Professor { factory Professor.fromJson(Map json) { return Professor( code: json['codigo'].toString(), - name: json['nome'].toString(), + name: shortName(json['nome'].toString()), classes: [], ); } diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 9bee8a241..293a905ad 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -29,35 +29,103 @@ class CourseUnitSheetView extends StatelessWidget { ); }); + courseUnitSheet.professors.forEach((element) async { + element.picture = await ProfileProvider.fetchOrGetCachedProfilePicture( + session, + studentNumber: int.parse(element.code), + ); + }); + return FutureBuilder( - builder: (BuildContext context, AsyncSnapshot> snapshot) { + builder: (BuildContext context, AsyncSnapshot snapshot) { return Container( padding: const EdgeInsets.only(left: 20, right: 20), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ + const Text( + 'Regentes', + style: TextStyle(fontSize: 18), + ), SizedBox( height: 100, width: double.infinity, - child: Row( - children: [ - ...(snapshot.data ?? []).map((regent) { - return CircleAvatar( - radius: 40, - backgroundImage: regent.picture != null - ? FileImage(regent.picture!) as ImageProvider - : const AssetImage( - 'assets/images/profile_placeholder.png', - ), // Provide path to your default image asset - ); - }), - ], + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + ...(snapshot.data?.regents ?? []).map((regent) { + return Row( + children: [ + CircleAvatar( + radius: 40, + backgroundImage: regent.picture != null + ? FileImage(regent.picture!) as ImageProvider + : const AssetImage( + 'assets/images/profile_placeholder.png', + ), + ), + Padding( + padding: const EdgeInsets.only( + left: 10, + right: 10, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + regent.name, + style: const TextStyle(fontSize: 17), + ), + const Text('Regente') + ], + ), + ), + ], + ); + }), + ], + ), ), ), + const Text( + 'Docentes', + style: TextStyle(fontSize: 18), + ), + SizedBox( + height: 75, + width: double.infinity, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + ...(snapshot.data?.professors ?? []) + .asMap() + .entries + .map((professor) { + final idx = professor.key; + return Transform.translate( + offset: Offset(-10.0 * idx, 0), + child: CircleAvatar( + radius: 20, + backgroundImage: professor.value.picture != null + ? FileImage(professor.value.picture!) + as ImageProvider + : const AssetImage( + 'assets/images/profile_placeholder.png', + ), + ), + ); + }) + ], + ), + )) ], ), ); }, - future: Future.value(courseUnitSheet.regents), + future: Future.value(courseUnitSheet), ); } From bf79269f594e839ae1447b9b48d38b3b7cf1ab30 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Tue, 19 Mar 2024 16:28:32 +0000 Subject: [PATCH 07/17] Full page w/o books --- .../course_units_info_fetcher.dart | 2 +- .../parsers/parser_course_unit_info.dart | 6 +- .../model/entities/course_units/sheet.dart | 2 - .../lazy/course_units_info_provider.dart | 4 +- .../common_widgets/generic_expandable.dart | 11 +- .../widgets/course_unit_info_card.dart | 23 +- .../widgets/course_unit_sheet.dart | 217 +++++++++--------- 7 files changed, 131 insertions(+), 134 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 2ed0abba8..4bf08d56a 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,8 +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_directory.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/sheet.dart'; +import 'package:uni/model/entities/session.dart'; class CourseUnitsInfoFetcher implements SessionDependantFetcher { @override diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 37421f6cd..56238c056 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unused_import + import 'dart:collection'; import 'dart:convert'; @@ -5,10 +7,10 @@ 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/sheet.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/course_units/sheet.dart'; import 'package:uni/model/entities/session.dart'; Future> parseFiles( @@ -53,8 +55,6 @@ Future parseSheet(http.Response response) async { regents.add(Professor.fromJson(element as Map)); }); - print(regents); - return Sheet( professors: professors, content: json['conteudo'].toString(), diff --git a/uni/lib/model/entities/course_units/sheet.dart b/uni/lib/model/entities/course_units/sheet.dart index 152407e50..ca7bf8d5f 100644 --- a/uni/lib/model/entities/course_units/sheet.dart +++ b/uni/lib/model/entities/course_units/sheet.dart @@ -1,7 +1,5 @@ import 'dart:io'; -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:uni/controller/parsers/parser_course_unit_info.dart'; class Sheet { 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 12d079981..3051c3f8c 100644 --- a/uni/lib/model/providers/lazy/course_units_info_provider.dart +++ b/uni/lib/model/providers/lazy/course_units_info_provider.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unused_import + import 'dart:collection'; import 'package:tuple/tuple.dart'; @@ -6,10 +8,10 @@ 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_directory.dart'; import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; +import 'package:uni/model/entities/course_units/sheet.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/entities/course_units/sheet.dart'; typedef SheetsMap = Map; typedef ClassesMap = Map>; diff --git a/uni/lib/view/common_widgets/generic_expandable.dart b/uni/lib/view/common_widgets/generic_expandable.dart index f19fb047b..4fae6e199 100644 --- a/uni/lib/view/common_widgets/generic_expandable.dart +++ b/uni/lib/view/common_widgets/generic_expandable.dart @@ -1,7 +1,7 @@ import 'package:expandable/expandable.dart'; import 'package:flutter/material.dart'; -abstract class GenericExpandable extends StatelessWidget { +class GenericExpandable extends StatelessWidget { const GenericExpandable( {super.key, required this.title, required this.content}); @@ -23,9 +23,12 @@ abstract class GenericExpandable extends StatelessWidget { end: Alignment.bottomCenter, ).createShader(bounds), blendMode: BlendMode.dstIn, - child: LimitedBox( - maxHeight: 100, - child: content, + child: ClipRect( + child: Align( + alignment: Alignment.topCenter, + heightFactor: 0.25, + child: content, + ), ), ), expanded: content, diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_info_card.dart b/uni/lib/view/course_unit_info/widgets/course_unit_info_card.dart index e5552046e..b79eb7a06 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_info_card.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_info_card.dart @@ -1,17 +1,22 @@ import 'package:flutter/material.dart'; -import 'package:uni/view/common_widgets/generic_expandable.dart'; import 'package:uni/view/common_widgets/generic_expansion_card.dart'; -class CourseUnitInfoCard extends GenericExpandable { - CourseUnitInfoCard(this.sectionTitle, this.info, {super.key}) +class CourseUnitInfoCard extends GenericExpansionCard { + const CourseUnitInfoCard(this.sectionTitle, this.content, {super.key}) : super( - title: normalizeTitle(sectionTitle), - content: info, + cardMargin: const EdgeInsets.only(bottom: 10), + smallTitle: true, ); final String sectionTitle; - final Widget info; -} + final Widget content; + + @override + Widget buildCardContent(BuildContext context) { + return Container(padding: const EdgeInsets.only(top: 10), child: content); + } -String normalizeTitle(String sectionTitle) { - return sectionTitle[0].toUpperCase() + sectionTitle.substring(1); + @override + String getTitle(BuildContext context) { + return sectionTitle; + } } diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 293a905ad..62fb605fd 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -1,18 +1,11 @@ -import 'dart:io'; -import 'dart:math'; - import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; -import 'package:html/dom.dart' as dom; import 'package:provider/provider.dart'; -import 'package:uni/model/providers/startup/session_provider.dart'; -import 'package:uni/model/providers/state_providers.dart'; -import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/course_units/sheet.dart'; +import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; - -import 'package:uni/view/course_unit_info/widgets/course_unit_info_card.dart'; +import 'package:uni/model/providers/startup/session_provider.dart'; +import 'package:uni/view/common_widgets/generic_expandable.dart'; class CourseUnitSheetView extends StatelessWidget { const CourseUnitSheetView(this.courseUnitSheet, {super.key}); @@ -22,106 +15,31 @@ class CourseUnitSheetView extends StatelessWidget { Widget build(BuildContext context) { final session = context.read().state!; - courseUnitSheet.regents.forEach((element) async { - element.picture = await ProfileProvider.fetchOrGetCachedProfilePicture( - session, - studentNumber: int.parse(element.code), - ); - }); - - courseUnitSheet.professors.forEach((element) async { - element.picture = await ProfileProvider.fetchOrGetCachedProfilePicture( - session, - studentNumber: int.parse(element.code), - ); - }); + fetchProfessorPictures(courseUnitSheet.professors, session); + fetchProfessorPictures(courseUnitSheet.regents, session); return FutureBuilder( builder: (BuildContext context, AsyncSnapshot snapshot) { return Container( padding: const EdgeInsets.only(left: 20, right: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Regentes', - style: TextStyle(fontSize: 18), - ), - SizedBox( - height: 100, - width: double.infinity, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: [ - ...(snapshot.data?.regents ?? []).map((regent) { - return Row( - children: [ - CircleAvatar( - radius: 40, - backgroundImage: regent.picture != null - ? FileImage(regent.picture!) as ImageProvider - : const AssetImage( - 'assets/images/profile_placeholder.png', - ), - ), - Padding( - padding: const EdgeInsets.only( - left: 10, - right: 10, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - regent.name, - style: const TextStyle(fontSize: 17), - ), - const Text('Regente') - ], - ), - ), - ], - ); - }), - ], - ), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Regentes', + style: TextStyle(fontSize: 18), ), - ), - const Text( - 'Docentes', - style: TextStyle(fontSize: 18), - ), - SizedBox( - height: 75, - width: double.infinity, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: [ - ...(snapshot.data?.professors ?? []) - .asMap() - .entries - .map((professor) { - final idx = professor.key; - return Transform.translate( - offset: Offset(-10.0 * idx, 0), - child: CircleAvatar( - radius: 20, - backgroundImage: professor.value.picture != null - ? FileImage(professor.value.picture!) - as ImageProvider - : const AssetImage( - 'assets/images/profile_placeholder.png', - ), - ), - ); - }) - ], - ), - )) - ], + showProfessors(snapshot.data?.regents ?? [], regent: true), + const Text( + 'Docentes', + style: TextStyle(fontSize: 18), + ), + showProfessors(snapshot.data?.professors ?? [], regent: false), + _buildCard('Programa', courseUnitSheet.content), + _buildCard('Avaliação', courseUnitSheet.evaluation), + ], + ), ), ); }, @@ -129,22 +47,93 @@ class CourseUnitSheetView extends StatelessWidget { ); } + void fetchProfessorPictures(List professors, Session session) { + professors.forEach((element) async { + element.picture = await ProfileProvider.fetchOrGetCachedProfilePicture( + session, + studentNumber: int.parse(element.code), + ); + }); + } + + Widget showProfessors(List professors, {required bool regent}) { + return SizedBox( + height: regent ? 100 : 75, + width: double.infinity, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: [ + ...professors.asMap().entries.map((professor) { + final idx = professor.key; + return Row( + children: [ + if (regent) + CircleAvatar( + radius: 40, + backgroundImage: professor.value.picture != null + ? FileImage(professor.value.picture!) as ImageProvider + : const AssetImage( + 'assets/images/profile_placeholder.png', + ), + ) + else + Transform.translate( + offset: Offset(-10.0 * idx, 0), + child: CircleAvatar( + radius: 20, + backgroundImage: professor.value.picture != null + ? FileImage(professor.value.picture!) + as ImageProvider + : const AssetImage( + 'assets/images/profile_placeholder.png', + ), + ), + ), + if (regent) + Padding( + padding: const EdgeInsets.only( + left: 10, + right: 10, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + professor.value.name, + style: const TextStyle(fontSize: 17), + ), + ], + ), + ), + ], + ); + }), + ], + ), + ), + ); + } + Widget _buildCard( String sectionTitle, dynamic sectionContent, ) { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: Column(children: [ - const Opacity( - opacity: 0.25, - child: Divider(color: Colors.grey), - ), - CourseUnitInfoCard( - sectionTitle, - HtmlWidget(sectionContent.toString()), - ), - ]), + child: Column( + children: [ + const Opacity( + opacity: 0.25, + child: Divider(color: Colors.grey), + ), + GenericExpandable( + content: HtmlWidget(sectionContent.toString()), + title: sectionTitle, + ), + ], + ), ); } } From 9b774832d427dd96ccd32d8ae0f4d18f38e570be Mon Sep 17 00:00:00 2001 From: DGoiana Date: Mon, 25 Mar 2024 22:21:39 +0000 Subject: [PATCH 08/17] Pictures fetch refactor --- .../widgets/course_unit_sheet.dart | 85 ++++++++++--------- 1 file changed, 47 insertions(+), 38 deletions(-) diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 62fb605fd..86c3b76f9 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:provider/provider.dart'; @@ -13,11 +15,6 @@ class CourseUnitSheetView extends StatelessWidget { @override Widget build(BuildContext context) { - final session = context.read().state!; - - fetchProfessorPictures(courseUnitSheet.professors, session); - fetchProfessorPictures(courseUnitSheet.regents, session); - return FutureBuilder( builder: (BuildContext context, AsyncSnapshot snapshot) { return Container( @@ -30,12 +27,20 @@ class CourseUnitSheetView extends StatelessWidget { 'Regentes', style: TextStyle(fontSize: 18), ), - showProfessors(snapshot.data?.regents ?? [], regent: true), + showProfessors( + context, + snapshot.data?.regents ?? [], + regent: true, + ), const Text( 'Docentes', style: TextStyle(fontSize: 18), ), - showProfessors(snapshot.data?.professors ?? [], regent: false), + showProfessors( + context, + snapshot.data?.professors ?? [], + regent: false, + ), _buildCard('Programa', courseUnitSheet.content), _buildCard('Avaliação', courseUnitSheet.evaluation), ], @@ -47,16 +52,9 @@ class CourseUnitSheetView extends StatelessWidget { ); } - void fetchProfessorPictures(List professors, Session session) { - professors.forEach((element) async { - element.picture = await ProfileProvider.fetchOrGetCachedProfilePicture( - session, - studentNumber: int.parse(element.code), - ); - }); - } - - Widget showProfessors(List professors, {required bool regent}) { + Widget showProfessors(BuildContext context, List professors, + {required bool regent}) { + final session = context.read().state!; return SizedBox( height: regent ? 100 : 75, width: double.infinity, @@ -68,28 +66,39 @@ class CourseUnitSheetView extends StatelessWidget { final idx = professor.key; return Row( children: [ - if (regent) - CircleAvatar( - radius: 40, - backgroundImage: professor.value.picture != null - ? FileImage(professor.value.picture!) as ImageProvider - : const AssetImage( - 'assets/images/profile_placeholder.png', - ), - ) - else - Transform.translate( - offset: Offset(-10.0 * idx, 0), - child: CircleAvatar( - radius: 20, - backgroundImage: professor.value.picture != null - ? FileImage(professor.value.picture!) - as ImageProvider - : const AssetImage( - 'assets/images/profile_placeholder.png', - ), - ), + FutureBuilder( + builder: + (BuildContext context, AsyncSnapshot snapshot) { + if (regent) { + return CircleAvatar( + radius: 40, + backgroundImage: + snapshot.hasData && snapshot.data != null + ? FileImage(snapshot.data!) as ImageProvider + : const AssetImage( + 'assets/images/profile_placeholder.png', + ), + ); + } else { + return Transform.translate( + offset: Offset(-10.0 * idx, 0), + child: CircleAvatar( + radius: 20, + backgroundImage: + snapshot.hasData && snapshot.data != null + ? FileImage(snapshot.data!) as ImageProvider + : const AssetImage( + 'assets/images/profile_placeholder.png', + ), + ), + ); + } + }, + future: ProfileProvider.fetchOrGetCachedProfilePicture( + session, + studentNumber: int.parse(professor.value.code), ), + ), if (regent) Padding( padding: const EdgeInsets.only( From f88399dedfb0054210e29c8e34be248b27e31179 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Tue, 2 Apr 2024 17:29:52 +0100 Subject: [PATCH 09/17] Creating animated expandable --- .../generic_animated_expandable.dart | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 uni/lib/view/common_widgets/generic_animated_expandable.dart diff --git a/uni/lib/view/common_widgets/generic_animated_expandable.dart b/uni/lib/view/common_widgets/generic_animated_expandable.dart new file mode 100644 index 000000000..e9f5f75ad --- /dev/null +++ b/uni/lib/view/common_widgets/generic_animated_expandable.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +class AnimatedExpandable extends StatefulWidget { + const AnimatedExpandable({ + required this.firstChild, + required this.secondChild, + super.key, + }); + + final Widget firstChild; + final Widget secondChild; + + @override + State createState() { + return AnimatedExpandableState(); + } +} + +class AnimatedExpandableState extends State { + bool _expanded = false; + + Widget build(BuildContext context) { + return AnimatedCrossFade( + duration: const Duration(milliseconds: 500), + firstChild: widget.firstChild, + secondChild: widget.secondChild, + crossFadeState: + _expanded ? CrossFadeState.showFirst : CrossFadeState.showSecond, + ); + } +} From d5058b76937f2dfc5f5b5751118d2129a961c878 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Tue, 2 Apr 2024 22:41:31 +0100 Subject: [PATCH 10/17] Animated expandable usage --- .../generic_animated_expandable.dart | 20 +- .../widgets/course_unit_sheet.dart | 303 +++++++++++------- uni/pubspec.yaml | 1 + 3 files changed, 201 insertions(+), 123 deletions(-) diff --git a/uni/lib/view/common_widgets/generic_animated_expandable.dart b/uni/lib/view/common_widgets/generic_animated_expandable.dart index e9f5f75ad..2242097f9 100644 --- a/uni/lib/view/common_widgets/generic_animated_expandable.dart +++ b/uni/lib/view/common_widgets/generic_animated_expandable.dart @@ -22,10 +22,24 @@ class AnimatedExpandableState extends State { Widget build(BuildContext context) { return AnimatedCrossFade( duration: const Duration(milliseconds: 500), - firstChild: widget.firstChild, - secondChild: widget.secondChild, + firstChild: GestureDetector( + onTap: () { + setState(() { + _expanded = !_expanded; + }); + }, + child: widget.firstChild, + ), + secondChild: GestureDetector( + onTap: () { + setState(() { + _expanded = !_expanded; + }); + }, + child: widget.secondChild, + ), crossFadeState: - _expanded ? CrossFadeState.showFirst : CrossFadeState.showSecond, + _expanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, ); } } diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 86c3b76f9..55cdb4b42 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -4,9 +4,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:provider/provider.dart'; import 'package:uni/model/entities/course_units/sheet.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/view/common_widgets/generic_animated_expandable.dart'; import 'package:uni/view/common_widgets/generic_expandable.dart'; class CourseUnitSheetView extends StatelessWidget { @@ -15,134 +15,197 @@ class CourseUnitSheetView extends StatelessWidget { @override Widget build(BuildContext context) { - return FutureBuilder( - builder: (BuildContext context, AsyncSnapshot snapshot) { - return Container( - padding: const EdgeInsets.only(left: 20, right: 20), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Regentes', - style: TextStyle(fontSize: 18), - ), - showProfessors( - context, - snapshot.data?.regents ?? [], - regent: true, - ), - const Text( - 'Docentes', - style: TextStyle(fontSize: 18), - ), - showProfessors( - context, - snapshot.data?.professors ?? [], - regent: false, - ), - _buildCard('Programa', courseUnitSheet.content), - _buildCard('Avaliação', courseUnitSheet.evaluation), - ], - ), - ), - ); - }, - future: Future.value(courseUnitSheet), - ); - } - - Widget showProfessors(BuildContext context, List professors, - {required bool regent}) { - final session = context.read().state!; - return SizedBox( - height: regent ? 100 : 75, - width: double.infinity, + return Container( + padding: const EdgeInsets.only(left: 20, right: 20), child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - ...professors.asMap().entries.map((professor) { - final idx = professor.key; - return Row( - children: [ - FutureBuilder( - builder: - (BuildContext context, AsyncSnapshot snapshot) { - if (regent) { - return CircleAvatar( - radius: 40, - backgroundImage: - snapshot.hasData && snapshot.data != null - ? FileImage(snapshot.data!) as ImageProvider - : const AssetImage( - 'assets/images/profile_placeholder.png', - ), - ); - } else { - return Transform.translate( - offset: Offset(-10.0 * idx, 0), - child: CircleAvatar( - radius: 20, - backgroundImage: - snapshot.hasData && snapshot.data != null - ? FileImage(snapshot.data!) as ImageProvider - : const AssetImage( - 'assets/images/profile_placeholder.png', - ), - ), - ); - } - }, - future: ProfileProvider.fetchOrGetCachedProfilePicture( - session, - studentNumber: int.parse(professor.value.code), - ), - ), - if (regent) - Padding( - padding: const EdgeInsets.only( - left: 10, - right: 10, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - professor.value.name, - style: const TextStyle(fontSize: 17), - ), - ], - ), - ), - ], - ); - }), + const Text( + 'Regentes', + style: TextStyle(fontSize: 18), + ), + buildRegentsRow(context, courseUnitSheet.regents), + const Text( + 'Docentes', + style: TextStyle(fontSize: 18), + ), + AnimatedExpandable( + firstChild: + buildProfessorsRow(context, courseUnitSheet.professors), + secondChild: + buildExpandedProfessors(context, courseUnitSheet.professors), + ), + _buildCard('Programa', courseUnitSheet.content), + _buildCard('Avaliação', courseUnitSheet.evaluation), ], ), ), ); } +} + +Widget buildRegentsRow(BuildContext context, List regents) { + final session = context.read().state!; + return SizedBox( + height: (regents.length * 80) + 20, + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ...regents.asMap().entries.map((regent) { + final idx = regent.key; + return Padding( + padding: EdgeInsets.only(bottom: idx == regents.length - 1 ? 0 : 5), + child: Row( + children: [ + FutureBuilder( + builder: + (BuildContext context, AsyncSnapshot snapshot) => + _buildAvatar(snapshot, 40), + future: ProfileProvider.fetchOrGetCachedProfilePicture( + session, + studentNumber: int.parse(regent.value.code), + ), + ), + Padding( + padding: const EdgeInsets.only( + left: 10, + right: 10, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + regent.value.name, + style: const TextStyle(fontSize: 17), + ), + ], + ), + ), + ], + ), + ); + }), + ], + ), + ); +} - Widget _buildCard( - String sectionTitle, - dynamic sectionContent, - ) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Column( +Widget buildProfessorsRow(BuildContext context, List professors) { + final session = context.read().state!; + return SizedBox( + height: 55, + width: double.infinity, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( children: [ - const Opacity( - opacity: 0.25, - child: Divider(color: Colors.grey), - ), - GenericExpandable( - content: HtmlWidget(sectionContent.toString()), - title: sectionTitle, - ), + ...professors.asMap().entries.map((professor) { + final idx = professor.key; + return Row( + children: [ + FutureBuilder( + builder: + (BuildContext context, AsyncSnapshot snapshot) => + Transform.translate( + offset: Offset(-10.0 * idx, 0), + child: _buildAvatar(snapshot, 20), + ), + future: ProfileProvider.fetchOrGetCachedProfilePicture( + session, + studentNumber: int.parse(professor.value.code), + ), + ), + ], + ); + }), ], ), - ); - } + ), + ); +} + +Widget buildExpandedProfessors( + BuildContext context, + List professors, +) { + final session = context.read().state!; + return SizedBox( + height: (professors.length * 45) + 10, + width: double.infinity, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ...professors.asMap().entries.map((professor) { + final idx = professor.key; + return Padding( + padding: + EdgeInsets.only(bottom: idx == professors.length - 1 ? 0 : 5), + child: Row( + children: [ + FutureBuilder( + builder: + (BuildContext context, AsyncSnapshot snapshot) => + _buildAvatar(snapshot, 20), + future: ProfileProvider.fetchOrGetCachedProfilePicture( + session, + studentNumber: int.parse(professor.value.code), + ), + ), + Padding( + padding: const EdgeInsets.only( + left: 10, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + professor.value.name, + style: const TextStyle(fontSize: 14), + ), + ], + ), + ), + ], + ), + ); + }), + ], + ), + ); +} + +Widget _buildCard( + String sectionTitle, + dynamic sectionContent, +) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Column( + children: [ + const Opacity( + opacity: 0.25, + child: Divider(color: Colors.grey), + ), + GenericExpandable( + content: HtmlWidget(sectionContent.toString()), + title: sectionTitle, + ), + ], + ), + ); +} + +Widget _buildAvatar(AsyncSnapshot snapshot, double radius) { + return CircleAvatar( + radius: radius, + backgroundImage: snapshot.hasData && snapshot.data != null + ? FileImage(snapshot.data!) as ImageProvider + : const AssetImage( + 'assets/images/profile_placeholder.png', + ), + ); } diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index b53f43adb..83e6e5b8f 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -40,6 +40,7 @@ dependencies: flutter_markdown: ^0.6.0 flutter_svg: ^2.0.9 flutter_widget_from_html: ^0.14.11 + flutter_widget_from_html_core: ^0.14.11 html: ^0.15.0 http: ^1.1.0 image: ^4.1.4 From 53a6e96dbba3980cf6780ef481195df515a32489 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 3 Apr 2024 14:50:49 +0100 Subject: [PATCH 11/17] Generic animated expandable minor changes --- .../generic_animated_expandable.dart | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/uni/lib/view/common_widgets/generic_animated_expandable.dart b/uni/lib/view/common_widgets/generic_animated_expandable.dart index 2242097f9..fc5a4da6f 100644 --- a/uni/lib/view/common_widgets/generic_animated_expandable.dart +++ b/uni/lib/view/common_widgets/generic_animated_expandable.dart @@ -19,27 +19,25 @@ class AnimatedExpandable extends StatefulWidget { class AnimatedExpandableState extends State { bool _expanded = false; + @override Widget build(BuildContext context) { - return AnimatedCrossFade( - duration: const Duration(milliseconds: 500), - firstChild: GestureDetector( - onTap: () { - setState(() { - _expanded = !_expanded; - }); - }, - child: widget.firstChild, - ), - secondChild: GestureDetector( - onTap: () { - setState(() { - _expanded = !_expanded; - }); - }, - child: widget.secondChild, + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + _expanded = !_expanded; + }); + }, + child: AnimatedCrossFade( + firstCurve: Curves.easeInOutCubic, + secondCurve: Curves.easeInOut, + sizeCurve: Curves.easeInOut, + duration: const Duration(milliseconds: 500), + firstChild: widget.firstChild, + secondChild: widget.secondChild, + crossFadeState: + _expanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, ), - crossFadeState: - _expanded ? CrossFadeState.showSecond : CrossFadeState.showFirst, ); } } From 955056acc8e0276a6d953d38d714b4e197fcd313 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 10 Apr 2024 19:49:16 +0100 Subject: [PATCH 12/17] Implementing sheet library --- uni/lib/controller/fetchers/book_fetcher.dart | 20 +++++++ .../parsers/parser_course_unit_info.dart | 10 ++++ .../model/entities/course_units/sheet.dart | 9 +++ .../widgets/course_unit_sheet.dart | 56 ++++++++++++++++++- 4 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 uni/lib/controller/fetchers/book_fetcher.dart diff --git a/uni/lib/controller/fetchers/book_fetcher.dart b/uni/lib/controller/fetchers/book_fetcher.dart new file mode 100644 index 000000000..30d1afad0 --- /dev/null +++ b/uni/lib/controller/fetchers/book_fetcher.dart @@ -0,0 +1,20 @@ +import 'dart:convert'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:uni/model/entities/course_units/sheet.dart'; + +class BookThumbFetcher { + Future fetchBookThumb(String isbn) async { + final url = Uri.https('openlibrary.org', '/api/books', + {'bibkeys': 'ISBN:$isbn', 'format': 'json', 'jscmd': 'data'}); + + final response = await http.get(url); + + final data = + json.decode(response.body)['ISBN:$isbn'] as Map; + + return Image(image: NetworkImage(data['cover']['medium'].toString())); + } +} diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 56238c056..04367a7ac 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -5,6 +5,7 @@ import 'dart:convert'; import 'package:html/parser.dart'; import 'package:http/http.dart' as http; +import 'package:uni/controller/fetchers/book_fetcher.dart'; 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'; @@ -50,16 +51,25 @@ Future parseSheet(http.Response response) async { final json = jsonDecode(response.body) as Map; final professors = getCourseUnitProfessors(json['ds'] as List); final regents = []; + final books = []; json['responsabilidades'].forEach((dynamic element) { regents.add(Professor.fromJson(element as Map)); }); + json['bibliografia'].forEach((dynamic element) { + books.add(Book( + title: element['titulo'].toString(), + isbn: element['isbn'].toString(), + )); + }); + return Sheet( professors: professors, content: json['conteudo'].toString(), evaluation: json['for_avaliacao'].toString(), regents: regents, + books: books, ); } diff --git a/uni/lib/model/entities/course_units/sheet.dart b/uni/lib/model/entities/course_units/sheet.dart index ca7bf8d5f..d33f77696 100644 --- a/uni/lib/model/entities/course_units/sheet.dart +++ b/uni/lib/model/entities/course_units/sheet.dart @@ -8,11 +8,20 @@ class Sheet { required this.content, required this.evaluation, required this.regents, + required this.books, }); List professors; List regents; String content; String evaluation; + List books; +} + +class Book { + Book({required this.title, required this.isbn}); + + String title; + String isbn; } class Professor { diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 55cdb4b42..7d6bb71fe 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -1,8 +1,11 @@ import 'dart:io'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:provider/provider.dart'; +import 'package:uni/controller/fetchers/book_fetcher.dart'; import 'package:uni/model/entities/course_units/sheet.dart'; import 'package:uni/model/providers/startup/profile_provider.dart'; import 'package:uni/model/providers/startup/session_provider.dart'; @@ -23,12 +26,12 @@ class CourseUnitSheetView extends StatelessWidget { children: [ const Text( 'Regentes', - style: TextStyle(fontSize: 18), + style: TextStyle(fontSize: 20), ), buildRegentsRow(context, courseUnitSheet.regents), const Text( 'Docentes', - style: TextStyle(fontSize: 18), + style: TextStyle(fontSize: 20), ), AnimatedExpandable( firstChild: @@ -38,6 +41,16 @@ class CourseUnitSheetView extends StatelessWidget { ), _buildCard('Programa', courseUnitSheet.content), _buildCard('Avaliação', courseUnitSheet.evaluation), + const Opacity( + opacity: 0.25, + child: Divider(color: Colors.grey), + ), + const Text( + 'Bibliografia', + style: TextStyle(fontSize: 20), + ), + if (courseUnitSheet.books.isNotEmpty) + buildBooksRow(context, courseUnitSheet.books) ], ), ), @@ -178,6 +191,45 @@ Widget buildExpandedProfessors( ); } +Widget buildBooksRow(BuildContext context, List books) { + return SizedBox( + height: 500, + width: double.infinity, + child: Wrap( + alignment: WrapAlignment.spaceBetween, + children: [ + ...books.asMap().entries.map((book) { + return FutureBuilder( + builder: (BuildContext context, AsyncSnapshot snapshot) { + return Padding( + padding: const EdgeInsets.only(left: 15, right: 15, top: 10), + child: Column( + children: [ + SizedBox( + width: 135, + height: 140, // adjust this value as needed + child: snapshot.data ?? + Image.asset('assets/images/profile_placeholder.png'), + ), + SizedBox( + width: 135, + child: Text( + book.value.title, + textAlign: TextAlign.center, + ), + ), + ], + ), + ); + }, + future: BookThumbFetcher().fetchBookThumb(book.value.isbn), + ); + }) + ], + ), + ); +} + Widget _buildCard( String sectionTitle, dynamic sectionContent, From 9cfadd75e59959cc51a20f66f42db936da3fbed7 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Thu, 13 Jun 2024 16:25:40 +0100 Subject: [PATCH 13/17] migrating to google books --- uni/lib/controller/fetchers/book_fetcher.dart | 21 ++++++++++++------- .../widgets/course_unit_sheet.dart | 6 +++++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/uni/lib/controller/fetchers/book_fetcher.dart b/uni/lib/controller/fetchers/book_fetcher.dart index 30d1afad0..075aa4b7b 100644 --- a/uni/lib/controller/fetchers/book_fetcher.dart +++ b/uni/lib/controller/fetchers/book_fetcher.dart @@ -1,20 +1,25 @@ import 'dart:convert'; -import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; -import 'package:uni/model/entities/course_units/sheet.dart'; +import 'package:uni/controller/networking/network_router.dart'; class BookThumbFetcher { Future fetchBookThumb(String isbn) async { - final url = Uri.https('openlibrary.org', '/api/books', - {'bibkeys': 'ISBN:$isbn', 'format': 'json', 'jscmd': 'data'}); + final url = Uri.https( + 'www.googleapis.com', '/books/v1/volumes', {'q': '+isbn:$isbn'}); - final response = await http.get(url); + final response = + await http.get(Uri.decodeComponent(url.toString()).toUri()); - final data = - json.decode(response.body)['ISBN:$isbn'] as Map; + final bookInformation = ((json.decode(response.body) + as Map)['items'] as List) + .first as Map; - return Image(image: NetworkImage(data['cover']['medium'].toString())); + final thumbnail = + ((bookInformation['volumeInfo'] as Map)['imageLinks'] + as Map)['thumbnail']; + + return Image(image: NetworkImage(thumbnail.toString())); } } diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 7d6bb71fe..294cd5ac7 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -209,7 +209,11 @@ Widget buildBooksRow(BuildContext context, List books) { width: 135, height: 140, // adjust this value as needed child: snapshot.data ?? - Image.asset('assets/images/profile_placeholder.png'), + const Image( + image: NetworkImage( + 'https://nidcap.org/wp-content/uploads/2021/03/book.png', + ), + ), ), SizedBox( width: 135, From 07f858727351b7b65d2390f02b51fea31b434265 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 6 Jul 2024 16:38:32 +0100 Subject: [PATCH 14/17] adjusting and rebasing --- uni/assets/images/book_placeholder.png | Bin 0 -> 4660 bytes uni/lib/controller/fetchers/book_fetcher.dart | 13 +++--- .../parsers/parser_course_unit_info.dart | 32 ++++++-------- .../lazy/course_units_info_provider.dart | 3 -- .../providers/startup/session_provider.dart | 2 +- uni/lib/view/bug_report/widgets/form.dart | 7 +-- .../common_widgets/generic_expandable.dart | 7 ++- .../common_widgets/last_update_timestamp.dart | 13 +++--- .../general/widgets/top_navigation_bar.dart | 2 +- .../view/common_widgets/toast_message.dart | 1 + .../widgets/course_unit_sheet.dart | 21 +++++---- .../view/home/widgets/exit_app_dialog.dart | 1 + .../home/widgets/remaining_exams_card.dart | 2 +- .../widgets/floorless_marker_popup.dart | 2 +- uni/lib/view/locations/widgets/marker.dart | 2 +- .../view/locations/widgets/marker_popup.dart | 2 +- .../profile/widgets/account_info_card.dart | 2 +- .../widgets/create_print_mb_dialog.dart | 10 +++-- .../profile/widgets/reference_section.dart | 2 +- uni/lib/view/theme.dart | 4 +- uni/pubspec.lock | 40 +++++++++--------- uni/pubspec.yaml | 2 +- 22 files changed, 85 insertions(+), 85 deletions(-) create mode 100644 uni/assets/images/book_placeholder.png diff --git a/uni/assets/images/book_placeholder.png b/uni/assets/images/book_placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..a2ebec47c01bcb89dbb1cbf983ad4c9105e893fd GIT binary patch literal 4660 zcmeHLYgE$N+GnasN>j-utt`_SG0Y2I@$O_gCSDrxQjwQIlSWMp!MkWv8kjYlM)QiA z6`JIw2GJCeGQ&)yj^-^C?=njISFunDeLY{^^}Zj@`EtI_+H0?8?Pu@bde+`+|Mvbp zPxj9~?)&y0-U|YO_IaFfMS(!y82zh*cWisU=p549Hpj!ypu9n#6etLEEfWOV*mhm} z3j~UXgFy4YfIxPIAdu!&T8pp4_QdWmPj^?)*1y_)PE{@l1TpY%J>{SDabaZ6Mfj$d zyv&%#b*Fr}g5rgO%o7w8xF z8coo6udWLyoTC0hOK%taCit7%e-HofX(UU&wb4OGn?!s)KMh=LLQB?u(=b>}Y9cYv zI_{_V26nglu7k_zx8tf=`RE42y9|b5^}xnjWKR+5?21}F!`!cDKlD>8MZ=)ot3xDL zclmY4Rs)YKzaRIzStX6-^tFn?%Kt;TV2!G_jydpKQDO4P2;73H4pB}8vfOA%V#qw< zc)Jn<9qpJ`iS~Qz%0R|{C0pjvqG|&??ok>HVRr-jxy~0hW#zfNEn5s!4-42>=Du$f zB)s&%et1{r(Maq<%#)XZ44}82w>%7C}upf=RtqwyYLyvj@4q$3%Ld~Y_6p% zu9-&EI4D-stA#WSv;j>eJvg?vnW0pwX6Y;n4|&Uf^j1%x1eB^_daXrDW0}x~%V{ae zIPyb%u_FC1xf4B)VA%ZgF$PLM&~cUbXjRP4=wR%R99>AjbJbqF@IO=A=y3%#yhqbc4;d)0*#tjiY`vJTjw=xkt&o3_3`JZP+zOInZ9{q}ccznl$_ zxJ@DdXu_+ewzmO&SD`>>_wF{~=2*t22DySZr~8)%%0%*c^iprx%lKl#kjC~#h&5bE z>N!ROAOlkTUP7jZWM-9v&Uc%0sc^cb$}6+2W1oUK8R*x ze=62_zy>-y-jT)23Z%)`e=@^D!?qXb*U}FdHV>eYb#5o`8s1~*SU<-5)`T`AuC74Z zG)(!Aj6TtF-`}#qCJ5HYv$6^chmyseu$_E?Z3?fnlb%N-_*CbmJg+jib0Pf-wS78^ zB(w!igT4FUOf^v|Kub+g?MzY$LhAOTqG9xWJJJ47RzA(sWUA*0>(L+EtVFuat<>o_ zD(n4#*{_FOA z-^}RD=H%D_q&{oiB!Kkm$I8g_)7d2!V>ANJy8PX&RB~iNp3V%3wJt%uwb$;erbt@4 z(w-IiR85Ygs!7Ghs&^tPEmh>&elUIB0l&kE4_4vyyQaq7ebl8aXl`Z9VKM1fgNjJr ztEc#M<079T%xsjXeA1||4{W*Y)(fT5H z_(TxAf_J2;UQT(Jcmc9k+TT+(N3;`Ylcd9vddsqlK2?2{ku7tpU%n&^sl~4F2M5pf z4X5?S>on(Pq&!-E1+AB~c-U8?32!CBdOYV@$P2aj3y?o4ve<;{vJmAiZI`8Px^(l4 zZ~@0A^)QoxSSM1k>1?C@i%`{~qsn_=U9XSc2|1s+%}JSQVX}3J88d1jQ2{t+%N93g zQ{p*)9^2LnAwSQDh~|~R`7PR>q-O# z137Bkn_%iu3vy%3s~pqEUSjE7TKW~u^6|?_WS<`qMf}+uO~*(3(tG3GQ}uVJXM)dO zleR?)H8}ynh&`Poh?c7Q#2zi`nNd%ZcbV#1N)HTn0DX~Qku!fRsce~g;i9RPgYw@J z;J;Bov$dMzUXSOg<@qSpP>f#K!GrnBryE0tF7KNLPHS}@&%m*$X~zwAL7eFM=%{K| zR4vO80)xTsln^B4U^nJqO&fp-?PFOruc1w@B@D zw>7mV*XIjAXzjm;Lc(1Ax1Y3vig&hRlxF~xfzE!Ohu&(D$nQ;vP4_Sf?XDs=wwINg zEqkd1&>N0W!4`9X2MgKhNA3+N(&v&;IBRDQEgD8Ni>v*M_vIZhx*TX?%jGqBp1PCa zT2oWqf{eu_e@}0iKK3Idc)V*W!tIP}^M~;Bn&l5q+zTT2SXX`UsWScOc7koXrHCB? z2+6HM#F4yX)YGPmzpk$6Ik+j*WoB)E-CzA88T2<+y4Zz7CrA96cFdd|K!$T z|D^}hyVGKive_NP$mY|>i~tWwl7yXK!!q>s)t_13{el6csyzmq=66>{)`YequBt*B zW9%;)g&}A9x!U1+6XYC6Ben0IDGR8%M~)2$)UV&R*g<>x#%6B9R$~XT^_0VmR`}u9 z=Lv@4c{)i&rwVC=Nm`;wg9bnnHaaB%3XE!J-_Aq#P*E_}04hpQJwRUnc?uq#jeNlR zl~h<=;rh8h-n_42U(U>m0~A9$oyl))X)~dxaXF;T*@A4!D*PO!s8s4LOaWYnQgw=< zmUjrhQ_7hkP6S_$OeFU}9O7;>$~G2JGKxd2lKz<}x}k2CE5q0=R5FK&b`i-#t`B`u6jKGM%~6npNj&20=8eVq_I_|c9E&OYN>7mF*L z`63(?*L3hlk~E9QOO?I*8ZsPGz=`P__CcJ3@ znU-~(FnXS67g@2#A}4qGtnqUlXrhimz@5`0R~82o6HQw}SNBMViH+w4v&^xw;x#kg zR6wm&0m@T>nFQM#IAyB0-@S55^xl`N&;6~alVB_PHo0H@qld8JjtoOe9F!@d%X(~^ zTG1x;bv|LA9lu6W5ovZeZKc!S#XXE=!{um}KzT8x+2# zvR<3G9?zwI)=7UoSy((?)%~RZv(;V)UCPoWxRhx9-Q9%*PvOEExnXX(@ff6tI2vYF zF2J5xYB=8RH%Tk$xy)SWr<@u$p6EFGcjEq^>J~#=J7GG$!Iuoa{dT*EqEB=?PrQU7 zM%abNMQj_8Io#aR7>+Q8TlcsfbYL_=S~-Z4Ai6&BwLQ<(KRK1MP)j?f?J) literal 0 HcmV?d00001 diff --git a/uni/lib/controller/fetchers/book_fetcher.dart b/uni/lib/controller/fetchers/book_fetcher.dart index 075aa4b7b..3c46fa06e 100644 --- a/uni/lib/controller/fetchers/book_fetcher.dart +++ b/uni/lib/controller/fetchers/book_fetcher.dart @@ -1,13 +1,14 @@ import 'dart:convert'; - -import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:uni/controller/networking/network_router.dart'; class BookThumbFetcher { - Future fetchBookThumb(String isbn) async { + Future fetchBookThumb(String isbn) async { final url = Uri.https( - 'www.googleapis.com', '/books/v1/volumes', {'q': '+isbn:$isbn'}); + 'www.googleapis.com', + '/books/v1/volumes', + {'q': '+isbn:$isbn'}, + ); final response = await http.get(Uri.decodeComponent(url.toString()).toUri()); @@ -16,10 +17,10 @@ class BookThumbFetcher { as Map)['items'] as List) .first as Map; - final thumbnail = + final thumbnailURL = ((bookInformation['volumeInfo'] as Map)['imageLinks'] as Map)['thumbnail']; - return Image(image: NetworkImage(thumbnail.toString())); + return thumbnailURL.toString(); } } diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index 04367a7ac..a6f5b48dc 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -1,11 +1,7 @@ -// ignore_for_file: unused_import - -import 'dart:collection'; import 'dart:convert'; import 'package:html/parser.dart'; import 'package:http/http.dart' as http; -import 'package:uni/controller/fetchers/book_fetcher.dart'; 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'; @@ -49,20 +45,20 @@ Future> parseFiles( Future parseSheet(http.Response response) async { final json = jsonDecode(response.body) as Map; - final professors = getCourseUnitProfessors(json['ds'] as List); - final regents = []; - final books = []; - - json['responsabilidades'].forEach((dynamic element) { - regents.add(Professor.fromJson(element as Map)); - }); - - json['bibliografia'].forEach((dynamic element) { - books.add(Book( + final professors = + getCourseUnitProfessors(json['ds'] as Iterable>); + final regents = + (json['responsabilidades'] as List>).map((element) { + return Professor.fromJson(element); + }).toList(); + + final books = + (json['bibliografia'] as List>).map((element) { + return Book( title: element['titulo'].toString(), isbn: element['isbn'].toString(), - )); - }); + ); + }).toList(); return Sheet( professors: professors, @@ -73,10 +69,10 @@ Future parseSheet(http.Response response) async { ); } -List getCourseUnitProfessors(List ds) { +List getCourseUnitProfessors(Iterable> ds) { final professors = []; for (final map in ds) { - for (final docente in map['docentes'] as Iterable) { + for (final docente in map['docentes'] as Iterable>) { final professor = Professor( code: docente['doc_codigo'].toString(), name: shortName(docente['nome'].toString()), 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 3051c3f8c..19ace085d 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,3 @@ -// ignore_for_file: unused_import - import 'dart:collection'; import 'package:tuple/tuple.dart'; @@ -7,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_directory.dart'; -import 'package:uni/model/entities/course_units/course_unit_sheet.dart'; import 'package:uni/model/entities/course_units/sheet.dart'; import 'package:uni/model/entities/session.dart'; import 'package:uni/model/providers/state_provider_notifier.dart'; diff --git a/uni/lib/model/providers/startup/session_provider.dart b/uni/lib/model/providers/startup/session_provider.dart index 037392a37..a2fb3566d 100644 --- a/uni/lib/model/providers/startup/session_provider.dart +++ b/uni/lib/model/providers/startup/session_provider.dart @@ -75,7 +75,7 @@ class SessionProvider extends StateProviderNotifier { persistentSession: persistentSession, ignoreCached: true, ); - } catch (e) { + } catch (error) { throw InternetStatusException(locale); } diff --git a/uni/lib/view/bug_report/widgets/form.dart b/uni/lib/view/bug_report/widgets/form.dart index 8d957ef94..38c165201 100644 --- a/uni/lib/view/bug_report/widgets/form.dart +++ b/uni/lib/view/bug_report/widgets/form.dart @@ -240,6 +240,7 @@ class BugReportFormState extends State { /// report is created in the project repository. /// If unsuccessful, the user receives an error message. Future submitBugReport() async { + final s = S.of(context); setState(() { _isButtonTapped = true; }); @@ -256,18 +257,18 @@ class BugReportFormState extends State { try { await submitSentryEvent(bugReport); Logger().i('Successfully submitted bug report.'); - if (context.mounted) toastMsg = S.of(context).success; + if (context.mounted) toastMsg = s.success; status = true; } catch (e, stackTrace) { await Sentry.captureException(e, stackTrace: stackTrace); Logger().e('Error while posting bug report:$e'); - if (context.mounted) toastMsg = S.of(context).sent_error; + if (context.mounted) toastMsg = s.sent_error; status = false; } clearForm(); - if (context.mounted) { + if (mounted) { FocusScope.of(context).requestFocus(FocusNode()); status ? await ToastMessage.success(context, toastMsg) diff --git a/uni/lib/view/common_widgets/generic_expandable.dart b/uni/lib/view/common_widgets/generic_expandable.dart index 4fae6e199..a22275c78 100644 --- a/uni/lib/view/common_widgets/generic_expandable.dart +++ b/uni/lib/view/common_widgets/generic_expandable.dart @@ -2,8 +2,11 @@ import 'package:expandable/expandable.dart'; import 'package:flutter/material.dart'; class GenericExpandable extends StatelessWidget { - const GenericExpandable( - {super.key, required this.title, required this.content}); + const GenericExpandable({ + required this.title, + required this.content, + super.key, + }); final String title; final Widget content; diff --git a/uni/lib/view/common_widgets/last_update_timestamp.dart b/uni/lib/view/common_widgets/last_update_timestamp.dart index 4e70543fe..3912aec73 100644 --- a/uni/lib/view/common_widgets/last_update_timestamp.dart +++ b/uni/lib/view/common_widgets/last_update_timestamp.dart @@ -24,13 +24,12 @@ class _LastUpdateTimeStampState> super.initState(); Timer.periodic( const Duration(seconds: 60), - (timer) => { - if (mounted) - { - setState(() { - currentTime = DateTime.now(); - }), - }, + (timer) { + if (mounted) { + setState(() { + currentTime = DateTime.now(); + }); + } }, ); } 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 fb1bbab51..a22633b05 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 @@ -51,7 +51,7 @@ class AppTopNavbar extends StatelessWidget implements PreferredSizeWidget { automaticallyImplyLeading: false, elevation: 0, iconTheme: Theme.of(context).iconTheme, - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, shadowColor: Theme.of(context).dividerColor, surfaceTintColor: Theme.of(context).colorScheme.onSecondary, titleSpacing: 0, diff --git a/uni/lib/view/common_widgets/toast_message.dart b/uni/lib/view/common_widgets/toast_message.dart index f8d7d7ee1..ca8271bf8 100644 --- a/uni/lib/view/common_widgets/toast_message.dart +++ b/uni/lib/view/common_widgets/toast_message.dart @@ -25,6 +25,7 @@ class MessageToast extends StatelessWidget { @override Widget build(BuildContext context) { + // ignore: deprecated_member_use, see #1210 return WillPopScope( onWillPop: () async => false, child: Dialog( diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 294cd5ac7..9d5e99054 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -1,8 +1,6 @@ import 'dart:io'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:provider/provider.dart'; import 'package:uni/controller/fetchers/book_fetcher.dart'; @@ -50,7 +48,7 @@ class CourseUnitSheetView extends StatelessWidget { style: TextStyle(fontSize: 20), ), if (courseUnitSheet.books.isNotEmpty) - buildBooksRow(context, courseUnitSheet.books) + buildBooksRow(context, courseUnitSheet.books), ], ), ), @@ -199,8 +197,8 @@ Widget buildBooksRow(BuildContext context, List books) { alignment: WrapAlignment.spaceBetween, children: [ ...books.asMap().entries.map((book) { - return FutureBuilder( - builder: (BuildContext context, AsyncSnapshot snapshot) { + return FutureBuilder( + builder: (BuildContext context, AsyncSnapshot snapshot) { return Padding( padding: const EdgeInsets.only(left: 15, right: 15, top: 10), child: Column( @@ -208,12 +206,13 @@ Widget buildBooksRow(BuildContext context, List books) { SizedBox( width: 135, height: 140, // adjust this value as needed - child: snapshot.data ?? - const Image( - image: NetworkImage( - 'https://nidcap.org/wp-content/uploads/2021/03/book.png', + child: snapshot.data != null + ? Image(image: NetworkImage(snapshot.data!)) + : const Image( + image: AssetImage( + 'assets/images/book_placeholder.png', + ), ), - ), ), SizedBox( width: 135, @@ -228,7 +227,7 @@ Widget buildBooksRow(BuildContext context, List books) { }, future: BookThumbFetcher().fetchBookThumb(book.value.isbn), ); - }) + }), ], ), ); diff --git a/uni/lib/view/home/widgets/exit_app_dialog.dart b/uni/lib/view/home/widgets/exit_app_dialog.dart index 02ac6f88c..5454f6e3e 100644 --- a/uni/lib/view/home/widgets/exit_app_dialog.dart +++ b/uni/lib/view/home/widgets/exit_app_dialog.dart @@ -14,6 +14,7 @@ class BackButtonExitWrapper extends StatelessWidget { @override Widget build(BuildContext context) { + // ignore: deprecated_member_use, see #1209 return WillPopScope( onWillPop: () { final userActionCompleter = Completer(); diff --git a/uni/lib/view/home/widgets/remaining_exams_card.dart b/uni/lib/view/home/widgets/remaining_exams_card.dart index f6fdb1eb0..87740ac61 100644 --- a/uni/lib/view/home/widgets/remaining_exams_card.dart +++ b/uni/lib/view/home/widgets/remaining_exams_card.dart @@ -19,7 +19,7 @@ class RemainingExamsWidget extends StatelessWidget { return Container( margin: const EdgeInsets.only(top: 8), child: RowContainer( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Container( padding: const EdgeInsets.all(11), child: Row( diff --git a/uni/lib/view/locations/widgets/floorless_marker_popup.dart b/uni/lib/view/locations/widgets/floorless_marker_popup.dart index 60b7b5574..1a9930c08 100644 --- a/uni/lib/view/locations/widgets/floorless_marker_popup.dart +++ b/uni/lib/view/locations/widgets/floorless_marker_popup.dart @@ -17,7 +17,7 @@ class FloorlessLocationMarkerPopup extends StatelessWidget { Widget build(BuildContext context) { final locations = locationGroup.floors.values.expand((x) => x).toList(); return Card( - color: Theme.of(context).colorScheme.background.withOpacity(0.8), + color: Theme.of(context).colorScheme.surface.withOpacity(0.8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), diff --git a/uni/lib/view/locations/widgets/marker.dart b/uni/lib/view/locations/widgets/marker.dart index c7ca2fb5e..8108da2fb 100644 --- a/uni/lib/view/locations/widgets/marker.dart +++ b/uni/lib/view/locations/widgets/marker.dart @@ -14,7 +14,7 @@ class LocationMarker extends Marker { point: latlng, builder: (BuildContext ctx) => DecoratedBox( decoration: BoxDecoration( - color: Theme.of(ctx).colorScheme.background, + color: Theme.of(ctx).colorScheme.surface, border: Border.all( color: Theme.of(ctx).colorScheme.primary, ), diff --git a/uni/lib/view/locations/widgets/marker_popup.dart b/uni/lib/view/locations/widgets/marker_popup.dart index 1c46a3fd9..f25f6030b 100644 --- a/uni/lib/view/locations/widgets/marker_popup.dart +++ b/uni/lib/view/locations/widgets/marker_popup.dart @@ -17,7 +17,7 @@ class LocationMarkerPopup extends StatelessWidget { @override Widget build(BuildContext context) { return Card( - color: Theme.of(context).colorScheme.background.withOpacity(0.8), + color: Theme.of(context).colorScheme.surface.withOpacity(0.8), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15), ), diff --git a/uni/lib/view/profile/widgets/account_info_card.dart b/uni/lib/view/profile/widgets/account_info_card.dart index b2fe21eee..2d78d24ee 100644 --- a/uni/lib/view/profile/widgets/account_info_card.dart +++ b/uni/lib/view/profile/widgets/account_info_card.dart @@ -145,7 +145,7 @@ class ReferenceList extends StatelessWidget { child: Text( S.of(context).no_references, style: Theme.of(context).textTheme.titleSmall, - textScaleFactor: 0.96, + textScaler: const TextScaler.linear(0.96), ), ); } 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 7198b32a8..393ea91d2 100644 --- a/uni/lib/view/profile/widgets/create_print_mb_dialog.dart +++ b/uni/lib/view/profile/widgets/create_print_mb_dialog.dart @@ -120,14 +120,16 @@ Future addMoneyDialog(BuildContext context) async { ); } -final CurrencyTextInputFormatter formatter = - CurrencyTextInputFormatter(locale: 'pt-PT', decimalDigits: 2, symbol: '€ '); +final formatter = CurrencyTextInputFormatter.currency( + locale: 'pt', + decimalDigits: 2, + symbol: '€ ', +); double valueTextToNumber(String value) => double.parse(value.substring(0, value.length - 2).replaceAll(',', '.')); -String numberToValueText(double number) => - formatter.format(number.toStringAsFixed(2)); +String numberToValueText(double number) => number.toStringAsFixed(2); Future generateReference(BuildContext context, double amount) async { if (amount < 1) { diff --git a/uni/lib/view/profile/widgets/reference_section.dart b/uni/lib/view/profile/widgets/reference_section.dart index d59b942f9..530ce54b9 100644 --- a/uni/lib/view/profile/widgets/reference_section.dart +++ b/uni/lib/view/profile/widgets/reference_section.dart @@ -43,7 +43,7 @@ class InfoText extends StatelessWidget { Widget build(BuildContext context) { return Text( text, - textScaleFactor: 0.9, + textScaler: const TextScaler.linear(0.9), style: Theme.of(context).textTheme.titleSmall?.copyWith( color: color, ), diff --git a/uni/lib/view/theme.dart b/uni/lib/view/theme.dart index c2eefca59..95477d53b 100644 --- a/uni/lib/view/theme.dart +++ b/uni/lib/view/theme.dart @@ -29,7 +29,7 @@ ThemeData applicationLightTheme = ThemeData( useMaterial3: true, colorScheme: ColorScheme.fromSeed( seedColor: darkRed, - background: _mildWhite, + surface: _mildWhite, primary: darkRed, onPrimary: Colors.white, secondary: darkRed, @@ -55,7 +55,7 @@ ThemeData applicationDarkTheme = ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: lightRed, brightness: Brightness.dark, - background: _darkBlack, + surface: _darkBlack, primary: _lightGrey, onPrimary: _darkishBlack, secondary: _lightBlue, diff --git a/uni/pubspec.lock b/uni/pubspec.lock index 673dc3837..d418d9288 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -301,10 +301,10 @@ packages: dependency: "direct main" description: name: currency_text_input_formatter - sha256: b60c298fec9f0e96dfad88d25d026a6bf43f4e2bb9c59218afd8de1e09f54a60 + sha256: d2eed8c7d40520729ac38252368954aba430a7ee510e5d5357e45cde1f6417a6 url: "https://pub.dev" source: hosted - version: "2.1.11" + version: "2.2.3" dart_style: dependency: transitive description: @@ -625,10 +625,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -689,26 +689,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lists: dependency: transitive description: @@ -769,10 +769,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mgrs_dart: dependency: transitive description: @@ -1271,26 +1271,26 @@ packages: dependency: "direct dev" description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.25.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.6.0" timelines: dependency: "direct main" description: @@ -1511,10 +1511,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" wakelock_plus: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 83e6e5b8f..2a7bdf69f 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -44,7 +44,7 @@ dependencies: html: ^0.15.0 http: ^1.1.0 image: ^4.1.4 - intl: ^0.18.0 + intl: ^0.19.0 latlong2: ^0.9.0 logger: ^2.0.2+1 material_design_icons_flutter: ^7.0.7296 From 975f40450aad35ef3729340776de26174ae36225 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 6 Jul 2024 16:46:37 +0100 Subject: [PATCH 15/17] formatting --- uni/lib/view/bug_report/widgets/form.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/view/bug_report/widgets/form.dart b/uni/lib/view/bug_report/widgets/form.dart index e6336f9b4..01fb7b0e0 100644 --- a/uni/lib/view/bug_report/widgets/form.dart +++ b/uni/lib/view/bug_report/widgets/form.dart @@ -241,7 +241,7 @@ class BugReportFormState extends State { /// If unsuccessful, the user receives an error message. Future submitBugReport() async { final s = S.of(context); - + setState(() { _isButtonTapped = true; }); From c3e8dfcd1082650e8c61f2c25dbc785350ebb7d5 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 6 Jul 2024 16:53:55 +0100 Subject: [PATCH 16/17] linting --- .../widgets/course_unit_sheet.dart | 12 ++++------ uni/pubspec.lock | 24 ++++++++++++------- uni/pubspec.yaml | 1 + 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index 9d5e99054..b6c72e6bf 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -71,9 +71,7 @@ Widget buildRegentsRow(BuildContext context, List regents) { child: Row( children: [ FutureBuilder( - builder: - (BuildContext context, AsyncSnapshot snapshot) => - _buildAvatar(snapshot, 40), + builder: (context, snapshot) => _buildAvatar(snapshot, 40), future: ProfileProvider.fetchOrGetCachedProfilePicture( session, studentNumber: int.parse(regent.value.code), @@ -118,9 +116,7 @@ Widget buildProfessorsRow(BuildContext context, List professors) { return Row( children: [ FutureBuilder( - builder: - (BuildContext context, AsyncSnapshot snapshot) => - Transform.translate( + builder: (context, snapshot) => Transform.translate( offset: Offset(-10.0 * idx, 0), child: _buildAvatar(snapshot, 20), ), @@ -158,7 +154,7 @@ Widget buildExpandedProfessors( children: [ FutureBuilder( builder: - (BuildContext context, AsyncSnapshot snapshot) => + (context, snapshot) => _buildAvatar(snapshot, 20), future: ProfileProvider.fetchOrGetCachedProfilePicture( session, @@ -198,7 +194,7 @@ Widget buildBooksRow(BuildContext context, List books) { children: [ ...books.asMap().entries.map((book) { return FutureBuilder( - builder: (BuildContext context, AsyncSnapshot snapshot) { + builder: (context, snapshot) { return Padding( padding: const EdgeInsets.only(left: 15, right: 15, top: 10), child: Column( diff --git a/uni/pubspec.lock b/uni/pubspec.lock index e03008252..b201d1c82 100644 --- a/uni/pubspec.lock +++ b/uni/pubspec.lock @@ -209,6 +209,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.7.5" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" cli_util: dependency: transitive description: @@ -361,6 +369,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.17" + expandable: + dependency: "direct main" + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.dev" + source: hosted + version: "5.0.1" expansion_tile_card: dependency: "direct main" description: @@ -1547,14 +1563,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - very_good_analysis: - dependency: "direct dev" - description: - name: very_good_analysis - sha256: "9ae7f3a3bd5764fb021b335ca28a34f040cd0ab6eec00a1b213b445dae58a4b8" - url: "https://pub.dev" - source: hosted - version: "5.1.0" video_player: dependency: transitive description: diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index fbbff79e2..1b2147fec 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -24,6 +24,7 @@ dependencies: currency_text_input_formatter: ^2.1.5 diacritic: ^0.1.5 email_validator: ^2.0.1 + expandable: ^5.0.1 expansion_tile_card: ^3.0.0 flutter: sdk: flutter From b0a8403b013028f5b8af9501842201f78f965839 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Sat, 6 Jul 2024 16:55:15 +0100 Subject: [PATCH 17/17] formatting --- .../parsers/parser_course_unit_info.dart | 26 ++++++---- .../widgets/course_unit_sheet.dart | 52 +++++++------------ 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/uni/lib/controller/parsers/parser_course_unit_info.dart b/uni/lib/controller/parsers/parser_course_unit_info.dart index cc2c25808..51025c96b 100644 --- a/uni/lib/controller/parsers/parser_course_unit_info.dart +++ b/uni/lib/controller/parsers/parser_course_unit_info.dart @@ -47,15 +47,21 @@ Future> parseFiles( Future parseSheet(http.Response response) async { final json = jsonDecode(response.body) as Map; - final professors = - getCourseUnitProfessors(json['ds'] as Iterable>); - final regents = - (json['responsabilidades'] as List>).map((element) { - return Professor.fromJson(element); + + final professors = getCourseUnitProfessors( + (json['ds'] as List) + .map((element) => element as Map) + .toList(), + ); + + final regents = (json['responsabilidades'] as List).map((element) { + return Professor.fromJson(element as Map); }).toList(); - final books = - (json['bibliografia'] as List>).map((element) { + final books = (json['bibliografia'] as List) + .map((element) => element as Map) + .toList() + .map((element) { return Book( title: element['titulo'].toString(), isbn: element['isbn'].toString(), @@ -71,12 +77,12 @@ Future parseSheet(http.Response response) async { ); } -List getCourseUnitProfessors(Iterable> ds) { +List getCourseUnitProfessors(List> ds) { final professors = []; for (final map in ds) { - for (final docente in map['docentes'] as Iterable>) { + for (final docente in map['docentes'] as List) { final professor = Professor( - code: docente['doc_codigo'].toString(), + code: (docente as Map)['doc_codigo'].toString(), name: shortName(docente['nome'].toString()), classes: [map['tipo'].toString()], ); diff --git a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart index b6c72e6bf..f5cdb1fd5 100644 --- a/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart +++ b/uni/lib/view/course_unit_info/widgets/course_unit_sheet.dart @@ -82,15 +82,10 @@ Widget buildRegentsRow(BuildContext context, List regents) { left: 10, right: 10, ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - regent.value.name, - style: const TextStyle(fontSize: 17), - ), - ], + child: Text( + regent.value.name, + style: const TextStyle(fontSize: 17), + textAlign: TextAlign.center, ), ), ], @@ -113,19 +108,15 @@ Widget buildProfessorsRow(BuildContext context, List professors) { children: [ ...professors.asMap().entries.map((professor) { final idx = professor.key; - return Row( - children: [ - FutureBuilder( - builder: (context, snapshot) => Transform.translate( - offset: Offset(-10.0 * idx, 0), - child: _buildAvatar(snapshot, 20), - ), - future: ProfileProvider.fetchOrGetCachedProfilePicture( - session, - studentNumber: int.parse(professor.value.code), - ), - ), - ], + return FutureBuilder( + builder: (context, snapshot) => Transform.translate( + offset: Offset(-10.0 * idx, 0), + child: _buildAvatar(snapshot, 20), + ), + future: ProfileProvider.fetchOrGetCachedProfilePicture( + session, + studentNumber: int.parse(professor.value.code), + ), ); }), ], @@ -153,9 +144,7 @@ Widget buildExpandedProfessors( child: Row( children: [ FutureBuilder( - builder: - (context, snapshot) => - _buildAvatar(snapshot, 20), + builder: (context, snapshot) => _buildAvatar(snapshot, 20), future: ProfileProvider.fetchOrGetCachedProfilePicture( session, studentNumber: int.parse(professor.value.code), @@ -165,15 +154,10 @@ Widget buildExpandedProfessors( padding: const EdgeInsets.only( left: 10, ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - professor.value.name, - style: const TextStyle(fontSize: 14), - ), - ], + child: Text( + professor.value.name, + style: const TextStyle(fontSize: 14), + textAlign: TextAlign.center, ), ), ],