diff --git a/.gitignore b/.gitignore index 2d983ca1..7a3eef47 100644 --- a/.gitignore +++ b/.gitignore @@ -145,6 +145,7 @@ app.*.symbols *.graphql.dart *.tailor.dart *.gen.dart +*.gr.dart diff --git a/README.md b/README.md index ecf01ac9..f9093dd3 100644 --- a/README.md +++ b/README.md @@ -143,6 +143,8 @@ fvm flutter run 2. Introductions to internationalizing flutter apps (making them available in different languages): https://docs.flutter.dev/ui/accessibility-and-internationalization/internationalization 3. Api helper module instructions: https://github.com/Solvro/topwr-mobile/tree/main/lib/api_base 4. We use `flutter_gen` for generating asset paths: https://pub.dev/packages/flutter_gen +5. For unified names read and follow: [taxonomy.md](./taxonomy.md) +6. For navigation we use `auto_route`, docs here: https://pub.dev/packages/auto_route # Before you push a commit - run the linter diff --git a/lib/api_base/directus_assets_url.dart b/lib/api_base/directus_assets_url.dart index 97eacbe8..b685b609 100644 --- a/lib/api_base/directus_assets_url.dart +++ b/lib/api_base/directus_assets_url.dart @@ -1,12 +1,12 @@ import "../config/api_base_config.dart"; -extension DirectusAssetsUrl on String { +extension DirectusAssetsUrlX on String { String get directusUrl { return "${ApiBaseEnv.assetsUrl}/$this"; } } -extension DirectusAssetsUrlNullable on String? { +extension DirectusAssetsUrlNullableX on String? { String get directusUrl { return this?.directusUrl ?? ""; } diff --git a/lib/api_base/ttl/local_timestamp_repository.dart b/lib/api_base/ttl/local_timestamp_repository.dart index 75ef72be..959fdcb2 100644 --- a/lib/api_base/ttl/local_timestamp_repository.dart +++ b/lib/api_base/ttl/local_timestamp_repository.dart @@ -9,11 +9,11 @@ import "ttl_timestamp.dart"; part "local_timestamp_repository.g.dart"; -class LocalTimestampRepo { +class LocalTimestampRepository { final TtlKey _key; final SharedPreferences _prefs; - LocalTimestampRepo(this._key, this._prefs); + LocalTimestampRepository(this._key, this._prefs); String get _storeKey => "${ApiBaseConfig.ttlPrefsPrefix}$_key"; @@ -36,10 +36,10 @@ Future _prefs(_PrefsRef ref) async { } @riverpod -Future localTimestampRepo( - LocalTimestampRepoRef ref, +Future localTimestampRepository( + LocalTimestampRepositoryRef ref, TtlKey key, ) async { final prefs = await ref.watch(_prefsProvider.future); - return LocalTimestampRepo(key, prefs); + return LocalTimestampRepository(key, prefs); } diff --git a/lib/api_base/ttl/ttl_service.dart b/lib/api_base/ttl/ttl_service.dart index d81e11fc..4d2eee6b 100644 --- a/lib/api_base/ttl/ttl_service.dart +++ b/lib/api_base/ttl/ttl_service.dart @@ -20,8 +20,8 @@ class TtlService extends _$TtlService { return FetchPolicy.cacheAndNetwork; // force re-fetch } - Future get repository async => - ref.watch(localTimestampRepoProvider.call(key).future); + Future get repository async => + ref.watch(localTimestampRepositoryProvider.call(key).future); Future> interceptAndSaveTimestamps( QueryResult event, diff --git a/lib/api_base/watch_query_adapter.dart b/lib/api_base/watch_query_adapter.dart index 1285595f..c4f3e6be 100644 --- a/lib/api_base/watch_query_adapter.dart +++ b/lib/api_base/watch_query_adapter.dart @@ -10,7 +10,7 @@ class GqlOfflineException implements Exception { final TtlKey ttlKey; } -extension _WatchQueryStreamAdapter on Ref { +extension _WatchQueryStreamAdapterX on Ref { void handleErrors(QueryResult event, TtlKey ttlKey) { if (!event.hasException) return; @@ -39,7 +39,7 @@ extension _WatchQueryStreamAdapter on Ref { } } -extension TTLWatchQueryAdapter on AutoDisposeStreamProviderRef { +extension TTLWatchQueryAdapterX on AutoDisposeStreamProviderRef { Stream watchQueryWithCache( WatchQueryOptions watchQueryOptions, TtlKey ttlKey, diff --git a/lib/config/url_icons.dart b/lib/config/contact_icons.dart similarity index 92% rename from lib/config/url_icons.dart rename to lib/config/contact_icons.dart index 4b042389..f8c77688 100644 --- a/lib/config/url_icons.dart +++ b/lib/config/contact_icons.dart @@ -1,6 +1,6 @@ import "../gen/assets.gen.dart"; -abstract class IconsConfig { +abstract class ContactIconsConfig { static final iconsPaths = { "facebook": Assets.contactIcons.fb, "instagram": Assets.contactIcons.ig, diff --git a/lib/config/map_view_config.dart b/lib/config/map_view_config.dart index ab6d24b7..59a8976b 100644 --- a/lib/config/map_view_config.dart +++ b/lib/config/map_view_config.dart @@ -36,8 +36,10 @@ abstract class MapWidgetConfig { ); static const mapType = MapType.normal; - static const mapMarkerOriginWidth = 28; - static const activeMapMarkerOriginWidth = 40; + static const mapMarkerWidth = 22.0; + static const mapMarkerHeight = 34.0; + static const activeMapMarkerWidth = 30.4; + static const activeMapMarkerHeight = 46.4; } abstract class BuildingSearchConfig { diff --git a/lib/config/nav_bar_config.dart b/lib/config/nav_bar_config.dart index 2aef1bc2..1d3666b0 100644 --- a/lib/config/nav_bar_config.dart +++ b/lib/config/nav_bar_config.dart @@ -1,19 +1,22 @@ +import "package:auto_route/auto_route.dart"; +import "package:collection/collection.dart"; import "package:enum_map/enum_map.dart"; import "package:flutter/material.dart"; import "../features/bottom_nav_bar/bottom_nav_bar_icon_icons.icons.dart"; -import "../features/iparking/widgets/i_parking_icons_icons.icons.dart"; +import "../features/navigator/app_router.dart"; +import "../features/parkings_view/widgets/parkings_icons.icons.dart"; part "nav_bar_config.g.dart"; @unmodifiableEnumMap enum NavBarEnum { home(BottomNavBarIcon.home_icon, 26, "Home"), - mapp(BottomNavBarIcon.map_icon, 20, "Map"), - parkings(IParkingIcons.directions_car, 19, "Parkings"), - faculties(BottomNavBarIcon.faculty_icon, 26, "Faculties"), - sciCircles(BottomNavBarIcon.sci_circle_icon, 20, "Scientific Circles"), - info(BottomNavBarIcon.info_icon, 20, "Info"); + buildings(BottomNavBarIcon.map_icon, 20, "Map"), + parkings(ParkingsIcons.directions_car, 19, "Parkings"), + departments(BottomNavBarIcon.faculty_icon, 26, "Faculties"), + scienceClubs(BottomNavBarIcon.sci_circle_icon, 20, "Science Clubs"), + guide(BottomNavBarIcon.info_icon, 20, "Info"); const NavBarEnum(this.icon, this.size, this.label); @@ -21,3 +24,29 @@ enum NavBarEnum { final double size; final String label; } + +abstract class NavBarConfig { + static const tabViews = UnmodifiableNavBarEnumMap( + home: HomeRoute(), + buildings: BuildingsRoute(), + parkings: ParkingsRoute(), + departments: DepartmentsRoute(), + scienceClubs: ScienceClubsRoute(), + guide: GuideRoute(), + ); +} + +extension IsRouteATabViewOnStringX on String { + NavBarEnum? get tabBarEnum => NavBarConfig.tabViews.entries + .firstWhereOrNull((entry) => entry.value.routeName == this) + ?.key; + + bool get isTabView => + NavBarConfig.tabViews.values.map((i) => i.routeName).contains(this); +} + +extension IsRouteATabViewX on PageRouteInfo { + NavBarEnum? get tabBarEnum => routeName.tabBarEnum; + + bool get isTabView => routeName.isTabView; +} diff --git a/lib/config/navigator_config.dart b/lib/config/navigator_config.dart deleted file mode 100644 index db1a70a1..00000000 --- a/lib/config/navigator_config.dart +++ /dev/null @@ -1,22 +0,0 @@ -import "../features/buildings_map/buildings_view.dart"; -import "../features/departments_tab/departments_tab.dart"; -import "../features/guide/guide_view_template.dart"; -import "../features/home_view/home_view.dart"; -import "../features/iparking/parking_view.dart"; -import "../features/student_research_group_tab/scientific_circles_tab.dart"; -import "nav_bar_config.dart"; - -abstract class NavigatorConfig { - static const initialTab = NavBarEnum.home; -} - -abstract class TabsConfig { - static const tabs = UnmodifiableNavBarEnumMap( - home: HomeView(), - mapp: BuildingMapView(), - faculties: DepartmentTab(), - sciCircles: ScientificCirclesTab(), - parkings: ParkingsMapView(), - info: GuideViewTemplate(), - ); -} diff --git a/lib/config/routes.dart b/lib/config/routes.dart deleted file mode 100644 index bc1ef840..00000000 --- a/lib/config/routes.dart +++ /dev/null @@ -1,8 +0,0 @@ -abstract class AppRoutes { - AppRoutes._(); - - static const root = "/"; - static const studyCircleDetails = "study-circle-details"; - static const aboutUsDetail = "about-us-detail"; - static const departmentDetails = "department-details"; -} diff --git a/lib/config/transitions.dart b/lib/config/transitions.dart new file mode 100644 index 00000000..ff957ba4 --- /dev/null +++ b/lib/config/transitions.dart @@ -0,0 +1,8 @@ +import "package:auto_route/auto_route.dart"; + +// TODO(simon-the-shark): adjust this settings if desired +abstract class TransitionsConfig { + static const durationInMiliseconds = 200; + static const slideLeftBuilder = TransitionsBuilders.slideLeftWithFade; + static const slideRightBuilder = TransitionsBuilders.slideRightWithFade; +} diff --git a/lib/config/ttl_config.dart b/lib/config/ttl_config.dart index 12ab70fa..577fccea 100644 --- a/lib/config/ttl_config.dart +++ b/lib/config/ttl_config.dart @@ -6,15 +6,15 @@ part "ttl_config.g.dart"; @unmodifiableEnumMap enum TtlKey { /// We need unique key for every data source for its ttl tracking - sciCirclesPreviewRepository, academicCalendarRepository, - infosPreviewRepository, - sciCirclesRepository, + newsRepository, + scienceClubsRepository, + scienceClubDetailsRepository, tagsRepository, - mapBuildingsRepository, departmentsRepository, + departmentDetailsRepository, aboutUsRepository, - departmentsDetailsRepository, + buildingsRepository, // ... add a new key here if you create a new repository } @@ -26,15 +26,15 @@ abstract class TtlStrategy { static const _ttlDurations = UnmodifiableTtlKeyMap( // TODO(simon-the-shark): specific values are yet ment to be accordingly adjusted. - infosPreviewRepository: day, + newsRepository: day, academicCalendarRepository: day, - sciCirclesPreviewRepository: thirtyDays, - sciCirclesRepository: thirtyDays, + scienceClubDetailsRepository: thirtyDays, + scienceClubsRepository: thirtyDays, tagsRepository: thirtyDays, - mapBuildingsRepository: thirtyDays, + buildingsRepository: thirtyDays, departmentsRepository: thirtyDays, aboutUsRepository: thirtyDays, - departmentsDetailsRepository: thirtyDays, + departmentDetailsRepository: thirtyDays, ); static Duration get(TtlKey key) { diff --git a/lib/config/ui_config.dart b/lib/config/ui_config.dart index 43410e35..6b3fdce1 100644 --- a/lib/config/ui_config.dart +++ b/lib/config/ui_config.dart @@ -1,6 +1,6 @@ import "package:flutter/material.dart"; -import "../features/iparking_chart/utils/range_hour_points.dart"; +import "../features/parking_chart/utils/range_hour_points.dart"; import "../theme/hex_color.dart"; abstract class MyAppConfig { @@ -24,7 +24,7 @@ abstract class DepartmentsConfig { static const listSeparatorSize = 16.0; - static const departmentsTabGridDelegate = + static const departmentsViewGridDelegate = SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 600, mainAxisExtent: 92, @@ -37,7 +37,7 @@ abstract class DateChipConfig { static const dateTimeFormat = "dd.MM.yyyy"; } -abstract class HomeScreenConfig { +abstract class HomeViewConfig { static const paddingSmall = 6.0; static const paddingMedium = 16.0; @@ -55,7 +55,7 @@ abstract class BigPreviewCardConfig { static const cardWidth = 240.0; } -abstract class SearchWidgetConfig { +abstract class SearchBoxConfig { static const height = 36.0; } @@ -77,15 +77,15 @@ abstract class WideTileCardConfig { ]; } -abstract class DetailsScreenConfig { +abstract class DetailViewsConfig { static const double spacerHeight = 16; } -abstract class DetailsScreenHeaderConfig { +abstract class DetailViewsHeaderConfig { static const double logoSize = 130; } -abstract class ScientificCirclesTabConfig { +abstract class ScienceClubsViewConfig { static const listSeparatorSize = 16.0; static const microPadding = 4.0; static const smallPadding = 16.0; @@ -109,11 +109,11 @@ abstract class ScientificCirclesTabConfig { ); } -abstract class ScientificCircleCardConfig { +abstract class ScienceClubCardConfig { static const trailingPadding = 2.0; } -abstract class IParkingConfig { +abstract class ParkingsConfig { static const padding = EdgeInsets.only( left: 13, top: 10, diff --git a/lib/features/guide/widgets/about_us/about_us_tab.dart b/lib/features/about_us_view/about_us_view.dart similarity index 75% rename from lib/features/guide/widgets/about_us/about_us_tab.dart rename to lib/features/about_us_view/about_us_view.dart index 83950edf..1a568664 100644 --- a/lib/features/guide/widgets/about_us/about_us_tab.dart +++ b/lib/features/about_us_view/about_us_view.dart @@ -1,26 +1,28 @@ +import "package:auto_route/auto_route.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "../../../../api_base/directus_assets_url.dart"; -import "../../../../config/ui_config.dart"; -import "../../../../theme/app_theme.dart"; -import "../../../../utils/context_extensions.dart"; -import "../../../../widgets/details_screen_sliver_header_section.dart"; -import "../../../../widgets/my_error_widget.dart"; -import "../../../study_circle_details/widgets/details_screen_app_bar.dart"; +import "../../api_base/directus_assets_url.dart"; +import "../../config/ui_config.dart"; +import "../../theme/app_theme.dart"; +import "../../utils/context_extensions.dart"; +import "../../widgets/detail_views/detail_view_app_bar.dart"; +import "../../widgets/detail_views/sliver_header_section.dart"; +import "../../widgets/my_error_widget.dart"; import "repository/about_us_repository.dart"; +import "widgets/contact_section.dart"; import "widgets/desription_section.dart"; -import "widgets/links_section.dart"; import "widgets/section_header.dart"; import "widgets/team_section.dart"; -class AboutUsTab extends StatelessWidget { - const AboutUsTab({super.key}); +@RoutePage() +class AboutUsView extends StatelessWidget { + const AboutUsView({super.key}); @override Widget build(BuildContext context) { return Scaffold( - appBar: DetailsScreenAppBar(context, title: context.localize.guide), + appBar: DetailViewAppBar(context, title: context.localize.guide), body: const _AboutUsView(), ); } @@ -59,7 +61,7 @@ class _AboutUsView extends ConsumerWidget { members: value?.getMemberData() ?? [], ), SectionHeader(text: context.localize.follow_solvro), - LinksSection( + ContactSection( links: value?.getSocialIcons() ?? [], ), const SizedBox( diff --git a/lib/features/guide/widgets/about_us/models/about_us_details.dart b/lib/features/about_us_view/models/about_us_details.dart similarity index 72% rename from lib/features/guide/widgets/about_us/models/about_us_details.dart rename to lib/features/about_us_view/models/about_us_details.dart index df80893e..36f492d8 100644 --- a/lib/features/guide/widgets/about_us/models/about_us_details.dart +++ b/lib/features/about_us_view/models/about_us_details.dart @@ -1,7 +1,7 @@ -import "../../../../../api_base/directus_assets_url.dart"; +import "../../../api_base/directus_assets_url.dart"; -import "../../../../../utils/determine_icon.dart"; -import "../../../../../utils/where_non_null_iterable.dart"; +import "../../../utils/determine_contact_icon.dart"; +import "../../../utils/where_non_null_iterable.dart"; import "../repository/about_us_repository.dart"; import "member_data.dart"; @@ -22,10 +22,10 @@ class AboutUsDetails { }).toList(); } - List getSocialIcons() { + List getSocialIcons() { return aboutUs?.solvroSocialLinks.whereNonNull .map( - (e) => UrlIconsModel(url: e.url), + (e) => ContactIconsModel(url: e.url), ) .toList() ?? []; diff --git a/lib/features/guide/widgets/about_us/models/member_data.dart b/lib/features/about_us_view/models/member_data.dart similarity index 78% rename from lib/features/guide/widgets/about_us/models/member_data.dart rename to lib/features/about_us_view/models/member_data.dart index 283eb9ab..fc82f1ee 100644 --- a/lib/features/guide/widgets/about_us/models/member_data.dart +++ b/lib/features/about_us_view/models/member_data.dart @@ -1,13 +1,13 @@ import "package:flutter/foundation.dart"; -import "../../../../../utils/determine_icon.dart"; +import "../../../utils/determine_contact_icon.dart"; @immutable class MemberData { final List socialLinks; final String? name; final String? subtitle; - final List links; + final List links; final String? imageUrl; MemberData({ @@ -17,8 +17,8 @@ class MemberData { required this.imageUrl, }) : links = _determineLinksIcons(socialLinks); - static List _determineLinksIcons(List urls) { - return urls.map((url) => UrlIconsModel(url: url)).toList(); + static List _determineLinksIcons(List urls) { + return urls.map((url) => ContactIconsModel(url: url)).toList(); } @override diff --git a/lib/features/guide/widgets/about_us/repository/about_us_repository.dart b/lib/features/about_us_view/repository/about_us_repository.dart similarity index 80% rename from lib/features/guide/widgets/about_us/repository/about_us_repository.dart rename to lib/features/about_us_view/repository/about_us_repository.dart index 8d528862..b66f3672 100644 --- a/lib/features/guide/widgets/about_us/repository/about_us_repository.dart +++ b/lib/features/about_us_view/repository/about_us_repository.dart @@ -1,7 +1,7 @@ import "package:riverpod_annotation/riverpod_annotation.dart"; -import "../../../../../api_base/watch_query_adapter.dart"; -import "../../../../../config/ttl_config.dart"; +import "../../../api_base/watch_query_adapter.dart"; +import "../../../config/ttl_config.dart"; import "../models/about_us_details.dart"; import "getAboutUsDetails.graphql.dart"; @@ -14,9 +14,7 @@ typedef AboutUsTeam = Query$getAbousUsDetails$AboutUs_Team; @riverpod Stream aboutUsRepository(AboutUsRepositoryRef ref) async* { final stream = ref.watchQueryWithCache( - _GetAboutUs( - eagerlyFetchResults: true, - ), + _GetAboutUs(eagerlyFetchResults: true), TtlKey.aboutUsRepository, ); yield* stream.map((event) { diff --git a/lib/features/guide/widgets/about_us/repository/getAboutUsDetails.graphql b/lib/features/about_us_view/repository/getAboutUsDetails.graphql similarity index 100% rename from lib/features/guide/widgets/about_us/repository/getAboutUsDetails.graphql rename to lib/features/about_us_view/repository/getAboutUsDetails.graphql diff --git a/lib/features/guide/widgets/about_us/utils/convert_html.dart b/lib/features/about_us_view/utils/html_util_extensions.dart similarity index 62% rename from lib/features/guide/widgets/about_us/utils/convert_html.dart rename to lib/features/about_us_view/utils/html_util_extensions.dart index 3e59df7d..20f5b5d7 100644 --- a/lib/features/guide/widgets/about_us/utils/convert_html.dart +++ b/lib/features/about_us_view/utils/html_util_extensions.dart @@ -1,22 +1,21 @@ import "package:flutter/material.dart"; -// unfortunetly it's a dependency of dependency and it's not re-exported -// ignore: depend_on_referenced_packages import "package:html/dom.dart" as html; -import "../../../../../theme/app_theme.dart"; -import "../../../../../theme/hex_color.dart"; -extension ToHtmlColorString on HexColor { +import "../../../theme/app_theme.dart"; +import "../../../theme/hex_color.dart"; + +extension ToHtmlColorStringX on HexColor { String get htmlFormat => '#${value.toRadixString(16).padLeft(8, '0').substring(2)}'; } -extension IsLinkTag on html.Element { +extension IsLinkTagX on html.Element { bool get isLink { return localName == "a"; } } -extension CustomHtmlStyles on BuildContext { +extension CustomHtmlStylesX on BuildContext { Map? customStylesBuilder(html.Element element) { final defaultStyles = { "text-align": "justify", diff --git a/lib/features/guide/widgets/about_us/widgets/links_section.dart b/lib/features/about_us_view/widgets/contact_section.dart similarity index 63% rename from lib/features/guide/widgets/about_us/widgets/links_section.dart rename to lib/features/about_us_view/widgets/contact_section.dart index 2fa4a44c..e80af7f1 100644 --- a/lib/features/guide/widgets/about_us/widgets/links_section.dart +++ b/lib/features/about_us_view/widgets/contact_section.dart @@ -1,15 +1,15 @@ import "package:flutter/material.dart"; import "package:flutter_svg/svg.dart"; -import "../../../../../config/ui_config.dart"; -import "../../../../../theme/app_theme.dart"; -import "../../../../../utils/determine_icon.dart"; -import "../../../../../utils/launch_url_util.dart"; +import "../../../config/ui_config.dart"; +import "../../../theme/app_theme.dart"; +import "../../../utils/determine_contact_icon.dart"; +import "../../../utils/launch_url_util.dart"; -class LinksSection extends StatelessWidget { - const LinksSection({super.key, required this.links}); +class ContactSection extends StatelessWidget { + const ContactSection({super.key, required this.links}); - final List links; + final List links; @override Widget build(BuildContext context) { @@ -20,15 +20,15 @@ class LinksSection extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ for (final item in links) - _IconWithUrl(url: item.url ?? "", icon: item.icon), + _ContactIcon(url: item.url ?? "", icon: item.icon), ], ), ); } } -class _IconWithUrl extends StatelessWidget { - const _IconWithUrl({required this.url, required this.icon}); +class _ContactIcon extends StatelessWidget { + const _ContactIcon({required this.url, required this.icon}); final String url; final String icon; @@ -49,9 +49,7 @@ class _IconWithUrl extends StatelessWidget { AboutUsConfig.iconPadding, ), child: Center( - child: SvgPicture.asset( - icon, - ), + child: SvgPicture.asset(icon), ), ), ), diff --git a/lib/features/guide/widgets/about_us/widgets/desription_section.dart b/lib/features/about_us_view/widgets/desription_section.dart similarity index 69% rename from lib/features/guide/widgets/about_us/widgets/desription_section.dart rename to lib/features/about_us_view/widgets/desription_section.dart index 017a8d64..8ba3ec18 100644 --- a/lib/features/guide/widgets/about_us/widgets/desription_section.dart +++ b/lib/features/about_us_view/widgets/desription_section.dart @@ -1,10 +1,10 @@ import "package:flutter/material.dart"; import "package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart"; -import "../../../../../config/ui_config.dart"; -import "../../../../../theme/app_theme.dart"; -import "../../../../../utils/launch_url_util.dart"; -import "../utils/convert_html.dart"; +import "../../../config/ui_config.dart"; +import "../../../theme/app_theme.dart"; +import "../../../utils/launch_url_util.dart"; +import "../utils/html_util_extensions.dart"; class DescriptionSection extends StatelessWidget { const DescriptionSection({super.key, required this.text}); @@ -26,12 +26,9 @@ class DescriptionSection extends StatelessWidget { child: HtmlWidget( text, textStyle: context.aboutUsTheme.body, - customStylesBuilder: (element) => context.customStylesBuilder( - element, - ), - onTapUrl: (url) async { - return LaunchUrlUtil.launch(url); - }, + customStylesBuilder: (element) => + context.customStylesBuilder(element), + onTapUrl: LaunchUrlUtil.launch, ), ), ), diff --git a/lib/features/guide/widgets/about_us/widgets/section_header.dart b/lib/features/about_us_view/widgets/section_header.dart similarity index 85% rename from lib/features/guide/widgets/about_us/widgets/section_header.dart rename to lib/features/about_us_view/widgets/section_header.dart index 94a28ef7..94e17774 100644 --- a/lib/features/guide/widgets/about_us/widgets/section_header.dart +++ b/lib/features/about_us_view/widgets/section_header.dart @@ -1,7 +1,7 @@ import "package:flutter/material.dart"; -import "../../../../../config/ui_config.dart"; -import "../../../../../theme/app_theme.dart"; +import "../../../config/ui_config.dart"; +import "../../../theme/app_theme.dart"; class SectionHeader extends StatelessWidget { const SectionHeader({super.key, required this.text}); diff --git a/lib/features/guide/widgets/about_us/widgets/team_section.dart b/lib/features/about_us_view/widgets/team_section.dart similarity index 91% rename from lib/features/guide/widgets/about_us/widgets/team_section.dart rename to lib/features/about_us_view/widgets/team_section.dart index 4c7500f6..c1e65e35 100644 --- a/lib/features/guide/widgets/about_us/widgets/team_section.dart +++ b/lib/features/about_us_view/widgets/team_section.dart @@ -1,12 +1,12 @@ import "package:flutter/material.dart"; import "package:flutter_svg/svg.dart"; -import "../../../../../config/ui_config.dart"; -import "../../../../../theme/app_theme.dart"; -import "../../../../../utils/determine_icon.dart"; -import "../../../../../utils/launch_url_util.dart"; +import "../../../config/ui_config.dart"; +import "../../../theme/app_theme.dart"; +import "../../../utils/determine_contact_icon.dart"; +import "../../../utils/launch_url_util.dart"; -import "../../../../../widgets/my_cached_image.dart"; +import "../../../widgets/my_cached_image.dart"; import "../models/member_data.dart"; class TeamSection extends StatelessWidget { @@ -91,7 +91,7 @@ class _Description extends StatelessWidget { }); final String name; final String subtitle; - final List links; + final List links; @override Widget build(BuildContext context) { diff --git a/lib/features/academic_calendar_greeting/model/academic_calendar_data.dart b/lib/features/academic_calendar/model/academic_calendar_extensions.dart similarity index 90% rename from lib/features/academic_calendar_greeting/model/academic_calendar_data.dart rename to lib/features/academic_calendar/model/academic_calendar_extensions.dart index 543d6dd9..9a8a1ca0 100644 --- a/lib/features/academic_calendar_greeting/model/academic_calendar_data.dart +++ b/lib/features/academic_calendar/model/academic_calendar_extensions.dart @@ -4,7 +4,7 @@ import "academic_day.dart"; import "academic_week_exception.dart"; import "weekday_enum.dart"; -extension AcademicCalendarDataExtraAttrs on AcademicCalendarData { +extension AcademicCalendarDataX on AcademicCalendarData { bool isHolidays() { return now.isBefore(semesterStartDate) || now.isAfter(examSessionLastDay); } @@ -38,7 +38,7 @@ extension AcademicCalendarDataExtraAttrs on AcademicCalendarData { } } -extension AcademicCalendarExtraAttrs on AcademicCalendar { +extension AcademicCalendarX on AcademicCalendar { AcademicDay? get academicDay { if (weeks.isTodayAnException) { return weeks.changedDay ?? data?.standardAcademicDay; diff --git a/lib/features/academic_calendar_greeting/model/academic_day.dart b/lib/features/academic_calendar/model/academic_day.dart similarity index 100% rename from lib/features/academic_calendar_greeting/model/academic_day.dart rename to lib/features/academic_calendar/model/academic_day.dart diff --git a/lib/features/academic_calendar_greeting/model/academic_week_exception.dart b/lib/features/academic_calendar/model/academic_week_exception.dart similarity index 91% rename from lib/features/academic_calendar_greeting/model/academic_week_exception.dart rename to lib/features/academic_calendar/model/academic_week_exception.dart index d1c9308f..0ea75cd1 100644 --- a/lib/features/academic_calendar_greeting/model/academic_week_exception.dart +++ b/lib/features/academic_calendar/model/academic_week_exception.dart @@ -5,7 +5,7 @@ import "../repository/academic_calendar_repo.dart"; import "academic_day.dart"; import "weekday_enum.dart"; -extension AcadWeekExceptionExtraAttrs on List { +extension AcademicWeekExceptionX on List { bool _checkIfThisIsToday(AcademicWeekException element) => element.day.isSameDay(now); diff --git a/lib/features/academic_calendar_greeting/model/weekday_enum.dart b/lib/features/academic_calendar/model/weekday_enum.dart similarity index 100% rename from lib/features/academic_calendar_greeting/model/weekday_enum.dart rename to lib/features/academic_calendar/model/weekday_enum.dart diff --git a/lib/features/academic_calendar_greeting/repository/academic_calendar_repo.dart b/lib/features/academic_calendar/repository/academic_calendar_repo.dart similarity index 94% rename from lib/features/academic_calendar_greeting/repository/academic_calendar_repo.dart rename to lib/features/academic_calendar/repository/academic_calendar_repo.dart index 4cb83ab3..4a517c73 100644 --- a/lib/features/academic_calendar_greeting/repository/academic_calendar_repo.dart +++ b/lib/features/academic_calendar/repository/academic_calendar_repo.dart @@ -19,7 +19,7 @@ Stream academicCalendarRepo(AcademicCalendarRepoRef ref) { return stream; } -extension FixNestedTypes on AcademicCalendar { +extension FixNestedTypesX on AcademicCalendar { AcademicCalendarData? get data => this.AcademicCalendarData; List get weeks => WeekExceptions; } diff --git a/lib/features/academic_calendar_greeting/repository/getAcademicCalendar.graphql b/lib/features/academic_calendar/repository/getAcademicCalendar.graphql similarity index 100% rename from lib/features/academic_calendar_greeting/repository/getAcademicCalendar.graphql rename to lib/features/academic_calendar/repository/getAcademicCalendar.graphql diff --git a/lib/features/academic_calendar_greeting/utils/counter_digits.dart b/lib/features/academic_calendar/utils/counter_digits.dart similarity index 100% rename from lib/features/academic_calendar_greeting/utils/counter_digits.dart rename to lib/features/academic_calendar/utils/counter_digits.dart diff --git a/lib/features/academic_calendar_greeting/utils/localize_academic_day.dart b/lib/features/academic_calendar/utils/localize_academic_day.dart similarity index 100% rename from lib/features/academic_calendar_greeting/utils/localize_academic_day.dart rename to lib/features/academic_calendar/utils/localize_academic_day.dart diff --git a/lib/features/academic_calendar_greeting/widgets/countdown_widget/digits_widgets.dart b/lib/features/academic_calendar/widgets/countdown_widget/digits_widgets.dart similarity index 100% rename from lib/features/academic_calendar_greeting/widgets/countdown_widget/digits_widgets.dart rename to lib/features/academic_calendar/widgets/countdown_widget/digits_widgets.dart diff --git a/lib/features/academic_calendar_greeting/widgets/countdown_widget/exam_session_countdown.dart b/lib/features/academic_calendar/widgets/countdown_widget/exam_session_countdown.dart similarity index 100% rename from lib/features/academic_calendar_greeting/widgets/countdown_widget/exam_session_countdown.dart rename to lib/features/academic_calendar/widgets/countdown_widget/exam_session_countdown.dart diff --git a/lib/features/academic_calendar_greeting/widgets/home_screen_greeting.dart b/lib/features/academic_calendar/widgets/home_screen_greeting.dart similarity index 96% rename from lib/features/academic_calendar_greeting/widgets/home_screen_greeting.dart rename to lib/features/academic_calendar/widgets/home_screen_greeting.dart index 2e4c2094..c69cf937 100644 --- a/lib/features/academic_calendar_greeting/widgets/home_screen_greeting.dart +++ b/lib/features/academic_calendar/widgets/home_screen_greeting.dart @@ -5,7 +5,7 @@ import "../../../theme/app_theme.dart"; import "../../../utils/context_extensions.dart"; import "../../../widgets/my_error_widget.dart"; import "../../home_view/widgets/loading_widgets/horizontal_rectangular_section_loading.dart"; -import "../model/academic_calendar_data.dart"; +import "../model/academic_calendar_extensions.dart"; import "../repository/academic_calendar_repo.dart"; import "../utils/localize_academic_day.dart"; diff --git a/lib/features/bottom_nav_bar/bottom_nav_bar.dart b/lib/features/bottom_nav_bar/bottom_nav_bar.dart index 4c80fee9..efc047c7 100644 --- a/lib/features/bottom_nav_bar/bottom_nav_bar.dart +++ b/lib/features/bottom_nav_bar/bottom_nav_bar.dart @@ -4,37 +4,39 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../config/nav_bar_config.dart"; import "../../theme/app_theme.dart"; -import "../navigator/navigator/nested_navigator.dart"; -import "../navigator/navigator/tab_bar_navigator.dart"; -import "bottom_nav_bar_controller.dart"; +import "../navigator/navigation_controller.dart"; class BottomNavBar extends ConsumerWidget { const BottomNavBar({super.key}); - @override Widget build(BuildContext context, WidgetRef ref) { - final selectedTab = ref.watch(bottomNavBarControllerProvider); - final navigator = ref.watch(navigatorProvider); - - return DecoratedBox( - decoration: BoxDecoration( - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(.08), - blurRadius: 20, - offset: const Offset(0, -1), - ), - ], + final activeTab = ref.watch(navigationControllerProvider).activeTab; + return Theme( + data: Theme.of(context).copyWith( + splashColor: Colors.transparent, + highlightColor: Colors.transparent, ), - child: BottomNavigationBar( - currentIndex: selectedTab.index, - onTap: (index) async => - navigator.changeTabBar(NavBarEnum.values[index]), - backgroundColor: context.colorTheme.greyLight, - showSelectedLabels: false, - showUnselectedLabels: false, - type: BottomNavigationBarType.fixed, - items: _NavigationBarItemsList(selectedTab, context), + child: DecoratedBox( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(.08), + blurRadius: 20, + offset: const Offset(0, -1), + ), + ], + ), + child: BottomNavigationBar( + currentIndex: activeTab.index, + onTap: (index) async => ref + .read(navigationControllerProvider.notifier) + .onTabBarChange(NavBarEnum.values[index]), + backgroundColor: context.colorTheme.greyLight, + showSelectedLabels: false, + showUnselectedLabels: false, + type: BottomNavigationBarType.fixed, + items: _NavigationBarItemsList(activeTab, context), + ), ), ); } diff --git a/lib/features/bottom_nav_bar/bottom_nav_bar_controller.dart b/lib/features/bottom_nav_bar/bottom_nav_bar_controller.dart deleted file mode 100644 index b46d4848..00000000 --- a/lib/features/bottom_nav_bar/bottom_nav_bar_controller.dart +++ /dev/null @@ -1,24 +0,0 @@ -import "package:riverpod_annotation/riverpod_annotation.dart"; - -import "../../config/nav_bar_config.dart"; -import "../../config/navigator_config.dart"; -import "../navigator/utils/selected_tab_observer.dart"; - -part "bottom_nav_bar_controller.g.dart"; - -@Riverpod(keepAlive: true) -class BottomNavBarController extends _$BottomNavBarController { - late final SelectedTabObserver selectedTabObserver = - SelectedTabObserver(_onTabChanged); - - @override - NavBarEnum build() { - return NavigatorConfig.initialTab; - } - - void _onTabChanged(NavBarEnum tab) { - if (state != tab) { - state = tab; - } - } -} diff --git a/lib/features/buildings_map/building_tile.dart b/lib/features/buildings_view/building_tile.dart similarity index 92% rename from lib/features/buildings_map/building_tile.dart rename to lib/features/buildings_view/building_tile.dart index fb55c058..f3c9348b 100644 --- a/lib/features/buildings_map/building_tile.dart +++ b/lib/features/buildings_view/building_tile.dart @@ -2,11 +2,11 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../api_base/directus_assets_url.dart"; -import "../../shared_repositories/buildings_repository/building_model.dart"; import "../../theme/app_theme.dart"; import "../../utils/context_extensions.dart"; import "../../widgets/wide_tile_card.dart"; import "controllers.dart"; +import "model/building_model.dart"; class BuildingTile extends ConsumerWidget { const BuildingTile( diff --git a/lib/features/buildings_map/buildings_view.dart b/lib/features/buildings_view/buildings_view.dart similarity index 77% rename from lib/features/buildings_map/buildings_view.dart rename to lib/features/buildings_view/buildings_view.dart index dbed585b..fc890809 100644 --- a/lib/features/buildings_map/buildings_view.dart +++ b/lib/features/buildings_view/buildings_view.dart @@ -1,21 +1,23 @@ +import "package:auto_route/auto_route.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:google_maps_flutter/google_maps_flutter.dart"; import "../../config/map_view_config.dart"; -import "../../shared_repositories/buildings_repository/building_model.dart"; import "../../utils/context_extensions.dart"; import "../map_view/map_view.dart"; import "../map_view/utils/map_marker_utils.dart"; import "building_tile.dart"; import "controllers.dart"; +import "model/building_model.dart"; -class BuildingMapView extends ConsumerWidget { - const BuildingMapView({super.key}); +@RoutePage() +class BuildingsView extends ConsumerWidget { + const BuildingsView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - return GeneralMapView( + return MapView( mapViewTexts: ( emptyList: context.localize.building_not_found, title: context.localize.buildings_title, @@ -28,8 +30,8 @@ class BuildingMapView extends ConsumerWidget { markerId: item.markerId, position: item.location, icon: isActive - ? MapMarkerUtils.activeMapMarker - : MapMarkerUtils.mapMarker, + ? MapMarkerUtils.activeBuildingMapMarker + : MapMarkerUtils.buildingMapMarker, onTap: () { ref.read(buildingsMapControllerProvider.notifier).onMarkerTap(item); }, diff --git a/lib/features/buildings_map/controllers.dart b/lib/features/buildings_view/controllers.dart similarity index 80% rename from lib/features/buildings_map/controllers.dart rename to lib/features/buildings_view/controllers.dart index 079c320f..4a5f36cb 100644 --- a/lib/features/buildings_map/controllers.dart +++ b/lib/features/buildings_view/controllers.dart @@ -1,12 +1,12 @@ import "package:google_maps_flutter/google_maps_flutter.dart"; import "package:riverpod_annotation/riverpod_annotation.dart"; -import "../../shared_repositories/buildings_repository/building_model.dart"; -import "../../shared_repositories/buildings_repository/map_buildings_repo.dart"; import "../map_view/controllers/active_map_marker_cntrl.dart"; import "../map_view/controllers/controllers_set.dart"; import "../map_view/controllers/map_controller.dart"; import "../map_view/controllers/map_data_controller.dart"; +import "model/building_model.dart"; +import "repository/buildings_repository.dart"; import "utils.dart"; part "controllers.g.dart"; @@ -21,9 +21,9 @@ class ActiveBuildingController extends _$ActiveBuildingController } @riverpod -class BuildingsListViewController extends _$BuildingsListViewController +class BuildingsViewController extends _$BuildingsViewController with MapDataController { - BuildingsListViewController() { + BuildingsViewController() { mapControllers = mapControllersBuildings; } @override @@ -55,7 +55,7 @@ class BuildingsMapController extends _$BuildingsMapController final MapControllers mapControllersBuildings = ( activeMarker: activeBuildingControllerProvider, - sourceRepo: mapBuildingsRepositoryProvider, + sourceRepo: buildingsRepositoryProvider, map: buildingsMapControllerProvider, - dataController: buildingsListViewControllerProvider, + dataController: buildingsViewControllerProvider, ); diff --git a/lib/shared_repositories/buildings_repository/building_model.dart b/lib/features/buildings_view/model/building_model.dart similarity index 85% rename from lib/shared_repositories/buildings_repository/building_model.dart rename to lib/features/buildings_view/model/building_model.dart index 89ffcd91..e3bb2bdb 100644 --- a/lib/shared_repositories/buildings_repository/building_model.dart +++ b/lib/features/buildings_view/model/building_model.dart @@ -1,7 +1,7 @@ import "package:google_maps_flutter/google_maps_flutter.dart"; -import "../../features/map_view/controllers/controllers_set.dart"; -import "map_buildings_repo.dart"; +import "../../map_view/controllers/controllers_set.dart"; +import "../repository/buildings_repository.dart"; class BuildingModel extends Building implements GoogleNavigable { BuildingModel.from(Building building) diff --git a/lib/features/buildings_view/repository/buildings_repository.dart b/lib/features/buildings_view/repository/buildings_repository.dart new file mode 100644 index 00000000..1f38ba7b --- /dev/null +++ b/lib/features/buildings_view/repository/buildings_repository.dart @@ -0,0 +1,24 @@ +import "package:riverpod_annotation/riverpod_annotation.dart"; + +import "../../../../../api_base/watch_query_adapter.dart"; +import "../../../../config/ttl_config.dart"; +import "../model/building_model.dart"; +import "getBuildings.graphql.dart"; + +part "buildings_repository.g.dart"; + +typedef Building = Query$GetBuildings$Buildings; + +@riverpod +Stream?> buildingsRepository( + BuildingsRepositoryRef ref, +) async* { + final stream = ref.watchQueryWithCache( + WatchOptions$Query$GetBuildings(eagerlyFetchResults: true), + TtlKey.buildingsRepository, + ); + + yield* stream.map( + (event) => event?.Buildings.map(BuildingModel.from).toList(), + ); +} diff --git a/lib/shared_repositories/buildings_repository/getMapBuildings.graphql b/lib/features/buildings_view/repository/getBuildings.graphql similarity index 84% rename from lib/shared_repositories/buildings_repository/getMapBuildings.graphql rename to lib/features/buildings_view/repository/getBuildings.graphql index 0f37002b..8756893d 100644 --- a/lib/shared_repositories/buildings_repository/getMapBuildings.graphql +++ b/lib/features/buildings_view/repository/getBuildings.graphql @@ -1,4 +1,4 @@ -query GetMapBuildings { +query GetBuildings { Buildings { id latitude diff --git a/lib/features/buildings_map/utils.dart b/lib/features/buildings_view/utils.dart similarity index 100% rename from lib/features/buildings_map/utils.dart rename to lib/features/buildings_view/utils.dart diff --git a/lib/features/department_detail_view/department_detail_view.dart b/lib/features/department_detail_view/department_detail_view.dart new file mode 100644 index 00000000..b6615365 --- /dev/null +++ b/lib/features/department_detail_view/department_detail_view.dart @@ -0,0 +1,93 @@ +import "package:auto_route/auto_route.dart"; +import "package:flutter/material.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; + +import "../../api_base/directus_assets_url.dart"; +import "../../config/ui_config.dart"; +import "../../theme/app_theme.dart"; +import "../../utils/context_extensions.dart"; +import "../../utils/determine_contact_icon.dart"; +import "../../utils/where_non_null_iterable.dart"; +import "../../widgets/detail_views/contact_section.dart"; +import "../../widgets/detail_views/detail_view_app_bar.dart"; +import "../../widgets/detail_views/sliver_header_section.dart"; +import "../../widgets/my_error_widget.dart"; +import "repository/department_details_repository.dart"; +import "utils/address_formatter.dart"; +import "utils/department_details_gradient.dart"; +import "widgets/department_detail_view_loading.dart"; +import "widgets/department_science_clubs_section.dart"; +import "widgets/fields_of_study_section.dart"; + +@RoutePage() +class DepartmentDetailView extends ConsumerWidget { + const DepartmentDetailView({@PathParam("id") required this.id, super.key}); + final String id; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final state = ref.watch(departmentDetailsRepositoryProvider(id)); + return Scaffold( + appBar: DetailViewAppBar(context, title: context.localize.departments), + body: switch (state) { + AsyncLoading() => const DepartmentDetailViewLoading(), + AsyncError(:final error) => MyErrorWidget(error), + AsyncValue(:final value) => CustomScrollView( + slivers: [ + SliverPersistentHeader( + delegate: SliverHeaderSection( + activeGradient: value?.Departments_by_id?.gradient, + logoImageUrl: value + ?.Departments_by_id?.logo?.filename_disk?.directusUrl, + backgroundImageUrl: null, + ), + ), + SliverList( + delegate: SliverChildListDelegate([ + const SizedBox(height: 8), + Text( + value?.Departments_by_id?.name ?? "", + style: context.textTheme.headline, + textAlign: TextAlign.center, + maxLines: 2, + ), + const SizedBox(height: 12), + Text( + value?.Departments_by_id?.address + ?.divideAddressInto3Lines ?? + "", + style: context.textTheme.body.copyWith(height: 1.2), + textAlign: TextAlign.center, + ), + const SizedBox(height: DetailViewsConfig.spacerHeight), + ContactSection( + title: context.localize.deans_office, + list: value?.Departments_by_id?.links.whereNonNull + .map( + (link) => ContactIconsModel( + text: link.name, + url: link.link, + ), + ) + .toList() ?? + List.empty(), + ), + FieldsOfStudySection( + fieldsOfStudy: value + ?.Departments_by_id?.fieldsOfStudies.whereNonNull + .map((e) => e.name) + .toList() ?? + List.empty(), + ), + DepartmentScienceClubsSection( + value?.Scientific_Circles.whereNonNull.toList() ?? + List.empty(), + ), + ]), + ), + ], + ), + }, + ); + } +} diff --git a/lib/features/department_details/repositories/department_details_repository.dart b/lib/features/department_detail_view/repository/department_details_repository.dart similarity index 88% rename from lib/features/department_details/repositories/department_details_repository.dart rename to lib/features/department_detail_view/repository/department_details_repository.dart index 91ff7839..51e7c10e 100644 --- a/lib/features/department_details/repositories/department_details_repository.dart +++ b/lib/features/department_detail_view/repository/department_details_repository.dart @@ -1,4 +1,5 @@ import "package:riverpod_annotation/riverpod_annotation.dart"; + import "../../../api_base/watch_query_adapter.dart"; import "../../../config/ttl_config.dart"; import "getDepartmentDetails.graphql.dart"; @@ -6,9 +7,10 @@ import "getDepartmentDetails.graphql.dart"; part "department_details_repository.g.dart"; typedef DepartmentDetails = Query$GetDepartmentDetails; -typedef StudyCircles = Query$GetDepartmentDetails$Scientific_Circles; +typedef ScienceClubs = Query$GetDepartmentDetails$Scientific_Circles; typedef DepartmentDetailsDetails = Query$GetDepartmentDetails$Departments_by_id; typedef _Vars = Variables$Query$GetDepartmentDetails; + @riverpod Stream departmentDetailsRepository( DepartmentDetailsRepositoryRef ref, @@ -22,7 +24,7 @@ Stream departmentDetailsRepository( fid: id, ), ), - TtlKey.departmentsDetailsRepository, + TtlKey.departmentDetailsRepository, ); yield* stream.map((event) => event); } diff --git a/lib/features/department_details/repositories/getDepartmentDetails.graphql b/lib/features/department_detail_view/repository/getDepartmentDetails.graphql similarity index 86% rename from lib/features/department_details/repositories/getDepartmentDetails.graphql rename to lib/features/department_detail_view/repository/getDepartmentDetails.graphql index 81ce4c5c..2d473062 100644 --- a/lib/features/department_details/repositories/getDepartmentDetails.graphql +++ b/lib/features/department_detail_view/repository/getDepartmentDetails.graphql @@ -1,14 +1,14 @@ query GetDepartmentDetails($id: ID!, $fid : GraphQLStringOrFloat) { Departments_by_id(id: $id){ name - logo{ + logo { filename_disk } address - fieldsOfStudies{ + fieldsOfStudies { name } - links{ + links { name link } @@ -19,7 +19,7 @@ query GetDepartmentDetails($id: ID!, $fid : GraphQLStringOrFloat) { id name shortDescription - logo{ + logo { filename_disk } } diff --git a/lib/features/department_details/utils/address_formatter.dart b/lib/features/department_detail_view/utils/address_formatter.dart similarity index 93% rename from lib/features/department_details/utils/address_formatter.dart rename to lib/features/department_detail_view/utils/address_formatter.dart index 690dcdbc..4d0d1058 100644 --- a/lib/features/department_details/utils/address_formatter.dart +++ b/lib/features/department_detail_view/utils/address_formatter.dart @@ -1,4 +1,4 @@ -extension AddressFormatter on String { +extension AddressFormatterX on String { String get divideAddressInto3Lines { final parts = split(" "); final postalCodeIndex = diff --git a/lib/features/department_details/utils/department_details_gradient.dart b/lib/features/department_detail_view/utils/department_details_gradient.dart similarity index 79% rename from lib/features/department_details/utils/department_details_gradient.dart rename to lib/features/department_detail_view/utils/department_details_gradient.dart index 6adf405f..e5ee39b3 100644 --- a/lib/features/department_details/utils/department_details_gradient.dart +++ b/lib/features/department_detail_view/utils/department_details_gradient.dart @@ -1,10 +1,11 @@ import "package:flutter/material.dart"; + import "../../../config/ui_config.dart"; import "../../../theme/hex_color.dart"; import "../../../utils/colors_sort.dart"; -import "../repositories/department_details_repository.dart"; +import "../repository/department_details_repository.dart"; -extension DepartmentsExtraParamsExt on DepartmentDetailsDetails { +extension DepartmentDetailsDetailsX on DepartmentDetailsDetails { LinearGradient get gradient => LinearGradient( colors: [ HexColor( diff --git a/lib/features/department_details/widgets/department_details_loading.dart b/lib/features/department_detail_view/widgets/department_detail_view_loading.dart similarity index 76% rename from lib/features/department_details/widgets/department_details_loading.dart rename to lib/features/department_detail_view/widgets/department_detail_view_loading.dart index 807ecfae..20c64d22 100644 --- a/lib/features/department_details/widgets/department_details_loading.dart +++ b/lib/features/department_detail_view/widgets/department_detail_view_loading.dart @@ -6,8 +6,8 @@ import "../../../widgets/loading_widgets/header_section_loading.dart"; import "../../../widgets/loading_widgets/shimmer_loading.dart"; import "../../home_view/widgets/loading_widgets/big_scrollable_section_loading.dart"; -class DepartmentDetailsLoading extends StatelessWidget { - const DepartmentDetailsLoading({super.key}); +class DepartmentDetailViewLoading extends StatelessWidget { + const DepartmentDetailViewLoading({super.key}); @override Widget build(BuildContext context) { @@ -17,9 +17,9 @@ class DepartmentDetailsLoading extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), children: const [ HeaderSectionLoading(), - SizedBox(height: DetailsScreenConfig.spacerHeight), + SizedBox(height: DetailViewsConfig.spacerHeight), ContactSectionLoading(), - SizedBox(height: DetailsScreenConfig.spacerHeight), + SizedBox(height: DetailViewsConfig.spacerHeight), BigScrollableSectionLoading(), ], ), diff --git a/lib/features/department_detail_view/widgets/department_science_clubs_section.dart b/lib/features/department_detail_view/widgets/department_science_clubs_section.dart new file mode 100644 index 00000000..37c72a28 --- /dev/null +++ b/lib/features/department_detail_view/widgets/department_science_clubs_section.dart @@ -0,0 +1,61 @@ +import "package:flutter/material.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; + +import "../../../api_base/directus_assets_url.dart"; +import "../../../config/ui_config.dart"; +import "../../../utils/context_extensions.dart"; +import "../../../widgets/big_preview_card.dart"; +import "../../../widgets/subsection_header.dart"; +import "../../home_view/widgets/paddings.dart"; +import "../../navigator/utils/navigation_commands.dart"; +import "../repository/department_details_repository.dart"; + +// TODO(simon-the-shark): Resolve if the list button should redirect to list of all study circles or only ones related to the department. +class DepartmentScienceClubsSection extends ConsumerWidget { + const DepartmentScienceClubsSection(this.scienceClubs, {super.key}); + final List scienceClubs; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Column( + children: [ + SubsectionHeader( + title: context.localize.study_circles, + actionTitle: context.localize.list, + onClick: ref.navigateScienceClubs, + ), + SizedBox( + height: BigPreviewCardConfig.cardHeight, + child: _ScienceClubsList(scienceClubs: scienceClubs), + ), + ], + ); + } +} + +class _ScienceClubsList extends ConsumerWidget { + final List scienceClubs; + + const _ScienceClubsList({required this.scienceClubs}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ListView.builder( + cacheExtent: 4, + shrinkWrap: true, + scrollDirection: Axis.horizontal, + itemCount: scienceClubs.length, + itemBuilder: (BuildContext context, int index) { + final sciClub = scienceClubs[index]; + return MediumLeftPadding( + child: BigPreviewCard( + title: sciClub.name, + shortDescription: sciClub.shortDescription ?? "", + photoUrl: sciClub.logo?.filename_disk?.directusUrl, + onClick: () async => ref.navigateSciClubsDetail(sciClub.id), + ), + ); + }, + ); + } +} diff --git a/lib/features/department_details/widgets/fields_of_study_section.dart b/lib/features/department_detail_view/widgets/fields_of_study_section.dart similarity index 100% rename from lib/features/department_details/widgets/fields_of_study_section.dart rename to lib/features/department_detail_view/widgets/fields_of_study_section.dart diff --git a/lib/features/department_details/department_details.dart b/lib/features/department_details/department_details.dart deleted file mode 100644 index bf7799a1..00000000 --- a/lib/features/department_details/department_details.dart +++ /dev/null @@ -1,99 +0,0 @@ -import "package:flutter/material.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; - -import "../../api_base/directus_assets_url.dart"; -import "../../config/ui_config.dart"; -import "../../theme/app_theme.dart"; -import "../../utils/context_extensions.dart"; -import "../../utils/determine_icon.dart"; -import "../../utils/where_non_null_iterable.dart"; -import "../../widgets/details_screen_contact_section.dart"; -import "../../widgets/details_screen_sliver_header_section.dart"; -import "../../widgets/my_error_widget.dart"; -import "../department_details/utils/address_formatter.dart"; -import "../department_details/utils/department_details_gradient.dart"; -import "../study_circle_details/widgets/details_screen_app_bar.dart"; -import "repositories/department_details_repository.dart"; -import "widgets/department_details_loading.dart"; -import "widgets/department_study_circle_section.dart"; -import "widgets/fields_of_study_section.dart"; - -class DepartmentDetails extends StatelessWidget { - const DepartmentDetails({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: DetailsScreenAppBar(context, title: context.localize.departments), - body: const _DepartmentDetailsDataView(), - ); - } -} - -class _DepartmentDetailsDataView extends ConsumerWidget { - const _DepartmentDetailsDataView(); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final itemId = ModalRoute.of(context)!.settings.arguments! as String; - final state = ref.watch(departmentDetailsRepositoryProvider(itemId)); - return switch (state) { - AsyncLoading() => const DepartmentDetailsLoading(), - AsyncError(:final error) => MyErrorWidget(error), - AsyncValue(:final value) => CustomScrollView( - slivers: [ - SliverPersistentHeader( - delegate: SliverHeaderSection( - activeGradient: value?.Departments_by_id?.gradient, - logoImageUrl: - value?.Departments_by_id?.logo?.filename_disk?.directusUrl, - backgroundImageUrl: null, - ), - ), - SliverList( - delegate: SliverChildListDelegate([ - const SizedBox(height: 8), - Text( - value?.Departments_by_id?.name ?? "", - style: context.textTheme.headline, - textAlign: TextAlign.center, - maxLines: 2, - ), - const SizedBox(height: 12), - Text( - value?.Departments_by_id?.address?.divideAddressInto3Lines ?? - "", - style: context.textTheme.body.copyWith(height: 1.2), - textAlign: TextAlign.center, - ), - const SizedBox(height: DetailsScreenConfig.spacerHeight), - ContactSection( - title: context.localize.deans_office, - list: value?.Departments_by_id?.links.whereNonNull - .map( - (link) => UrlIconsModel( - text: link.name, - url: link.link, - ), - ) - .toList() ?? - List.empty(), - ), - FieldsOfStudySection( - fieldsOfStudy: value - ?.Departments_by_id?.fieldsOfStudies.whereNonNull - .map((e) => e.name) - .toList() ?? - List.empty(), - ), - DepartmentsStudyCirclesSection( - value?.Scientific_Circles.whereNonNull.toList() ?? - List.empty(), - ), - ]), - ), - ], - ), - }; - } -} diff --git a/lib/features/department_details/widgets/department_study_circle_section.dart b/lib/features/department_details/widgets/department_study_circle_section.dart deleted file mode 100644 index 4bffeebe..00000000 --- a/lib/features/department_details/widgets/department_study_circle_section.dart +++ /dev/null @@ -1,70 +0,0 @@ -import "package:flutter/material.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "../../../api_base/directus_assets_url.dart"; -import "../../../config/nav_bar_config.dart"; -import "../../../config/ui_config.dart"; -import "../../../utils/context_extensions.dart"; -import "../../../widgets/big_preview_card.dart"; -import "../../../widgets/subsection_header.dart"; -import "../../home_view/widgets/paddings.dart"; -import "../../navigator/navigator/detail_view_navigator.dart"; -import "../../navigator/navigator/nested_navigator.dart"; -import "../../navigator/navigator/tab_bar_navigator.dart"; -import "../repositories/department_details_repository.dart"; - -// TODO(simon-the-shark): Resolve if the list button should redirect to list of all study circles or only ones related to the department. -class DepartmentsStudyCirclesSection extends ConsumerWidget { - const DepartmentsStudyCirclesSection(this.studyCircles, {super.key}); - final List studyCircles; - @override - Widget build(BuildContext context, WidgetRef ref) { - return Column( - children: [ - SubsectionHeader( - title: context.localize.study_circles, - actionTitle: context.localize.list, - onClick: () async { - await ref - .read(navigatorProvider) - .changeTabBar(NavBarEnum.sciCircles); - }, - ), - SizedBox( - height: BigPreviewCardConfig.cardHeight, - child: _StudyCirclesList(studyCircles: studyCircles), - ), - ], - ); - } -} - -class _StudyCirclesList extends ConsumerWidget { - final List studyCircles; - - const _StudyCirclesList({required this.studyCircles}); - - static Future goToDetailView(WidgetRef ref, String id) async { - await ref.read(navigatorProvider).navigateToStudyCircleDetails(id); - } - - @override - Widget build(BuildContext context, WidgetRef ref) { - return ListView.builder( - cacheExtent: 4, - shrinkWrap: true, - scrollDirection: Axis.horizontal, - itemCount: studyCircles.length, - itemBuilder: (BuildContext context, int index) { - final circle = studyCircles[index]; - return MediumLeftPadding( - child: BigPreviewCard( - title: circle.name, - shortDescription: circle.shortDescription ?? "", - photoUrl: circle.logo?.filename_disk?.directusUrl, - onClick: () async => goToDetailView(ref, circle.id), - ), - ); - }, - ); - } -} diff --git a/lib/features/departments_tab/departments_tab.dart b/lib/features/departments_view/departments_view.dart similarity index 65% rename from lib/features/departments_tab/departments_tab.dart rename to lib/features/departments_view/departments_view.dart index 17c972e8..42dd362e 100644 --- a/lib/features/departments_tab/departments_tab.dart +++ b/lib/features/departments_view/departments_view.dart @@ -1,21 +1,22 @@ +import "package:auto_route/auto_route.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../config/ui_config.dart"; -import "../../shared_repositories/departments_repository/departments_repository.dart"; import "../../theme/app_theme.dart"; import "../../utils/context_extensions.dart"; import "../../utils/where_non_null_iterable.dart"; import "../../widgets/my_error_widget.dart"; import "../../widgets/search_box_app_bar.dart"; -import "../navigator/navigator/detail_view_navigator.dart"; -import "../navigator/navigator/nested_navigator.dart"; -import "departments_tab_controller.dart"; +import "../navigator/utils/navigation_commands.dart"; +import "departments_view_controllers.dart"; +import "repository/departments_repository.dart"; import "widgets/department_card.dart"; -import "widgets/departments_list_loading.dart"; +import "widgets/departments_view_loading.dart"; -class DepartmentTab extends ConsumerWidget { - const DepartmentTab({super.key}); +@RoutePage() +class DepartmentsView extends ConsumerWidget { + const DepartmentsView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -27,21 +28,21 @@ class DepartmentTab extends ConsumerWidget { .watch(searchDepartmentsControllerProvider.notifier) .onTextChanged, ), - body: const _DepartmentsTabListBody(), + body: const _DepartmentsViewListBody(), ); } } -class _DepartmentsTabListBody extends ConsumerWidget { - const _DepartmentsTabListBody(); +class _DepartmentsViewListBody extends ConsumerWidget { + const _DepartmentsViewListBody(); @override Widget build(BuildContext context, WidgetRef ref) { - final state = ref.watch(departmentListProvider); + final state = ref.watch(departmentsListProvider); return Padding( padding: const EdgeInsets.symmetric(horizontal: 24), child: switch (state) { - AsyncLoading() => const DepartmentsListLoading(), + AsyncLoading() => const DepartmentsViewLoading(), AsyncError(:final error) => MyErrorWidget(error), AsyncValue(:final value) => _DepartmentsDataView(value.whereNonNull.toList()), @@ -54,10 +55,6 @@ class _DepartmentsDataView extends ConsumerWidget { const _DepartmentsDataView(this.departments); final List departments; - static Future goToDetailView(WidgetRef ref, String id) async { - await ref.read(navigatorProvider).navigateToDepartmentDetails(id); - } - @override Widget build(BuildContext context, WidgetRef ref) { if (departments.isEmpty) { @@ -70,11 +67,12 @@ class _DepartmentsDataView extends ConsumerWidget { } return GridView.builder( padding: const EdgeInsets.only(bottom: 24), - gridDelegate: DepartmentsConfig.departmentsTabGridDelegate, + gridDelegate: DepartmentsConfig.departmentsViewGridDelegate, itemCount: departments.length, itemBuilder: (context, index) => DepartmentCard( departments[index], - onClick: () async => goToDetailView(ref, departments[index].id), + onClick: () async => + ref.navigateDepartmentDetail(departments[index].id), ), ); } diff --git a/lib/features/departments_tab/departments_tab_controller.dart b/lib/features/departments_view/departments_view_controllers.dart similarity index 76% rename from lib/features/departments_tab/departments_tab_controller.dart rename to lib/features/departments_view/departments_view_controllers.dart index 8ac8e41c..db309551 100644 --- a/lib/features/departments_tab/departments_tab_controller.dart +++ b/lib/features/departments_view/departments_view_controllers.dart @@ -1,8 +1,8 @@ import "package:riverpod_annotation/riverpod_annotation.dart"; -import "../../shared_repositories/departments_repository/departments_repository.dart"; +import "repository/departments_repository.dart"; -part "departments_tab_controller.g.dart"; +part "departments_view_controllers.g.dart"; @riverpod class SearchDepartmentsController extends _$SearchDepartmentsController { @@ -15,7 +15,7 @@ class SearchDepartmentsController extends _$SearchDepartmentsController { } @riverpod -Future?> departmentList(DepartmentListRef ref) async { +Future?> departmentsList(DepartmentsListRef ref) async { final originalList = await ref.watch(departmentsRepositoryProvider.future); final query = ref.watch(searchDepartmentsControllerProvider); return originalList diff --git a/lib/shared_repositories/departments_repository/departments_extra_params_ext.dart b/lib/features/departments_view/repository/departments_extensions.dart similarity index 74% rename from lib/shared_repositories/departments_repository/departments_extra_params_ext.dart rename to lib/features/departments_view/repository/departments_extensions.dart index 98542b82..549b34e0 100644 --- a/lib/shared_repositories/departments_repository/departments_extra_params_ext.dart +++ b/lib/features/departments_view/repository/departments_extensions.dart @@ -1,11 +1,11 @@ import "package:flutter/material.dart"; -import "../../config/ui_config.dart"; -import "../../theme/hex_color.dart"; -import "../../utils/colors_sort.dart"; +import "../../../config/ui_config.dart"; +import "../../../theme/hex_color.dart"; +import "../../../utils/colors_sort.dart"; import "departments_repository.dart"; -extension DepartmentsExtraParamsExt on Department { +extension DepartmentsX on Department { LinearGradient get gradient => LinearGradient( colors: [ HexColor( @@ -18,7 +18,7 @@ extension DepartmentsExtraParamsExt on Department { ); } -extension _GetDeptCode on Department? { +extension _GetDepartmentsCodeX on Department? { static const _fallbackCode = 0; int extractIntFromStrCode() { @@ -29,7 +29,7 @@ extension _GetDeptCode on Department? { } } -extension SortDepartmentsByCode on List { +extension SortByCodeX on List { void sortByCodeOrder() { sort( (a, b) => a.extractIntFromStrCode().compareTo( diff --git a/lib/shared_repositories/departments_repository/departments_repository.dart b/lib/features/departments_view/repository/departments_repository.dart similarity index 87% rename from lib/shared_repositories/departments_repository/departments_repository.dart rename to lib/features/departments_view/repository/departments_repository.dart index a10f5d43..b685caf2 100644 --- a/lib/shared_repositories/departments_repository/departments_repository.dart +++ b/lib/features/departments_view/repository/departments_repository.dart @@ -1,8 +1,8 @@ import "package:riverpod_annotation/riverpod_annotation.dart"; import "../../../../api_base/watch_query_adapter.dart"; -import "../../config/ttl_config.dart"; -import "departments_extra_params_ext.dart"; +import "../../../config/ttl_config.dart"; +import "departments_extensions.dart"; import "getDepartments.graphql.dart"; part "departments_repository.g.dart"; diff --git a/lib/shared_repositories/departments_repository/getDepartments.graphql b/lib/features/departments_view/repository/getDepartments.graphql similarity index 100% rename from lib/shared_repositories/departments_repository/getDepartments.graphql rename to lib/features/departments_view/repository/getDepartments.graphql diff --git a/lib/features/departments_tab/widgets/department_card.dart b/lib/features/departments_view/widgets/department_card.dart similarity index 85% rename from lib/features/departments_tab/widgets/department_card.dart rename to lib/features/departments_view/widgets/department_card.dart index c695be3e..3230acaa 100644 --- a/lib/features/departments_tab/widgets/department_card.dart +++ b/lib/features/departments_view/widgets/department_card.dart @@ -2,10 +2,10 @@ import "package:flutter/material.dart"; import "../../../api_base/directus_assets_url.dart"; import "../../../config/ui_config.dart"; -import "../../../shared_repositories/departments_repository/departments_extra_params_ext.dart"; -import "../../../shared_repositories/departments_repository/departments_repository.dart"; import "../../../widgets/my_cached_image.dart"; import "../../../widgets/wide_tile_card.dart"; +import "../repository/departments_extensions.dart"; +import "../repository/departments_repository.dart"; class DepartmentCard extends StatelessWidget { final Department department; diff --git a/lib/features/departments_tab/widgets/departments_list_loading.dart b/lib/features/departments_view/widgets/departments_view_loading.dart similarity index 69% rename from lib/features/departments_tab/widgets/departments_list_loading.dart rename to lib/features/departments_view/widgets/departments_view_loading.dart index 55fc7e71..83e8bf62 100644 --- a/lib/features/departments_tab/widgets/departments_list_loading.dart +++ b/lib/features/departments_view/widgets/departments_view_loading.dart @@ -3,13 +3,13 @@ import "package:flutter/material.dart"; import "../../../../widgets/loading_widgets/specific_imitations/wide_tile_loading.dart"; import "../../../config/ui_config.dart"; -class DepartmentsListLoading extends StatelessWidget { - const DepartmentsListLoading({super.key}); +class DepartmentsViewLoading extends StatelessWidget { + const DepartmentsViewLoading({super.key}); @override Widget build(BuildContext context) { return GridView.builder( - gridDelegate: DepartmentsConfig.departmentsTabGridDelegate, + gridDelegate: DepartmentsConfig.departmentsViewGridDelegate, itemBuilder: (context, index) => const WideTileLoading(), physics: const NeverScrollableScrollPhysics(), ); diff --git a/lib/features/guide/guide_view_template.dart b/lib/features/guide_view/guide_view.dart similarity index 81% rename from lib/features/guide/guide_view_template.dart rename to lib/features/guide_view/guide_view.dart index 6c8b7269..7bbb08af 100644 --- a/lib/features/guide/guide_view_template.dart +++ b/lib/features/guide_view/guide_view.dart @@ -1,13 +1,14 @@ +import "package:auto_route/auto_route.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../theme/app_theme.dart"; import "../../utils/context_extensions.dart"; -import "../navigator/navigator/detail_view_navigator.dart"; -import "../navigator/navigator/nested_navigator.dart"; +import "../navigator/utils/navigation_commands.dart"; -class GuideViewTemplate extends ConsumerWidget { - const GuideViewTemplate({super.key}); +@RoutePage() +class GuideView extends ConsumerWidget { + const GuideView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -21,7 +22,7 @@ class GuideViewTemplate extends ConsumerWidget { child: Column( children: [ GestureDetector( - onTap: ref.watch(navigatorProvider).navigateToAboutUs, + onTap: ref.navigateAboutUs, child: Container( padding: const EdgeInsets.all(10), width: double.infinity, diff --git a/lib/features/home_view/home_view.dart b/lib/features/home_view/home_view.dart index 8ab6c206..b4520cc6 100644 --- a/lib/features/home_view/home_view.dart +++ b/lib/features/home_view/home_view.dart @@ -1,16 +1,18 @@ +import "package:auto_route/auto_route.dart"; import "package:flutter/material.dart"; import "../../config/ui_config.dart"; import "../../theme/app_theme.dart"; -import "../academic_calendar_greeting/widgets/countdown_widget/exam_session_countdown.dart"; -import "../academic_calendar_greeting/widgets/home_screen_greeting.dart"; +import "../academic_calendar/widgets/countdown_widget/exam_session_countdown.dart"; +import "../academic_calendar/widgets/home_screen_greeting.dart"; import "widgets/buildings_section/buildings_section.dart"; -import "widgets/departments_section/department_section.dart"; +import "widgets/departments_section/departments_section.dart"; import "widgets/logo_app_bar.dart"; import "widgets/news_section.dart"; -import "widgets/parking_section.dart"; -import "widgets/study_circles_section.dart"; +import "widgets/parkings_section.dart"; +import "widgets/science_clubs_section.dart"; +@RoutePage() class HomeView extends StatelessWidget { const HomeView({super.key}); @@ -19,11 +21,11 @@ class HomeView extends StatelessWidget { final List sections = [ const Greeting(), const ExamSessionCountdown(), - const ParkingSection(), + const ParkingsSection(), const NewsSection(), const BuildingsSection(), - const StudyCirclesSection(), - const DepartmentSection(), + const ScienceClubsSection(), + const DepartmentsSection(), ]; return Scaffold( backgroundColor: context.colorTheme.whiteSoap, @@ -32,7 +34,7 @@ class HomeView extends StatelessWidget { padding: const EdgeInsets.only(bottom: 48), itemBuilder: (context, index) => sections[index], separatorBuilder: (context, index) => - const SizedBox(height: HomeScreenConfig.paddingMedium), + const SizedBox(height: HomeViewConfig.paddingMedium), itemCount: sections.length, ), ); diff --git a/lib/features/home_view/repositories/infos_repository/infos_preview_repository.dart b/lib/features/home_view/repositories/infos_repository/infos_preview_repository.dart deleted file mode 100644 index d597af16..00000000 --- a/lib/features/home_view/repositories/infos_repository/infos_preview_repository.dart +++ /dev/null @@ -1,20 +0,0 @@ -import "package:riverpod_annotation/riverpod_annotation.dart"; - -import "../../../../api_base/watch_query_adapter.dart"; -import "../../../../config/ttl_config.dart"; -import "getInfosPreview.graphql.dart"; - -part "infos_preview_repository.g.dart"; - -typedef InfosPreview = Query$GetInfosPreview$Posts; - -@riverpod -Stream?> infosPreviewRepository( - InfosPreviewRepositoryRef ref, -) async* { - final stream = ref.watchQueryWithCache( - WatchOptions$Query$GetInfosPreview(eagerlyFetchResults: true), - TtlKey.infosPreviewRepository, - ); - yield* stream.map((event) => event?.Posts); -} diff --git a/lib/features/home_view/repositories/infos_repository/getInfosPreview.graphql b/lib/features/home_view/repositories/news/getNews.graphql similarity index 79% rename from lib/features/home_view/repositories/infos_repository/getInfosPreview.graphql rename to lib/features/home_view/repositories/news/getNews.graphql index 20e006b4..42d9ee1f 100644 --- a/lib/features/home_view/repositories/infos_repository/getInfosPreview.graphql +++ b/lib/features/home_view/repositories/news/getNews.graphql @@ -1,4 +1,4 @@ -query GetInfosPreview { +query GetNews { Posts { content title diff --git a/lib/features/home_view/repositories/news/news_repository.dart b/lib/features/home_view/repositories/news/news_repository.dart new file mode 100644 index 00000000..5916512e --- /dev/null +++ b/lib/features/home_view/repositories/news/news_repository.dart @@ -0,0 +1,18 @@ +import "package:riverpod_annotation/riverpod_annotation.dart"; + +import "../../../../api_base/watch_query_adapter.dart"; +import "../../../../config/ttl_config.dart"; +import "getNews.graphql.dart"; + +part "news_repository.g.dart"; + +typedef NewsPost = Query$GetNews$Posts; + +@riverpod +Stream?> newsRepository(NewsRepositoryRef ref) async* { + final stream = ref.watchQueryWithCache( + WatchOptions$Query$GetNews(eagerlyFetchResults: true), + TtlKey.newsRepository, + ); + yield* stream.map((event) => event?.Posts); +} diff --git a/lib/features/home_view/widgets/buildings_section/building_card.dart b/lib/features/home_view/widgets/buildings_section/building_card.dart index bdb72ee1..36fa5ef4 100644 --- a/lib/features/home_view/widgets/buildings_section/building_card.dart +++ b/lib/features/home_view/widgets/buildings_section/building_card.dart @@ -40,7 +40,7 @@ class BuildingCard extends StatelessWidget { child: Text( buildingName, style: context.textTheme.headlineWhite - .copyWith(shadows: HomeScreenConfig.squareCardTextShadow), + .copyWith(shadows: HomeViewConfig.squareCardTextShadow), ), ), TileSplash(onTap: onTap), diff --git a/lib/features/home_view/widgets/buildings_section/buildings_section.dart b/lib/features/home_view/widgets/buildings_section/buildings_section.dart index cf170139..a2474ab5 100644 --- a/lib/features/home_view/widgets/buildings_section/buildings_section.dart +++ b/lib/features/home_view/widgets/buildings_section/buildings_section.dart @@ -1,17 +1,17 @@ +import "dart:async"; + import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../../../api_base/directus_assets_url.dart"; -import "../../../../config/nav_bar_config.dart"; -import "../../../../shared_repositories/buildings_repository/building_model.dart"; -import "../../../../shared_repositories/buildings_repository/map_buildings_repo.dart"; import "../../../../utils/context_extensions.dart"; import "../../../../utils/where_non_null_iterable.dart"; import "../../../../widgets/my_error_widget.dart"; import "../../../../widgets/subsection_header.dart"; -import "../../../buildings_map/controllers.dart"; -import "../../../navigator/navigator/nested_navigator.dart"; -import "../../../navigator/navigator/tab_bar_navigator.dart"; +import "../../../buildings_view/controllers.dart"; +import "../../../buildings_view/model/building_model.dart"; +import "../../../buildings_view/repository/buildings_repository.dart"; +import "../../../navigator/utils/navigation_commands.dart"; import "../loading_widgets/scrollable_section_loading.dart"; import "../paddings.dart"; import "building_card.dart"; @@ -19,16 +19,13 @@ import "building_card.dart"; class BuildingsSection extends ConsumerWidget { const BuildingsSection({super.key}); - static Future goToMapTab(WidgetRef ref) async => - ref.read(navigatorProvider).changeTabBar(NavBarEnum.mapp); - @override Widget build(BuildContext context, WidgetRef ref) => Column( children: [ SubsectionHeader( title: context.localize.buildings_title, actionTitle: context.localize.map_button, - onClick: () async => BuildingsSection.goToMapTab(ref), + onClick: ref.navigateBuildings, ), const _BuildingsList(), ], @@ -40,7 +37,7 @@ class _BuildingsList extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final state = ref.watch(mapBuildingsRepositoryProvider); + final state = ref.watch(buildingsRepositoryProvider); return switch (state) { AsyncLoading() => const MediumLeftPadding( child: ScrollableSectionLoading(), @@ -73,10 +70,10 @@ class _DataListBuildingsTiles extends ConsumerWidget { buildingName: mapItem.name, imageUrl: mapItem.cover?.filename_disk?.directusUrl, onTap: () async { - await BuildingsSection.goToMapTab(ref); + unawaited(ref.navigateBuildings()); ref .watch(activeBuildingControllerProvider.notifier) - .selectBuilding(mapItem); + .selectItem(mapItem); }, ), ); diff --git a/lib/features/home_view/widgets/departments_section/deparment_box.dart b/lib/features/home_view/widgets/departments_section/deparment_box.dart index be8ad492..206f5571 100644 --- a/lib/features/home_view/widgets/departments_section/deparment_box.dart +++ b/lib/features/home_view/widgets/departments_section/deparment_box.dart @@ -2,11 +2,11 @@ import "package:flutter/material.dart"; import "../../../../api_base/directus_assets_url.dart"; import "../../../../config/ui_config.dart"; -import "../../../../shared_repositories/departments_repository/departments_extra_params_ext.dart"; -import "../../../../shared_repositories/departments_repository/departments_repository.dart"; import "../../../../theme/app_theme.dart"; import "../../../../widgets/my_cached_image.dart"; import "../../../../widgets/tile_splash.dart"; +import "../../../departments_view/repository/departments_extensions.dart"; +import "../../../departments_view/repository/departments_repository.dart"; class DepartmentBox extends StatelessWidget { final Department department; @@ -47,7 +47,7 @@ class DepartmentBox extends StatelessWidget { Text( department.code, style: context.textTheme.titleWhite - .copyWith(shadows: HomeScreenConfig.squareCardTextShadow), + .copyWith(shadows: HomeViewConfig.squareCardTextShadow), ), Text( department.name, diff --git a/lib/features/home_view/widgets/departments_section/department_section.dart b/lib/features/home_view/widgets/departments_section/departments_section.dart similarity index 66% rename from lib/features/home_view/widgets/departments_section/department_section.dart rename to lib/features/home_view/widgets/departments_section/departments_section.dart index daab22e3..4497d371 100644 --- a/lib/features/home_view/widgets/departments_section/department_section.dart +++ b/lib/features/home_view/widgets/departments_section/departments_section.dart @@ -1,24 +1,18 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "../../../../config/nav_bar_config.dart"; -import "../../../../shared_repositories/departments_repository/departments_repository.dart"; import "../../../../utils/context_extensions.dart"; import "../../../../utils/where_non_null_iterable.dart"; import "../../../../widgets/my_error_widget.dart"; import "../../../../widgets/subsection_header.dart"; -import "../../../navigator/navigator/detail_view_navigator.dart"; -import "../../../navigator/navigator/nested_navigator.dart"; -import "../../../navigator/navigator/tab_bar_navigator.dart"; +import "../../../departments_view/repository/departments_repository.dart"; +import "../../../navigator/utils/navigation_commands.dart"; import "../loading_widgets/scrollable_section_loading.dart"; import "../paddings.dart"; import "deparment_box.dart"; -class DepartmentSection extends ConsumerWidget { - const DepartmentSection({super.key}); - - static Future goToFacultiesTab(WidgetRef ref) async => - ref.read(navigatorProvider).changeTabBar(NavBarEnum.mapp); +class DepartmentsSection extends ConsumerWidget { + const DepartmentsSection({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -28,7 +22,7 @@ class DepartmentSection extends ConsumerWidget { SubsectionHeader( title: context.localize.departments, actionTitle: context.localize.list, - onClick: () async => goToFacultiesTab(ref), + onClick: ref.navigateDepartments, ), SmallHorizontalPadding( child: switch (state) { @@ -52,10 +46,6 @@ class _DepartmentsDataList extends ConsumerWidget { final List departments; - static Future goToDetailView(WidgetRef ref, String id) async { - await ref.read(navigatorProvider).navigateToDepartmentDetails(id); - } - @override Widget build(BuildContext context, WidgetRef ref) { return ListView.builder( @@ -64,7 +54,8 @@ class _DepartmentsDataList extends ConsumerWidget { itemBuilder: (context, index) => MediumLeftPadding( child: DepartmentBox( departments[index], - onClick: () async => goToDetailView(ref, departments[index].id), + onClick: () async => + ref.navigateDepartmentDetail(departments[index].id), ), ), ); diff --git a/lib/features/home_view/widgets/news_section.dart b/lib/features/home_view/widgets/news_section.dart index a21e676f..4a71a459 100644 --- a/lib/features/home_view/widgets/news_section.dart +++ b/lib/features/home_view/widgets/news_section.dart @@ -2,30 +2,26 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../../api_base/directus_assets_url.dart"; -import "../../../config/nav_bar_config.dart"; import "../../../config/ui_config.dart"; import "../../../utils/context_extensions.dart"; import "../../../utils/where_non_null_iterable.dart"; import "../../../widgets/big_preview_card.dart"; import "../../../widgets/my_error_widget.dart"; import "../../../widgets/subsection_header.dart"; -import "../../navigator/navigator/nested_navigator.dart"; -import "../../navigator/navigator/tab_bar_navigator.dart"; -import "../repositories/infos_repository/infos_preview_repository.dart"; +import "../../navigator/utils/navigation_commands.dart"; +import "../repositories/news/news_repository.dart"; import "loading_widgets/big_scrollable_section_loading.dart"; import "paddings.dart"; class NewsSection extends ConsumerWidget { const NewsSection({super.key}); - static Future goToInfoTab(WidgetRef ref) async => - ref.read(navigatorProvider).changeTabBar(NavBarEnum.info); @override Widget build(BuildContext context, WidgetRef ref) => Column( children: [ SubsectionHeader( title: context.localize.whats_up, - onClick: () async => goToInfoTab(ref), + onClick: ref.navigateGuide, ), const _NewsList(), ], @@ -37,14 +33,20 @@ class _NewsList extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final state = ref.watch(infosPreviewRepositoryProvider); + final state = ref.watch(newsRepositoryProvider); + return switch (state) { AsyncLoading() => const Padding( padding: EdgeInsets.only( - left: HomeScreenConfig.paddingMedium, - top: HomeScreenConfig.paddingMedium, + left: HomeViewConfig.paddingMedium, + top: HomeViewConfig.paddingMedium * + 2, // twice the padding of normal state, just to look cool + ), + child: SizedBox( + height: + BigPreviewCardConfig.cardHeight - HomeViewConfig.paddingMedium, + child: BigScrollableSectionLoading(), ), - child: BigScrollableSectionLoading(), ), AsyncError(:final error) => MyErrorWidget(error), AsyncValue(:final value) => Column( @@ -52,9 +54,9 @@ class _NewsList extends ConsumerWidget { children: [ Padding( padding: const EdgeInsets.only( - left: HomeScreenConfig.paddingSmall, - right: HomeScreenConfig.paddingSmall, - top: HomeScreenConfig.paddingMedium, + left: HomeViewConfig.paddingSmall, + right: HomeViewConfig.paddingSmall, + top: HomeViewConfig.paddingMedium, ), child: SizedBox( height: BigPreviewCardConfig.cardHeight, @@ -70,7 +72,7 @@ class _NewsList extends ConsumerWidget { class _NewsDataList extends StatelessWidget { const _NewsDataList(this.value); - final List value; + final List value; @override Widget build(BuildContext context) { diff --git a/lib/features/home_view/widgets/paddings.dart b/lib/features/home_view/widgets/paddings.dart index 0abac8d4..d0772672 100644 --- a/lib/features/home_view/widgets/paddings.dart +++ b/lib/features/home_view/widgets/paddings.dart @@ -6,8 +6,8 @@ class SmallHorizontalPadding extends Padding { const SmallHorizontalPadding({super.key, super.child}) : super( padding: const EdgeInsets.only( - left: HomeScreenConfig.paddingSmall, - right: HomeScreenConfig.paddingSmall, + left: HomeViewConfig.paddingSmall, + right: HomeViewConfig.paddingSmall, ), ); } @@ -15,6 +15,6 @@ class SmallHorizontalPadding extends Padding { class MediumLeftPadding extends Padding { const MediumLeftPadding({super.key, super.child}) : super( - padding: const EdgeInsets.only(left: HomeScreenConfig.paddingMedium), + padding: const EdgeInsets.only(left: HomeViewConfig.paddingMedium), ); } diff --git a/lib/features/home_view/widgets/parking_section.dart b/lib/features/home_view/widgets/parkings_section.dart similarity index 71% rename from lib/features/home_view/widgets/parking_section.dart rename to lib/features/home_view/widgets/parkings_section.dart index 5ad058c8..92de0915 100644 --- a/lib/features/home_view/widgets/parking_section.dart +++ b/lib/features/home_view/widgets/parkings_section.dart @@ -1,25 +1,22 @@ +import "dart:async"; + import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; -import "../../../config/nav_bar_config.dart"; import "../../../utils/context_extensions.dart"; import "../../../utils/where_non_null_iterable.dart"; import "../../../widgets/my_error_widget.dart"; import "../../../widgets/subsection_header.dart"; -import "../../iparking/controllers.dart"; -import "../../iparking/models/parking_model.dart"; -import "../../iparking/repositories/parkings_repo.dart"; -import "../../navigator/navigator/nested_navigator.dart"; -import "../../navigator/navigator/tab_bar_navigator.dart"; +import "../../navigator/utils/navigation_commands.dart"; +import "../../parkings_view/controllers.dart"; +import "../../parkings_view/models/parking.dart"; +import "../../parkings_view/repository/parkings_repository.dart"; import "buildings_section/building_card.dart"; import "loading_widgets/scrollable_section_loading.dart"; import "paddings.dart"; -class ParkingSection extends ConsumerWidget { - const ParkingSection({super.key}); - - static Future goToParkingsTab(WidgetRef ref) async => - ref.read(navigatorProvider).changeTabBar(NavBarEnum.parkings); +class ParkingsSection extends ConsumerWidget { + const ParkingsSection({super.key}); @override Widget build(BuildContext context, WidgetRef ref) => Column( @@ -27,7 +24,7 @@ class ParkingSection extends ConsumerWidget { SubsectionHeader( title: context.localize.parkings_title, actionTitle: context.localize.map_button, - onClick: () async => ParkingSection.goToParkingsTab(ref), + onClick: ref.navigateParkings, ), const _ParkingsList(), ], @@ -39,7 +36,7 @@ class _ParkingsList extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final state = ref.watch(parkingsRepoProvider); + final state = ref.watch(parkingsRepositoryProvider); return switch (state) { AsyncLoading() => const MediumLeftPadding( child: ScrollableSectionLoading(), @@ -58,7 +55,7 @@ class _ParkingsList extends ConsumerWidget { class _DataListParkingsTiles extends ConsumerWidget { const _DataListParkingsTiles(this.parkings); - final List parkings; + final List parkings; @override Widget build(BuildContext context, WidgetRef ref) { @@ -72,10 +69,10 @@ class _DataListParkingsTiles extends ConsumerWidget { buildingName: parking.symbol, imageUrl: parking.iParkPhotoUrl, onTap: () async { - await ParkingSection.goToParkingsTab(ref); + unawaited(ref.navigateParkings()); ref .watch(activeParkingControllerProvider.notifier) - .selectBuilding(parking); + .selectItem(parking); }, ), ); diff --git a/lib/features/home_view/widgets/study_circles_section.dart b/lib/features/home_view/widgets/science_clubs_section.dart similarity index 50% rename from lib/features/home_view/widgets/study_circles_section.dart rename to lib/features/home_view/widgets/science_clubs_section.dart index 98500c87..0a167521 100644 --- a/lib/features/home_view/widgets/study_circles_section.dart +++ b/lib/features/home_view/widgets/science_clubs_section.dart @@ -2,22 +2,19 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../../api_base/directus_assets_url.dart"; -import "../../../config/nav_bar_config.dart"; import "../../../config/ui_config.dart"; -import "../../../shared_repositories/sci_clubs_repository/scientific_circles_repository.dart"; import "../../../utils/context_extensions.dart"; import "../../../utils/where_non_null_iterable.dart"; import "../../../widgets/big_preview_card.dart"; import "../../../widgets/my_error_widget.dart"; import "../../../widgets/subsection_header.dart"; -import "../../navigator/navigator/detail_view_navigator.dart"; -import "../../navigator/navigator/nested_navigator.dart"; -import "../../navigator/navigator/tab_bar_navigator.dart"; +import "../../navigator/utils/navigation_commands.dart"; +import "../../science_clubs_view/repositories/science_clubs/science_clubs_repository.dart"; import "loading_widgets/big_scrollable_section_loading.dart"; import "paddings.dart"; -class StudyCirclesSection extends ConsumerWidget { - const StudyCirclesSection({super.key}); +class ScienceClubsSection extends ConsumerWidget { + const ScienceClubsSection({super.key}); @override Widget build(BuildContext context, WidgetRef ref) => Column( @@ -25,40 +22,36 @@ class StudyCirclesSection extends ConsumerWidget { SubsectionHeader( title: context.localize.study_circles, actionTitle: context.localize.list, - onClick: () async { - await ref - .read(navigatorProvider) - .changeTabBar(NavBarEnum.sciCircles); - }, + onClick: ref.navigateScienceClubs, ), - const _StudyCirclesList(), + const _ScienceClubsList(), ], ); } -class _StudyCirclesList extends ConsumerWidget { - const _StudyCirclesList(); +class _ScienceClubsList extends ConsumerWidget { + const _ScienceClubsList(); @override Widget build(BuildContext context, WidgetRef ref) { - final state = ref.watch(scientificCirclesRepositoryProvider); + final state = ref.watch(scienceClubsRepositoryProvider); return switch (state) { AsyncLoading() => const Padding( padding: EdgeInsets.only( - left: HomeScreenConfig.paddingMedium, - top: HomeScreenConfig.paddingMedium, + left: HomeViewConfig.paddingMedium, + top: HomeViewConfig.paddingMedium, ), child: BigScrollableSectionLoading(), ), AsyncError(:final error) => MyErrorWidget(error), AsyncValue(:final value) => Container( padding: const EdgeInsets.only( - left: HomeScreenConfig.paddingSmall, - right: HomeScreenConfig.paddingSmall, - top: HomeScreenConfig.paddingMedium, + left: HomeViewConfig.paddingSmall, + right: HomeViewConfig.paddingSmall, + top: HomeViewConfig.paddingMedium, ), height: BigPreviewCardConfig.cardHeight, - child: _StudyCirclesDataList( + child: _ScienceClubsDataList( value.whereNonNull.toList(), ), ) @@ -66,14 +59,10 @@ class _StudyCirclesList extends ConsumerWidget { } } -class _StudyCirclesDataList extends ConsumerWidget { - const _StudyCirclesDataList(this.studyCircles); +class _ScienceClubsDataList extends ConsumerWidget { + const _ScienceClubsDataList(this.scienceClubs); - final List studyCircles; - - static Future goToDetailView(WidgetRef ref, String id) async { - await ref.read(navigatorProvider).navigateToStudyCircleDetails(id); - } + final List scienceClubs; @override Widget build(BuildContext context, WidgetRef ref) { @@ -81,15 +70,15 @@ class _StudyCirclesDataList extends ConsumerWidget { cacheExtent: 4, shrinkWrap: true, scrollDirection: Axis.horizontal, - itemCount: studyCircles.length, + itemCount: scienceClubs.length, itemBuilder: (BuildContext context, int index) { - final circle = studyCircles[index]; + final sciClub = scienceClubs[index]; return MediumLeftPadding( child: BigPreviewCard( - title: circle.name, - shortDescription: circle.shortDescription ?? "", - photoUrl: circle.logo?.filename_disk?.directusUrl, - onClick: () async => goToDetailView(ref, circle.id), + title: sciClub.name, + shortDescription: sciClub.shortDescription ?? "", + photoUrl: sciClub.logo?.filename_disk?.directusUrl, + onClick: () async => ref.navigateSciClubsDetail(sciClub.id), ), ); }, diff --git a/lib/features/iparking_chart/repositories/chart_repo.dart b/lib/features/iparking_chart/repositories/chart_repo.dart deleted file mode 100644 index 683b6fea..00000000 --- a/lib/features/iparking_chart/repositories/chart_repo.dart +++ /dev/null @@ -1,20 +0,0 @@ -import "package:riverpod_annotation/riverpod_annotation.dart"; - -import "../../iparking/api_client/iparking_commands.dart"; -import "../../iparking/models/parking_model.dart"; -import "../models/chart_data_model.dart"; - -part "chart_repo.g.dart"; - -@riverpod -Future chartRepo( - ChartRepoRef ref, - ParkingPlace parkingPlace, -) async { - final response = await ref.postIParkingCommand>( - FetchChartCommand(parkingPlace.id), - ); - return RawChartData.fromJson( - response.data?["slots"], - ); -} diff --git a/lib/features/map_view/controllers/active_map_marker_cntrl.dart b/lib/features/map_view/controllers/active_map_marker_cntrl.dart index ba0c121b..affb81e0 100644 --- a/lib/features/map_view/controllers/active_map_marker_cntrl.dart +++ b/lib/features/map_view/controllers/active_map_marker_cntrl.dart @@ -10,7 +10,7 @@ mixin ActiveMarkerController return null; } - void selectBuilding(T item) { + void selectItem(T item) { state = item; } @@ -18,11 +18,11 @@ mixin ActiveMarkerController state = null; } - void toggleBuilding(T item) { + void toggleItem(T item) { if (state == item) { unselect(); } else { - selectBuilding(item); + selectItem(item); } } diff --git a/lib/features/map_view/controllers/bottom_sheet_controller.dart b/lib/features/map_view/controllers/bottom_sheet_controller.dart index 2ee5cca5..f41253ab 100644 --- a/lib/features/map_view/controllers/bottom_sheet_controller.dart +++ b/lib/features/map_view/controllers/bottom_sheet_controller.dart @@ -9,7 +9,7 @@ DraggableScrollableController bottomSheetController( ) => DraggableScrollableController(); -extension SafeDraggableScrollableControllerWrapper +extension SafeDraggableScrollableControllerWrapperX on DraggableScrollableController { void resetSafe() { if (isAttached) reset(); diff --git a/lib/features/map_view/controllers/map_controller.dart b/lib/features/map_view/controllers/map_controller.dart index 39e92b57..67ce35fc 100644 --- a/lib/features/map_view/controllers/map_controller.dart +++ b/lib/features/map_view/controllers/map_controller.dart @@ -2,9 +2,7 @@ import "dart:async"; import "package:flutter/material.dart"; import "package:google_maps_flutter/google_maps_flutter.dart"; - import "package:google_maps_flutter_android/google_maps_flutter_android.dart"; - import "package:google_maps_flutter_platform_interface/google_maps_flutter_platform_interface.dart"; import "package:riverpod_annotation/riverpod_annotation.dart"; @@ -47,7 +45,7 @@ mixin MapController } void onMarkerTap(T item) { - ref.read(mapControllers.activeMarker.notifier).toggleBuilding(item); + ref.read(mapControllers.activeMarker.notifier).toggleItem(item); ref.read(bottomSheetControllerProvider).resetSafe(); if (ref.read(mapControllers.activeMarker) == item) { zoomOnMarker(item); diff --git a/lib/features/map_view/map_view.dart b/lib/features/map_view/map_view.dart index 99e118b2..fddcc262 100644 --- a/lib/features/map_view/map_view.dart +++ b/lib/features/map_view/map_view.dart @@ -13,8 +13,8 @@ import "widgets/bottom_scroll_sheet/sheet_layout_scheme.dart"; import "widgets/map_config.dart"; import "widgets/map_widget.dart"; -class GeneralMapView extends ConsumerWidget { - const GeneralMapView({ +class MapView extends ConsumerWidget { + const MapView({ required this.mapViewTexts, required this.mapControllers, required this.markerBuilder, @@ -47,7 +47,7 @@ class GeneralMapView extends ConsumerWidget { child: Scaffold( backgroundColor: context.colorTheme.whiteSoap, body: kIsWeb || isBigScreen - ? HorizontalWebLayout() + ? _HorizontalWebLayout() : Stack( children: [ MapWidget(), @@ -60,8 +60,8 @@ class GeneralMapView extends ConsumerWidget { } } -class HorizontalWebLayout extends StatelessWidget { - const HorizontalWebLayout({ +class _HorizontalWebLayout extends StatelessWidget { + const _HorizontalWebLayout({ super.key, }); diff --git a/lib/features/map_view/utils/map_marker_utils.dart b/lib/features/map_view/utils/map_marker_utils.dart index 22553338..e15e1914 100644 --- a/lib/features/map_view/utils/map_marker_utils.dart +++ b/lib/features/map_view/utils/map_marker_utils.dart @@ -1,59 +1,53 @@ -import "dart:ui" as ui; - import "package:flutter/foundation.dart"; -import "package:flutter/services.dart"; import "package:flutter/widgets.dart"; import "package:google_maps_flutter/google_maps_flutter.dart"; + import "../../../config/map_view_config.dart"; import "../../../gen/assets.gen.dart"; class MapMarkerUtils { - static late final BitmapDescriptor mapMarker; - static late final BitmapDescriptor activeMapMarker; + static late final BitmapDescriptor buildingMapMarker; + static late final BitmapDescriptor activeBuildingMapMarker; static late final BitmapDescriptor parkingMapMarker; static late final BitmapDescriptor activeParkingMapMarker; static Future loadMapMarkerAssets(BuildContext context) async { - final pixelRatio = - kIsWeb ? 1 : MediaQuery.devicePixelRatioOf(context).toInt(); - - mapMarker = await _AssetScaledLoader.loadScaledBitmapDescriptor( + final configuration = context.mapMarkerConfiguration; + final activeMarkerConfig = context.activeMapMarkerConfiguration; + buildingMapMarker = await BitmapDescriptor.asset( + configuration, Assets.mapMarkers.mapMarker.path, - width: MapWidgetConfig.mapMarkerOriginWidth * pixelRatio, ); - - activeMapMarker = await _AssetScaledLoader.loadScaledBitmapDescriptor( + activeBuildingMapMarker = await BitmapDescriptor.asset( + activeMarkerConfig, Assets.mapMarkers.activeMapMarker.path, - width: MapWidgetConfig.activeMapMarkerOriginWidth * pixelRatio, ); - parkingMapMarker = await _AssetScaledLoader.loadScaledBitmapDescriptor( + parkingMapMarker = await BitmapDescriptor.asset( + configuration, Assets.mapMarkers.parkingMapMarker.path, - width: MapWidgetConfig.mapMarkerOriginWidth * pixelRatio, ); - activeParkingMapMarker = - await _AssetScaledLoader.loadScaledBitmapDescriptor( + activeParkingMapMarker = await BitmapDescriptor.asset( + activeMarkerConfig, Assets.mapMarkers.activeParkingMapMarker.path, - width: MapWidgetConfig.activeMapMarkerOriginWidth * pixelRatio, ); } } -class _AssetScaledLoader { - static Future loadScaledBitmapDescriptor( - String assetName, { - required int width, - }) async { - try { - final data = await rootBundle.load(assetName); - final codec = await ui.instantiateImageCodec( - data.buffer.asUint8List(), - targetWidth: width, +extension _ImageConfigurationX on BuildContext { + double get pixelRatio => kIsWeb ? 1.0 : MediaQuery.devicePixelRatioOf(this); + + ImageConfiguration get mapMarkerConfiguration => ImageConfiguration( + devicePixelRatio: pixelRatio, + size: const Size( + MapWidgetConfig.mapMarkerWidth, + MapWidgetConfig.mapMarkerHeight, + ), + ); + ImageConfiguration get activeMapMarkerConfiguration => ImageConfiguration( + devicePixelRatio: pixelRatio, + size: const Size( + MapWidgetConfig.activeMapMarkerWidth, + MapWidgetConfig.activeMapMarkerHeight, + ), ); - final fi = await codec.getNextFrame(); - final decode = await fi.image.toByteData(format: ui.ImageByteFormat.png); - return BitmapDescriptor.bytes(decode!.buffer.asUint8List()); - } on Exception { - return BitmapDescriptor.defaultMarker; // fallback - } - } } diff --git a/lib/features/map_view/widgets/bottom_scroll_sheet/navigate_button.dart b/lib/features/map_view/widgets/bottom_scroll_sheet/navigate_button.dart index b886f8ac..090cfe30 100644 --- a/lib/features/map_view/widgets/bottom_scroll_sheet/navigate_button.dart +++ b/lib/features/map_view/widgets/bottom_scroll_sheet/navigate_button.dart @@ -4,7 +4,7 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../../../config/map_view_config.dart"; import "../../../../theme/app_theme.dart"; import "../../../../utils/context_extensions.dart"; -import "../../../iparking/widgets/i_parking_icons_icons.icons.dart"; +import "../../../parkings_view/widgets/parkings_icons.icons.dart"; import "../../controllers/controllers_set.dart"; import "../map_config.dart"; @@ -19,7 +19,7 @@ class NavigateButton extends ConsumerWidget { ), child: TextButton.icon( icon: Icon( - IParkingIcons.map_nav, + ParkingsIcons.map_nav, color: context.colorTheme.orangePomegranade, size: 16, ), diff --git a/lib/features/map_view/widgets/bottom_scroll_sheet/sheet_layout_scheme.dart b/lib/features/map_view/widgets/bottom_scroll_sheet/sheet_layout_scheme.dart index aa591fbc..a6e5158e 100644 --- a/lib/features/map_view/widgets/bottom_scroll_sheet/sheet_layout_scheme.dart +++ b/lib/features/map_view/widgets/bottom_scroll_sheet/sheet_layout_scheme.dart @@ -26,7 +26,7 @@ class SheetLayoutScheme extends ConsumerWidget { onQueryChanged: ref .watch(context.mapDataController().notifier) .onSearchQueryChanged, - onSearchboxTap: + onSearchBoxTap: ref.watch(bottomSheetPixelsProvider.notifier).onSearchBoxTap, actions: [ if (ref.watch(context.activeMarkerController()) != null) diff --git a/lib/features/map_view/widgets/map_config.dart b/lib/features/map_view/widgets/map_config.dart index 71723002..97ebe085 100644 --- a/lib/features/map_view/widgets/map_config.dart +++ b/lib/features/map_view/widgets/map_config.dart @@ -53,7 +53,7 @@ class MapConfig extends InheritedWidget { } } -extension MapConfigExt on BuildContext { +extension MapConfigX on BuildContext { MapConfig config() { return MapConfig.of(this); } diff --git a/lib/features/navigator/app_router.dart b/lib/features/navigator/app_router.dart new file mode 100644 index 00000000..cc04a24f --- /dev/null +++ b/lib/features/navigator/app_router.dart @@ -0,0 +1,106 @@ +import "package:auto_route/auto_route.dart"; +import "package:flutter/material.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:riverpod_annotation/riverpod_annotation.dart"; + +import "../../config/transitions.dart"; +import "../about_us_view/about_us_view.dart"; +import "../buildings_view/buildings_view.dart"; +import "../department_detail_view/department_detail_view.dart"; +import "../departments_view/departments_view.dart"; +import "../guide_view/guide_view.dart"; +import "../home_view/home_view.dart"; +import "../parkings_view/parkings_view.dart"; +import "../science_club_detail_view/science_club_detail_view.dart"; +import "../science_clubs_view/science_clubs_view.dart"; +import "root_view.dart"; +import "utils/tab_bar_transition.dart"; + +part "app_router.g.dart"; +part "app_router.gr.dart"; + +@AutoRouterConfig(replaceInRouteName: "View,Route") +class AppRouter extends _$AppRouter { + final Ref ref; + + AppRouter({required this.ref}); + + @override + List get routes => [ + AutoRoute( + path: "/", + page: RootRoute.page, + children: [ + CustomRoute( + path: "", + page: HomeRoute.page, + durationInMilliseconds: TransitionsConfig.durationInMiliseconds, + reverseDurationInMilliseconds: + TransitionsConfig.durationInMiliseconds, + transitionsBuilder: ref.tabBarTransitionBuilder(HomeRoute.name), + ), + CustomRoute( + path: "map", + page: BuildingsRoute.page, + durationInMilliseconds: TransitionsConfig.durationInMiliseconds, + reverseDurationInMilliseconds: + TransitionsConfig.durationInMiliseconds, + transitionsBuilder: + ref.tabBarTransitionBuilder(BuildingsRoute.name), + ), + CustomRoute( + path: "parkings", + page: ParkingsRoute.page, + durationInMilliseconds: TransitionsConfig.durationInMiliseconds, + reverseDurationInMilliseconds: + TransitionsConfig.durationInMiliseconds, + transitionsBuilder: + ref.tabBarTransitionBuilder(ParkingsRoute.name), + ), + CustomRoute( + path: "departments", + page: DepartmentsRoute.page, + durationInMilliseconds: TransitionsConfig.durationInMiliseconds, + reverseDurationInMilliseconds: + TransitionsConfig.durationInMiliseconds, + transitionsBuilder: + ref.tabBarTransitionBuilder(DepartmentsRoute.name), + ), + AutoRoute( + path: "departments/:id", + page: DepartmentDetailRoute.page, + ), + CustomRoute( + path: "sci-clubs", + page: ScienceClubsRoute.page, + durationInMilliseconds: TransitionsConfig.durationInMiliseconds, + reverseDurationInMilliseconds: + TransitionsConfig.durationInMiliseconds, + transitionsBuilder: + ref.tabBarTransitionBuilder(ScienceClubsRoute.name), + ), + AutoRoute( + path: "sci-clubs/:id", + page: ScienceClubDetailRoute.page, + ), + CustomRoute( + path: "guide", + page: GuideRoute.page, + durationInMilliseconds: TransitionsConfig.durationInMiliseconds, + reverseDurationInMilliseconds: + TransitionsConfig.durationInMiliseconds, + transitionsBuilder: ref.tabBarTransitionBuilder(GuideRoute.name), + ), + AutoRoute( + path: "aboutUs", + page: AboutUsRoute.page, + ), + ], + ), + ]; +} + +@riverpod +AppRouter appRouter(AppRouterRef ref) { + return AppRouter(ref: ref); +} diff --git a/lib/features/navigator/navigation_controller.dart b/lib/features/navigator/navigation_controller.dart new file mode 100644 index 00000000..057ce529 --- /dev/null +++ b/lib/features/navigator/navigation_controller.dart @@ -0,0 +1,77 @@ +import "dart:async"; + +import "package:auto_route/auto_route.dart"; +import "package:collection/collection.dart"; +import "package:flutter/material.dart"; +import "package:riverpod_annotation/riverpod_annotation.dart"; + +import "../../config/nav_bar_config.dart"; + +part "navigation_controller.g.dart"; + +typedef TRoute = PageRouteInfo; + +typedef NavigationState = ({ + bool isStackPoppable, + NavBarEnum activeTab, +}); + +@riverpod +class NavigationController extends _$NavigationController { + final navigatorKey = GlobalKey(); + + StackRouter? get _router => navigatorKey.currentState?.controller; + + bool didLastPop = false; // used only for tabbar transition + + Iterable get stack => + _router?.stackData.map((e) => e.route.toPageRouteInfo()) ?? []; + + Future push(TRoute route) async { + didLastPop = false; + final popFutureResults = _router?.push(route); // push the route + await Future.delayed( + Duration.zero, + refreshState, // refresh the state after the push + ); + // await the route's pop + if (popFutureResults != null) await popFutureResults; + didLastPop = true; + refreshState(); // refresh the state after the pop + } + + /// this is called only when you actually **click** in the nav tab bar + /// so we don't call this on other navigation actions (like `Lista >` buttons) + Future onTabBarChange(NavBarEnum item) async { + final route = NavBarConfig.tabViews.get(item); + if (stack.last.routeName != route.routeName) { + _popUntilTabBarView(); + await push(route); + } + } + + /// pops details views above tab views + void _popUntilTabBarView() { + _router?.popUntil( + (element) => (element.settings as AutoRoutePage) + .routeData + .topMatch + .toPageRouteInfo() + .isTabView, + ); + } + + void refreshState() { + state = build(); + } + + @override + NavigationState build() { + return ( + isStackPoppable: stack.length > 1, + activeTab: + stack.lastWhereOrNull((element) => element.isTabView)?.tabBarEnum ?? + NavBarEnum.home, + ); + } +} diff --git a/lib/features/navigator/navigator/detail_view_navigator.dart b/lib/features/navigator/navigator/detail_view_navigator.dart deleted file mode 100644 index 07e91cb1..00000000 --- a/lib/features/navigator/navigator/detail_view_navigator.dart +++ /dev/null @@ -1,28 +0,0 @@ -import "../../../config/nav_bar_config.dart"; -import "../../../config/routes.dart"; -import "nested_navigator.dart"; -import "tab_bar_navigator.dart"; - -/// Extracted logic of navigating to detailed views -extension DetailViewNavigator on NestedNavigator { - Future navigateToStudyCircleDetails(String argument) async { - await changeTabBar(NavBarEnum.sciCircles); - await navigatorKey.currentState?.pushNamed( - AppRoutes.studyCircleDetails, - arguments: argument, - ); - } - - Future navigateToAboutUs() async { - await changeTabBar(NavBarEnum.info); - await navigatorKey.currentState?.pushNamed(AppRoutes.aboutUsDetail); - } - - Future navigateToDepartmentDetails(String argument) async { - await changeTabBar(NavBarEnum.faculties); - await navigatorKey.currentState?.pushNamed( - AppRoutes.departmentDetails, - arguments: argument, - ); - } -} diff --git a/lib/features/navigator/navigator/nested_navigator.dart b/lib/features/navigator/navigator/nested_navigator.dart deleted file mode 100644 index 1adf7a78..00000000 --- a/lib/features/navigator/navigator/nested_navigator.dart +++ /dev/null @@ -1,38 +0,0 @@ -import "package:flutter/material.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; - -import "../../../config/navigator_config.dart"; -import "../../../config/routes.dart"; -import "../../bottom_nav_bar/bottom_nav_bar_controller.dart"; -import "../page_routes/detail_page_route.dart"; -import "../page_routes/tabbar_page_route.dart"; -import "../utils/android_pop_bug_workaround.dart"; -import "../utils/extract_tabbar_arg.dart"; - -class NestedNavigator { - NestedNavigator(this.ref); - final Ref ref; - final navigatorKey = GlobalKey(); - - Future handleNestedPop() async { - if (androidSpecialPopTreatment) { - return handleAndroidSpecialPop(); - } - await navigatorKey.currentState?.maybePop(); - } - - Route onGenerateRoute(RouteSettings settings) { - if (settings.name == AppRoutes.root) { - final previousTab = ref.read(bottomNavBarControllerProvider); - return TabBarPageRoute( - previousTab: previousTab, - newTab: settings.selectedTab ?? NavigatorConfig.initialTab, - ); - } - return DetailPageRoute(settings); - } -} - -final navigatorProvider = Provider((ref) { - return NestedNavigator(ref); -}); diff --git a/lib/features/navigator/navigator/tab_bar_navigator.dart b/lib/features/navigator/navigator/tab_bar_navigator.dart deleted file mode 100644 index 774ff8e3..00000000 --- a/lib/features/navigator/navigator/tab_bar_navigator.dart +++ /dev/null @@ -1,21 +0,0 @@ -import "../../../config/nav_bar_config.dart"; -import "../../../config/routes.dart"; -import "../../bottom_nav_bar/bottom_nav_bar_controller.dart"; -import "nested_navigator.dart"; - -/// Extracted logic of navigating to tabs in tab view -extension TabBarNavigator on NestedNavigator { - Future changeTabBar(NavBarEnum newTab) async { - final currentTab = ref.read(bottomNavBarControllerProvider); - if (currentTab != newTab) { - navigatorKey.currentState?.popUntil( - (route) => route.settings.name == AppRoutes.root, - ); - - await navigatorKey.currentState?.pushNamed( - AppRoutes.root, - arguments: newTab, - ); - } - } -} diff --git a/lib/features/navigator/page_routes/detail_page_route.dart b/lib/features/navigator/page_routes/detail_page_route.dart deleted file mode 100644 index ff6bb3c4..00000000 --- a/lib/features/navigator/page_routes/detail_page_route.dart +++ /dev/null @@ -1,21 +0,0 @@ -import "package:flutter/material.dart"; - -import "../../../config/routes.dart"; -import "../../../widgets/my_error_widget.dart"; -import "../../department_details/department_details.dart"; -import "../../guide/widgets/about_us/about_us_tab.dart"; -import "../../study_circle_details/study_circle_details.dart"; - -class DetailPageRoute extends MaterialPageRoute { - DetailPageRoute(RouteSettings settings) - : super( - builder: switch (settings.name) { - // here will be more detail routes - AppRoutes.studyCircleDetails => (_) => const StudyCircleDetails(), - AppRoutes.departmentDetails => (_) => const DepartmentDetails(), - AppRoutes.aboutUsDetail => (_) => const AboutUsTab(), - _ => (_) => MyErrorWidget("Invalid route: ${settings.name}") - }, - settings: settings, - ); -} diff --git a/lib/features/navigator/page_routes/tabbar_page_route.dart b/lib/features/navigator/page_routes/tabbar_page_route.dart deleted file mode 100644 index 44b3314e..00000000 --- a/lib/features/navigator/page_routes/tabbar_page_route.dart +++ /dev/null @@ -1,62 +0,0 @@ -import "package:flutter/material.dart"; - -import "../../../config/nav_bar_config.dart"; -import "../../../config/navigator_config.dart"; -import "../../../config/routes.dart"; -import "../../../theme/app_theme.dart"; - -class TabBarPageRoute extends MaterialPageRoute { - TabBarPageRoute({ - required this.previousTab, - required this.newTab, - }) : super( - builder: (context) { - return TabsConfig.tabs.get(newTab); - }, - settings: RouteSettings( - name: AppRoutes.root, - arguments: newTab, - ), - maintainState: - false, // to avoid trashing RAM when changing tabs multiple times - ); - - final NavBarEnum previousTab; - final NavBarEnum newTab; - - @override - Widget buildTransitions( - BuildContext context, - Animation animation, - Animation secondaryAnimation, - Widget child, - ) { - final isNewTabMoreToRight = newTab.index >= previousTab.index; - - final animationTween = Tween( - begin: isNewTabMoreToRight - ? const Offset(1, 0) // slide from right - : const Offset(-1, 0), // slide from left - end: Offset.zero, - ).chain(CurveTween(curve: Curves.ease)); - - return SlideTransition( - position: animation.drive(animationTween), - child: child, - ); - } -} - -class InfoPlaceholder extends StatelessWidget { - const InfoPlaceholder({super.key}); - - @override - Widget build(BuildContext context) { - return ColoredBox( - color: context.colorTheme.whiteSoap, - child: const Center( - child: Text("Info"), - ), - ); - } -} diff --git a/lib/features/navigator/root_navigator_widget.dart b/lib/features/navigator/root_navigator_widget.dart deleted file mode 100644 index e3d2caa4..00000000 --- a/lib/features/navigator/root_navigator_widget.dart +++ /dev/null @@ -1,38 +0,0 @@ -import "package:flutter/material.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; - -import "../bottom_nav_bar/bottom_nav_bar.dart"; -import "../bottom_nav_bar/bottom_nav_bar_controller.dart"; -import "navigator/nested_navigator.dart"; -import "utils/android_pop_bug_workaround.dart"; - -class RootNavigatorWidget extends ConsumerWidget { - const RootNavigatorWidget({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final navigator = ref.watch(navigatorProvider); - final bottomNavController = - ref.watch(bottomNavBarControllerProvider.notifier); - return Scaffold( - body: PopScope( - canPop: !navigator.androidSpecialPopTreatment, - child: NavigatorPopHandler( - onPop: navigator.handleNestedPop, - child: Navigator( - key: navigator.navigatorKey, - onGenerateRoute: navigator.onGenerateRoute, - observers: [bottomNavController.selectedTabObserver], - ), - ), - ), - bottomNavigationBar: Theme( - data: Theme.of(context).copyWith( - splashColor: Colors.transparent, - highlightColor: Colors.transparent, - ), - child: const BottomNavBar(), - ), - ); - } -} diff --git a/lib/features/navigator/root_view.dart b/lib/features/navigator/root_view.dart new file mode 100644 index 00000000..e200ba4b --- /dev/null +++ b/lib/features/navigator/root_view.dart @@ -0,0 +1,30 @@ +import "package:auto_route/auto_route.dart"; +import "package:flutter/material.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; + +import "../bottom_nav_bar/bottom_nav_bar.dart"; +import "navigation_controller.dart"; +import "utils/android_pop_bug_workaround.dart"; + +@RoutePage() +class RootView extends ConsumerWidget { + const RootView({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final specialPop = ref.androidSpecialPopTreatment; + return PopScope( + canPop: !specialPop, // android pop bug workaround + child: NavigatorPopHandler( + onPop: specialPop ? ref.handleAndroidSpecialPop : null, + child: Scaffold( + body: AutoRouter( + // this widget act as nested [Navigator] for the app + key: ref.watch(navigationControllerProvider.notifier).navigatorKey, + ), + bottomNavigationBar: const BottomNavBar(), + ), + ), + ); + } +} diff --git a/lib/features/navigator/utils/android_pop_bug_workaround.dart b/lib/features/navigator/utils/android_pop_bug_workaround.dart index d4f1dccf..2e7a3b42 100644 --- a/lib/features/navigator/utils/android_pop_bug_workaround.dart +++ b/lib/features/navigator/utils/android_pop_bug_workaround.dart @@ -2,16 +2,18 @@ import "dart:io"; import "package:flutter/foundation.dart"; import "package:flutter/services.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; -import "../navigator/nested_navigator.dart"; +import "../navigation_controller.dart"; -extension AndroidPopBugWorkaround on NestedNavigator { +extension AndroidPopBugWorkaroundX on WidgetRef { static const platform = MethodChannel("topwr.app.android.channel"); - bool get androidSpecialPopTreatment => - !kIsWeb && - Platform.isAndroid && - navigatorKey.currentState?.canPop() == false; + bool get androidSpecialPopTreatment { + return !kIsWeb && + Platform.isAndroid && + !read(navigationControllerProvider).isStackPoppable; + } Future handleAndroidSpecialPop() async { await platform.invokeMethod("putAppInBackground"); diff --git a/lib/features/navigator/utils/extract_tabbar_arg.dart b/lib/features/navigator/utils/extract_tabbar_arg.dart deleted file mode 100644 index 21d94cec..00000000 --- a/lib/features/navigator/utils/extract_tabbar_arg.dart +++ /dev/null @@ -1,10 +0,0 @@ -import "package:flutter/widgets.dart"; - -import "../../../config/nav_bar_config.dart"; - -extension ExtractTabbarArg on RouteSettings? { - NavBarEnum? get selectedTab { - final args = this?.arguments; - return args is NavBarEnum ? args : null; - } -} diff --git a/lib/features/navigator/utils/navigation_commands.dart b/lib/features/navigator/utils/navigation_commands.dart new file mode 100644 index 00000000..6fb0e833 --- /dev/null +++ b/lib/features/navigator/utils/navigation_commands.dart @@ -0,0 +1,47 @@ +import "package:flutter_riverpod/flutter_riverpod.dart"; + +import "../app_router.dart"; +import "../navigation_controller.dart"; + +/// just a one place to gather implementation details of navigation flow +/// - for easy maintainance +extension NavigationX on WidgetRef { + NavigationController get _router => + read(navigationControllerProvider.notifier); + + Future navigateHome() async { + await _router.push(const HomeRoute()); + } + + Future navigateBuildings() async { + await _router.push(const BuildingsRoute()); + } + + Future navigateParkings() async { + await _router.push(const ParkingsRoute()); + } + + Future navigateDepartments() async { + await _router.push(const DepartmentsRoute()); + } + + Future navigateDepartmentDetail(String id) async { + await _router.push(DepartmentDetailRoute(id: id)); + } + + Future navigateScienceClubs() async { + await _router.push(const ScienceClubsRoute()); + } + + Future navigateSciClubsDetail(String id) async { + await _router.push(ScienceClubDetailRoute(id: id)); + } + + Future navigateGuide() async { + await _router.push(const GuideRoute()); + } + + Future navigateAboutUs() async { + await _router.push(const AboutUsRoute()); + } +} diff --git a/lib/features/navigator/utils/selected_tab_observer.dart b/lib/features/navigator/utils/selected_tab_observer.dart deleted file mode 100644 index 464ed3f9..00000000 --- a/lib/features/navigator/utils/selected_tab_observer.dart +++ /dev/null @@ -1,48 +0,0 @@ -import "package:flutter/material.dart"; - -import "../../../config/nav_bar_config.dart"; -import "extract_tabbar_arg.dart"; - -class SelectedTabObserver extends RouteObserver> { - void Function(NavBarEnum tab) onTabChanged; - - /// Watches the changes of new navigation routes and calls onTabChanged - SelectedTabObserver(this.onTabChanged); - - void _recordCurrentlySelectedTab(Route route) { - final routesTabData = route.settings.selectedTab; - if (routesTabData != null) { - onTabChanged(routesTabData); - } - } - - @override - void didPush(Route route, Route? previousRoute) { - super.didPush(route, previousRoute); - _recordCurrentlySelectedTab(route); - } - - @override - void didRemove(Route route, Route? previousRoute) { - super.didRemove(route, previousRoute); - if (previousRoute != null) { - _recordCurrentlySelectedTab(previousRoute); - } - } - - @override - void didReplace({Route? newRoute, Route? oldRoute}) { - super.didReplace(newRoute: newRoute, oldRoute: oldRoute); - if (newRoute != null) { - _recordCurrentlySelectedTab(newRoute); - } - } - - @override - void didPop(Route route, Route? previousRoute) { - super.didPop(route, previousRoute); - if (previousRoute != null) { - _recordCurrentlySelectedTab(previousRoute); - } - } -} diff --git a/lib/features/navigator/utils/tab_bar_transition.dart b/lib/features/navigator/utils/tab_bar_transition.dart new file mode 100644 index 00000000..63384d8f --- /dev/null +++ b/lib/features/navigator/utils/tab_bar_transition.dart @@ -0,0 +1,37 @@ +import "package:flutter/material.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; + +import "../../../config/nav_bar_config.dart"; +import "../../../config/transitions.dart"; +import "../../../utils/last_or_null.dart"; +import "../navigation_controller.dart"; + +extension TabBarRouteTransitionBuilderX on Ref { + RouteTransitionsBuilder tabBarTransitionBuilder(String thisRouteName) { + return (a, b, c, d) { + final previousTab = _getPreviousTab(thisRouteName)?.tabBarEnum; + final thisTabBarEnum = thisRouteName.tabBarEnum; + + final isNewTabMoreToRight = + (thisTabBarEnum?.index ?? 0) >= (previousTab?.index ?? 0); + + return isNewTabMoreToRight + ? TransitionsConfig.slideLeftBuilder(a, b, c, d) + : TransitionsConfig.slideRightBuilder(a, b, c, d); + }; + } + + /// read navigator state and returns previous tab for the given one + /// to compare and decide the transition direction (left or right) + TRoute? _getPreviousTab(String thisRouteName) { + final navigator = read(navigationControllerProvider.notifier); + final stack = navigator.stack.toList(); + final didLastPop = navigator.didLastPop; + final isThisLastRouteOnStack = stack.lastOrNull?.routeName == thisRouteName; + return isThisLastRouteOnStack + ? stack.preLastOrNull + : didLastPop + ? stack.lastOrNull + : stack.prePreLastOrNull; + } +} diff --git a/lib/features/offline_messages/messages_config.dart b/lib/features/offline_messages/messages_config.dart index 03dda458..20234af7 100644 --- a/lib/features/offline_messages/messages_config.dart +++ b/lib/features/offline_messages/messages_config.dart @@ -3,18 +3,18 @@ import "package:flutter/material.dart"; import "../../config/ttl_config.dart"; import "../../utils/context_extensions.dart"; -extension GqlOfflineMessage on BuildContext { +extension GqlOfflineMessageX on BuildContext { UnmodifiableTtlKeyMap _offlineMessagesLocalized() => UnmodifiableTtlKeyMap( - infosPreviewRepository: localize.offline_news, + newsRepository: localize.offline_news, academicCalendarRepository: localize.offline_academic_calendar, - sciCirclesPreviewRepository: localize.offline_sci_clubs, - sciCirclesRepository: localize.offline_sci_clubs, + scienceClubDetailsRepository: localize.offline_sci_clubs, + scienceClubsRepository: localize.offline_sci_clubs, tagsRepository: localize.offline_sci_clubs, - mapBuildingsRepository: localize.offline_buildings, + buildingsRepository: localize.offline_buildings, departmentsRepository: localize.offline_departments, aboutUsRepository: localize.offline_about_us, - departmentsDetailsRepository: localize.offline_department_details, + departmentDetailsRepository: localize.offline_department_details, ); String gqlOfflineMessageLocalized(TtlKey key) { diff --git a/lib/features/offline_messages/widgets/grapgql_offline_message.dart b/lib/features/offline_messages/widgets/grapgql_offline_message.dart index fab966fc..17e54a3a 100644 --- a/lib/features/offline_messages/widgets/grapgql_offline_message.dart +++ b/lib/features/offline_messages/widgets/grapgql_offline_message.dart @@ -9,6 +9,7 @@ import "general_offline_message.dart"; class OfflineGraphQLMessage extends ConsumerWidget { const OfflineGraphQLMessage(this.exceptionSourceKey, {super.key}); final TtlKey exceptionSourceKey; + @override Widget build(BuildContext context, WidgetRef ref) { return OfflineMessage( diff --git a/lib/features/iparking_chart/chart_elements/chart_border.dart b/lib/features/parking_chart/chart_elements/chart_border.dart similarity index 100% rename from lib/features/iparking_chart/chart_elements/chart_border.dart rename to lib/features/parking_chart/chart_elements/chart_border.dart diff --git a/lib/features/iparking_chart/chart_elements/chart_grid.dart b/lib/features/parking_chart/chart_elements/chart_grid.dart similarity index 100% rename from lib/features/iparking_chart/chart_elements/chart_grid.dart rename to lib/features/parking_chart/chart_elements/chart_grid.dart diff --git a/lib/features/iparking_chart/chart_elements/chart_line.dart b/lib/features/parking_chart/chart_elements/chart_line.dart similarity index 92% rename from lib/features/iparking_chart/chart_elements/chart_line.dart rename to lib/features/parking_chart/chart_elements/chart_line.dart index dfbde35b..43bfa03e 100644 --- a/lib/features/iparking_chart/chart_elements/chart_line.dart +++ b/lib/features/parking_chart/chart_elements/chart_line.dart @@ -13,7 +13,7 @@ class ChartLine extends LineChartBarData { barWidth: 1, isStrokeCapRound: true, dotData: const FlDotData( - checkToShowDot: IChartUtils.showDotOrNot, + checkToShowDot: ChartUtilsX.showDotOrNot, ), belowBarData: BarAreaData( show: true, diff --git a/lib/features/iparking_chart/chart_elements/labels_bottom.dart b/lib/features/parking_chart/chart_elements/labels_bottom.dart similarity index 91% rename from lib/features/iparking_chart/chart_elements/labels_bottom.dart rename to lib/features/parking_chart/chart_elements/labels_bottom.dart index bddf780a..07dd907f 100644 --- a/lib/features/iparking_chart/chart_elements/labels_bottom.dart +++ b/lib/features/parking_chart/chart_elements/labels_bottom.dart @@ -13,7 +13,7 @@ class BottomLabels extends AxisTitles { getTitlesWidget: (double val, _) => SideTitleWidget( axisSide: AxisSide.bottom, child: Text( - IChartUtils.getLabelForValue(val), + ChartUtilsX.getLabelForValue(val), style: context.iParkingTheme.chart, ), ), diff --git a/lib/features/iparking_chart/chart_elements/labels_left.dart b/lib/features/parking_chart/chart_elements/labels_left.dart similarity index 95% rename from lib/features/iparking_chart/chart_elements/labels_left.dart rename to lib/features/parking_chart/chart_elements/labels_left.dart index 2e7736fd..0ffd586d 100644 --- a/lib/features/iparking_chart/chart_elements/labels_left.dart +++ b/lib/features/parking_chart/chart_elements/labels_left.dart @@ -3,7 +3,7 @@ import "package:flutter/widgets.dart"; import "../../../theme/app_theme.dart"; -extension WillMaxLabelOverlap on TitleMeta { +extension WillMaxLabelOverlapX on TitleMeta { bool get isMaxLabelOverlapping { return max % appliedInterval ~/ 10 == 0; } diff --git a/lib/features/iparking_chart/models/chart_point.dart b/lib/features/parking_chart/models/chart_point.dart similarity index 87% rename from lib/features/iparking_chart/models/chart_point.dart rename to lib/features/parking_chart/models/chart_point.dart index 8d56ef47..5551bfcd 100644 --- a/lib/features/iparking_chart/models/chart_point.dart +++ b/lib/features/parking_chart/models/chart_point.dart @@ -1,8 +1,8 @@ import "package:collection/collection.dart"; import "package:fl_chart/fl_chart.dart"; -import "chart_data_model.dart"; import "hour_label.dart"; +import "raw_chart_data.dart"; class ChartPoint extends FlSpot { ChartPoint(super.x, super.y); @@ -14,7 +14,7 @@ class ChartPoint extends FlSpot { ); } -extension ToCharPointsExt on RawChartData { +extension ToCharPointsX on RawChartData { Iterable toChartPoints() sync* { for (final pair in IterableZip( [labels, data], diff --git a/lib/features/iparking_chart/models/hour_label.dart b/lib/features/parking_chart/models/hour_label.dart similarity index 100% rename from lib/features/iparking_chart/models/hour_label.dart rename to lib/features/parking_chart/models/hour_label.dart diff --git a/lib/features/iparking_chart/models/chart_data_model.dart b/lib/features/parking_chart/models/raw_chart_data.dart similarity index 77% rename from lib/features/iparking_chart/models/chart_data_model.dart rename to lib/features/parking_chart/models/raw_chart_data.dart index 07051d96..6de67b9d 100644 --- a/lib/features/iparking_chart/models/chart_data_model.dart +++ b/lib/features/parking_chart/models/raw_chart_data.dart @@ -1,14 +1,14 @@ import "package:freezed_annotation/freezed_annotation.dart"; -part "chart_data_model.freezed.dart"; -part "chart_data_model.g.dart"; +part "raw_chart_data.freezed.dart"; +part "raw_chart_data.g.dart"; @freezed class RawChartData with _$RawChartData { const factory RawChartData({ required List data, required List labels, - }) = _ChartData; + }) = _RawChartData; factory RawChartData.fromJson(Map json) => _$RawChartDataFromJson(json); diff --git a/lib/features/iparking_chart/chart_widget.dart b/lib/features/parking_chart/parking_chart.dart similarity index 84% rename from lib/features/iparking_chart/chart_widget.dart rename to lib/features/parking_chart/parking_chart.dart index cc44c18b..32afc77c 100644 --- a/lib/features/iparking_chart/chart_widget.dart +++ b/lib/features/parking_chart/parking_chart.dart @@ -7,19 +7,19 @@ import "../../theme/iparking_theme.dart"; import "../../utils/context_extensions.dart"; import "../../widgets/loading_widgets/simple_previews/preview_card_loading.dart"; import "../../widgets/my_error_widget.dart"; -import "../iparking/models/parking_model.dart"; +import "../parkings_view/models/parking.dart"; import "models/chart_point.dart"; -import "repositories/chart_repo.dart"; -import "widgets/ichart.dart"; +import "repository/chart_repository.dart"; +import "widgets/chart_widget.dart"; class ParkingChart extends ConsumerWidget { - const ParkingChart(this.parkingPlace, {super.key}); + const ParkingChart(this.parking, {super.key}); - final ParkingPlace parkingPlace; + final Parking parking; @override Widget build(BuildContext context, WidgetRef ref) { - final chartData = ref.watch(chartRepoProvider.call(parkingPlace)); + final chartData = ref.watch(chartRepositoryProvider(parking)); return switch (chartData) { AsyncLoading() => Padding( padding: const EdgeInsets.only( @@ -57,7 +57,7 @@ class ParkingChart extends ConsumerWidget { right: 25, bottom: 10, ), - child: IChart(chartPoints, parkingPlace), + child: ChartWidget(chartPoints, parking), ); }, ), diff --git a/lib/features/parking_chart/repository/chart_repository.dart b/lib/features/parking_chart/repository/chart_repository.dart new file mode 100644 index 00000000..f365e240 --- /dev/null +++ b/lib/features/parking_chart/repository/chart_repository.dart @@ -0,0 +1,20 @@ +import "package:riverpod_annotation/riverpod_annotation.dart"; + +import "../../parkings_view/api_client/iparking_commands.dart"; +import "../../parkings_view/models/parking.dart"; +import "../models/raw_chart_data.dart"; + +part "chart_repository.g.dart"; + +@riverpod +Future chartRepository( + ChartRepositoryRef ref, + Parking parking, +) async { + final response = await ref.postIParkingCommand>( + FetchChartCommand(parking.id), + ); + return RawChartData.fromJson( + response.data?["slots"], + ); +} diff --git a/lib/features/iparking_chart/utils/chart_utils.dart b/lib/features/parking_chart/utils/chart_utils.dart similarity index 77% rename from lib/features/iparking_chart/utils/chart_utils.dart rename to lib/features/parking_chart/utils/chart_utils.dart index 9d1fe77c..bd91fea9 100644 --- a/lib/features/iparking_chart/utils/chart_utils.dart +++ b/lib/features/parking_chart/utils/chart_utils.dart @@ -4,16 +4,16 @@ import "package:collection/collection.dart"; import "package:fl_chart/fl_chart.dart"; import "../../../config/ui_config.dart"; -import "../../iparking/models/parking_model.dart"; +import "../../parkings_view/models/parking.dart"; import "../models/chart_point.dart"; import "../models/hour_label.dart"; -extension IChartUtils on List { +extension ChartUtilsX on List { double get minX => first.x; double get maxX => last.x; - double maxY(ParkingPlace parkingPlace) => max( - double.tryParse(parkingPlace.places) ?? 0, + double maxY(Parking parking) => max( + double.tryParse(parking.places) ?? 0, map((e) => e.y).max, ); diff --git a/lib/features/iparking_chart/utils/range_hour_points.dart b/lib/features/parking_chart/utils/range_hour_points.dart similarity index 100% rename from lib/features/iparking_chart/utils/range_hour_points.dart rename to lib/features/parking_chart/utils/range_hour_points.dart diff --git a/lib/features/iparking_chart/widgets/ichart.dart b/lib/features/parking_chart/widgets/chart_widget.dart similarity index 71% rename from lib/features/iparking_chart/widgets/ichart.dart rename to lib/features/parking_chart/widgets/chart_widget.dart index ebf7c3ec..311e8432 100644 --- a/lib/features/iparking_chart/widgets/ichart.dart +++ b/lib/features/parking_chart/widgets/chart_widget.dart @@ -1,7 +1,7 @@ import "package:fl_chart/fl_chart.dart"; import "package:flutter/material.dart"; -import "../../iparking/models/parking_model.dart"; +import "../../parkings_view/models/parking.dart"; import "../chart_elements/chart_border.dart"; import "../chart_elements/chart_grid.dart"; import "../chart_elements/chart_line.dart"; @@ -11,11 +11,11 @@ import "../models/chart_point.dart"; import "../utils/chart_utils.dart"; import "reversed_label.dart"; -class IChart extends StatelessWidget { - const IChart(this.chartData, this.parkingPlace, {super.key}); +class ChartWidget extends StatelessWidget { + const ChartWidget(this.chartData, this.parking, {super.key}); final List chartData; - final ParkingPlace parkingPlace; + final Parking parking; @override Widget build(BuildContext context) { @@ -28,15 +28,15 @@ class IChart extends StatelessWidget { borderData: ChartBorder(context), gridData: ChartGrid(context), titlesData: FlTitlesData( - rightTitles: const HideLabels(), - topTitles: const HideLabels(), + rightTitles: const _HideLabels(), + topTitles: const _HideLabels(), bottomTitles: BottomLabels(context), leftTitles: LeftLabels(context), ), lineBarsData: [ChartLine(context, chartData)], minX: chartData.minX, maxX: chartData.maxX, - maxY: chartData.maxY(parkingPlace), + maxY: chartData.maxY(parking), minY: 0, ), ), @@ -46,6 +46,6 @@ class IChart extends StatelessWidget { } } -class HideLabels extends AxisTitles { - const HideLabels() : super(sideTitles: const SideTitles()); +class _HideLabels extends AxisTitles { + const _HideLabels() : super(sideTitles: const SideTitles()); } diff --git a/lib/features/iparking_chart/widgets/reversed_label.dart b/lib/features/parking_chart/widgets/reversed_label.dart similarity index 100% rename from lib/features/iparking_chart/widgets/reversed_label.dart rename to lib/features/parking_chart/widgets/reversed_label.dart diff --git a/lib/features/iparking/README.md b/lib/features/parkings_view/README.md similarity index 100% rename from lib/features/iparking/README.md rename to lib/features/parkings_view/README.md diff --git a/lib/features/iparking/api_client/iparking_client.dart b/lib/features/parkings_view/api_client/iparking_client.dart similarity index 87% rename from lib/features/iparking/api_client/iparking_client.dart rename to lib/features/parkings_view/api_client/iparking_client.dart index 5ff1a195..f02e148a 100644 --- a/lib/features/iparking/api_client/iparking_client.dart +++ b/lib/features/parkings_view/api_client/iparking_client.dart @@ -5,7 +5,7 @@ import "../../../config/api_base_config.dart"; part "iparking_client.g.dart"; -abstract class IParkingConfig { +abstract class ParkingsConfig { static const parkingsRefreshInterval = Duration(minutes: 1); static final rootUrl = ApiBaseEnv.iparkingUrl; static const soapEndpoint = "/modules/iparking/scripts/ipk_operations.php"; @@ -26,8 +26,8 @@ abstract class IParkingConfig { Dio iParkingClient(IParkingClientRef ref) { return Dio( BaseOptions( - baseUrl: IParkingConfig.soapFullUrl, - headers: IParkingConfig.headers, + baseUrl: ParkingsConfig.soapFullUrl, + headers: ParkingsConfig.headers, ), ); } diff --git a/lib/features/iparking/api_client/iparking_commands.dart b/lib/features/parkings_view/api_client/iparking_commands.dart similarity index 94% rename from lib/features/iparking/api_client/iparking_commands.dart rename to lib/features/parkings_view/api_client/iparking_commands.dart index 6bc53406..4a9c308d 100644 --- a/lib/features/iparking/api_client/iparking_commands.dart +++ b/lib/features/parkings_view/api_client/iparking_commands.dart @@ -22,7 +22,7 @@ class FetchChartCommand extends DelegatingMap { class ParkingsOfflineException implements Exception {} -extension IParkingCommands on AutoDisposeRef { +extension IParkingCommandsX on AutoDisposeRef { Future> postIParkingCommand(Map command) async { try { return await watch(iParkingClientProvider).post("", data: command); diff --git a/lib/features/iparking/controllers.dart b/lib/features/parkings_view/controllers.dart similarity index 65% rename from lib/features/iparking/controllers.dart rename to lib/features/parkings_view/controllers.dart index 122a453c..1c49c2e2 100644 --- a/lib/features/iparking/controllers.dart +++ b/lib/features/parkings_view/controllers.dart @@ -5,34 +5,34 @@ import "../map_view/controllers/active_map_marker_cntrl.dart"; import "../map_view/controllers/controllers_set.dart"; import "../map_view/controllers/map_controller.dart"; import "../map_view/controllers/map_data_controller.dart"; -import "models/parking_model.dart"; -import "repositories/parkings_repo.dart"; +import "models/parking.dart"; +import "repository/parkings_repository.dart"; part "controllers.g.dart"; @riverpod class ActiveParkingController extends _$ActiveParkingController - with ActiveMarkerController { + with ActiveMarkerController { @override - ParkingPlace? build() { + Parking? build() { return null; } } @riverpod -class ParkingsListViewController extends _$ParkingsListViewController - with MapDataController { - ParkingsListViewController() { +class ParkingsViewController extends _$ParkingsViewController + with MapDataController { + ParkingsViewController() { mapControllers = parkingsMapControllers; } @override // ignore: unnecessary_overrides - FutureOr?> build() async { + FutureOr?> build() async { return super.build(); } @override - bool filterMethod(ParkingPlace item, String filterStr) { + bool filterMethod(Parking item, String filterStr) { return item.name.toLowerCase().contains(filterStr) || item.symbol.toLowerCase().contains(filterStr) || item.address.toLowerCase().contains(filterStr); @@ -41,7 +41,7 @@ class ParkingsListViewController extends _$ParkingsListViewController @riverpod class ParkingsMapController extends _$ParkingsMapController - with MapController { + with MapController { ParkingsMapController() { mapControllers = parkingsMapControllers; } @@ -52,9 +52,9 @@ class ParkingsMapController extends _$ParkingsMapController } } -final MapControllers parkingsMapControllers = ( +final MapControllers parkingsMapControllers = ( activeMarker: activeParkingControllerProvider, - sourceRepo: parkingsRepoProvider, + sourceRepo: parkingsRepositoryProvider, map: parkingsMapControllerProvider, - dataController: parkingsListViewControllerProvider, + dataController: parkingsViewControllerProvider, ); diff --git a/lib/features/iparking/models/parking_model.dart b/lib/features/parkings_view/models/parking.dart similarity index 82% rename from lib/features/iparking/models/parking_model.dart rename to lib/features/parkings_view/models/parking.dart index 3d662cd5..1627fe75 100644 --- a/lib/features/iparking/models/parking_model.dart +++ b/lib/features/parkings_view/models/parking.dart @@ -6,14 +6,14 @@ import "package:google_maps_flutter/google_maps_flutter.dart"; import "../../map_view/controllers/controllers_set.dart"; import "../api_client/iparking_client.dart"; -part "parking_model.freezed.dart"; -part "parking_model.g.dart"; +part "parking.freezed.dart"; +part "parking.g.dart"; @freezed -class ParkingPlace with _$ParkingPlace implements GoogleNavigable { +class Parking with _$Parking implements GoogleNavigable { @Implements() @JsonSerializable(fieldRename: FieldRename.snake) - const factory ParkingPlace({ + const factory Parking({ required String id, required String parkingId, @JsonKey(name: "czas_pomiaru") required String measurementTime, @@ -33,13 +33,13 @@ class ParkingPlace with _$ParkingPlace implements GoogleNavigable { required String address, required String trend, }) = _ParkingPlace; - const ParkingPlace._(); + const Parking._(); - factory ParkingPlace.fromJson(Map json) => - _$ParkingPlaceFromJson(json); + factory Parking.fromJson(Map json) => + _$ParkingFromJson(json); String get iParkPhotoUrl { - return IParkingConfig.rootUrl + photo.trim(); + return ParkingsConfig.rootUrl + photo.trim(); } double get latitude => double.tryParse(geoLat) ?? 0; @@ -61,13 +61,13 @@ class ParkingPlace with _$ParkingPlace implements GoogleNavigable { String get counterText => "$numberOfPlaces ${trend.dashForm}"; } -extension _FormatIParkingDate on String { +extension _FormatIParkingDateX on String { String get formatIParkingDate { return substring(0, length - 3); } } -extension TrendDash on String { +extension GetTrendDashX on String { String get dashForm { switch (this) { case "0": diff --git a/lib/features/iparking/parking_view.dart b/lib/features/parkings_view/parkings_view.dart similarity index 85% rename from lib/features/iparking/parking_view.dart rename to lib/features/parkings_view/parkings_view.dart index cb706359..4fe9f8f9 100644 --- a/lib/features/iparking/parking_view.dart +++ b/lib/features/parkings_view/parkings_view.dart @@ -1,3 +1,4 @@ +import "package:auto_route/auto_route.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:google_maps_flutter/google_maps_flutter.dart"; @@ -7,15 +8,16 @@ import "../../utils/context_extensions.dart"; import "../map_view/map_view.dart"; import "../map_view/utils/map_marker_utils.dart"; import "controllers.dart"; -import "models/parking_model.dart"; +import "models/parking.dart"; import "widgets/parking_tile.dart"; -class ParkingsMapView extends ConsumerWidget { - const ParkingsMapView({super.key}); +@RoutePage() +class ParkingsView extends ConsumerWidget { + const ParkingsView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - return GeneralMapView( + return MapView( mapViewTexts: ( emptyList: context.localize.parkings_not_found, title: context.localize.parkings_title, diff --git a/lib/features/iparking/repositories/parkings_repo.dart b/lib/features/parkings_view/repository/parkings_repository.dart similarity index 63% rename from lib/features/iparking/repositories/parkings_repo.dart rename to lib/features/parkings_view/repository/parkings_repository.dart index a2223a49..ff459582 100644 --- a/lib/features/iparking/repositories/parkings_repo.dart +++ b/lib/features/parkings_view/repository/parkings_repository.dart @@ -5,11 +5,11 @@ import "package:riverpod_annotation/riverpod_annotation.dart"; import "../api_client/iparking_client.dart"; import "../api_client/iparking_commands.dart"; -import "../models/parking_model.dart"; +import "../models/parking.dart"; -part "parkings_repo.g.dart"; +part "parkings_repository.g.dart"; -extension RefIntervalRefresh on Ref { +extension RefIntervalRefreshX on Ref { void setRefresh(Duration interval) { final timer = Timer.periodic( interval, @@ -20,14 +20,11 @@ extension RefIntervalRefresh on Ref { } @riverpod -Stream?> parkingsRepo(ParkingsRepoRef ref) async* { - ref.setRefresh(IParkingConfig.parkingsRefreshInterval); +Stream?> parkingsRepository(ParkingsRepositoryRef ref) async* { + ref.setRefresh(ParkingsConfig.parkingsRefreshInterval); final response = await ref.postIParkingCommand>( FetchPlacesCommand(DateTime.now()), ); final list = response.data?["places"] as List; - yield list - .whereType>() - .map(ParkingPlace.fromJson) - .toList(); + yield list.whereType>().map(Parking.fromJson).toList(); } diff --git a/lib/features/iparking/widgets/offline_parkings.dart b/lib/features/parkings_view/widgets/offline_parkings_view.dart similarity index 63% rename from lib/features/iparking/widgets/offline_parkings.dart rename to lib/features/parkings_view/widgets/offline_parkings_view.dart index d8096d8b..c5cfe4b4 100644 --- a/lib/features/iparking/widgets/offline_parkings.dart +++ b/lib/features/parkings_view/widgets/offline_parkings_view.dart @@ -3,16 +3,16 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../../utils/context_extensions.dart"; import "../../offline_messages/widgets/general_offline_message.dart"; -import "../repositories/parkings_repo.dart"; +import "../repository/parkings_repository.dart"; -class OfflineParkings extends ConsumerWidget { - const OfflineParkings({super.key}); +class OfflineParkingsView extends ConsumerWidget { + const OfflineParkingsView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return OfflineMessage( context.localize.offlineParkings, - onRefresh: () => ref.refresh(parkingsRepoProvider), + onRefresh: () => ref.refresh(parkingsRepositoryProvider), ); } } diff --git a/lib/features/iparking/widgets/parking_tile.dart b/lib/features/parkings_view/widgets/parking_tile.dart similarity index 89% rename from lib/features/iparking/widgets/parking_tile.dart rename to lib/features/parkings_view/widgets/parking_tile.dart index a522e01f..8de727cb 100644 --- a/lib/features/iparking/widgets/parking_tile.dart +++ b/lib/features/parkings_view/widgets/parking_tile.dart @@ -2,7 +2,7 @@ import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "../controllers.dart"; -import "../models/parking_model.dart"; +import "../models/parking.dart"; import "parking_wide_tile_card.dart"; class ParkingTile extends ConsumerWidget { @@ -12,7 +12,7 @@ class ParkingTile extends ConsumerWidget { super.key, }); - final ParkingPlace parking; + final Parking parking; final bool isActive; @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/lib/features/iparking/widgets/parking_wide_tile_card.dart b/lib/features/parkings_view/widgets/parking_wide_tile_card.dart similarity index 87% rename from lib/features/iparking/widgets/parking_wide_tile_card.dart rename to lib/features/parkings_view/widgets/parking_wide_tile_card.dart index 43d7d3e1..40e15221 100644 --- a/lib/features/iparking/widgets/parking_wide_tile_card.dart +++ b/lib/features/parkings_view/widgets/parking_wide_tile_card.dart @@ -3,9 +3,9 @@ import "package:flutter/material.dart"; import "../../../config/ui_config.dart"; import "../../../theme/app_theme.dart"; import "../../../theme/iparking_theme.dart"; -import "../../iparking_chart/chart_widget.dart"; -import "../models/parking_model.dart"; -import "i_parking_icons_icons.icons.dart"; +import "../../parking_chart/parking_chart.dart"; +import "../models/parking.dart"; +import "parkings_icons.icons.dart"; class ParkingWideTileCard extends StatelessWidget { const ParkingWideTileCard({ @@ -16,8 +16,9 @@ class ParkingWideTileCard extends StatelessWidget { }); final VoidCallback? onTap; - final ParkingPlace parking; + final Parking parking; final bool isActive; + @override Widget build(BuildContext context) { return GestureDetector( @@ -41,13 +42,13 @@ class ParkingWideTileCard extends StatelessWidget { ), Container( width: double.infinity, - padding: IParkingConfig.padding, + padding: ParkingsConfig.padding, child: _LeftColumn(parking, isActive: isActive), ), Container( width: double.infinity, height: WideTileCardConfig.imageSize, - padding: IParkingConfig.padding, + padding: ParkingsConfig.padding, child: _RightColumn(parking, isActive: isActive), ), ], @@ -60,7 +61,7 @@ class ParkingWideTileCard extends StatelessWidget { class _LeftColumn extends StatelessWidget { const _LeftColumn(this.parking, {required this.isActive}); - final ParkingPlace parking; + final Parking parking; final bool isActive; @override @@ -75,7 +76,7 @@ class _LeftColumn extends StatelessWidget { : context.iParkingTheme.title, ), Padding( - padding: IParkingConfig.extraIndentPadd, + padding: ParkingsConfig.extraIndentPadd, child: isActive ? Text( parking.addresFormatted, @@ -89,7 +90,7 @@ class _LeftColumn extends StatelessWidget { const SizedBox(height: 2), if (!isActive) Padding( - padding: IParkingConfig.extraIndentPadd, + padding: ParkingsConfig.extraIndentPadd, child: Text( parking.openingHours, style: context.iParkingTheme.small, @@ -109,7 +110,7 @@ class _LeftColumn extends StatelessWidget { class _RightColumn extends StatelessWidget { const _RightColumn(this.parking, {required this.isActive}); - final ParkingPlace parking; + final Parking parking; final bool isActive; @override @@ -120,14 +121,14 @@ class _RightColumn extends StatelessWidget { children: [ if (!isActive) const Icon( - IParkingIcons.ipark_info, + ParkingsIcons.ipark_info, color: Colors.white, size: 25, shadows: iparkingShadows, ), if (isActive) const Icon( - IParkingIcons.ip_close, + ParkingsIcons.ip_close, color: Colors.white, size: 20, ), diff --git a/lib/features/iparking/widgets/i_parking_icons_icons.icons.dart b/lib/features/parkings_view/widgets/parkings_icons.icons.dart similarity index 96% rename from lib/features/iparking/widgets/i_parking_icons_icons.icons.dart rename to lib/features/parkings_view/widgets/parkings_icons.icons.dart index 2a962d12..6f28b024 100644 --- a/lib/features/iparking/widgets/i_parking_icons_icons.icons.dart +++ b/lib/features/parkings_view/widgets/parkings_icons.icons.dart @@ -17,8 +17,8 @@ import 'package:flutter/widgets.dart'; import '../../../gen/fonts.gen.dart'; -class IParkingIcons { - IParkingIcons._(); +class ParkingsIcons { + ParkingsIcons._(); static const _kFontFam = FontFamily.iParkingIcons; static const String? _kFontPkg = null; diff --git a/lib/features/study_circle_details/repository/getScientificCircleDetails.graphql b/lib/features/science_club_detail_view/repository/getScienceClubDetails.graphql similarity index 82% rename from lib/features/study_circle_details/repository/getScientificCircleDetails.graphql rename to lib/features/science_club_detail_view/repository/getScienceClubDetails.graphql index 79b8433b..b7582bd3 100644 --- a/lib/features/study_circle_details/repository/getScientificCircleDetails.graphql +++ b/lib/features/science_club_detail_view/repository/getScienceClubDetails.graphql @@ -1,4 +1,4 @@ -query GetScientificCircleDetails($id: ID!) { +query GetScienceClubDetails($id: ID!) { Scientific_Circles_by_id(id: $id) { name description diff --git a/lib/features/science_club_detail_view/repository/science_club_details_repository.dart b/lib/features/science_club_detail_view/repository/science_club_details_repository.dart new file mode 100644 index 00000000..9b6c4b5a --- /dev/null +++ b/lib/features/science_club_detail_view/repository/science_club_details_repository.dart @@ -0,0 +1,28 @@ +import "package:riverpod_annotation/riverpod_annotation.dart"; + +import "../../../api_base/watch_query_adapter.dart"; +import "../../../config/ttl_config.dart"; +import "getScienceClubDetails.graphql.dart"; + +part "science_club_details_repository.g.dart"; + +typedef ScienceClubDetails + = Query$GetScienceClubDetails$Scientific_Circles_by_id; + +typedef _Vars = Variables$Query$GetScienceClubDetails; +typedef _GetSciClubDetails = WatchOptions$Query$GetScienceClubDetails; + +@riverpod +Stream scienceClubDetailsRepository( + ScienceClubDetailsRepositoryRef ref, + String id, +) async* { + final stream = ref.watchQueryWithCache( + _GetSciClubDetails( + eagerlyFetchResults: true, + variables: _Vars(id: id), + ), + TtlKey.scienceClubDetailsRepository, + ); + yield* stream.map((event) => event?.Scientific_Circles_by_id); +} diff --git a/lib/features/study_circle_details/study_circle_details.dart b/lib/features/science_club_detail_view/science_club_detail_view.dart similarity index 65% rename from lib/features/study_circle_details/study_circle_details.dart rename to lib/features/science_club_detail_view/science_club_detail_view.dart index 8c5cdf0f..a2c2c651 100644 --- a/lib/features/study_circle_details/study_circle_details.dart +++ b/lib/features/science_club_detail_view/science_club_detail_view.dart @@ -1,3 +1,4 @@ +import "package:auto_route/auto_route.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; @@ -5,41 +6,44 @@ import "../../api_base/directus_assets_url.dart"; import "../../config/ui_config.dart"; import "../../theme/app_theme.dart"; import "../../utils/context_extensions.dart"; -import "../../utils/determine_icon.dart"; +import "../../utils/determine_contact_icon.dart"; import "../../utils/where_non_null_iterable.dart"; -import "../../widgets/details_screen_contact_section.dart"; -import "../../widgets/details_screen_sliver_header_section.dart"; +import "../../widgets/detail_views/contact_section.dart"; +import "../../widgets/detail_views/detail_view_app_bar.dart"; +import "../../widgets/detail_views/sliver_header_section.dart"; import "../../widgets/loading_widgets/contact_section_loading.dart"; import "../../widgets/loading_widgets/header_section_loading.dart"; import "../../widgets/loading_widgets/shimmer_loading.dart"; import "../../widgets/my_error_widget.dart"; -import "repository/study_circle_repository.dart"; -import "widgets/details_screen_about_us_section.dart"; -import "widgets/details_screen_app_bar.dart"; -import "widgets/loading_widgets/about_us_section_loading.dart"; - -class StudyCircleDetails extends StatelessWidget { - const StudyCircleDetails({super.key}); +import "repository/science_club_details_repository.dart"; +import "widgets/about_us_section.dart"; +import "widgets/about_us_section_loading.dart"; +@RoutePage() +class ScienceClubDetailView extends StatelessWidget { + const ScienceClubDetailView({ + @PathParam("id") required this.id, + super.key, + }); + final String id; @override Widget build(BuildContext context) { return Scaffold( - appBar: - DetailsScreenAppBar(context, title: context.localize.study_circles), - body: const _CircleDetailsDataView(), + appBar: DetailViewAppBar(context, title: context.localize.study_circles), + body: _SciClubDetailDataView(id), ); } } -class _CircleDetailsDataView extends ConsumerWidget { - const _CircleDetailsDataView(); +class _SciClubDetailDataView extends ConsumerWidget { + const _SciClubDetailDataView(this.id); + final String id; @override Widget build(BuildContext context, WidgetRef ref) { - final itemId = ModalRoute.of(context)!.settings.arguments! as String; - final state = ref.watch(studyCircleRepositoryProvider(itemId)); + final state = ref.watch(scienceClubDetailsRepositoryProvider(id)); return switch (state) { - AsyncLoading() => const _StudyCircleDetailsLoading(), + AsyncLoading() => const _SciClubDetailViewLoading(), AsyncError(:final error) => MyErrorWidget(error), AsyncValue(:final value) => CustomScrollView( slivers: [ @@ -64,12 +68,12 @@ class _CircleDetailsDataView extends ConsumerWidget { style: context.textTheme.body, textAlign: TextAlign.center, ), - const SizedBox(height: DetailsScreenConfig.spacerHeight), + const SizedBox(height: DetailViewsConfig.spacerHeight), ContactSection( title: context.localize.contact, list: value?.links.whereNonNull .map( - (a) => UrlIconsModel( + (a) => ContactIconsModel( text: a.name, url: a.link, ), @@ -77,7 +81,7 @@ class _CircleDetailsDataView extends ConsumerWidget { .toList() ?? List.empty(), ), - const SizedBox(height: DetailsScreenConfig.spacerHeight), + const SizedBox(height: DetailViewsConfig.spacerHeight), AboutUsSection( text: value?.description ?? "", ), @@ -89,8 +93,8 @@ class _CircleDetailsDataView extends ConsumerWidget { } } -class _StudyCircleDetailsLoading extends StatelessWidget { - const _StudyCircleDetailsLoading(); +class _SciClubDetailViewLoading extends StatelessWidget { + const _SciClubDetailViewLoading(); @override Widget build(BuildContext context) { @@ -100,9 +104,9 @@ class _StudyCircleDetailsLoading extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), children: const [ HeaderSectionLoading(), - SizedBox(height: DetailsScreenConfig.spacerHeight), + SizedBox(height: DetailViewsConfig.spacerHeight), ContactSectionLoading(), - SizedBox(height: DetailsScreenConfig.spacerHeight), + SizedBox(height: DetailViewsConfig.spacerHeight), AboutUsSectionLoading(), ], ), diff --git a/lib/features/study_circle_details/widgets/details_screen_about_us_section.dart b/lib/features/science_club_detail_view/widgets/about_us_section.dart similarity index 100% rename from lib/features/study_circle_details/widgets/details_screen_about_us_section.dart rename to lib/features/science_club_detail_view/widgets/about_us_section.dart diff --git a/lib/features/study_circle_details/widgets/loading_widgets/about_us_section_loading.dart b/lib/features/science_club_detail_view/widgets/about_us_section_loading.dart similarity index 84% rename from lib/features/study_circle_details/widgets/loading_widgets/about_us_section_loading.dart rename to lib/features/science_club_detail_view/widgets/about_us_section_loading.dart index 1fa5e9aa..67a42bf2 100644 --- a/lib/features/study_circle_details/widgets/loading_widgets/about_us_section_loading.dart +++ b/lib/features/science_club_detail_view/widgets/about_us_section_loading.dart @@ -1,7 +1,8 @@ import "package:flutter/material.dart"; -import "../../../../config/ui_config.dart"; -import "../../../../widgets/loading_widgets/scrolable_loader_builder.dart"; -import "../../../../widgets/loading_widgets/shimmer_loading.dart"; + +import "../../../config/ui_config.dart"; +import "../../../widgets/loading_widgets/scrolable_loader_builder.dart"; +import "../../../widgets/loading_widgets/shimmer_loading.dart"; class AboutUsSectionLoading extends StatelessWidget { const AboutUsSectionLoading({super.key}); @@ -23,7 +24,7 @@ class AboutUsSectionLoading extends StatelessWidget { ), ), ), - const SizedBox(height: DetailsScreenConfig.spacerHeight), + const SizedBox(height: DetailViewsConfig.spacerHeight), SizedBox( height: MediaQuery.of(context).size.height / 10, child: ShimmerLoadingItem( diff --git a/lib/features/student_research_group_tab/repositories/scientific_circles_tab_controller.dart b/lib/features/science_clubs_view/controllers/science_clubs_view_controller.dart similarity index 50% rename from lib/features/student_research_group_tab/repositories/scientific_circles_tab_controller.dart rename to lib/features/science_clubs_view/controllers/science_clubs_view_controller.dart index 9943fb81..ea7e46f8 100644 --- a/lib/features/student_research_group_tab/repositories/scientific_circles_tab_controller.dart +++ b/lib/features/science_clubs_view/controllers/science_clubs_view_controller.dart @@ -1,14 +1,14 @@ import "package:riverpod_annotation/riverpod_annotation.dart"; -import "../../../shared_repositories/sci_clubs_repository/scientific_circles_repository.dart"; +import "../repositories/science_clubs/science_clubs_repository.dart"; + +import "../repositories/tags/tags_repository.dart"; import "selected_tag_controller.dart"; -import "tags_repository.dart"; -part "scientific_circles_tab_controller.g.dart"; +part "science_clubs_view_controller.g.dart"; @riverpod -class SearchScientificCirclesController - extends _$SearchScientificCirclesController { +class SearchScienceClubsController extends _$SearchScienceClubsController { @override String build() => ""; @@ -18,12 +18,11 @@ class SearchScientificCirclesController } @riverpod -Future?> _sciCirclesFilteredByTextQuery( - _SciCirclesFilteredByTextQueryRef ref, +Future?> _sciClubsFilteredByTextQuery( + _SciClubsFilteredByTextQueryRef ref, ) async { - final originalList = - await ref.watch(scientificCirclesRepositoryProvider.future); - final query = ref.watch(searchScientificCirclesControllerProvider); + final originalList = await ref.watch(scienceClubsRepositoryProvider.future); + final query = ref.watch(searchScienceClubsControllerProvider); return originalList?.where( (element) => element == null || @@ -34,18 +33,15 @@ Future?> _sciCirclesFilteredByTextQuery( } @riverpod -Future?> scientificCircleList( - ScientificCircleListRef ref, +Future?> scienceClubsList( + ScienceClubsListRef ref, ) async { - final circles = - await ref.watch(_sciCirclesFilteredByTextQueryProvider.future); - + final sciClubs = await ref.watch(_sciClubsFilteredByTextQueryProvider.future); final selectedCategory = ref.watch(selectedTagControllerProvider); if (selectedCategory == ref.watch(allTagSingletonProvider).name) { - return circles; + return sciClubs; } - - final filteredAndSelectedTag = circles?.where((circle) { + final filteredAndSelectedTag = sciClubs?.where((circle) { return circle?.tags?.any((tag) => tag?.Tags_id?.name == selectedCategory) ?? false; }); diff --git a/lib/features/student_research_group_tab/repositories/selected_tag_controller.dart b/lib/features/science_clubs_view/controllers/selected_tag_controller.dart similarity index 86% rename from lib/features/student_research_group_tab/repositories/selected_tag_controller.dart rename to lib/features/science_clubs_view/controllers/selected_tag_controller.dart index ae78f3d0..e8f9879c 100644 --- a/lib/features/student_research_group_tab/repositories/selected_tag_controller.dart +++ b/lib/features/science_clubs_view/controllers/selected_tag_controller.dart @@ -1,6 +1,6 @@ import "package:riverpod_annotation/riverpod_annotation.dart"; -import "tags_repository.dart"; +import "../repositories/tags/tags_repository.dart"; part "selected_tag_controller.g.dart"; diff --git a/lib/shared_repositories/sci_clubs_repository/getScientificCircles.graphql b/lib/features/science_clubs_view/repositories/science_clubs/getScienceClubs.graphql similarity index 88% rename from lib/shared_repositories/sci_clubs_repository/getScientificCircles.graphql rename to lib/features/science_clubs_view/repositories/science_clubs/getScienceClubs.graphql index a3a81a80..46e28a86 100644 --- a/lib/shared_repositories/sci_clubs_repository/getScientificCircles.graphql +++ b/lib/features/science_clubs_view/repositories/science_clubs/getScienceClubs.graphql @@ -1,4 +1,4 @@ -query GetScientificCircles { +query GetScienceClubs { Scientific_Circles(limit: 300) { id name diff --git a/lib/features/science_clubs_view/repositories/science_clubs/science_clubs_repository.dart b/lib/features/science_clubs_view/repositories/science_clubs/science_clubs_repository.dart new file mode 100644 index 00000000..37a47b53 --- /dev/null +++ b/lib/features/science_clubs_view/repositories/science_clubs/science_clubs_repository.dart @@ -0,0 +1,36 @@ +import "package:riverpod_annotation/riverpod_annotation.dart"; + +import "../../../../../api_base/watch_query_adapter.dart"; +import "../../../../config/ttl_config.dart"; +import "getScienceClubs.graphql.dart"; + +part "science_clubs_repository.g.dart"; + +typedef ScienceClub = Query$GetScienceClubs$Scientific_Circles; +// just alias for shorter type name + +@riverpod +Stream?> scienceClubsRepository( + ScienceClubsRepositoryRef ref, +) async* { + final stream = ref.watchQueryWithCache( + WatchOptions$Query$GetScienceClubs(eagerlyFetchResults: true), + TtlKey.scienceClubsRepository, + ); + yield* stream.map( + (event) => event?.Scientific_Circles.sortBySourceTypes().toList(), + ); +} + +extension SortBySourceTypeX on Iterable { + Iterable _filter(String source) { + return where((element) => element.source == source); + } + + Iterable sortBySourceTypes() { + final p0 = _filter("manual_entry"); + final p1 = _filter("aktywni_website"); + final p2 = _filter("student_department"); + return p0.followedBy(p1).followedBy(p2).toList(); + } +} diff --git a/lib/features/student_research_group_tab/repositories/getTags.graphql b/lib/features/science_clubs_view/repositories/tags/getTags.graphql similarity index 100% rename from lib/features/student_research_group_tab/repositories/getTags.graphql rename to lib/features/science_clubs_view/repositories/tags/getTags.graphql diff --git a/lib/features/student_research_group_tab/repositories/tags_repository.dart b/lib/features/science_clubs_view/repositories/tags/tags_repository.dart similarity index 73% rename from lib/features/student_research_group_tab/repositories/tags_repository.dart rename to lib/features/science_clubs_view/repositories/tags/tags_repository.dart index e04f6998..0cde924b 100644 --- a/lib/features/student_research_group_tab/repositories/tags_repository.dart +++ b/lib/features/science_clubs_view/repositories/tags/tags_repository.dart @@ -1,14 +1,14 @@ import "package:riverpod_annotation/riverpod_annotation.dart"; import "../../../../api_base/watch_query_adapter.dart"; -import "../../../config/ttl_config.dart"; -import "../../../utils/watch_locale.dart"; -import "../../../utils/where_non_null_iterable.dart"; +import "../../../../config/ttl_config.dart"; +import "../../../../utils/watch_locale.dart"; +import "../../../../utils/where_non_null_iterable.dart"; import "getTags.graphql.dart"; part "tags_repository.g.dart"; -typedef Tag = Query$GetTags$Tags; // just alias for shorter type name +typedef Tag = Query$GetTags$Tags; @riverpod Stream?> remoteTagsRepository(RemoteTagsRepositoryRef ref) async* { @@ -26,7 +26,7 @@ Tag allTagSingleton(AllTagSingletonRef ref) => Tag(name: ref.watch(watchLocaleProvider).all); @riverpod -Future> tagsListRepository(TagsListRepositoryRef ref) async { +Future> tagsRepository(TagsRepositoryRef ref) async { final allTag = ref.watch(allTagSingletonProvider); final restTags = await ref.watch(remoteTagsRepositoryProvider.future); return [allTag, ...restTags.whereNonNull]; diff --git a/lib/features/student_research_group_tab/scientific_circles_tab.dart b/lib/features/science_clubs_view/science_clubs_view.dart similarity index 65% rename from lib/features/student_research_group_tab/scientific_circles_tab.dart rename to lib/features/science_clubs_view/science_clubs_view.dart index 580cb181..408aa4e3 100644 --- a/lib/features/student_research_group_tab/scientific_circles_tab.dart +++ b/lib/features/science_clubs_view/science_clubs_view.dart @@ -1,19 +1,19 @@ +import "package:auto_route/auto_route.dart"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../utils/context_extensions.dart"; import "../../utils/where_non_null_iterable.dart"; import "../../widgets/search_box_app_bar.dart"; -import "repositories/scientific_circles_tab_controller.dart"; -import "repositories/tags_repository.dart"; -import "widgets/scientific_circles_list.dart"; +import "controllers/science_clubs_view_controller.dart"; +import "repositories/tags/tags_repository.dart"; +import "widgets/science_clubs_list.dart"; import "widgets/tags_loading.dart"; import "widgets/tags_row.dart"; -class ScientificCirclesTab extends ConsumerWidget { - const ScientificCirclesTab({ - super.key, - }); +@RoutePage() +class ScienceClubsView extends ConsumerWidget { + const ScienceClubsView({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -23,20 +23,20 @@ class ScientificCirclesTab extends ConsumerWidget { title: context.localize.study_circles, bottomPadding: 16, onQueryChanged: ref - .watch(searchScientificCirclesControllerProvider.notifier) + .watch(searchScienceClubsControllerProvider.notifier) .onTextChanged, ), - body: const _ScientificCirclesBody(), + body: const _SciClubsBody(), ); } } -class _ScientificCirclesBody extends ConsumerWidget { - const _ScientificCirclesBody(); +class _SciClubsBody extends ConsumerWidget { + const _SciClubsBody(); @override Widget build(BuildContext context, WidgetRef ref) { - final tags = ref.watch(tagsListRepositoryProvider); + final tags = ref.watch(tagsRepositoryProvider); return Column( children: [ SizedBox( @@ -49,7 +49,7 @@ class _ScientificCirclesBody extends ConsumerWidget { ) }, ), - const ScientificCirclesList(), + const ScienceClubsList(), ], ); } diff --git a/lib/features/student_research_group_tab/widgets/ensure_visible_tags.dart b/lib/features/science_clubs_view/widgets/ensure_visible_tags.dart similarity index 97% rename from lib/features/student_research_group_tab/widgets/ensure_visible_tags.dart rename to lib/features/science_clubs_view/widgets/ensure_visible_tags.dart index 8cbf3858..436f4c86 100644 --- a/lib/features/student_research_group_tab/widgets/ensure_visible_tags.dart +++ b/lib/features/science_clubs_view/widgets/ensure_visible_tags.dart @@ -2,7 +2,7 @@ import "dart:math"; import "package:flutter/material.dart"; -import "../../../utils/calc_lines.dart"; +import "../../../utils/calculate_lines.dart"; import "../../../widgets/dual_text_max_lines.dart"; class EnsureVisibleTags extends DualTextMaxLines { diff --git a/lib/features/student_research_group_tab/widgets/scientific_circle_card.dart b/lib/features/science_clubs_view/widgets/science_club_card.dart similarity index 64% rename from lib/features/student_research_group_tab/widgets/scientific_circle_card.dart rename to lib/features/science_clubs_view/widgets/science_club_card.dart index 424feeb9..da0164c5 100644 --- a/lib/features/student_research_group_tab/widgets/scientific_circle_card.dart +++ b/lib/features/science_clubs_view/widgets/science_club_card.dart @@ -2,31 +2,32 @@ import "package:flutter/material.dart"; import "../../../api_base/directus_assets_url.dart"; import "../../../config/ui_config.dart"; -import "../../../shared_repositories/sci_clubs_repository/scientific_circles_repository.dart"; import "../../../widgets/my_cached_image.dart"; import "../../../widgets/wide_tile_card.dart"; +import "../repositories/science_clubs/science_clubs_repository.dart"; -class ResearchGroupCard extends StatelessWidget { - final ScientificCircle sciCircle; +class ScienceClubCard extends StatelessWidget { + final ScienceClub sciClub; final VoidCallback? onTap; - const ResearchGroupCard(this.sciCircle, this.onTap, {super.key}); + + const ScienceClubCard(this.sciClub, this.onTap, {super.key}); @override Widget build(BuildContext context) { return WideTileCard( - title: sciCircle.name, - subtitle: sciCircle.department?.name ?? "", + title: sciClub.name, + subtitle: sciClub.department?.name ?? "", onTap: onTap, - secondSubtitle: sciCircle.tags + secondSubtitle: sciClub.tags ?.map((tag) => "#${tag?.Tags_id?.name}") .toList() .join(", "), activeShadows: null, trailing: Padding( padding: const EdgeInsets.only( - right: ScientificCircleCardConfig.trailingPadding, - top: ScientificCircleCardConfig.trailingPadding, - bottom: ScientificCircleCardConfig.trailingPadding, + right: ScienceClubCardConfig.trailingPadding, + top: ScienceClubCardConfig.trailingPadding, + bottom: ScienceClubCardConfig.trailingPadding, ), child: SizedBox.square( dimension: WideTileCardConfig.imageSize, @@ -39,7 +40,7 @@ class ResearchGroupCard extends StatelessWidget { ), ), child: MyCachedImage( - sciCircle.logo?.filename_disk.directusUrl, + sciClub.logo?.filename_disk.directusUrl, boxFit: BoxFit.contain, noShimmeringLoading: true, ), diff --git a/lib/features/science_clubs_view/widgets/science_clubs_list.dart b/lib/features/science_clubs_view/widgets/science_clubs_list.dart new file mode 100644 index 00000000..7730f930 --- /dev/null +++ b/lib/features/science_clubs_view/widgets/science_clubs_list.dart @@ -0,0 +1,63 @@ +import "package:flutter/material.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; + +import "../../../config/ui_config.dart"; +import "../../../theme/app_theme.dart"; +import "../../../utils/context_extensions.dart"; +import "../../../utils/where_non_null_iterable.dart"; +import "../../../widgets/my_error_widget.dart"; +import "../../navigator/utils/navigation_commands.dart"; +import "../repositories/science_clubs/science_clubs_repository.dart"; +import "science_club_card.dart"; +import "science_clubs_view_loading.dart"; + +class ScienceClubsList extends ConsumerWidget { + const ScienceClubsList({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final state = ref.watch(scienceClubsRepositoryProvider); + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: ScienceClubsViewConfig.mediumPadding, + ), + child: switch (state) { + AsyncLoading() => const ScienceClubsViewLoading(), + AsyncError(:final error) => MyErrorWidget(error), + AsyncValue(:final value) => + _ScienceClubsListView(value.whereNonNull.toList()), + }, + ), + ); + } +} + +class _ScienceClubsListView extends ConsumerWidget { + const _ScienceClubsListView(this.filteredCircles); + + final List filteredCircles; + + @override + Widget build(BuildContext context, WidgetRef ref) { + if (filteredCircles.isEmpty) { + return Center( + child: Text( + context.localize.sci_circle_not_found, + style: context.textTheme.body, + ), + ); + } + return GridView.builder( + padding: const EdgeInsets.only( + bottom: ScienceClubsViewConfig.mediumPadding, + ), + gridDelegate: ScienceClubsViewConfig.researchGroupTabGridDelegate, + itemCount: filteredCircles.length, + itemBuilder: (context, index) => ScienceClubCard( + filteredCircles[index], + () async => ref.navigateSciClubsDetail(filteredCircles[index].id), + ), + ); + } +} diff --git a/lib/features/student_research_group_tab/widgets/scientific_circle_loading.dart b/lib/features/science_clubs_view/widgets/science_clubs_view_loading.dart similarity index 67% rename from lib/features/student_research_group_tab/widgets/scientific_circle_loading.dart rename to lib/features/science_clubs_view/widgets/science_clubs_view_loading.dart index 99014516..ffb37df0 100644 --- a/lib/features/student_research_group_tab/widgets/scientific_circle_loading.dart +++ b/lib/features/science_clubs_view/widgets/science_clubs_view_loading.dart @@ -3,13 +3,13 @@ import "package:flutter/material.dart"; import "../../../../widgets/loading_widgets/specific_imitations/wide_tile_loading.dart"; import "../../../config/ui_config.dart"; -class ScientificCirclesLoading extends StatelessWidget { - const ScientificCirclesLoading({super.key}); +class ScienceClubsViewLoading extends StatelessWidget { + const ScienceClubsViewLoading({super.key}); @override Widget build(BuildContext context) { return GridView.builder( - gridDelegate: ScientificCirclesTabConfig.researchGroupTabGridDelegate, + gridDelegate: ScienceClubsViewConfig.researchGroupTabGridDelegate, itemBuilder: (context, index) => const WideTileLoading(), physics: const NeverScrollableScrollPhysics(), ); diff --git a/lib/features/student_research_group_tab/widgets/tags_loading.dart b/lib/features/science_clubs_view/widgets/tags_loading.dart similarity index 81% rename from lib/features/student_research_group_tab/widgets/tags_loading.dart rename to lib/features/science_clubs_view/widgets/tags_loading.dart index cf3b07ca..c2acf387 100644 --- a/lib/features/student_research_group_tab/widgets/tags_loading.dart +++ b/lib/features/science_clubs_view/widgets/tags_loading.dart @@ -10,10 +10,10 @@ class TagsLoading extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only( - left: ScientificCirclesTabConfig.smallPadding, + left: ScienceClubsViewConfig.smallPadding, ), child: GridView.builder( - gridDelegate: ScientificCirclesTabConfig.tagsGridDelegate, + gridDelegate: ScienceClubsViewConfig.tagsGridDelegate, itemBuilder: (context, index) => const ButtonLoading(), physics: const NeverScrollableScrollPhysics(), ), diff --git a/lib/features/student_research_group_tab/widgets/tags_row.dart b/lib/features/science_clubs_view/widgets/tags_row.dart similarity index 83% rename from lib/features/student_research_group_tab/widgets/tags_row.dart rename to lib/features/science_clubs_view/widgets/tags_row.dart index 2a2bde71..0833a5cf 100644 --- a/lib/features/student_research_group_tab/widgets/tags_row.dart +++ b/lib/features/science_clubs_view/widgets/tags_row.dart @@ -3,11 +3,12 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../../config/ui_config.dart"; import "../../../theme/app_theme.dart"; -import "../repositories/selected_tag_controller.dart"; -import "../repositories/tags_repository.dart"; +import "../controllers/selected_tag_controller.dart"; +import "../repositories/tags/tags_repository.dart"; class TagsRow extends ConsumerWidget { final List allTags; + const TagsRow({super.key, required this.allTags}); @override @@ -17,9 +18,9 @@ class TagsRow extends ConsumerWidget { scrollDirection: Axis.horizontal, child: Padding( padding: const EdgeInsets.only( - left: ScientificCirclesTabConfig.smallPadding, - right: ScientificCirclesTabConfig.smallPadding, - bottom: ScientificCirclesTabConfig.smallPadding, + left: ScienceClubsViewConfig.smallPadding, + right: ScienceClubsViewConfig.smallPadding, + bottom: ScienceClubsViewConfig.smallPadding, ), child: Row( children: [ @@ -37,11 +38,12 @@ class _TagsRowItem extends ConsumerWidget { final Tag tag; final String? selectedTag; + @override Widget build(BuildContext context, WidgetRef ref) { return Padding( padding: const EdgeInsets.symmetric( - horizontal: ScientificCirclesTabConfig.microPadding, + horizontal: ScienceClubsViewConfig.microPadding, ), child: ChoiceChip( showCheckmark: false, @@ -66,7 +68,7 @@ class _TagsRowItem extends ConsumerWidget { ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( - ScientificCirclesTabConfig.buttonBorderRadius, + ScienceClubsViewConfig.buttonBorderRadius, ), ), ), diff --git a/lib/features/student_research_group_tab/widgets/scientific_circles_list.dart b/lib/features/student_research_group_tab/widgets/scientific_circles_list.dart deleted file mode 100644 index 4dd951a9..00000000 --- a/lib/features/student_research_group_tab/widgets/scientific_circles_list.dart +++ /dev/null @@ -1,68 +0,0 @@ -import "package:flutter/material.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; - -import "../../../config/ui_config.dart"; -import "../../../shared_repositories/sci_clubs_repository/scientific_circles_repository.dart"; -import "../../../theme/app_theme.dart"; -import "../../../utils/context_extensions.dart"; -import "../../../utils/where_non_null_iterable.dart"; -import "../../../widgets/my_error_widget.dart"; -import "../../navigator/navigator/detail_view_navigator.dart"; -import "../../navigator/navigator/nested_navigator.dart"; -import "../repositories/scientific_circles_tab_controller.dart"; -import "scientific_circle_card.dart"; -import "scientific_circle_loading.dart"; - -class ScientificCirclesList extends ConsumerWidget { - const ScientificCirclesList({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final state = ref.watch(scientificCircleListProvider); - - return Expanded( - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: ScientificCirclesTabConfig.mediumPadding, - ), - child: switch (state) { - AsyncLoading() => const ScientificCirclesLoading(), - AsyncError(:final error) => MyErrorWidget(error), - AsyncValue(:final value) => - _ScientificCirclesDataView(value.whereNonNull.toList()), - }, - ), - ); - } -} - -class _ScientificCirclesDataView extends ConsumerWidget { - const _ScientificCirclesDataView(this.filteredCircles); - - final List filteredCircles; - - @override - Widget build(BuildContext context, WidgetRef ref) { - if (filteredCircles.isEmpty) { - return Center( - child: Text( - context.localize.sci_circle_not_found, - style: context.textTheme.body, - ), - ); - } - return GridView.builder( - padding: const EdgeInsets.only( - bottom: ScientificCirclesTabConfig.mediumPadding, - ), - gridDelegate: ScientificCirclesTabConfig.researchGroupTabGridDelegate, - itemCount: filteredCircles.length, - itemBuilder: (context, index) => - ResearchGroupCard(filteredCircles[index], () async { - await ref - .read(navigatorProvider) - .navigateToStudyCircleDetails(filteredCircles[index].id); - }), - ); - } -} diff --git a/lib/features/study_circle_details/repository/study_circle_repository.dart b/lib/features/study_circle_details/repository/study_circle_repository.dart deleted file mode 100644 index 95f655f8..00000000 --- a/lib/features/study_circle_details/repository/study_circle_repository.dart +++ /dev/null @@ -1,28 +0,0 @@ -import "package:riverpod_annotation/riverpod_annotation.dart"; - -import "../../../api_base/watch_query_adapter.dart"; -import "../../../config/ttl_config.dart"; -import "getScientificCircleDetails.graphql.dart"; - -part "study_circle_repository.g.dart"; - -typedef StudyCircleDetails - = Query$GetScientificCircleDetails$Scientific_Circles_by_id; - -typedef _Vars = Variables$Query$GetScientificCircleDetails; -typedef _GetStudyCircles = WatchOptions$Query$GetScientificCircleDetails; - -@riverpod -Stream studyCircleRepository( - StudyCircleRepositoryRef ref, - String id, -) async* { - final stream = ref.watchQueryWithCache( - _GetStudyCircles( - eagerlyFetchResults: true, - variables: _Vars(id: id), - ), - TtlKey.sciCirclesPreviewRepository, - ); - yield* stream.map((event) => event?.Scientific_Circles_by_id); -} diff --git a/lib/main.dart b/lib/main.dart index afedf0d7..92ddc08b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,7 @@ import "package:flutter_gen/gen_l10n/app_localizations.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "config/ui_config.dart"; -import "features/navigator/root_navigator_widget.dart"; +import "features/navigator/app_router.dart"; import "features/splash_screen/splash_screen.dart"; import "features/splash_screen/splash_screen_controller.dart"; import "theme/app_theme.dart"; @@ -20,11 +20,12 @@ void main() async { ); } -class MyApp extends StatelessWidget { +class MyApp extends ConsumerWidget { const MyApp({super.key}); + @override - Widget build(BuildContext context) { - return MaterialApp( + Widget build(BuildContext context, WidgetRef ref) { + return MaterialApp.router( title: MyAppConfig.title, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, @@ -35,7 +36,7 @@ class MyApp extends StatelessWidget { ), ), debugShowCheckedModeBanner: false, - home: const RootNavigatorWidget(), + routerConfig: ref.watch(appRouterProvider).config(), ); } } diff --git a/lib/shared_repositories/buildings_repository/map_buildings_repo.dart b/lib/shared_repositories/buildings_repository/map_buildings_repo.dart deleted file mode 100644 index 8091159d..00000000 --- a/lib/shared_repositories/buildings_repository/map_buildings_repo.dart +++ /dev/null @@ -1,24 +0,0 @@ -import "package:riverpod_annotation/riverpod_annotation.dart"; - -import "../../../api_base/watch_query_adapter.dart"; -import "../../config/ttl_config.dart"; -import "building_model.dart"; -import "getMapBuildings.graphql.dart"; - -part "map_buildings_repo.g.dart"; - -typedef Building = Query$GetMapBuildings$Buildings; - -@riverpod -Stream?> mapBuildingsRepository( - MapBuildingsRepositoryRef ref, -) async* { - final stream = ref.watchQueryWithCache( - WatchOptions$Query$GetMapBuildings(eagerlyFetchResults: true), - TtlKey.mapBuildingsRepository, - ); - - yield* stream.map( - (event) => event?.Buildings.map(BuildingModel.from).toList(), - ); -} diff --git a/lib/shared_repositories/sci_clubs_repository/scientific_circles_repository.dart b/lib/shared_repositories/sci_clubs_repository/scientific_circles_repository.dart deleted file mode 100644 index 53f6435e..00000000 --- a/lib/shared_repositories/sci_clubs_repository/scientific_circles_repository.dart +++ /dev/null @@ -1,36 +0,0 @@ -import "package:riverpod_annotation/riverpod_annotation.dart"; - -import "../../../api_base/watch_query_adapter.dart"; -import "../../config/ttl_config.dart"; -import "getScientificCircles.graphql.dart"; - -part "scientific_circles_repository.g.dart"; - -typedef ScientificCircle = Query$GetScientificCircles$Scientific_Circles; -// just alias for shorter type name - -@riverpod -Stream?> scientificCirclesRepository( - ScientificCirclesRepositoryRef ref, -) async* { - final stream = ref.watchQueryWithCache( - WatchOptions$Query$GetScientificCircles(eagerlyFetchResults: true), - TtlKey.sciCirclesRepository, - ); - yield* stream.map( - (event) => event?.Scientific_Circles.sortBySourceTypes().toList(), - ); -} - -extension SortBySourceType on Iterable { - Iterable _filter(String source) { - return where((element) => element.source == source); - } - - Iterable sortBySourceTypes() { - final p0 = _filter("manual_entry"); - final p1 = _filter("aktywni_website"); - final p2 = _filter("student_department"); - return p0.followedBy(p1).followedBy(p2).toList(); - } -} diff --git a/lib/utils/calc_lines.dart b/lib/utils/calculate_lines.dart similarity index 87% rename from lib/utils/calc_lines.dart rename to lib/utils/calculate_lines.dart index fd6068a7..1451023d 100644 --- a/lib/utils/calc_lines.dart +++ b/lib/utils/calculate_lines.dart @@ -1,6 +1,6 @@ import "package:flutter/material.dart"; -extension CalcLinesUIUtils on TextSpan { +extension CalculateLinesX on TextSpan { int calculateLines(double availableWidth) { final textPainter = TextPainter( text: this, diff --git a/lib/utils/colors_sort.dart b/lib/utils/colors_sort.dart index 24b36627..34c92e6e 100644 --- a/lib/utils/colors_sort.dart +++ b/lib/utils/colors_sort.dart @@ -1,6 +1,6 @@ import "package:flutter/material.dart"; -extension ColorsSortExt on List { +extension ColorsSortX on List { void sortByLightness() { sort( (a, b) { diff --git a/lib/utils/context_extensions.dart b/lib/utils/context_extensions.dart index b7e83d8b..d8a776d2 100644 --- a/lib/utils/context_extensions.dart +++ b/lib/utils/context_extensions.dart @@ -2,7 +2,7 @@ import "package:flutter/material.dart"; import "package:flutter_gen/gen_l10n/app_localizations.dart"; import "package:flutter_gen/gen_l10n/app_localizations_pl.dart"; -extension BuildContextShortcutExtension on BuildContext { +extension BuildContextX on BuildContext { AppLocalizations get localize { return AppLocalizations.of(this) ?? AppLocalizationsPl(); } diff --git a/lib/utils/datetime_utils.dart b/lib/utils/datetime_utils.dart index b14640b9..1cd896df 100644 --- a/lib/utils/datetime_utils.dart +++ b/lib/utils/datetime_utils.dart @@ -1,6 +1,6 @@ DateTime get now => DateTime.now(); -extension DateTimeUtils on DateTime { +extension DateTimeUtilsX on DateTime { DateTime findMondayOfTheWeek() { final difference = weekday - DateTime.monday; return subtract(Duration(days: difference)); diff --git a/lib/utils/determine_icon.dart b/lib/utils/determine_contact_icon.dart similarity index 63% rename from lib/utils/determine_icon.dart rename to lib/utils/determine_contact_icon.dart index 051182b6..a7409f37 100644 --- a/lib/utils/determine_icon.dart +++ b/lib/utils/determine_contact_icon.dart @@ -1,30 +1,30 @@ import "package:collection/collection.dart"; import "package:flutter/widgets.dart"; -import "../config/url_icons.dart"; +import "../config/contact_icons.dart"; @immutable -class UrlIconsModel { +class ContactIconsModel { final String icon; final String? text; final String? url; - UrlIconsModel({ + ContactIconsModel({ String? text, this.url, }) : icon = url.determineIcon(), text = text ?? url; } -extension IconDeterminer on String? { +extension IconDeterminerX on String? { String determineIcon() { return this != null - ? IconsConfig.iconsPaths.entries + ? ContactIconsConfig.iconsPaths.entries .firstWhereOrNull( (e) => this!.contains(e.key), ) ?.value ?? - IconsConfig.defaultIcon - : IconsConfig.defaultIcon; + ContactIconsConfig.defaultIcon + : ContactIconsConfig.defaultIcon; } } diff --git a/lib/utils/last_or_null.dart b/lib/utils/last_or_null.dart new file mode 100644 index 00000000..4374e55e --- /dev/null +++ b/lib/utils/last_or_null.dart @@ -0,0 +1,7 @@ +extension LastOrNullX on List { + T? get lastOrNull => isNotEmpty ? last : null; + + T? get preLastOrNull => length > 1 ? this[length - 2] : null; + + T? get prePreLastOrNull => length > 2 ? this[length - 3] : null; +} diff --git a/lib/utils/where_non_null_iterable.dart b/lib/utils/where_non_null_iterable.dart index f2c265f3..62d788fb 100644 --- a/lib/utils/where_non_null_iterable.dart +++ b/lib/utils/where_non_null_iterable.dart @@ -6,13 +6,13 @@ Iterable _whereNonNullIterable(Iterable source) sync* { } } -extension WhereNonNullExt on Iterable { +extension WhereNonNullX on Iterable { Iterable get whereNonNull { return _whereNonNullIterable(this); } } -extension WhereNonNullExtNullable on Iterable? { +extension WhereNonNullNullableX on Iterable? { Iterable get whereNonNull { if (this == null) return []; return this!.whereNonNull; diff --git a/lib/widgets/big_preview_card.dart b/lib/widgets/big_preview_card.dart index 934c2061..b8eed135 100644 --- a/lib/widgets/big_preview_card.dart +++ b/lib/widgets/big_preview_card.dart @@ -21,7 +21,7 @@ class BigPreviewCard extends StatelessWidget { final String shortDescription; final String? photoUrl; final DateTime? date; - final void Function() onClick; + final VoidCallback? onClick; @override Widget build(BuildContext context) { diff --git a/lib/widgets/my_icon.dart b/lib/widgets/detail_views/contact_icon_widget.dart similarity index 85% rename from lib/widgets/my_icon.dart rename to lib/widgets/detail_views/contact_icon_widget.dart index 8eec1b93..df3a7119 100644 --- a/lib/widgets/my_icon.dart +++ b/lib/widgets/detail_views/contact_icon_widget.dart @@ -1,9 +1,9 @@ import "package:flutter/material.dart"; import "package:flutter_svg/svg.dart"; -import "../theme/app_theme.dart"; +import "../../theme/app_theme.dart"; -class MyIcon extends StatelessWidget { - const MyIcon({ +class ContactIconWidget extends StatelessWidget { + const ContactIconWidget({ super.key, required this.icon, }); diff --git a/lib/widgets/details_screen_contact_section.dart b/lib/widgets/detail_views/contact_section.dart similarity index 83% rename from lib/widgets/details_screen_contact_section.dart rename to lib/widgets/detail_views/contact_section.dart index 603a2405..586362c8 100644 --- a/lib/widgets/details_screen_contact_section.dart +++ b/lib/widgets/detail_views/contact_section.dart @@ -1,15 +1,15 @@ import "package:flutter/cupertino.dart"; import "package:flutter/gestures.dart"; -import "../theme/app_theme.dart"; -import "../utils/determine_icon.dart"; -import "../utils/launch_url_util.dart"; -import "my_icon.dart"; +import "../../theme/app_theme.dart"; +import "../../utils/determine_contact_icon.dart"; +import "../../utils/launch_url_util.dart"; +import "contact_icon_widget.dart"; class ContactSection extends StatelessWidget { const ContactSection({super.key, required this.list, required this.title}); - final List list; + final List list; final String title; @override @@ -25,7 +25,7 @@ class ContactSection extends StatelessWidget { for (final item in list) Padding( padding: const EdgeInsets.only(bottom: 16), - child: _IconWithUrl( + child: _ContactIcon( url: item.url ?? "", text: item.text ?? "", icon: item.icon, @@ -37,8 +37,8 @@ class ContactSection extends StatelessWidget { } } -class _IconWithUrl extends StatelessWidget { - const _IconWithUrl({ +class _ContactIcon extends StatelessWidget { + const _ContactIcon({ required this.url, required this.icon, required this.text, @@ -52,7 +52,7 @@ class _IconWithUrl extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - MyIcon(icon: icon), + ContactIconWidget(icon: icon), const SizedBox(width: 16), Expanded( child: RichText( diff --git a/lib/features/study_circle_details/widgets/details_screen_app_bar.dart b/lib/widgets/detail_views/detail_view_app_bar.dart similarity index 76% rename from lib/features/study_circle_details/widgets/details_screen_app_bar.dart rename to lib/widgets/detail_views/detail_view_app_bar.dart index 0ebdbfe1..8e954755 100644 --- a/lib/features/study_circle_details/widgets/details_screen_app_bar.dart +++ b/lib/widgets/detail_views/detail_view_app_bar.dart @@ -1,9 +1,9 @@ import "package:flutter/material.dart"; -import "../../../theme/app_theme.dart"; +import "../../theme/app_theme.dart"; -class DetailsScreenAppBar extends AppBar { - DetailsScreenAppBar(BuildContext context, {super.key, required String title}) +class DetailViewAppBar extends AppBar { + DetailViewAppBar(BuildContext context, {super.key, required String title}) : super( centerTitle: false, automaticallyImplyLeading: false, diff --git a/lib/widgets/details_screen_sliver_header_section.dart b/lib/widgets/detail_views/sliver_header_section.dart similarity index 92% rename from lib/widgets/details_screen_sliver_header_section.dart rename to lib/widgets/detail_views/sliver_header_section.dart index 2af5fab4..c6dde40d 100644 --- a/lib/widgets/details_screen_sliver_header_section.dart +++ b/lib/widgets/detail_views/sliver_header_section.dart @@ -1,7 +1,9 @@ import "dart:math"; + import "package:flutter/material.dart"; -import "../config/ui_config.dart"; -import "my_cached_image.dart"; + +import "../../config/ui_config.dart"; +import "../my_cached_image.dart"; class SliverHeaderSection extends SliverPersistentHeaderDelegate { SliverHeaderSection({ @@ -22,9 +24,9 @@ class SliverHeaderSection extends SliverPersistentHeaderDelegate { double get minExtent => 0; double calcLogoSize(double shrinkOffset) { - final ratio = min(1, shrinkOffset / DetailsScreenHeaderConfig.logoSize); + final ratio = min(1, shrinkOffset / DetailViewsHeaderConfig.logoSize); final adjustedRatio = 1.0 - (ratio * 0.3); - return max(0, DetailsScreenHeaderConfig.logoSize * adjustedRatio); + return max(0, DetailViewsHeaderConfig.logoSize * adjustedRatio); } double calcLogoOpacity(double shrinkOffset, double logoSize) { diff --git a/lib/widgets/loading_widgets/contact_section_loading.dart b/lib/widgets/loading_widgets/contact_section_loading.dart index cc3ebe53..d715bc02 100644 --- a/lib/widgets/loading_widgets/contact_section_loading.dart +++ b/lib/widgets/loading_widgets/contact_section_loading.dart @@ -24,7 +24,7 @@ class ContactSectionLoading extends StatelessWidget { ), ), ), - const SizedBox(height: DetailsScreenConfig.spacerHeight), + const SizedBox(height: DetailViewsConfig.spacerHeight), Padding( padding: const EdgeInsets.only(bottom: 16), child: SizedBox( @@ -37,7 +37,7 @@ class ContactSectionLoading extends StatelessWidget { }, separatorBuilder: (BuildContext context, int index) { return const SizedBox( - height: DetailsScreenConfig.spacerHeight, + height: DetailViewsConfig.spacerHeight, ); }, itemCount: 3, diff --git a/lib/widgets/loading_widgets/specific_imitations/big_preview_card_loading.dart b/lib/widgets/loading_widgets/specific_imitations/big_preview_card_loading.dart index ef5c00b4..5cb882e0 100644 --- a/lib/widgets/loading_widgets/specific_imitations/big_preview_card_loading.dart +++ b/lib/widgets/loading_widgets/specific_imitations/big_preview_card_loading.dart @@ -16,7 +16,7 @@ class BigPreviewCardLoading extends StatelessWidget { ShimmerLoadingItem( child: Container( width: BigPreviewCardConfig.cardWidth, - height: 135, + height: 155, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), @@ -35,13 +35,22 @@ class _LoadingText extends StatelessWidget { @override Widget build(BuildContext context) { + const gap = 22.5; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 16), - PreviewTextPrototype(width: BigPreviewCardConfig.cardWidth), - const SizedBox(height: 16), - PreviewTextPrototype(width: BigPreviewCardConfig.cardWidth / 1.5), + const SizedBox(height: gap), + PreviewTextPrototype(width: BigPreviewCardConfig.cardWidth, height: 25), + const SizedBox(height: gap), + PreviewTextPrototype( + width: BigPreviewCardConfig.cardWidth / 1.3, + height: 25, + ), + const SizedBox(height: gap), + PreviewTextPrototype( + width: BigPreviewCardConfig.cardWidth / 2.5, + height: 25, + ), ], ); } diff --git a/lib/widgets/my_error_widget.dart b/lib/widgets/my_error_widget.dart index d850097f..34133a96 100644 --- a/lib/widgets/my_error_widget.dart +++ b/lib/widgets/my_error_widget.dart @@ -2,9 +2,9 @@ import "package:flutter/material.dart"; import "package:logger/logger.dart"; import "../api_base/watch_query_adapter.dart"; -import "../features/iparking/api_client/iparking_commands.dart"; -import "../features/iparking/widgets/offline_parkings.dart"; import "../features/offline_messages/widgets/grapgql_offline_message.dart"; +import "../features/parkings_view/api_client/iparking_commands.dart"; +import "../features/parkings_view/widgets/offline_parkings_view.dart"; class MyErrorWidget extends StatelessWidget { const MyErrorWidget(this.error, {super.key}); @@ -13,7 +13,7 @@ class MyErrorWidget extends StatelessWidget { Widget build(BuildContext context) { Logger().e(error.toString()); return switch (error) { - ParkingsOfflineException() => const OfflineParkings(), + ParkingsOfflineException() => const OfflineParkingsView(), GqlOfflineException(:final ttlKey) => OfflineGraphQLMessage(ttlKey), _ => Center( child: Text( diff --git a/lib/widgets/search_widget.dart b/lib/widgets/search_box.dart similarity index 90% rename from lib/widgets/search_widget.dart rename to lib/widgets/search_box.dart index dec134d0..e0d28350 100644 --- a/lib/widgets/search_widget.dart +++ b/lib/widgets/search_box.dart @@ -7,11 +7,12 @@ import "../gen/assets.gen.dart"; import "../theme/app_theme.dart"; import "../utils/context_extensions.dart"; -class SearchWidget extends ConsumerStatefulWidget { +class SearchBox extends ConsumerStatefulWidget { final void Function(String query) onQueryChanged; final VoidCallback? onTap; final String? searchText; - const SearchWidget({ + + const SearchBox({ super.key, required this.onQueryChanged, this.onTap, @@ -19,10 +20,10 @@ class SearchWidget extends ConsumerStatefulWidget { }); @override - ConsumerState createState() => _SearchWidgetState(); + ConsumerState createState() => _SearchBoxState(); } -class _SearchWidgetState extends ConsumerState { +class _SearchBoxState extends ConsumerState { final controller = TextEditingController(); final focusNode = FocusNode(); bool showCloseIcon = false; @@ -56,7 +57,7 @@ class _SearchWidgetState extends ConsumerState { onTap: widget.onTap, onTapOutside: _onTapOutside, decoration: InputDecoration( - constraints: const BoxConstraints(maxHeight: SearchWidgetConfig.height), + constraints: const BoxConstraints(maxHeight: SearchBoxConfig.height), contentPadding: EdgeInsets.zero, filled: true, fillColor: context.colorTheme.greyLight, diff --git a/lib/widgets/search_box_app_bar.dart b/lib/widgets/search_box_app_bar.dart index 7d2c9aad..cc550c88 100644 --- a/lib/widgets/search_box_app_bar.dart +++ b/lib/widgets/search_box_app_bar.dart @@ -2,7 +2,7 @@ import "package:flutter/material.dart"; import "../config/ui_config.dart"; import "../theme/app_theme.dart"; -import "search_widget.dart"; +import "search_box.dart"; class SearchBoxAppBar extends AppBar { static const defaultBottomPadding = 32.0; @@ -15,7 +15,7 @@ class SearchBoxAppBar extends AppBar { super.actions, super.primary, super.key, - VoidCallback? onSearchboxTap, + VoidCallback? onSearchBoxTap, double bottomPadding = defaultBottomPadding, }) : super( automaticallyImplyLeading: false, @@ -27,16 +27,16 @@ class SearchBoxAppBar extends AppBar { titleSpacing: defaultHorizontalPadding, bottom: PreferredSize( preferredSize: - Size.fromHeight(SearchWidgetConfig.height + bottomPadding), + Size.fromHeight(SearchBoxConfig.height + bottomPadding), child: Padding( padding: EdgeInsets.only( bottom: bottomPadding, left: defaultHorizontalPadding, right: defaultHorizontalPadding, ), - child: SearchWidget( + child: SearchBox( onQueryChanged: onQueryChanged, - onTap: onSearchboxTap, + onTap: onSearchBoxTap, ), ), ), diff --git a/lib/widgets/subsection_header.dart b/lib/widgets/subsection_header.dart index 2dc04f68..23eecfb0 100644 --- a/lib/widgets/subsection_header.dart +++ b/lib/widgets/subsection_header.dart @@ -13,7 +13,7 @@ class SubsectionHeader extends StatelessWidget { final String title; final String? actionTitle; - final void Function()? onClick; + final VoidCallback? onClick; @override Widget build(BuildContext context) { diff --git a/lib/widgets/tile_splash.dart b/lib/widgets/tile_splash.dart index 9328b5e0..567c9410 100644 --- a/lib/widgets/tile_splash.dart +++ b/lib/widgets/tile_splash.dart @@ -3,7 +3,7 @@ import "package:flutter/material.dart"; class TileSplash extends StatelessWidget { const TileSplash({super.key, this.onTap}); - final void Function()? onTap; + final VoidCallback? onTap; @override Widget build(BuildContext context) { diff --git a/lib/widgets/wide_tile_card.dart b/lib/widgets/wide_tile_card.dart index ac40babe..99dc9b5a 100644 --- a/lib/widgets/wide_tile_card.dart +++ b/lib/widgets/wide_tile_card.dart @@ -1,7 +1,7 @@ import "package:flutter/material.dart"; import "../config/ui_config.dart"; -import "../features/student_research_group_tab/widgets/ensure_visible_tags.dart"; +import "../features/science_clubs_view/widgets/ensure_visible_tags.dart"; import "../theme/app_theme.dart"; import "my_cached_image.dart"; diff --git a/pubspec.yaml b/pubspec.yaml index 73c0da58..6a6ddc10 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,6 +59,7 @@ dependencies: html: ^0.15.4 google_maps_flutter_android: ^2.12.0 google_maps_flutter_platform_interface: ^2.8.0 + auto_route: ^8.3.0 dev_dependencies: flutter_test: @@ -77,6 +78,7 @@ dev_dependencies: graphql_codegen: ^0.14.0 flutter_gen_runner: ^5.6.0 total_lints: ^3.4.0 + auto_route_generator: ^8.1.0 dependency_overrides: analyzer: ^6.5.0 diff --git a/taxonomy.md b/taxonomy.md new file mode 100644 index 00000000..03fb2aa7 --- /dev/null +++ b/taxonomy.md @@ -0,0 +1,66 @@ +# ToPwr Developer Taxonomy: In-Code Naming Conventions + +Due to multiple split conventions and absurdly diverse names for similar entities in our group project, here is our unified naming dictionary to use while coding this app. + +## Purpose +This guide defines what to call classes, variables, folders (everything) in the codebase for consistency and ease of collaboration among developers. + +**Example:** +Use `ScienceClubsView` or `scienceClubsRepository` consistently in the codebase instead of alternatives like `scienceCircle` or `studentResearchGroup`. + +## General Rules +1. Opt for simplicity and shorter names while conveying as much information as possible. +2. Use singular and plural forms appropriately (e.g., `Department` and `Departments` when most appropriate). + +## Entities +Even if backend names differ, use these names on the Flutter side: +- Building +- Parking +- Department (widely used in the codebase, though not the most accurate translation) +- Science Club +- Guide +- Academic Calendar +- Contact Icons +- News (coming soon) + +*If any important entity is missing, add it with your PR.* + +## Widgets/Views +Not related to actual data entities, but more widget-oriented names that have had multiple variations and are now unified. + +- Home/HomeView +- AboutUs/AboutUsView +- SplashScreen +- CountDown +- DateChip +- BigPreviewCard +- WideTileCard +- SearchBox + +## Views +For all main pages (the ones you want to generate navigation routes for), use: `XView` +- Example: `HomeView`, `ScienceClubsView`, `ScienceClubDetailView` +- Avoid: ~~HomeScreen, HomePage~~ + +This is configured for `auto_route` generator to create route objects. + +## Detail Views +For list views and their corresponding detail views, use `XsView` and `XDetailView` +- Example: `ScienceClubsView` and `ScienceClubDetailView` + +### Details Models +For models used in DetailView, use `XDetails` +- Example: `DepartmentDetails` + +## Features Folders +1. If a feature is a view, name it accordingly: `departments_view` or `department_detail_view`. +2. If a feature is not view-oriented, use a simple, descriptive keyword: `academic_calendar`, `navigator`, `parking_chart`. + +## Extensions +For all `extensions`, append `X` in the name. +```dart +extension ReverseIteratorX on DoubleLinkedQueue{ + Iterable get reverseIterator sync* { + // ... + } +}