diff --git a/packages/catalog/pubspec.lock b/packages/catalog/pubspec.lock index 377776e..c75cb27 100644 --- a/packages/catalog/pubspec.lock +++ b/packages/catalog/pubspec.lock @@ -118,6 +118,30 @@ packages: url: "https://pub.dev" source: hosted version: "8.9.2" + cached_network_image: + dependency: transitive + description: + name: cached_network_image + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" + url: "https://pub.dev" + source: hosted + version: "1.3.1" cave: dependency: "direct main" description: @@ -205,6 +229,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + dio: + dependency: transitive + description: + name: dio + sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260" + url: "https://pub.dev" + source: hosted + version: "5.7.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8" + url: "https://pub.dev" + source: hosted + version: "2.0.0" email_validator: dependency: transitive description: @@ -221,6 +261,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" ficonsax: dependency: transitive description: @@ -258,6 +306,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.5.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + url: "https://pub.dev" + source: hosted + version: "3.4.1" flutter_lints: dependency: "direct dev" description: @@ -274,6 +330,54 @@ packages: url: "https://pub.dev" source: hosted version: "5.9.1" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "165164745e6afb5c0e3e3fcc72a012fb9e58496fb26ffb92cf22e16a821e85d0" + url: "https://pub.dev" + source: hosted + version: "9.2.2" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "1693ab11121a5f925bbea0be725abfcfbbcf36c1e29e571f84a0c0f436147a81" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_shaders: dependency: transitive description: @@ -376,10 +480,10 @@ packages: dependency: transitive description: name: js - sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.6.7" json_annotation: dependency: transitive description: @@ -484,6 +588,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" package_config: dependency: transitive description: @@ -508,6 +620,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + url: "https://pub.dev" + source: hosted + version: "2.1.4" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a + url: "https://pub.dev" + source: hosted + version: "2.2.12" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + url: "https://pub.dev" + source: hosted + version: "2.4.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" petitparser: dependency: transitive description: @@ -516,6 +676,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.2" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -548,6 +716,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + qr: + dependency: transitive + description: + name: qr + sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + qr_flutter: + dependency: transitive + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.dev" + source: hosted + version: "4.1.0" resizable_widget: dependency: transitive description: @@ -556,6 +740,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" shelf: dependency: transitive description: @@ -572,6 +820,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + shimmer: + dependency: transitive + description: + name: shimmer + sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9" + url: "https://pub.dev" + source: hosted + version: "3.0.0" sky_engine: dependency: transitive description: flutter @@ -593,6 +849,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "79a297dc3cc137e758c6a4baf83342b039e5a6d2436fcdf3f96a00adaaf2ad62" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490" + url: "https://pub.dev" + source: hosted + version: "2.5.4+5" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "769733dddf94622d5541c73e4ddc6aa7b252d865285914b6fcd54a63c4b4f027" + url: "https://pub.dev" + source: hosted + version: "2.4.1-1" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" stack_trace: dependency: transitive description: @@ -625,6 +929,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" + url: "https://pub.dev" + source: hosted + version: "3.3.0+3" term_glyph: dependency: transitive description: @@ -657,6 +969,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: @@ -753,6 +1073,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.8.0" + win32: + dependency: transitive + description: + name: win32 + sha256: e1d0cc62e65dc2561f5071fcbccecf58ff20c344f8f3dc7d4922df372a11df1f + url: "https://pub.dev" + source: hosted + version: "5.7.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" xml: dependency: transitive description: @@ -770,5 +1106,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.5.0-259.0.dev <4.0.0" - flutter: ">=3.22.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/packages/cave/lib/cave.dart b/packages/cave/lib/cave.dart index 47baa6b..281cd1b 100644 --- a/packages/cave/lib/cave.dart +++ b/packages/cave/lib/cave.dart @@ -1,4 +1,4 @@ -library cave; +library; export 'themes/themes.dart'; export 'widgets/widgets.dart'; @@ -12,3 +12,5 @@ export 'package:flutter_secure_storage/flutter_secure_storage.dart'; export 'utils/utils.dart'; export 'package:shared_preferences/shared_preferences.dart'; export 'package:shimmer/shimmer.dart'; +export 'package:cached_network_image/cached_network_image.dart'; +export 'package:qr_flutter/qr_flutter.dart'; diff --git a/packages/cave/lib/utils/exceptions/base_exception.dart b/packages/cave/lib/utils/exceptions/base_exception.dart index 502fbd3..08141a9 100644 --- a/packages/cave/lib/utils/exceptions/base_exception.dart +++ b/packages/cave/lib/utils/exceptions/base_exception.dart @@ -1,6 +1,4 @@ import 'package:cave/cave.dart'; -import 'client_exception.dart'; -import 'empty_exception.dart'; base class Devfest2024Exception implements Exception { const Devfest2024Exception(); diff --git a/packages/cave/lib/utils/network/data_transformer.dart b/packages/cave/lib/utils/network/data_transformer.dart index e1cfc1d..65f9fd0 100644 --- a/packages/cave/lib/utils/network/data_transformer.dart +++ b/packages/cave/lib/utils/network/data_transformer.dart @@ -1,6 +1,5 @@ import 'package:cave/cave.dart'; import 'package:flutter/foundation.dart'; -import '../exceptions/exceptions.dart'; typedef Devfest2024ExceptionOr = Either; typedef FutureDevfest2024ExceptionOr = Future>; diff --git a/packages/cave/lib/utils/services/local_storage_service.dart b/packages/cave/lib/utils/services/local_storage_service.dart index 3d8cbb1..dcbe3cf 100644 --- a/packages/cave/lib/utils/services/local_storage_service.dart +++ b/packages/cave/lib/utils/services/local_storage_service.dart @@ -17,6 +17,9 @@ final class ConferenceAppStorageService { iOptions: _getIosOptions(), ); + SharedPreferencesAsync get _sharedPreferencesInstance => + SharedPreferencesAsync(); + Future setUserToken(String token) async { return await _securedStorageInstance.write(key: 'user-token', value: token); } @@ -24,4 +27,13 @@ final class ConferenceAppStorageService { Future get userToken async { return await _securedStorageInstance.read(key: 'user-token') ?? ''; } + + Future setIsFirstLaunch(bool isFirstLaunch) async { + return await _sharedPreferencesInstance.setBool( + 'is-first-launch', isFirstLaunch); + } + + Future get isFirstLaunch async { + return await _sharedPreferencesInstance.getBool('is-first-launch') ?? true; + } } diff --git a/packages/cave/pubspec.yaml b/packages/cave/pubspec.yaml index f757b5e..5179f22 100644 --- a/packages/cave/pubspec.yaml +++ b/packages/cave/pubspec.yaml @@ -21,6 +21,8 @@ dependencies: flutter_secure_storage: ^9.2.2 shared_preferences: ^2.3.2 shimmer: ^3.0.0 + cached_network_image: ^3.4.1 + qr_flutter: ^4.1.0 dev_dependencies: flutter_lints: ^5.0.0 diff --git a/packages/conferenceapp/android/app/src/main/AndroidManifest.xml b/packages/conferenceapp/android/app/src/main/AndroidManifest.xml index ca45c90..7076b91 100644 --- a/packages/conferenceapp/android/app/src/main/AndroidManifest.xml +++ b/packages/conferenceapp/android/app/src/main/AndroidManifest.xml @@ -1,28 +1,29 @@ + + + android:icon="@mipmap/ic_launcher" + android:label="Devfest24"> + android:name="io.flutter.embedding.android.NormalTheme" + android:resource="@style/NormalTheme" /> - - + + - - + + diff --git a/packages/conferenceapp/ios/Podfile.lock b/packages/conferenceapp/ios/Podfile.lock index fa6322b..60f5d13 100644 --- a/packages/conferenceapp/ios/Podfile.lock +++ b/packages/conferenceapp/ios/Podfile.lock @@ -76,6 +76,11 @@ PODS: - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS + - url_launcher_ios (0.0.1): + - Flutter DEPENDENCIES: - firebase_core (from `.symlinks/plugins/firebase_core/ios`) @@ -85,6 +90,8 @@ DEPENDENCIES: - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) SPEC REPOS: trunk: @@ -119,6 +126,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/path_provider_foundation/darwin" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: Firebase: cec914dab6fd7b1bd8ab56ea07ce4e03dd251c2d @@ -143,6 +154,8 @@ SPEC CHECKSUMS: path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sqflite_darwin: a553b1fd6fe66f53bbb0fe5b4f5bab93f08d7a13 + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe PODFILE CHECKSUM: 1959d098c91d8a792531a723c4a9d7e9f6a01e38 diff --git a/packages/conferenceapp/lib/conference_app.dart b/packages/conferenceapp/lib/conference_app.dart index 317f98f..5cf3637 100644 --- a/packages/conferenceapp/lib/conference_app.dart +++ b/packages/conferenceapp/lib/conference_app.dart @@ -4,6 +4,8 @@ import 'package:devfest24/src/routing/routing.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'src/features/onboarding/presentation/presentation.dart'; + class ConferenceApp extends ConsumerStatefulWidget { const ConferenceApp({super.key}); @@ -17,14 +19,25 @@ class _ConferenceAppState extends ConsumerState { @override void initState() { super.initState(); - Devfest2024Router.instance.initialiseRouter(ref); + + Future.microtask(() async { + final [dynamic isFirstLaunch, dynamic token] = await Future.wait([ + ConferenceAppStorageService.instance.isFirstLaunch, + ConferenceAppStorageService.instance.userToken, + ]); + + if (isFirstLaunch == true && (token.toString()).isEmpty) { + Devfest2024Router.rootNavigatorKey.currentContext + ?.goNamedAndPopAll(OnboardingHomeScreen.route); + return; + } + }); } void _unfocus() { FocusManager.instance.primaryFocus?.unfocus(); } - // This widget is the root of your application. @override Widget build(BuildContext context) { return GestureDetector( @@ -33,9 +46,11 @@ class _ConferenceAppState extends ConsumerState { designSize: designSize, minTextAdapt: true, builder: (_, child) { - return MaterialApp.router( + return MaterialApp( title: 'Devfest24 Conference App', - routerConfig: Devfest2024Router.instance.router, + navigatorKey: Devfest2024Router.rootNavigatorKey, + initialRoute: Devfest2024Router.initialRoute, + onGenerateRoute: Devfest2024Router.instance.onGenerateRoutes, builder: (context, child) => AccessibilityTools( minimumTapAreas: const MinimumTapAreas(mobile: 30, desktop: 44), checkFontOverflows: true, diff --git a/packages/conferenceapp/lib/src/features/dashboard/application/agenda/view_model.dart b/packages/conferenceapp/lib/src/features/dashboard/application/agenda/view_model.dart index 42e129f..e8e5d41 100644 --- a/packages/conferenceapp/lib/src/features/dashboard/application/agenda/view_model.dart +++ b/packages/conferenceapp/lib/src/features/dashboard/application/agenda/view_model.dart @@ -11,6 +11,17 @@ final agendasViewModelNotifier = () => AgendaViewModel(), ); +final dayOneSessionsProvider = Provider.autoDispose>((ref) { + return ref.watch(agendasViewModelNotifier.select((vm) => vm.agendas + .where((agenda) => agenda.start?.day == 15) + .fold([], (previous, next) => [...previous, ...next.sessions.sessions]))); +}, dependencies: [agendasViewModelNotifier]); +final dayTwoSessionsProvider = Provider.autoDispose>((ref) { + return ref.watch(agendasViewModelNotifier.select((vm) => vm.agendas + .where((agenda) => agenda.start?.day == 16) + .fold([], (previous, next) => [...previous, ...next.sessions.sessions]))); +}, dependencies: [agendasViewModelNotifier]); + final class AgendaViewModel extends AutoDisposeNotifier { late DashboardApiService _apiService; @@ -27,7 +38,17 @@ final class AgendaViewModel extends AutoDisposeNotifier { state = model.emit( result.fold( - (left) => state.copyWith(uiState: UiState.error, error: left), + (left) { + if (left is WithCachedDataException) { + return state.copyWith( + uiState: UiState.error, + error: left, + agendas: (left.result as AgendasDto).agendas, + ); + } + + return state.copyWith(uiState: UiState.error, error: left); + }, (right) => state.copyWith(uiState: UiState.success, agendas: right.agendas), ), diff --git a/packages/conferenceapp/lib/src/features/dashboard/application/speakers/view_model.dart b/packages/conferenceapp/lib/src/features/dashboard/application/speakers/view_model.dart index 4fab9e4..047d405 100644 --- a/packages/conferenceapp/lib/src/features/dashboard/application/speakers/view_model.dart +++ b/packages/conferenceapp/lib/src/features/dashboard/application/speakers/view_model.dart @@ -11,6 +11,21 @@ final speakersViewModelNotifier = () => SpeakersViewModel(), ); +final dayOneSpeakersProvider = Provider.autoDispose>((ref) { + return ref.watch(speakersViewModelNotifier.select((vm) => vm.speakers + .where((speaker) => speaker.sessions + .where((session) => session.startTime!.day == 15) + .isNotEmpty) + .toList())); +}, dependencies: [speakersViewModelNotifier]); +final dayTwoSpeakersProvider = Provider.autoDispose>((ref) { + return ref.watch(speakersViewModelNotifier.select((vm) => vm.speakers + .where((speaker) => speaker.sessions + .where((session) => session.startTime!.day == 16) + .isNotEmpty) + .toList())); +}, dependencies: [speakersViewModelNotifier]); + final class SpeakersViewModel extends AutoDisposeNotifier { late DashboardApiService _apiService; diff --git a/packages/conferenceapp/lib/src/features/dashboard/application/sponsors/view_model.dart b/packages/conferenceapp/lib/src/features/dashboard/application/sponsors/view_model.dart index 1fadd73..e8a64df 100644 --- a/packages/conferenceapp/lib/src/features/dashboard/application/sponsors/view_model.dart +++ b/packages/conferenceapp/lib/src/features/dashboard/application/sponsors/view_model.dart @@ -11,6 +11,12 @@ final sponsorsViewModelNotifier = () => SponsorsViewModel(), ); +final sponsorsProvider = Provider.autoDispose>((ref) { + return ref.watch(sponsorsViewModelNotifier.select((vm) => vm.sponsorCategories + .fold([], + (previous, next) => [...previous, ...next.sponsors]))); +}); + final class SponsorsViewModel extends AutoDisposeNotifier { late DashboardApiService _apiService; diff --git a/packages/conferenceapp/lib/src/features/dashboard/application/user/view_model.dart b/packages/conferenceapp/lib/src/features/dashboard/application/user/view_model.dart index 24657f3..5a075ce 100644 --- a/packages/conferenceapp/lib/src/features/dashboard/application/user/view_model.dart +++ b/packages/conferenceapp/lib/src/features/dashboard/application/user/view_model.dart @@ -30,6 +30,6 @@ final class UserViewModel extends AutoDisposeNotifier { (right) => state.copyWith(uiState: UiState.success, user: right), ), ); - }); + }, displayError: false); } } diff --git a/packages/conferenceapp/lib/src/features/dashboard/presentation/screens/dashboard.dart b/packages/conferenceapp/lib/src/features/dashboard/presentation/screens/dashboard.dart index d34dd2d..2079223 100644 --- a/packages/conferenceapp/lib/src/features/dashboard/presentation/screens/dashboard.dart +++ b/packages/conferenceapp/lib/src/features/dashboard/presentation/screens/dashboard.dart @@ -2,7 +2,6 @@ import 'package:cave/cave.dart'; import 'package:devfest24/src/features/dashboard/application/application.dart'; import 'package:devfest24/src/features/home/presentation/presentation.dart'; import 'package:devfest24/src/features/more/presentation/presentation.dart'; -import 'package:devfest24/src/features/reserve/presentation/presentation.dart'; import 'package:devfest24/src/features/schedule/presentation/presentation.dart'; import 'package:devfest24/src/features/speakers/presentation/presentation.dart'; import 'package:flutter/material.dart'; @@ -11,11 +10,16 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../shared/shared.dart'; class DashboardScreen extends StatelessWidget { + static const route = '/home'; + const DashboardScreen({super.key}); @override Widget build(BuildContext context) { - return const DefaultTabController(length: 5, child: _View()); + return DefaultTabController( + length: _ViewState._tabs.length, + child: _View(), + ); } } @@ -32,7 +36,6 @@ class _ViewState extends ConsumerState<_View> { KeepAliveWidget(child: HomeScreen()), KeepAliveWidget(child: ScheduleHomeScreen()), KeepAliveWidget(child: SpeakersHomeScreen()), - KeepAliveWidget(child: ReserveHomeScreen()), KeepAliveWidget(child: MoreHomeScreen()), ]; @@ -57,6 +60,7 @@ class _ViewState extends ConsumerState<_View> { ref.read(speakersViewModelNotifier.notifier).fetchSpeakers(), ref.read(agendasViewModelNotifier.notifier).fetchAgenda(), ]); + ConferenceAppStorageService.instance.setIsFirstLaunch(false); }); } @@ -94,10 +98,6 @@ class _ViewState extends ConsumerState<_View> { label: 'Speakers', icon: Icon(IconsaxOutline.microphone), ), - DevfestBottomNavItem( - label: 'Reserve', - icon: Icon(IconsaxOutline.ticket), - ), DevfestBottomNavItem( label: 'More', icon: Icon(IconsaxOutline.more_square), diff --git a/packages/conferenceapp/lib/src/features/home/presentation/screens/home.dart b/packages/conferenceapp/lib/src/features/home/presentation/screens/home.dart index e787c1c..070840a 100644 --- a/packages/conferenceapp/lib/src/features/home/presentation/screens/home.dart +++ b/packages/conferenceapp/lib/src/features/home/presentation/screens/home.dart @@ -1,11 +1,15 @@ +// ignore_for_file: depend_on_referenced_packages + import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; -import 'package:devfest24/src/features/dashboard/application/user/view_model.dart'; +import 'package:devfest24/src/features/dashboard/application/application.dart'; +import 'package:devfest24/src/features/dashboard/model/model.dart'; import 'package:devfest24/src/shared/shared.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../widgets/widgets.dart'; +import 'package:collection/collection.dart'; class HomeScreen extends ConsumerStatefulWidget { const HomeScreen({super.key}); @@ -15,6 +19,8 @@ class HomeScreen extends ConsumerStatefulWidget { } class _HomeScreenState extends ConsumerState { + EventDay _day = EventDay.one; + @override Widget build(BuildContext context) { return Scaffold( @@ -29,7 +35,10 @@ class _HomeScreenState extends ConsumerState { ], ), actions: [ - const CheckInButton(), + CheckInButton( + isLoggedIn: ref.watch( + userViewModelNotifier.select((vm) => vm.user.id.isNotEmpty)), + ), Constants.horizontalMargin.horizontalSpace, ], ), @@ -37,13 +46,21 @@ class _HomeScreenState extends ConsumerState { padding: EdgeInsets.symmetric(horizontal: Constants.horizontalMargin.w), child: RefreshIndicator( onRefresh: () async { - await ref.read(userViewModelNotifier.notifier).fetchUserProfile(); + await Future.wait([ + ref + .read(userViewModelNotifier.notifier) + .fetchUserProfile(refresh: true), + ref + .read(sponsorsViewModelNotifier.notifier) + .fetchSponsors(refresh: true), + ]); }, child: CustomScrollView( slivers: [ - const SliverToBoxAdapter( + SliverToBoxAdapter( child: HeaderText( - title: Text('๐ŸŒค๏ธ Good morning, Aise'), + title: Text( + '๐ŸŒค๏ธ Good morning, ${ref.watch(userViewModelNotifier.select((value) => value.user.fullName.split(' ').first))}'), subtitle: Text( 'You start on the street, work till you are eleniyan.'), ), @@ -56,25 +73,45 @@ class _HomeScreenState extends ConsumerState { PinnedHeaderSliver( child: HomeAgendaHeader( title: const Text('๐Ÿ“† Schedule'), - onEventDayChanged: (day) {}, - onFilterSelected: () {}, + eventDay: _day, + onEventDayChanged: (day) { + setState(() { + _day = day; + }); + }, ), ), - SliverList.separated( - itemCount: 2, - itemBuilder: (context, index) => AgendaScheduleTile( - onTap: () {}, + [ + ProviderScope( + overrides: [ + _agendaSessionsProvider.overrideWithValue(ref + .watch(dayOneSessionsProvider) + .where((session) => session.categories.isEmpty) + .toList() + .safeSublist(2)), + ], + child: _AgendaSessions(), ), - separatorBuilder: (context, _) => - Constants.smallVerticalGutter.verticalSpace, - ), + ProviderScope( + overrides: [ + _agendaSessionsProvider.overrideWithValue(ref + .watch(dayTwoSessionsProvider) + .where((session) => session.categories.isEmpty) + .toList() + .safeSublist(2)), + ], + child: _AgendaSessions(), + ), + ].elementAt(_day.index), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(top: Constants.verticalGutter) .h, child: InkWell( - onTap: () {}, + onTap: () { + DefaultTabController.of(context).index = 1; + }, child: Padding( padding: const EdgeInsets.symmetric( vertical: Constants.smallVerticalGutter) @@ -99,24 +136,39 @@ class _HomeScreenState extends ConsumerState { PinnedHeaderSliver( child: HomeAgendaHeader( title: const Text('๐ŸŽค Speakers'), - onEventDayChanged: (day) {}, + eventDay: _day, + onEventDayChanged: (day) { + setState(() { + _day = day; + }); + }, ), ), - SliverList.separated( - itemCount: 2, - itemBuilder: (context, index) => AgendaTalkTile( - onTap: () {}, + [ + ProviderScope( + overrides: [ + _speakersProvider.overrideWithValue( + ref.watch(dayOneSpeakersProvider).safeSublist(2)) + ], + child: _Speakers(), ), - separatorBuilder: (context, _) => - Constants.smallVerticalGutter.verticalSpace, - ), + ProviderScope( + overrides: [ + _speakersProvider.overrideWithValue( + ref.watch(dayTwoSpeakersProvider).safeSublist(2)) + ], + child: _Speakers(), + ), + ].elementAt(_day.index), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.only(top: Constants.verticalGutter) .h, child: InkWell( - onTap: () {}, + onTap: () { + DefaultTabController.of(context).index = 2; + }, child: Padding( padding: const EdgeInsets.symmetric( vertical: Constants.smallVerticalGutter) @@ -124,7 +176,7 @@ class _HomeScreenState extends ConsumerState { child: const Center( child: IconText( Icons.arrow_forward, - 'View All Talks', + 'View All Speakers', alignment: IconTextAlignment.right, ), ), @@ -144,9 +196,12 @@ class _HomeScreenState extends ConsumerState { ), ), SliverList.separated( - itemCount: 4, + itemCount: ref.watch(sponsorsProvider).length, itemBuilder: (context, index) => ConferenceSponsorTile( - linkOnTap: () {}, + sponsor: ref.watch(sponsorsProvider)[index], + linkOnTap: () { + launchWebUrl(ref.read(sponsorsProvider)[index].website); + }, ), separatorBuilder: (context, _) => Constants.smallVerticalGutter.verticalSpace, @@ -162,3 +217,67 @@ class _HomeScreenState extends ConsumerState { ); } } + +final _agendaSessionsProvider = Provider.autoDispose>((ref) { + throw UnimplementedError(); +}); + +class _AgendaSessions extends ConsumerWidget { + const _AgendaSessions(); + + @override + Widget build(BuildContext context, ref) { + final sessions = ref.watch(_agendaSessionsProvider); + return SliverList.separated( + itemCount: sessions.length, + itemBuilder: (context, index) { + final session = sessions[index]; + final agenda = ref + .watch(agendasViewModelNotifier) + .agendas + .firstWhere((agenda) => session.periodId == agenda.id); + return AgendaScheduleTile( + session: session, + start: agenda.start!, + onTap: () {}, + ); + }, + separatorBuilder: (context, _) => + Constants.smallVerticalGutter.verticalSpace, + ); + } +} + +final _speakersProvider = Provider.autoDispose>((ref) { + throw UnimplementedError(); +}); + +class _Speakers extends ConsumerWidget { + const _Speakers(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final speakers = ref.watch(_speakersProvider); + return SliverList.separated( + itemCount: speakers.length, + itemBuilder: (context, index) { + final speaker = speakers[index]; + final isDayOne = speaker.sessions.first.startTime?.day == 15; + final session = isDayOne + ? ref.watch(dayOneSessionsProvider).firstWhereOrNull( + (session) => session.id == speaker.sessions.first.id) + : ref.watch(dayTwoSessionsProvider).firstWhereOrNull( + (session) => session.id == speaker.sessions.first.id); + + if (session == null) return const SizedBox.shrink(); + return AgendaTalkTile( + speaker: speaker, + session: session, + onTap: () {}, + ); + }, + separatorBuilder: (context, _) => + Constants.smallVerticalGutter.verticalSpace, + ); + } +} diff --git a/packages/conferenceapp/lib/src/features/home/presentation/widgets/agenda_talk_tile.dart b/packages/conferenceapp/lib/src/features/home/presentation/widgets/agenda_talk_tile.dart index 5b90e2e..f07be60 100644 --- a/packages/conferenceapp/lib/src/features/home/presentation/widgets/agenda_talk_tile.dart +++ b/packages/conferenceapp/lib/src/features/home/presentation/widgets/agenda_talk_tile.dart @@ -1,13 +1,17 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; +import 'package:devfest24/src/features/dashboard/model/model.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../../../../shared/shared.dart'; class AgendaTalkTile extends StatelessWidget { - const AgendaTalkTile({super.key, this.onTap}); + const AgendaTalkTile( + {super.key, required this.speaker, required this.session, this.onTap}); + final SpeakerDto speaker; + final SessionDto session; final VoidCallback? onTap; @override @@ -39,12 +43,13 @@ class AgendaTalkTile extends StatelessWidget { horizontal: Constants.largeHorizontalGutter, vertical: 14) .r, child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'mobile development'.toUpperCase(), + session.categories.first.toUpperCase(), style: DevfestTheme.of(context) .textTheme ?.bodyBody3Medium @@ -58,7 +63,9 @@ class AgendaTalkTile extends StatelessWidget { ), Constants.verticalGutter.verticalSpace, Text( - 'Appreciating the usefulness of football memes in decoding intent', + session.title, + maxLines: 3, + overflow: TextOverflow.ellipsis, style: DevfestTheme.of(context) .textTheme ?.bodyBody1Semibold @@ -67,7 +74,9 @@ class AgendaTalkTile extends StatelessWidget { ), Constants.smallVerticalGutter.verticalSpace, Text( - 'Celebrate the women tech makers at their annual breakfast', + session.descrption, + maxLines: 3, + overflow: TextOverflow.ellipsis, style: DevfestTheme.of(context) .textTheme ?.bodyBody2Medium @@ -75,10 +84,10 @@ class AgendaTalkTile extends StatelessWidget { .applyColor(DevfestColors.grey50.possibleDarkVariant), ), Constants.verticalGutter.verticalSpace, - const SpeakerInfo( - name: 'Samuel Abada', - shortBio: 'Flutter Engineer, Tesla', - avatarUrl: '', + SpeakerInfo( + name: speaker.fullname, + shortBio: '${speaker.title}, ${speaker.company}', + avatarUrl: speaker.imageUrl, ), Constants.verticalGutter.verticalSpace, Row( @@ -116,7 +125,7 @@ class SpeakerInfo extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - const Material( + Material( color: DevfestColors.primariesGreen80, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all( @@ -126,7 +135,11 @@ class SpeakerInfo extends StatelessWidget { ), child: Padding( padding: EdgeInsets.all(2), - child: FlutterLogo(size: 48), + child: CachedNetworkImage( + imageUrl: avatarUrl, + height: 48, + width: 48, + ), ), ), Constants.horizontalGutter.horizontalSpace, diff --git a/packages/conferenceapp/lib/src/features/home/presentation/widgets/schedule_tile.dart b/packages/conferenceapp/lib/src/features/home/presentation/widgets/schedule_tile.dart index b53260e..11f5268 100644 --- a/packages/conferenceapp/lib/src/features/home/presentation/widgets/schedule_tile.dart +++ b/packages/conferenceapp/lib/src/features/home/presentation/widgets/schedule_tile.dart @@ -1,13 +1,17 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; +import 'package:devfest24/src/features/dashboard/model/model.dart'; import 'package:devfest24/src/shared/extensions.dart'; import 'package:devfest24/src/shared/widgets/icon_text.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; class AgendaScheduleTile extends StatelessWidget { - const AgendaScheduleTile({super.key, this.onTap}); + const AgendaScheduleTile( + {super.key, required this.start, required this.session, this.onTap}); + final DateTime start; + final SessionDto session; final VoidCallback? onTap; @override @@ -33,10 +37,12 @@ class AgendaScheduleTile extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const _Initials(initial: 'W'), + _Initials(initial: session.title[0]), Constants.verticalGutter.verticalSpace, Text( - '๐Ÿ˜˜ Women Tech Makers Breakfast', + session.title, + maxLines: 3, + overflow: TextOverflow.ellipsis, style: DevfestTheme.of(context) .textTheme ?.titleTitle2Semibold @@ -44,7 +50,9 @@ class AgendaScheduleTile extends StatelessWidget { ), Constants.smallVerticalGutter.verticalSpace, Text( - 'Celebrate the women tech makers at their annual breakfast', + session.descrption, + maxLines: 3, + overflow: TextOverflow.ellipsis, style: DevfestTheme.of(context) .textTheme ?.bodyBody2Medium @@ -56,10 +64,10 @@ class AgendaScheduleTile extends StatelessWidget { children: [ IconText( IconsaxOutline.clock, - dateFormat.format(DateTime.now()), + dateFormat.format(start), ), Constants.largeHorizontalGutter.horizontalSpace, - const IconText(IconsaxOutline.location, 'Hall A') + IconText(IconsaxOutline.location, session.venue.id), ], ), ], diff --git a/packages/conferenceapp/lib/src/features/home/presentation/widgets/sponsor_tile.dart b/packages/conferenceapp/lib/src/features/home/presentation/widgets/sponsor_tile.dart index b9ea874..e0f3cf9 100644 --- a/packages/conferenceapp/lib/src/features/home/presentation/widgets/sponsor_tile.dart +++ b/packages/conferenceapp/lib/src/features/home/presentation/widgets/sponsor_tile.dart @@ -3,9 +3,13 @@ import 'package:cave/constants.dart'; import 'package:devfest24/src/shared/shared.dart'; import 'package:flutter/material.dart'; +import '../../../dashboard/model/model.dart'; + class ConferenceSponsorTile extends StatelessWidget { - const ConferenceSponsorTile({super.key, this.linkOnTap}); + const ConferenceSponsorTile( + {super.key, required this.sponsor, this.linkOnTap}); + final SponsorDto sponsor; final VoidCallback? linkOnTap; @override @@ -28,14 +32,15 @@ class ConferenceSponsorTile extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const _SponsorTierTag('Platinum Sponsor'), + _SponsorTierTag(sponsor.category), Padding( padding: const EdgeInsets.symmetric( vertical: Constants.largeVerticalGutter) .h, child: Center( - child: SvgPicture.asset( - 'assets/svgs/Google.svg', + child: CachedNetworkImage( + imageUrl: sponsor.logoUrl, + height: 122.h, ), ), ), @@ -50,7 +55,7 @@ class ConferenceSponsorTile extends StatelessWidget { ), Constants.smallVerticalGutter.verticalSpace, Text( - 'Google', + sponsor.name, style: DevfestTheme.of(context) .textTheme ?.bodyBody1Medium diff --git a/packages/conferenceapp/lib/src/features/more/presentation/screens/home.dart b/packages/conferenceapp/lib/src/features/more/presentation/screens/home.dart index eeb598e..7950dc2 100644 --- a/packages/conferenceapp/lib/src/features/more/presentation/screens/home.dart +++ b/packages/conferenceapp/lib/src/features/more/presentation/screens/home.dart @@ -1,16 +1,19 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; -import 'package:devfest24/src/features/more/presentation/widgets/widgets.dart'; import 'package:devfest24/src/routing/routing.dart'; import 'package:devfest24/src/shared/shared.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -class MoreHomeScreen extends StatelessWidget { +import '../../../dashboard/application/application.dart'; +import '../../../onboarding/presentation/presentation.dart'; +import '../presentation.dart'; + +class MoreHomeScreen extends ConsumerWidget { const MoreHomeScreen({super.key}); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, ref) { return Scaffold( appBar: AppBar(toolbarHeight: 0), body: Column( @@ -21,7 +24,17 @@ class MoreHomeScreen extends StatelessWidget { horizontal: Constants.horizontalMargin.w), child: Column( children: [ - const SignedInUserHeaderTile(), + if (ref.watch(userViewModelNotifier + .select((vm) => vm.user.id.isNotEmpty))) + const SignedInUserHeaderTile() + else + SignedOutUserHeaderTile( + signInOnTap: () { + context.goNamedAndPopAll(OnboardingLoginScreen.route); + ConferenceAppStorageService.instance + .setIsFirstLaunch(true); + }, + ), MoreSection( title: const Text('GENERAL'), options: [ @@ -32,7 +45,7 @@ class MoreHomeScreen extends StatelessWidget { size: 22.r, ), onTap: () { - context.goNamed(Devfest2024Routes.profile.name); + context.goNamed(ProfileScreen.route); }, ), MoreButton( @@ -42,7 +55,7 @@ class MoreHomeScreen extends StatelessWidget { size: 22.r, ), onTap: () { - context.goNamed(Devfest2024Routes.myQrCode.name); + context.goNamed(MyQrCodeScreen.route); }, ), MoreButton( @@ -60,7 +73,7 @@ class MoreHomeScreen extends StatelessWidget { size: 22.r, ), onTap: () { - context.goNamed(Devfest2024Routes.venueMap.name); + context.goNamed(VenueMapScreen.route); }, ), ], diff --git a/packages/conferenceapp/lib/src/features/more/presentation/screens/my_qr_code.dart b/packages/conferenceapp/lib/src/features/more/presentation/screens/my_qr_code.dart index daed9b2..bd79aae 100644 --- a/packages/conferenceapp/lib/src/features/more/presentation/screens/my_qr_code.dart +++ b/packages/conferenceapp/lib/src/features/more/presentation/screens/my_qr_code.dart @@ -1,19 +1,21 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; +import 'package:devfest24/src/features/dashboard/application/application.dart'; +import 'package:devfest24/src/routing/routing.dart'; import 'package:devfest24/src/shared/shared.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; class MyQrCodeScreen extends StatelessWidget { + static const route = '/more/my-qr-code'; + const MyQrCodeScreen({super.key}); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - leading: GoBackButton( - onTap: context.pop, - ), + leading: GoBackButton(onTap: context.pop), leadingWidth: 120.w, ), body: Padding( @@ -38,7 +40,23 @@ class MyQrCodeScreen extends StatelessWidget { ?.bodyBody2Medium ?.medium .applyColor(DevfestColors.grey50.possibleDarkVariant), - ) + ), + const Spacer(), + Consumer( + builder: (context, ref, child) { + if (ref + .watch(userViewModelNotifier.select((vm) => vm.user.id)) + .isEmpty) { + return const SizedBox.shrink(); + } + return QrImageView( + data: ref + .watch(userViewModelNotifier.select((vm) => vm.user.id)), + size: 296.w, + ); + }, + ), + const Spacer(flex: 3), ], ), ), diff --git a/packages/conferenceapp/lib/src/features/more/presentation/screens/profile.dart b/packages/conferenceapp/lib/src/features/more/presentation/screens/profile.dart index 02c0aa6..e5e9eb1 100644 --- a/packages/conferenceapp/lib/src/features/more/presentation/screens/profile.dart +++ b/packages/conferenceapp/lib/src/features/more/presentation/screens/profile.dart @@ -1,15 +1,21 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; import 'package:devfest24/src/shared/shared.dart'; -import 'package:go_router/go_router.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../../routing/routing.dart'; +import '../../../dashboard/application/application.dart'; +import '../../../onboarding/presentation/presentation.dart'; import '../widgets/widgets.dart'; import 'package:flutter/material.dart'; -class ProfileScreen extends StatelessWidget { +class ProfileScreen extends ConsumerWidget { + static const route = '/more/profile'; + const ProfileScreen({super.key}); @override - Widget build(BuildContext context) { + Widget build(BuildContext context, ref) { + final user = ref.watch(userViewModelNotifier).user; return Scaffold( extendBodyBehindAppBar: true, appBar: AppBar( @@ -22,12 +28,21 @@ class ProfileScreen extends StatelessWidget { ), body: Column( children: [ - SignedInUserHeaderTile( - height: 300.h, - margin: EdgeInsets.zero, - borderRadius: BorderRadius.zero, - gap: (Constants.verticalGutter * 2).verticalSpace, - ), + if (ref.watch( + userViewModelNotifier.select((vm) => vm.user.id.isNotEmpty))) + SignedInUserHeaderTile( + height: 300.h, + margin: EdgeInsets.zero, + borderRadius: BorderRadius.zero, + gap: (Constants.verticalGutter * 2).verticalSpace, + ) + else + SignedOutUserHeaderTile( + signInOnTap: () { + context.goNamedAndPopAll(OnboardingLoginScreen.route); + ConferenceAppStorageService.instance.setIsFirstLaunch(true); + }, + ), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.symmetric( @@ -41,24 +56,24 @@ class ProfileScreen extends StatelessWidget { ), child: Column( children: [ - const _ProfileInfoTile( + _ProfileInfoTile( title: '๐Ÿ“ง Email Address', - info: 'samuelabada2@gmail.com', + info: user.emailAddress, ), Constants.verticalGutter.verticalSpace, - const _ProfileInfoTile( + _ProfileInfoTile( title: '๐Ÿคนโ€โ™€๏ธ Area of Expertise', - info: 'Mobile Development', + info: user.role, ), Constants.verticalGutter.verticalSpace, - const _ProfileInfoTile( + _ProfileInfoTile( title: '๐Ÿ˜Š Level of Experience', - info: 'Senior', + info: user.levelOfExpertise, ), Constants.verticalGutter.verticalSpace, - const _ProfileInfoTile( - title: '๐Ÿ˜ Number of Years of Experience', - info: '6+ Years', + _ProfileInfoTile( + title: '๐Ÿ‘• Shirt Size', + info: user.shirtSize, ), ], ), diff --git a/packages/conferenceapp/lib/src/features/more/presentation/screens/venue_map.dart b/packages/conferenceapp/lib/src/features/more/presentation/screens/venue_map.dart index a9c4d58..058f332 100644 --- a/packages/conferenceapp/lib/src/features/more/presentation/screens/venue_map.dart +++ b/packages/conferenceapp/lib/src/features/more/presentation/screens/venue_map.dart @@ -2,9 +2,9 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; import 'package:devfest24/src/features/more/presentation/map/map.dart'; import 'package:devfest24/src/features/more/presentation/widgets/map.dart'; +import 'package:devfest24/src/routing/routing.dart'; import 'package:flutter/material.dart' hide Action; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; import '../../../../shared/shared.dart'; import '../widgets/map_layout.dart'; @@ -22,6 +22,8 @@ final showBlocksProvider = StateProvider.autoDispose((ref) { }); class VenueMapScreen extends ConsumerStatefulWidget { + static const route = '/home/venue-map'; + const VenueMapScreen({super.key}); @override diff --git a/packages/conferenceapp/lib/src/features/more/presentation/widgets/user_header_tiles.dart b/packages/conferenceapp/lib/src/features/more/presentation/widgets/user_header_tiles.dart index 2bcae65..e8d63ba 100644 --- a/packages/conferenceapp/lib/src/features/more/presentation/widgets/user_header_tiles.dart +++ b/packages/conferenceapp/lib/src/features/more/presentation/widgets/user_header_tiles.dart @@ -1,7 +1,9 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; +import 'package:devfest24/src/features/dashboard/application/application.dart'; import 'package:devfest24/src/shared/shared.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; class SignedOutUserHeaderTile extends StatelessWidget { const SignedOutUserHeaderTile({super.key, this.signInOnTap}); @@ -154,57 +156,60 @@ class SignedInUserHeaderTile extends StatelessWidget { Radius.circular(Constants.largeVerticalGutter), ), ), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - const _UserInitialTag('s'), - Constants.smallVerticalGutter.verticalSpace, - Text( - 'Samuel Abada', - style: DevfestTheme.of(context) - .textTheme - ?.titleTitle2Semibold - ?.semi - .applyColor(DevfestColors.grey100), - ), - gap ?? const Spacer(), - Row( - children: [ - Expanded( - child: _TicketInfoTile( - title: IconText( - IconsaxOutline.ticket_star, - 'Type: 2 Day Ticket', - textStyle: DevfestTheme.of(context) - .textTheme - ?.bodyBody4Semibold - ?.semi - .applyColor(DevfestColors.grey100), - iconColor: DevfestColors.grey100, - iconSize: 24, + child: Consumer(builder: (context, ref, child) { + final user = ref.watch(userViewModelNotifier.select((vm) => vm.user)); + return Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + _UserInitialTag(user.fullName.split(' ').first[0]), + Constants.smallVerticalGutter.verticalSpace, + Text( + user.fullName, + style: DevfestTheme.of(context) + .textTheme + ?.titleTitle2Semibold + ?.semi + .applyColor(DevfestColors.grey100), + ), + gap ?? const Spacer(), + Row( + children: [ + Expanded( + child: _TicketInfoTile( + title: IconText( + IconsaxOutline.ticket_star, + 'Type: ${user.ticket['title'] ?? ''}', + textStyle: DevfestTheme.of(context) + .textTheme + ?.bodyBody4Semibold + ?.semi + .applyColor(DevfestColors.grey100), + iconColor: DevfestColors.grey100, + iconSize: 24, + ), ), ), - ), - Constants.horizontalGutter.horizontalSpace, - Expanded( - child: _TicketInfoTile( - title: IconText( - IconsaxOutline.ticket_star, - 'ID: 413 012 123', - textStyle: DevfestTheme.of(context) - .textTheme - ?.bodyBody4Semibold - ?.semi - .applyColor(DevfestColors.grey100), - iconColor: DevfestColors.grey100, - iconSize: 24, + Constants.horizontalGutter.horizontalSpace, + Expanded( + child: _TicketInfoTile( + title: IconText( + IconsaxOutline.ticket_star, + 'ID: ${user.id}', + textStyle: DevfestTheme.of(context) + .textTheme + ?.bodyBody4Semibold + ?.semi + .applyColor(DevfestColors.grey100), + iconColor: DevfestColors.grey100, + iconSize: 24, + ), ), ), - ), - ], - ), - ], - ), + ], + ), + ], + ); + }), ), ); } diff --git a/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/home.dart b/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/home.dart index 5c47812..6d6b5c1 100644 --- a/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/home.dart +++ b/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/home.dart @@ -3,62 +3,68 @@ import 'package:cave/constants.dart'; import 'package:devfest24/src/routing/routing.dart'; import 'package:devfest24/src/shared/shared.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; + +import 'login.dart'; class OnboardingHomeScreen extends StatelessWidget { + static const route = '/onboarding'; + const OnboardingHomeScreen({super.key}); @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - leadingWidth: 100.w, - leading: Row( - children: [ - Padding( - padding: EdgeInsets.only(left: Constants.horizontalMargin.w), - child: GdgLogo.normal(), - ), - ], + return PopScope( + canPop: false, + child: Scaffold( + appBar: AppBar( + leadingWidth: 100.w, + leading: Row( + children: [ + Padding( + padding: EdgeInsets.only(left: Constants.horizontalMargin.w), + child: GdgLogo.normal(), + ), + ], + ), ), - ), - body: SafeArea( - child: Stack( - children: [ - Padding( - padding: EdgeInsets.symmetric( - horizontal: Constants.horizontalMargin.w), - child: Column( - children: [ - const HeaderText( - title: Text( - 'DevFest Lagos like you have never seen it before'), - subtitle: Text( - 'We said we will see you again next year, here we are, we have missed you and prepared amazing things for you ๐Ÿฅบ', + body: SafeArea( + child: Stack( + children: [ + Padding( + padding: EdgeInsets.symmetric( + horizontal: Constants.horizontalMargin.w), + child: Column( + children: [ + const HeaderText( + title: Text( + 'DevFest Lagos like you have never seen it before'), + subtitle: Text( + 'We said we will see you again next year, here we are, we have missed you and prepared amazing things for you ๐Ÿฅบ', + ), ), - ), - SizedBox(height: (Constants.largeVerticalGutter * 3).h), - DevfestFilledButton( - onPressed: () { - context.goNamed(Devfest2024Routes.onboardingLogin.name); - }, - title: const Text('Let\'s Get This Bread Quickly'), - ) - ], + SizedBox(height: (Constants.largeVerticalGutter * 3).h), + DevfestFilledButton( + onPressed: () { + context.goNamedAndPopAll(OnboardingLoginScreen.route); + }, + title: const Text('Let\'s Get This Bread Quickly'), + ) + ], + ), ), - ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: SvgPicture.asset( - 'images/conference_onboarding.svg', - package: 'cave', - semanticsLabel: 'Onboarding Image', - fit: BoxFit.contain, + Positioned( + bottom: 0, + left: 0, + right: 0, + child: SvgPicture.asset( + 'images/conference_onboarding.svg', + package: 'cave', + semanticsLabel: 'Onboarding Image', + fit: BoxFit.contain, + ), ), - ), - ], + ], + ), ), ), ); diff --git a/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/login.dart b/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/login.dart index ddeee33..060f74f 100644 --- a/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/login.dart +++ b/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/login.dart @@ -5,12 +5,15 @@ import 'package:devfest24/src/routing/routing.dart'; import 'package:devfest24/src/shared/shared.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; + +import '../../../dashboard/presentation/screens/dashboard.dart'; final _emailRegexp = RegExp( r'^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'); class OnboardingLoginScreen extends ConsumerStatefulWidget { + static const route = '/onboarding/login'; + const OnboardingLoginScreen({super.key}); @override @@ -26,7 +29,7 @@ class _OnboardingLoginScreenState extends ConsumerState { super.initState(); ref.listenManual(sessionSignInVMNotifier, (previous, next) { if (next.uiState.isSuccess) { - context.goNamed(Devfest2024Routes.dashboard.name); + context.goNamedAndPopAll(DashboardScreen.route); return; } }); @@ -170,7 +173,7 @@ class _OnboardingLoginScreenState extends ConsumerState { titleStyle: TextStyle( color: DevfestColors.grey10.possibleDarkVariant), onPressed: () { - context.goNamed(Devfest2024Routes.dashboard.name); + context.goNamedAndPopAll(DashboardScreen.route); }, ), ], diff --git a/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/signature.dart b/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/signature.dart index 9af1ec5..a5c082f 100644 --- a/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/signature.dart +++ b/packages/conferenceapp/lib/src/features/onboarding/presentation/screens/signature.dart @@ -6,12 +6,14 @@ import 'package:devfest24/src/features/onboarding/presentation/widgets/widgets.d import 'package:devfest24/src/shared/shared.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:go_router/go_router.dart'; import '../../../../routing/routing.dart'; +import '../../../dashboard/presentation/screens/dashboard.dart'; import '../widgets/path_to_svg_parser.dart'; class OnboardingSignatureScreen extends StatefulWidget { + static const route = '/onboarding/login/signature'; + const OnboardingSignatureScreen({super.key}); @override @@ -47,7 +49,7 @@ class _OnboardingSignatureScreenState extends State { color: DevfestColors.grey50.possibleDarkVariant, ).semi, onPressed: () { - context.goNamed(Devfest2024Routes.dashboard.name); + context.goNamedAndPopAll(DashboardScreen.route); }, ), ), diff --git a/packages/conferenceapp/lib/src/features/reserve/presentation/screens/home.dart b/packages/conferenceapp/lib/src/features/reserve/presentation/screens/home.dart index cbe5eb0..2e37f61 100644 --- a/packages/conferenceapp/lib/src/features/reserve/presentation/screens/home.dart +++ b/packages/conferenceapp/lib/src/features/reserve/presentation/screens/home.dart @@ -2,12 +2,19 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; import 'package:devfest24/src/features/reserve/presentation/widgets/widgets.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../shared/shared.dart'; +import '../../../dashboard/application/application.dart'; -class ReserveHomeScreen extends StatelessWidget { +class ReserveHomeScreen extends ConsumerStatefulWidget { const ReserveHomeScreen({super.key}); + @override + ConsumerState createState() => _ReserveHomeScreenState(); +} + +class _ReserveHomeScreenState extends ConsumerState { @override Widget build(BuildContext context) { return Scaffold( @@ -22,7 +29,10 @@ class ReserveHomeScreen extends StatelessWidget { ], ), actions: [ - const CheckInButton(), + CheckInButton( + isLoggedIn: ref.watch( + userViewModelNotifier.select((vm) => vm.user.id.isNotEmpty)), + ), Constants.horizontalMargin.horizontalSpace, ], ), diff --git a/packages/conferenceapp/lib/src/features/schedule/presentation/screens/home.dart b/packages/conferenceapp/lib/src/features/schedule/presentation/screens/home.dart index e437949..c1208f8 100644 --- a/packages/conferenceapp/lib/src/features/schedule/presentation/screens/home.dart +++ b/packages/conferenceapp/lib/src/features/schedule/presentation/screens/home.dart @@ -4,11 +4,26 @@ import 'package:devfest24/src/features/schedule/presentation/presentation.dart'; import 'package:devfest24/src/routing/routing.dart'; import 'package:devfest24/src/shared/shared.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; -class ScheduleHomeScreen extends StatelessWidget { +import '../../../dashboard/application/application.dart'; + +class ScheduleHomeScreen extends ConsumerStatefulWidget { const ScheduleHomeScreen({super.key}); + @override + ConsumerState createState() => _ScheduleHomeScreenState(); +} + +class _ScheduleHomeScreenState extends ConsumerState { + @override + void initState() { + super.initState(); + + ref.listenManual(dayOneSessionsProvider, (_, next) {}); + ref.listenManual(dayTwoSessionsProvider, (_, next) {}); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -23,7 +38,10 @@ class ScheduleHomeScreen extends StatelessWidget { ], ), actions: [ - const CheckInButton(isLoggedIn: true), + CheckInButton( + isLoggedIn: ref.watch( + userViewModelNotifier.select((vm) => vm.user.id.isNotEmpty)), + ), Constants.horizontalMargin.horizontalSpace, ], ), @@ -31,31 +49,49 @@ class ScheduleHomeScreen extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: Constants.horizontalMargin) .w, - child: CustomScrollView( - slivers: [ - SliverAgendaHeader( - title: const Text('๐Ÿ“† Schedule'), - subtitle: const Text( - 'Our schedule is packed with incredible content all for you!!', + child: RefreshIndicator( + onRefresh: () async { + await ref + .read(agendasViewModelNotifier.notifier) + .fetchAgenda(refresh: true); + }, + child: CustomScrollView( + slivers: [ + SliverAgendaHeader( + title: const Text('๐Ÿ“† Schedule'), + subtitle: const Text( + 'Our schedule is packed with incredible content all for you!!', + ), + onFilterSelected: () {}, + onEventDayChanged: (_) {}, ), - onFilterSelected: () {}, - onEventDayChanged: (_) {}, - ), - SliverList.separated( - itemBuilder: (context, index) => ConferenceScheduleTile( - type: index % 2 == 0 - ? ScheduleTileType.breakout - : ScheduleTileType.session, - onTap: () { - context.goNamed(Devfest2024Routes.scheduleDetails.name); + SliverList.separated( + itemBuilder: (context, index) { + final session = ref.watch(dayOneSessionsProvider)[index]; + final agenda = ref + .watch(agendasViewModelNotifier) + .agendas + .firstWhere((agenda) => session.periodId == agenda.id); + return ConferenceScheduleTile( + start: agenda.start!, + duration: agenda.duration, + session: session, + type: session.categories.isEmpty + ? ScheduleTileType.breakout + : ScheduleTileType.session, + onTap: () { + context.goNamed(ScheduleDetailsScreen.route, + arguments: session); + }, + ); }, + separatorBuilder: (context, index) => + Constants.verticalGutter.verticalSpace, + itemCount: ref.watch(dayOneSessionsProvider).length, ), - separatorBuilder: (context, index) => - Constants.verticalGutter.verticalSpace, - itemCount: 10, - ), - SliverToBoxAdapter(child: Constants.verticalGutter.verticalSpace), - ], + SliverToBoxAdapter(child: Constants.verticalGutter.verticalSpace), + ], + ), ), ), ); diff --git a/packages/conferenceapp/lib/src/features/schedule/presentation/screens/schedule_details.dart b/packages/conferenceapp/lib/src/features/schedule/presentation/screens/schedule_details.dart index e1c6bf9..826d40d 100644 --- a/packages/conferenceapp/lib/src/features/schedule/presentation/screens/schedule_details.dart +++ b/packages/conferenceapp/lib/src/features/schedule/presentation/screens/schedule_details.dart @@ -1,12 +1,17 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; import 'package:cave/ui_utils/container_properties.dart'; +import 'package:devfest24/src/features/dashboard/model/model.dart'; +import 'package:devfest24/src/routing/routing.dart'; +import 'package:devfest24/src/shared/shared.dart'; import 'package:devfest24/src/shared/widgets/speaker_talk_info_pill.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; class ScheduleDetailsScreen extends StatelessWidget { - const ScheduleDetailsScreen({super.key}); + static const route = '/home/schedule-details'; + + const ScheduleDetailsScreen({super.key, required this.session}); + final SessionDto session; @override Widget build(BuildContext context) { @@ -23,7 +28,7 @@ class ScheduleDetailsScreen extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - '๐Ÿ˜˜ Women Tech Makers Breakfast', + session.title, style: DevfestTheme.of(context).textTheme?.titleTitle2Semibold?.semi, ), @@ -49,7 +54,7 @@ class ScheduleDetailsScreen extends StatelessWidget { ), Constants.horizontalGutter.horizontalSpace, Text( - 'Femi Falana', + session.speakers.getNames, style: DevfestTheme.of(context).textTheme?.bodyBody1Medium?.semi, ), @@ -80,7 +85,7 @@ class ScheduleDetailsScreen extends StatelessWidget { ), Constants.verticalGutter.verticalSpace, Text( - 'Wake up to reality! Nothing ever goes as planned in this accursed world. The longer you live, the more you realize that the only things that truly exist in this reality are merely pain, suffering and futility. Listen, everywhere you look in this world, wherever there is light, there will always be shadows to be found as well. As long as there is a concept of victors, the vanquished will also exist. The selfish intent of wanting to preserve peace, initiates war and hatred is born in order to protect love. There are nexuses causal relationships that cannot be separated.', + session.descrption, style: DevfestTheme.of(context).textTheme?.bodyBody2Medium?.semi, ), ], diff --git a/packages/conferenceapp/lib/src/features/schedule/presentation/widgets/conference_schedule_tile.dart b/packages/conferenceapp/lib/src/features/schedule/presentation/widgets/conference_schedule_tile.dart index 863bc85..6b1da18 100644 --- a/packages/conferenceapp/lib/src/features/schedule/presentation/widgets/conference_schedule_tile.dart +++ b/packages/conferenceapp/lib/src/features/schedule/presentation/widgets/conference_schedule_tile.dart @@ -4,6 +4,8 @@ import 'package:devfest24/src/shared/shared.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import '../../../dashboard/model/model.dart'; + enum ScheduleTileType { session, breakout } class ConferenceScheduleTile extends StatefulWidget { @@ -11,8 +13,14 @@ class ConferenceScheduleTile extends StatefulWidget { super.key, this.onTap, required this.type, + required this.session, + required this.start, + required this.duration, }); + final SessionDto session; + final DateTime start; + final int duration; final VoidCallback? onTap; final ScheduleTileType type; @@ -77,15 +85,15 @@ class _ConferenceScheduleTileState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - dateFormat.format( - DateTime.now().subtract(const Duration(hours: 5))), + dateFormat.format(widget.start), style: DevfestTheme.of(context) .textTheme ?.bodyBody4Medium ?.medium, ), Text( - dateFormat.format(DateTime.now()), + dateFormat.format( + widget.start.add(Duration(minutes: widget.duration))), style: DevfestTheme.of(context) .textTheme ?.bodyBody4Medium @@ -97,8 +105,9 @@ class _ConferenceScheduleTileState extends State { Expanded( key: _expandedTileKey, child: switch (widget.type) { - ScheduleTileType.session => const _SessionInfo(), - ScheduleTileType.breakout => const _BreakoutScheduleInfo(), + ScheduleTileType.session => _SessionInfo(widget.session), + ScheduleTileType.breakout => + _BreakoutScheduleInfo(widget.session), }, ), ], @@ -109,7 +118,9 @@ class _ConferenceScheduleTileState extends State { } class _SessionInfo extends StatelessWidget { - const _SessionInfo(); + const _SessionInfo(this.session); + + final SessionDto session; @override Widget build(BuildContext context) { @@ -134,7 +145,7 @@ class _SessionInfo extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'mobile development'.toUpperCase(), + session.categories.first.toUpperCase(), style: DevfestTheme.of(context) .textTheme ?.bodyBody3Medium @@ -148,7 +159,9 @@ class _SessionInfo extends StatelessWidget { ), Constants.verticalGutter.verticalSpace, Text( - 'Appreciating the usefulness of football memes in decoding intent', + session.title, + maxLines: 3, + overflow: TextOverflow.ellipsis, style: DevfestTheme.of(context) .textTheme ?.bodyBody1Semibold @@ -157,7 +170,9 @@ class _SessionInfo extends StatelessWidget { ), Constants.smallVerticalGutter.verticalSpace, Text( - 'Celebrate the women tech makers at their annual breakfast', + session.descrption, + maxLines: 3, + overflow: TextOverflow.ellipsis, style: DevfestTheme.of(context) .textTheme ?.bodyBody2Medium @@ -165,13 +180,14 @@ class _SessionInfo extends StatelessWidget { .applyColor(DevfestColors.grey50.possibleDarkVariant), ), Constants.verticalGutter.verticalSpace, - const SpeakerInfo( - name: 'Samuel Abada', - shortBio: 'Flutter Engineer, Tesla', - avatarUrl: '', + SpeakerInfo( + name: session.speakers.first.fullname, + shortBio: + '${session.speakers.first.title}, ${session.speakers.first.company}', + avatarUrl: session.speakers.first.imageUrl, ), Constants.verticalGutter.verticalSpace, - const IconText(IconsaxOutline.location, 'Hall A'), + IconText(IconsaxOutline.location, session.venue.id), ], ), ), @@ -224,6 +240,8 @@ class SpeakerInfo extends StatelessWidget { (Constants.smallVerticalGutter / 2).verticalSpace, Text( shortBio, + maxLines: 2, + overflow: TextOverflow.ellipsis, style: DevfestTheme.of(context) .textTheme ?.bodyBody3Medium @@ -239,7 +257,9 @@ class SpeakerInfo extends StatelessWidget { } class _BreakoutScheduleInfo extends StatelessWidget { - const _BreakoutScheduleInfo(); + const _BreakoutScheduleInfo(this.session); + + final SessionDto session; @override Widget build(BuildContext context) { @@ -255,16 +275,20 @@ class _BreakoutScheduleInfo extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const _Initials(initial: 'W'), + _Initials(initial: session.title[0]), Constants.verticalGutter.verticalSpace, Text( - '๐Ÿ˜˜ Women Tech Makers Breakfast', + session.title, + maxLines: 3, + overflow: TextOverflow.ellipsis, style: DevfestTheme.of(context).textTheme?.titleTitle2Semibold?.semi, ), Constants.smallVerticalGutter.verticalSpace, Text( - 'Celebrate the women tech makers at their annual breakfast', + session.descrption, + maxLines: 3, + overflow: TextOverflow.ellipsis, style: DevfestTheme.of(context) .textTheme ?.bodyBody2Medium @@ -272,7 +296,7 @@ class _BreakoutScheduleInfo extends StatelessWidget { .copyWith(color: DevfestColors.grey50.possibleDarkVariant), ), Constants.verticalGutter.verticalSpace, - const IconText(IconsaxOutline.location, 'Hall A'), + IconText(IconsaxOutline.location, session.venue.id), ], ), ), diff --git a/packages/conferenceapp/lib/src/features/speakers/presentation/screens/home.dart b/packages/conferenceapp/lib/src/features/speakers/presentation/screens/home.dart index d2f7d78..90d0299 100644 --- a/packages/conferenceapp/lib/src/features/speakers/presentation/screens/home.dart +++ b/packages/conferenceapp/lib/src/features/speakers/presentation/screens/home.dart @@ -1,15 +1,43 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; +import 'package:devfest24/src/features/dashboard/model/model.dart'; import 'package:devfest24/src/features/speakers/presentation/widgets/widgets.dart'; import 'package:devfest24/src/routing/routing.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../shared/shared.dart'; +import '../../../dashboard/application/application.dart'; +import 'screens.dart'; -class SpeakersHomeScreen extends StatelessWidget { +class SpeakersHomeScreen extends ConsumerStatefulWidget { const SpeakersHomeScreen({super.key}); + @override + ConsumerState createState() => _SpeakersHomeScreenState(); +} + +class _SpeakersHomeScreenState extends ConsumerState { + EventDay _day = EventDay.one; + late ScrollController _scrollController; + Map scrollOffsets = {}; + + @override + void initState() { + super.initState(); + + _scrollController = ScrollController() + ..addListener(() { + scrollOffsets[_day.index] = _scrollController.offset; + }); + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -24,36 +52,96 @@ class SpeakersHomeScreen extends StatelessWidget { ], ), actions: [ - const CheckInButton(), + CheckInButton( + isLoggedIn: ref.watch( + userViewModelNotifier.select((vm) => vm.user.id.isNotEmpty)), + ), Constants.horizontalMargin.horizontalSpace, ], ), body: Padding( padding: EdgeInsets.symmetric(horizontal: Constants.horizontalMargin.w), - child: CustomScrollView( - slivers: [ - SliverAgendaHeader( - title: const Text('๐ŸŽค Speakers'), - subtitle: const Text( - 'Industry veterans ready to share knowledge with you on their experiences', - ), - onFilterSelected: () {}, - onEventDayChanged: (_) {}, - ), - SliverList.separated( - itemBuilder: (context, index) => SpeakerTile( - onTap: () { - context.goNamed(Devfest2024Routes.speakerDetails.name); + child: RefreshIndicator( + onRefresh: () async { + await ref + .read(speakersViewModelNotifier.notifier) + .fetchSpeakers(refresh: true); + }, + child: CustomScrollView( + controller: _scrollController, + key: PageStorageKey('SpeakersPageScrollView'), + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverAgendaHeader( + title: const Text('๐ŸŽค Speakers'), + subtitle: const Text( + 'Industry veterans ready to share knowledge with you on their experiences', + ), + eventDay: _day, + onEventDayChanged: (day) { + setState(() { + _day = day; + }); + + if (scrollOffsets.containsKey(_day.index)) { + _scrollController.jumpTo(scrollOffsets[_day.index]!); + } else { + _scrollController.jumpTo(0); + } }, + onFilterSelected: () {}, ), - separatorBuilder: (context, index) => - Constants.verticalGutter.verticalSpace, - itemCount: 20, - ), - SliverToBoxAdapter(child: Constants.verticalGutter.verticalSpace), - ], + [ + ProviderScope( + overrides: [ + _speakersProvider + .overrideWithValue(ref.watch(dayOneSpeakersProvider)), + ], + child: _SpeakersList(), + ), + ProviderScope( + overrides: [ + _speakersProvider + .overrideWithValue(ref.watch(dayTwoSpeakersProvider)), + ], + child: _SpeakersList(), + ), + ].elementAt(_day.index), + SliverToBoxAdapter(child: Constants.verticalGutter.verticalSpace), + ], + ), ), ), ); } } + +final _speakersProvider = Provider.autoDispose>((ref) { + throw UnimplementedError(); +}); + +class _SpeakersList extends ConsumerWidget { + const _SpeakersList(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final speakers = ref.watch(_speakersProvider); + return SliverList.separated( + itemBuilder: (context, index) { + final speaker = speakers[index]; + return SpeakerTile( + speaker: speaker, + onTap: () { + context.goNamed( + SpeakerDetailsScreen.route, + arguments: speaker, + ); + }, + ); + }, + separatorBuilder: (context, index) => + Constants.verticalGutter.verticalSpace, + itemCount: speakers.length, + ); + } +} diff --git a/packages/conferenceapp/lib/src/features/speakers/presentation/screens/speaker_details.dart b/packages/conferenceapp/lib/src/features/speakers/presentation/screens/speaker_details.dart index 16d3fc2..67526e9 100644 --- a/packages/conferenceapp/lib/src/features/speakers/presentation/screens/speaker_details.dart +++ b/packages/conferenceapp/lib/src/features/speakers/presentation/screens/speaker_details.dart @@ -2,15 +2,28 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; import 'package:cave/ui_utils/container_properties.dart'; import 'package:devfest24/src/features/speakers/presentation/widgets/speaker_social_media.dart'; +import 'package:devfest24/src/routing/routing.dart'; +import 'package:devfest24/src/shared/shared.dart'; import 'package:devfest24/src/shared/widgets/speaker_talk_info_pill.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:iconoir_flutter/iconoir_flutter.dart' hide Text, List, Key, Radius, Brightness, Map; -class SpeakerDetailsScreen extends StatelessWidget { - const SpeakerDetailsScreen({super.key}); +import '../../../dashboard/model/model.dart'; + +class SpeakerDetailsScreen extends ConsumerStatefulWidget { + static const route = '/home/speaker-details'; + const SpeakerDetailsScreen({super.key, required this.speaker}); + + final SpeakerDto speaker; + @override + ConsumerState createState() => + _SpeakerDetailsScreenState(); +} + +class _SpeakerDetailsScreenState extends ConsumerState { @override Widget build(BuildContext context) { return Scaffold( @@ -64,12 +77,11 @@ class SpeakerDetailsScreen extends StatelessWidget { border: Border.all( color: DevfestColors.primariesBlue60, width: 2), ), - child: Image.asset( - 'assets/images/mastersam.png', - semanticLabel: 'Speaker\'s photograph', + padding: EdgeInsets.all(2), + child: CachedNetworkImage( + imageUrl: widget.speaker.imageUrl, height: 56.h, width: 56.w, - fit: BoxFit.cover, ), ), Constants.horizontalGutter.horizontalSpace, @@ -78,7 +90,7 @@ class SpeakerDetailsScreen extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.end, children: [ Text( - 'Samuel Abada', + widget.speaker.fullname, style: DevfestTheme.of(context) .textTheme ?.bodyBody1Semibold @@ -86,7 +98,7 @@ class SpeakerDetailsScreen extends StatelessWidget { ), Constants.smallVerticalGutter.verticalSpace, Text( - 'Flutter Engineer, Tesla', + '${widget.speaker.title}, ${widget.speaker.company}', style: DevfestTheme.of(context) .textTheme ?.bodyBody2Medium @@ -112,17 +124,6 @@ class SpeakerDetailsScreen extends StatelessWidget { ], ), Constants.largeVerticalGutter.verticalSpace, - DevfestFilledButton( - title: Text( - 'Reserve My Spot', - style: DevfestTheme.of(context) - .textTheme - ?.buttonMediumSemibold - ?.semi - .applyColor(DevfestColors.grey100), - ), - ), - Constants.largeVerticalGutter.verticalSpace, Text( 'SPEAKER BIO', style: DevfestTheme.of(context) @@ -133,9 +134,9 @@ class SpeakerDetailsScreen extends StatelessWidget { ), Constants.verticalGutter.verticalSpace, Text( - 'Samuel is a developer from Akure who thinks he is Batman, and unfortunately, with his vast resources and endless wealth he has found a way to balance squashing bugs and the heads of criminals within Nigeria', - style: - DevfestTheme.of(context).textTheme?.bodyBody2Medium?.semi), + widget.speaker.bio, + style: DevfestTheme.of(context).textTheme?.bodyBody2Medium?.semi, + ), Constants.largeVerticalGutter.verticalSpace, Text( 'SPEAKER SOCIALS', @@ -149,20 +150,21 @@ class SpeakerDetailsScreen extends StatelessWidget { Wrap( spacing: 16.w, runSpacing: 16.h, - children: const [ - SpeakerSocialMedia( - icon: Icon( - IconsaxOutline.link_21, - semanticLabel: 'Social Media Link', - ), - onTap: null), - SpeakerSocialMedia(icon: Twitter(), onTap: null), - SpeakerSocialMedia(icon: Facebook(), onTap: null), - SpeakerSocialMedia(icon: Linkedin(), onTap: null), - SpeakerSocialMedia(icon: Behance(), onTap: null), - SpeakerSocialMedia(icon: Github(), onTap: null), - SpeakerSocialMedia(icon: Dribbble(), onTap: null), - ], + children: widget.speaker.links.entries.map((entry) { + final icon = switch (entry.key.toLowerCase()) { + 'github' => Github(), + 'linkedin' => Linkedin(), + 'twitter' => Twitter(), + 'facebook' => Facebook(), + 'dribble' => Dribbble(), + _ => Icon(IconsaxOutline.link_21, + semanticLabel: 'Social Media Link'), + }; + + return SpeakerSocialMedia( + icon: icon, + onTap: () => launchWebUrl(entry.value.toString())); + }).toList(), ) ], ), diff --git a/packages/conferenceapp/lib/src/features/speakers/presentation/widgets/speaker_tile.dart b/packages/conferenceapp/lib/src/features/speakers/presentation/widgets/speaker_tile.dart index 4e51884..de5b3f1 100644 --- a/packages/conferenceapp/lib/src/features/speakers/presentation/widgets/speaker_tile.dart +++ b/packages/conferenceapp/lib/src/features/speakers/presentation/widgets/speaker_tile.dart @@ -1,11 +1,13 @@ import 'package:cave/cave.dart'; import 'package:cave/constants.dart'; +import 'package:devfest24/src/features/dashboard/model/model.dart'; import 'package:flutter/material.dart'; import '../../../../shared/shared.dart'; class SpeakerTile extends StatelessWidget { - const SpeakerTile({super.key, this.onTap}); + const SpeakerTile({super.key, required this.speaker, this.onTap}); + final SpeakerDto speaker; final VoidCallback? onTap; @override @@ -18,10 +20,10 @@ class SpeakerTile extends StatelessWidget { borderRadius: const BorderRadius.all( Radius.circular(Constants.verticalGutter), ), - child: const SpeakerInfo( - name: 'Samuel Abada', - shortBio: 'Flutter Engineer, Tesla', - avatarUrl: '', + child: SpeakerInfo( + name: speaker.fullname, + shortBio: '${speaker.title}, ${speaker.company}', + avatarUrl: speaker.imageUrl, ), ), ); @@ -45,17 +47,22 @@ class SpeakerInfo extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - const Material( + Material( color: DevfestColors.primariesGreen80, - shape: RoundedRectangleBorder( + shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(Constants.smallVerticalGutter), ), side: BorderSide(color: DevfestColors.primariesBlue50, width: 2), ), child: Padding( - padding: EdgeInsets.all(2), - child: FlutterLogo(size: 48), + padding: const EdgeInsets.all(2), + child: CachedNetworkImage( + imageUrl: avatarUrl, + height: 48.h, + width: 48.w, + ), + // FlutterLogo(size: 48), ), ), Constants.horizontalGutter.horizontalSpace, diff --git a/packages/conferenceapp/lib/src/routing/router.dart b/packages/conferenceapp/lib/src/routing/router.dart index 5a27c10..b1d8287 100644 --- a/packages/conferenceapp/lib/src/routing/router.dart +++ b/packages/conferenceapp/lib/src/routing/router.dart @@ -1,14 +1,11 @@ -import 'package:cave/cave.dart'; import 'package:devfest24/src/features/dashboard/presentation/screens/dashboard.dart'; import 'package:devfest24/src/features/more/presentation/presentation.dart'; import 'package:devfest24/src/features/onboarding/presentation/presentation.dart'; import 'package:devfest24/src/features/schedule/presentation/presentation.dart'; import 'package:devfest24/src/features/speakers/presentation/presentation.dart'; -import 'routes.dart'; +import '../features/dashboard/model/model.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; class Devfest2024Router { static final rootNavigatorKey = GlobalKey(); @@ -17,75 +14,118 @@ class Devfest2024Router { static final Devfest2024Router instance = Devfest2024Router._(); - void initialiseRouter(WidgetRef ref) { - router = _getRouter(ref); + static String initialRoute = DashboardScreen.route; + + Route onGenerateRoutes(RouteSettings settings) { + return switch (settings.name) { + OnboardingHomeScreen.route => + MaterialPageRoute(builder: (_) => const OnboardingHomeScreen()), + OnboardingLoginScreen.route => + MaterialPageRoute(builder: (_) => const OnboardingLoginScreen()), + OnboardingSignatureScreen.route => + MaterialPageRoute(builder: (_) => const OnboardingSignatureScreen()), + DashboardScreen.route => + MaterialPageRoute(builder: (_) => const DashboardScreen()), + ScheduleDetailsScreen.route => MaterialPageRoute( + builder: (_) => ScheduleDetailsScreen( + session: settings.arguments as SessionDto, + )), + SpeakerDetailsScreen.route => MaterialPageRoute( + builder: (_) => + SpeakerDetailsScreen(speaker: settings.arguments as SpeakerDto)), + MyQrCodeScreen.route => + MaterialPageRoute(builder: (_) => const MyQrCodeScreen()), + ProfileScreen.route => + MaterialPageRoute(builder: (_) => const ProfileScreen()), + VenueMapScreen.route => + MaterialPageRoute(builder: (_) => const VenueMapScreen()), + _ => MaterialPageRoute( + builder: (_) => Scaffold( + body: Center( + child: Text('No route defined for ${settings.name}'), + ), + ), + ), + }; } - late GoRouter router; +// GoRouter _getRouter(WidgetRef ref) { +// return GoRouter( +// navigatorKey: rootNavigatorKey, +// initialLocation: '/', +// extraCodec: const ConferenceExtraCodec(), +// redirect: (context, state) async { +// final token = await ConferenceAppStorageService.instance.userToken; +// if (token.isEmpty) { +// return '/${Devfest2024Routes.onboardingHome.path}'; +// } +// +// return null; +// }, +// routes: [ +// GoRoute( +// path: '/${Devfest2024Routes.onboardingHome.path}', +// name: Devfest2024Routes.onboardingHome.name, +// builder: (context, state) => const OnboardingHomeScreen(), +// routes: [ +// GoRoute( +// path: Devfest2024Routes.onboardingLogin.path, +// name: Devfest2024Routes.onboardingLogin.name, +// builder: (context, state) => const OnboardingLoginScreen(), +// ), +// GoRoute( +// path: Devfest2024Routes.onboardingSignature.path, +// name: Devfest2024Routes.onboardingSignature.name, +// builder: (context, state) => const OnboardingSignatureScreen(), +// ), +// ], +// ), +// GoRoute( +// path: '/', +// name: Devfest2024Routes.dashboard.name, +// builder: (context, state) => const DashboardScreen(), +// routes: [ +// GoRoute( +// path: Devfest2024Routes.speakerDetails.path, +// name: Devfest2024Routes.speakerDetails.name, +// builder: (context, state) => SpeakerDetailsScreen( +// speaker: state.extra as SpeakerDto, +// ), +// ), +// GoRoute( +// path: Devfest2024Routes.scheduleDetails.path, +// name: Devfest2024Routes.scheduleDetails.name, +// builder: (context, state) => const ScheduleDetailsScreen(), +// ), +// GoRoute( +// path: Devfest2024Routes.profile.path, +// name: Devfest2024Routes.profile.name, +// builder: (context, state) => const ProfileScreen(), +// ), +// GoRoute( +// path: Devfest2024Routes.myQrCode.path, +// name: Devfest2024Routes.myQrCode.name, +// builder: (context, state) => const MyQrCodeScreen(), +// ), +// GoRoute( +// path: Devfest2024Routes.venueMap.path, +// name: Devfest2024Routes.venueMap.name, +// builder: (context, state) => const VenueMapScreen(), +// ), +// ], +// ), +// ], +// ); +// } +} - GoRouter _getRouter(WidgetRef ref) { - return GoRouter( - navigatorKey: rootNavigatorKey, - initialLocation: '/', - routes: [ - GoRoute( - path: '/${Devfest2024Routes.onboardingHome.path}', - name: Devfest2024Routes.onboardingHome.name, - builder: (context, state) => const OnboardingHomeScreen(), - routes: [ - GoRoute( - path: Devfest2024Routes.onboardingLogin.path, - name: Devfest2024Routes.onboardingLogin.name, - builder: (context, state) => const OnboardingLoginScreen(), - ), - GoRoute( - path: Devfest2024Routes.onboardingSignature.path, - name: Devfest2024Routes.onboardingSignature.name, - builder: (context, state) => const OnboardingSignatureScreen(), - ), - ], - ), - GoRoute( - path: '/', - name: Devfest2024Routes.dashboard.name, - builder: (context, state) => const DashboardScreen(), - redirect: (context, state) async { - final token = - await ConferenceAppStorageService.instance.userToken; - if (token.isEmpty) { - return '/${Devfest2024Routes.onboardingHome.path}'; - } +extension BuildContextX on BuildContext { + void pop([T? result]) => Navigator.of(this).pop(result); - return null; - }, - routes: [ - GoRoute( - path: Devfest2024Routes.speakerDetails.path, - name: Devfest2024Routes.speakerDetails.name, - builder: (context, state) => const SpeakerDetailsScreen(), - ), - GoRoute( - path: Devfest2024Routes.scheduleDetails.path, - name: Devfest2024Routes.scheduleDetails.name, - builder: (context, state) => const ScheduleDetailsScreen(), - ), - GoRoute( - path: Devfest2024Routes.profile.path, - name: Devfest2024Routes.profile.name, - builder: (context, state) => const ProfileScreen(), - ), - GoRoute( - path: Devfest2024Routes.myQrCode.path, - name: Devfest2024Routes.myQrCode.name, - builder: (context, state) => const MyQrCodeScreen(), - ), - GoRoute( - path: Devfest2024Routes.venueMap.path, - name: Devfest2024Routes.venueMap.name, - builder: (context, state) => const VenueMapScreen(), - ), - ], - ), - ]); - } + Future goNamed(String routeName, {Object? arguments}) => + Navigator.of(this).pushNamed(routeName, arguments: arguments); + + Future goNamedAndPopAll(String routeName, {Object? arguments}) => + Navigator.of(this).pushNamedAndRemoveUntil(routeName, (route) => false, + arguments: arguments); } diff --git a/packages/conferenceapp/lib/src/routing/routes.dart b/packages/conferenceapp/lib/src/routing/routes.dart deleted file mode 100644 index 7a8cd6d..0000000 --- a/packages/conferenceapp/lib/src/routing/routes.dart +++ /dev/null @@ -1,15 +0,0 @@ -enum Devfest2024Routes { - onboardingHome('onboarding'), - onboardingLogin('log-in'), - onboardingSignature('signature'), - dashboard(''), - speakerDetails('speaker-details'), - scheduleDetails('schedule-details'), - profile('profile'), - myQrCode('qr-code'), - venueMap('map'); - - const Devfest2024Routes(this.path); - - final String path; -} diff --git a/packages/conferenceapp/lib/src/routing/routing.dart b/packages/conferenceapp/lib/src/routing/routing.dart index 8d1b6be..58b1402 100644 --- a/packages/conferenceapp/lib/src/routing/routing.dart +++ b/packages/conferenceapp/lib/src/routing/routing.dart @@ -1,3 +1,2 @@ //GENERATED BARREL FILE export 'router.dart'; -export 'routes.dart'; diff --git a/packages/conferenceapp/lib/src/shared/extensions.dart b/packages/conferenceapp/lib/src/shared/extensions.dart index 3e4fdc9..69b9c70 100644 --- a/packages/conferenceapp/lib/src/shared/extensions.dart +++ b/packages/conferenceapp/lib/src/shared/extensions.dart @@ -1,4 +1,5 @@ import 'package:cave/cave.dart'; +import 'package:devfest24/src/features/dashboard/model/model.dart'; import 'package:devfest24/src/routing/routing.dart'; import 'package:flutter/material.dart'; @@ -26,3 +27,21 @@ extension DevfestColorsX on Color { return this; } } + +extension ListX on List { + List safeSublist(int length) { + try { + return sublist(0, length); + } on RangeError { + return this; + } + } +} + +extension SpeakerNames on List { + String get getNames => getSpeakerNames(this); + String getSpeakerNames(List speakers) { + final names = speakers.map((speaker) => speaker.fullname).toList(); + return names.join(', '); + } +} diff --git a/packages/conferenceapp/lib/src/shared/shared.dart b/packages/conferenceapp/lib/src/shared/shared.dart index 9728d84..5618258 100644 --- a/packages/conferenceapp/lib/src/shared/shared.dart +++ b/packages/conferenceapp/lib/src/shared/shared.dart @@ -1,3 +1,4 @@ export 'ui_model/ui_state_model.dart'; export 'widgets/widgets.dart'; export 'extensions.dart'; +export 'utility.dart'; diff --git a/packages/conferenceapp/lib/src/shared/ui_model/ui_state_model.dart b/packages/conferenceapp/lib/src/shared/ui_model/ui_state_model.dart index e990237..3cca04e 100644 --- a/packages/conferenceapp/lib/src/shared/ui_model/ui_state_model.dart +++ b/packages/conferenceapp/lib/src/shared/ui_model/ui_state_model.dart @@ -3,7 +3,6 @@ import 'package:cave/cave.dart'; import 'package:devfest24/src/routing/routing.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; part 'ui_state_model_mutex.dart'; @@ -80,10 +79,6 @@ extension ViewModelX on T { ); ScaffoldMessenger.of(context).showSnackBar(snackbar); - - if (error is UnauthorizedUserException) { - context.go(Devfest2024Routes.onboardingHome.name); - } } } diff --git a/packages/conferenceapp/lib/src/shared/utility.dart b/packages/conferenceapp/lib/src/shared/utility.dart new file mode 100644 index 0000000..1c34816 --- /dev/null +++ b/packages/conferenceapp/lib/src/shared/utility.dart @@ -0,0 +1,11 @@ +import 'dart:developer'; + +import 'package:url_launcher/url_launcher.dart'; + +Future launchWebUrl(String path) async { + final uri = Uri.parse(path); + + if (!await launchUrl(uri)) { + log('Failed to launch url'); + } +} diff --git a/packages/conferenceapp/lib/src/shared/widgets/agenda_header.dart b/packages/conferenceapp/lib/src/shared/widgets/agenda_header.dart index 333acd4..cbaaff2 100644 --- a/packages/conferenceapp/lib/src/shared/widgets/agenda_header.dart +++ b/packages/conferenceapp/lib/src/shared/widgets/agenda_header.dart @@ -100,8 +100,8 @@ class AgendaHeader extends StatelessWidget { ), ], ), - Constants.verticalGutter.verticalSpace, ], + Constants.verticalGutter.verticalSpace, ], ), ); @@ -183,6 +183,7 @@ class _SliverAgendaHeaderState extends State { color: DevfestTheme.of(context).backgroundColor, child: AgendaHeader( title: widget.title, + eventDay: widget.eventDay, subtitle: switch (hideHeaders) { true => null, false => widget.subtitle, @@ -213,6 +214,7 @@ class _SliverAgendaHeaderState extends State { }, titleStyle: widget.titleStyle, onFilterSelected: widget.onFilterSelected, + eventDay: widget.eventDay, onEventDayChanged: widget.onEventDayChanged, gutter: switch (hideHeaders) { true => Constants.smallVerticalGutter.verticalSpace, @@ -233,6 +235,7 @@ class _SliverAgendaHeaderState extends State { }, titleStyle: widget.titleStyle, onFilterSelected: widget.onFilterSelected, + eventDay: widget.eventDay, onEventDayChanged: widget.onEventDayChanged, gutter: switch (hideHeaders) { true => Constants.smallVerticalGutter.verticalSpace, diff --git a/packages/conferenceapp/lib/src/shared/widgets/check_in_button.dart b/packages/conferenceapp/lib/src/shared/widgets/check_in_button.dart index 28e3ac2..10c16f8 100644 --- a/packages/conferenceapp/lib/src/shared/widgets/check_in_button.dart +++ b/packages/conferenceapp/lib/src/shared/widgets/check_in_button.dart @@ -1,6 +1,9 @@ import 'package:cave/cave.dart'; +import 'package:devfest24/src/routing/routing.dart'; import 'package:flutter/material.dart'; +import '../../features/onboarding/presentation/presentation.dart'; + class CheckInButton extends StatelessWidget { const CheckInButton({ super.key, @@ -19,7 +22,10 @@ class CheckInButton extends StatelessWidget { width: 113.w, child: isLoggedIn ? _CheckInButton(onCheckInTap) - : _LoginCheckInButton(onCheckInTap), + : _LoginCheckInButton(() { + context.goNamedAndPopAll(OnboardingLoginScreen.route); + ConferenceAppStorageService.instance.setIsFirstLaunch(true); + }), ); } } diff --git a/packages/conferenceapp/pubspec.lock b/packages/conferenceapp/pubspec.lock index e834a7c..4f3540e 100644 --- a/packages/conferenceapp/pubspec.lock +++ b/packages/conferenceapp/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: "direct main" description: name: accessibility_tools - sha256: "7c0b5ac466ea8b971c1b081f3ade0979e6563e8926da62c3b90def78c44dd96b" + sha256: ae082866ddc7d4b06d7344f20f551d71302c80070d1e6c5f112f1d09b98de95c url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" args: dependency: transitive description: @@ -33,6 +33,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + cached_network_image: + dependency: transitive + description: + name: cached_network_image + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" + url: "https://pub.dev" + source: hosted + version: "1.3.1" cached_value: dependency: transitive description: @@ -80,6 +104,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27 + url: "https://pub.dev" + source: hosted + version: "3.0.5" dio: dependency: transitive description: @@ -168,6 +200,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.18.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -181,14 +221,22 @@ packages: url: "https://pub.dev" source: hosted version: "4.5.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + url: "https://pub.dev" + source: hosted + version: "3.4.1" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "5.0.0" flutter_riverpod: dependency: "direct main" description: @@ -279,14 +327,6 @@ packages: description: flutter source: sdk version: "0.0.0" - go_router: - dependency: "direct main" - description: - name: go_router - sha256: "5cf5fdcf853b0629deb35891c7af643be900c3dcaed7489009f9e7dbcfe55ab6" - url: "https://pub.dev" - source: hosted - version: "14.2.8" http: dependency: transitive description: @@ -355,18 +395,10 @@ packages: dependency: transitive description: name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "5.0.0" matcher: dependency: transitive description: @@ -407,6 +439,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.2.3" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" package_info_plus: dependency: "direct main" description: @@ -511,6 +551,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + qr: + dependency: transitive + description: + name: qr + sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + qr_flutter: + dependency: transitive + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.dev" + source: hosted + version: "4.1.0" riverpod: dependency: transitive description: @@ -519,6 +575,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.1" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" shared_preferences: dependency: transitive description: @@ -575,6 +639,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" + shimmer: + dependency: transitive + description: + name: shimmer + sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9" + url: "https://pub.dev" + source: hosted + version: "3.0.0" sky_engine: dependency: transitive description: flutter @@ -588,6 +660,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "79a297dc3cc137e758c6a4baf83342b039e5a6d2436fcdf3f96a00adaaf2ad62" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490" + url: "https://pub.dev" + source: hosted + version: "2.5.4+5" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "769733dddf94622d5541c73e4ddc6aa7b252d865285914b6fcd54a63c4b4f027" + url: "https://pub.dev" + source: hosted + version: "2.4.1-1" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" stack_trace: dependency: transitive description: @@ -620,6 +740,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" + url: "https://pub.dev" + source: hosted + version: "3.3.0+3" term_glyph: dependency: transitive description: @@ -644,6 +772,78 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "8fc3bae0b68c02c47c5c86fa8bfa74471d42687b0eded01b78de87872db745e2" + url: "https://pub.dev" + source: hosted + version: "6.3.12" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af + url: "https://pub.dev" + source: hosted + version: "3.2.0" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: diff --git a/packages/conferenceapp/pubspec.yaml b/packages/conferenceapp/pubspec.yaml index dae192a..e25cd73 100644 --- a/packages/conferenceapp/pubspec.yaml +++ b/packages/conferenceapp/pubspec.yaml @@ -10,7 +10,7 @@ environment: sdk: '>=3.4.4 <4.0.0' dependencies: - accessibility_tools: ^2.2.2 + accessibility_tools: ^2.2.3 cave: path: ../cave @@ -19,17 +19,18 @@ dependencies: flutter: sdk: flutter flutter_riverpod: ^2.5.1 - go_router: ^14.2.8 intl: ^0.19.0 mesh: ^0.4.1 package_info_plus: ^8.0.2 path_provider: ^2.1.4 iconoir_flutter: ^7.9.0 + url_launcher: ^6.3.1 + dependency_overrides: firebase_core: 3.3.0 dev_dependencies: - flutter_lints: ^4.0.0 + flutter_lints: ^5.0.0 flutter_test: sdk: flutter @@ -38,7 +39,6 @@ flutter: assets: - assets/svgs/ - - assets/images/ # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in diff --git a/packages/volunteerapp/lib/volunteer_app.dart b/packages/volunteerapp/lib/volunteer_app.dart index 7da9fb0..0545318 100644 --- a/packages/volunteerapp/lib/volunteer_app.dart +++ b/packages/volunteerapp/lib/volunteer_app.dart @@ -62,29 +62,6 @@ class _ConferenceAppState extends ConsumerState { DevFestThemeData.light(), ], ), - darkTheme: ThemeData( - colorScheme: ColorScheme.fromSeed( - seedColor: Colors.deepPurple, - brightness: Brightness.dark, - surface: DevFestThemeData.dark().backgroundColor, - ), - appBarTheme: AppBarTheme( - elevation: 0, - color: DevFestThemeData.dark().backgroundColor, - surfaceTintColor: Colors.transparent, - scrolledUnderElevation: 0, - ), - scaffoldBackgroundColor: DevFestThemeData.dark().backgroundColor, - useMaterial3: true, - textTheme: const TextTheme( - displayMedium: TextStyle(color: DevfestColors.grey100), - ), - extensions: >[ - /// Use the below format for raw theme data - /// DevFestTheme(textTheme: DevfestTextTheme()), - DevFestThemeData.dark(), - ], - ), ); }, ), diff --git a/packages/volunteerapp/pubspec.lock b/packages/volunteerapp/pubspec.lock index f312a5f..0159dcf 100644 --- a/packages/volunteerapp/pubspec.lock +++ b/packages/volunteerapp/pubspec.lock @@ -33,6 +33,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + cached_network_image: + dependency: transitive + description: + name: cached_network_image + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" + url: "https://pub.dev" + source: hosted + version: "1.3.1" cave: dependency: "direct main" description: @@ -72,6 +96,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" dio: dependency: transitive description: @@ -160,6 +192,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.18.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -173,6 +213,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.5.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + url: "https://pub.dev" + source: hosted + version: "3.4.1" flutter_lints: dependency: "direct dev" description: @@ -383,6 +431,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.2.3" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -471,6 +527,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + qr: + dependency: transitive + description: + name: qr + sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + qr_flutter: + dependency: transitive + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.dev" + source: hosted + version: "4.1.0" riverpod: dependency: transitive description: @@ -479,6 +551,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.5.1" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" shared_preferences: dependency: transitive description: @@ -556,6 +636,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "79a297dc3cc137e758c6a4baf83342b039e5a6d2436fcdf3f96a00adaaf2ad62" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490" + url: "https://pub.dev" + source: hosted + version: "2.5.4+5" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "769733dddf94622d5541c73e4ddc6aa7b252d865285914b6fcd54a63c4b4f027" + url: "https://pub.dev" + source: hosted + version: "2.4.1-1" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" stack_trace: dependency: transitive description: @@ -588,6 +716,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" + url: "https://pub.dev" + source: hosted + version: "3.3.0+3" term_glyph: dependency: transitive description: @@ -612,6 +748,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: