diff --git a/lib/data/datasources/category_remote_datasource.dart b/lib/data/datasources/category_remote_datasource.dart new file mode 100644 index 0000000..3bc9359 --- /dev/null +++ b/lib/data/datasources/category_remote_datasource.dart @@ -0,0 +1,57 @@ +import 'package:dio/dio.dart'; +import '../../core/error/failures.dart'; +import '../../core/network/api_client.dart'; +import '../../domain/models/category.dart'; +import '../../presentation/providers/auth_provider.dart'; +import '../../presentation/states/auth_state.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +abstract class CategoryRemoteDatasource { + Future getAllCategories(); +} + +class CategoryRemoteDatasourceImpl implements CategoryRemoteDatasource { + final ApiClient apiClient; + final Ref ref; + + CategoryRemoteDatasourceImpl(this.apiClient, this.ref); + + @override + Future getAllCategories() async { + try { + // Check if the user is authenticated + final authState = ref.read(authNotifierProvider); + if (authState is! AuthSuccess) { + throw ServerFailure( + message: 'Authentication required to fetch categories', + ); + } + + // Get the access token + final token = authState.response.tokens.accessToken; + + // Make the API request with the token + final response = await apiClient.get( + '/categories/all', + options: Options( + headers: { + 'Authorization': 'Bearer $token', + }, + ), + ); + + // Pass the direct response data to the fromJson method + return CategoriesResponse.fromJson(response.data); + } on DioException catch (e) { + throw ServerFailure( + message: e.message ?? 'An error occurred while fetching categories', + code: e.response?.statusCode, + ); + } catch (e) { + if (e is ServerFailure) { + rethrow; + } + throw ServerFailure(message: e.toString()); + } + } +} diff --git a/lib/data/datasources/hashtag_remote_datasource.dart b/lib/data/datasources/hashtag_remote_datasource.dart new file mode 100644 index 0000000..9689c80 --- /dev/null +++ b/lib/data/datasources/hashtag_remote_datasource.dart @@ -0,0 +1,57 @@ +import 'package:dio/dio.dart'; +import '../../core/error/failures.dart'; +import '../../core/network/api_client.dart'; +import '../../domain/models/hashtag.dart'; +import '../../presentation/providers/auth_provider.dart'; +import '../../presentation/states/auth_state.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +abstract class HashtagRemoteDatasource { + Future getAllHashtags(); +} + +class HashtagRemoteDatasourceImpl implements HashtagRemoteDatasource { + final ApiClient apiClient; + final Ref ref; + + HashtagRemoteDatasourceImpl(this.apiClient, this.ref); + + @override + Future getAllHashtags() async { + try { + // Check if the user is authenticated + final authState = ref.read(authNotifierProvider); + if (authState is! AuthSuccess) { + throw ServerFailure( + message: 'Authentication required to fetch hashtags', + ); + } + + // Get the access token + final token = authState.response.tokens.accessToken; + + // Make the API request with the token + final response = await apiClient.get( + '/hashtags', + options: Options( + headers: { + 'Authorization': 'Bearer $token', + }, + ), + ); + + // Pass the direct response data to the fromJson method + return HashtagsResponse.fromJson(response.data); + } on DioException catch (e) { + throw ServerFailure( + message: e.message ?? 'An error occurred while fetching hashtags', + code: e.response?.statusCode, + ); + } catch (e) { + if (e is ServerFailure) { + rethrow; + } + throw ServerFailure(message: e.toString()); + } + } +} diff --git a/lib/data/repositories/category_repository_impl.dart b/lib/data/repositories/category_repository_impl.dart new file mode 100644 index 0000000..ba57fa3 --- /dev/null +++ b/lib/data/repositories/category_repository_impl.dart @@ -0,0 +1,14 @@ +import '../../domain/models/category.dart'; +import '../../domain/repositories/category_repository.dart'; +import '../datasources/category_remote_datasource.dart'; + +class CategoryRepositoryImpl implements CategoryRepository { + final CategoryRemoteDatasource remoteDatasource; + + CategoryRepositoryImpl(this.remoteDatasource); + + @override + Future getAllCategories() { + return remoteDatasource.getAllCategories(); + } +} diff --git a/lib/data/repositories/hashtag_repository_impl.dart b/lib/data/repositories/hashtag_repository_impl.dart new file mode 100644 index 0000000..07c8b01 --- /dev/null +++ b/lib/data/repositories/hashtag_repository_impl.dart @@ -0,0 +1,14 @@ +import '../../domain/models/hashtag.dart'; +import '../../domain/repositories/hashtag_repository.dart'; +import '../datasources/hashtag_remote_datasource.dart'; + +class HashtagRepositoryImpl implements HashtagRepository { + final HashtagRemoteDatasource remoteDatasource; + + HashtagRepositoryImpl(this.remoteDatasource); + + @override + Future getAllHashtags() { + return remoteDatasource.getAllHashtags(); + } +} diff --git a/lib/domain/models/category.dart b/lib/domain/models/category.dart new file mode 100644 index 0000000..7e483b5 --- /dev/null +++ b/lib/domain/models/category.dart @@ -0,0 +1,44 @@ +class Category { + final String id; + final String name; + final String createdAt; + final String updatedAt; + + Category({ + required this.id, + required this.name, + required this.createdAt, + required this.updatedAt, + }); + + factory Category.fromJson(Map json) => Category( + id: json['id'] ?? '', + name: json['name'] ?? '', + createdAt: json['createdAt'] ?? '', + updatedAt: json['updatedAt'] ?? '', + ); +} + +class CategoriesResponse { + final List categories; + + CategoriesResponse({required this.categories}); + + factory CategoriesResponse.fromJson(dynamic json) { + // Handle both array response and object with data field + List categoriesData = []; + + if (json is List) { + // Direct array response + categoriesData = json; + } else if (json is Map) { + // Response with data field + categoriesData = json['data'] ?? []; + } + + final categories = categoriesData + .map((categoryJson) => Category.fromJson(categoryJson)) + .toList(); + return CategoriesResponse(categories: categories); + } +} diff --git a/lib/domain/models/hashtag.dart b/lib/domain/models/hashtag.dart new file mode 100644 index 0000000..17d5bcc --- /dev/null +++ b/lib/domain/models/hashtag.dart @@ -0,0 +1,44 @@ +class Hashtag { + final String id; + final String name; + final String createdAt; + final String updatedAt; + + Hashtag({ + required this.id, + required this.name, + required this.createdAt, + required this.updatedAt, + }); + + factory Hashtag.fromJson(Map json) => Hashtag( + id: json['id'] ?? '', + name: json['name'] ?? '', + createdAt: json['createdAt'] ?? '', + updatedAt: json['updatedAt'] ?? '', + ); +} + +class HashtagsResponse { + final List hashtags; + + HashtagsResponse({required this.hashtags}); + + factory HashtagsResponse.fromJson(dynamic json) { + // Handle both array response and object with data field + List hashtagsData = []; + + if (json is List) { + // Direct array response + hashtagsData = json; + } else if (json is Map) { + // Response with data field + hashtagsData = json['data'] ?? []; + } + + final hashtags = hashtagsData + .map((hashtagJson) => Hashtag.fromJson(hashtagJson)) + .toList(); + return HashtagsResponse(hashtags: hashtags); + } +} diff --git a/lib/domain/repositories/category_repository.dart b/lib/domain/repositories/category_repository.dart new file mode 100644 index 0000000..777bbd9 --- /dev/null +++ b/lib/domain/repositories/category_repository.dart @@ -0,0 +1,5 @@ +import '../models/category.dart'; + +abstract class CategoryRepository { + Future getAllCategories(); +} diff --git a/lib/domain/repositories/hashtag_repository.dart b/lib/domain/repositories/hashtag_repository.dart new file mode 100644 index 0000000..0cb8b6b --- /dev/null +++ b/lib/domain/repositories/hashtag_repository.dart @@ -0,0 +1,5 @@ +import '../models/hashtag.dart'; + +abstract class HashtagRepository { + Future getAllHashtags(); +} diff --git a/lib/domain/usecases/get_all_categories.dart b/lib/domain/usecases/get_all_categories.dart new file mode 100644 index 0000000..bbc4abd --- /dev/null +++ b/lib/domain/usecases/get_all_categories.dart @@ -0,0 +1,12 @@ +import '../models/category.dart'; +import '../repositories/category_repository.dart'; + +class GetAllCategories { + final CategoryRepository repository; + + GetAllCategories(this.repository); + + Future call() async { + return await repository.getAllCategories(); + } +} diff --git a/lib/domain/usecases/get_all_hashtags.dart b/lib/domain/usecases/get_all_hashtags.dart new file mode 100644 index 0000000..147084d --- /dev/null +++ b/lib/domain/usecases/get_all_hashtags.dart @@ -0,0 +1,12 @@ +import '../models/hashtag.dart'; +import '../repositories/hashtag_repository.dart'; + +class GetAllHashtags { + final HashtagRepository repository; + + GetAllHashtags(this.repository); + + Future call() async { + return await repository.getAllHashtags(); + } +} diff --git a/lib/features/create_wager/create_wager_screen.dart b/lib/features/create_wager/create_wager_screen.dart index 918d2fb..bff8da3 100644 --- a/lib/features/create_wager/create_wager_screen.dart +++ b/lib/features/create_wager/create_wager_screen.dart @@ -1,13 +1,67 @@ part of '../feature.dart'; -class CreateWagerScreen extends ConsumerWidget { +class CreateWagerScreen extends ConsumerStatefulWidget { const CreateWagerScreen({super.key}); @override - Widget build(BuildContext context, WidgetRef ref) { + ConsumerState createState() => _CreateWagerScreenState(); +} + +class _CreateWagerScreenState extends ConsumerState { + bool _isLoadingCategories = false; + bool _isLoadingHashtags = false; + String? _categoriesError; + String? _hashtagsError; + + @override + void initState() { + super.initState(); + // Fetch categories and hashtags when screen initializes + SchedulerBinding.instance.addPostFrameCallback((_) { + // Initial fetch of categories and hashtags + _fetchCategoriesAndHashtags(); + }); + } + + void _fetchCategoriesAndHashtags() async { + setState(() { + _isLoadingCategories = true; + _isLoadingHashtags = true; + _categoriesError = null; + _hashtagsError = null; + }); + + try { + await ref.read(categoriesNotifierProvider.notifier).fetchCategories(); + } catch (e) { + setState(() { + _categoriesError = e.toString(); + }); + } finally { + setState(() { + _isLoadingCategories = false; + }); + } + + try { + await ref.read(hashtagsApiNotifierProvider.notifier).fetchHashtags(); + } catch (e) { + setState(() { + _hashtagsError = e.toString(); + }); + } finally { + setState(() { + _isLoadingHashtags = false; + }); + } + } + + @override + Widget build(BuildContext context) { final size = MediaQuery.of(context).size; final selectedHashtags = ref.watch(selectedHashtagsProvider); final selectedCategory = ref.watch(selectedCategoryProvider); + String getDisplayText() { if (selectedHashtags.isEmpty) { return 'addHashtags'.tr(); @@ -44,6 +98,23 @@ class CreateWagerScreen extends ConsumerWidget { verticalSpace(size.height * 0.02), if (context.isMobile) Divider(), verticalSpace(size.height * 0.03), + // Show API states if needed for debugging + if (_categoriesError != null || _hashtagsError != null) + Container( + padding: EdgeInsets.all(10), + margin: EdgeInsets.only(bottom: 10), + decoration: BoxDecoration( + color: Colors.red.withValues(alpha: 26), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + _categoriesError != null + ? 'Error loading categories: $_categoriesError' + : 'Error loading hashtags: $_hashtagsError', + style: TextStyle(color: Colors.red), + ), + ), + // Continue with the rest of the UI Row( children: [ Expanded( @@ -60,11 +131,20 @@ class CreateWagerScreen extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: Text( - selectedCategory ?? 'selectCategory'.tr(), - style: AppTheme.of(context).textMediumMedium, - overflow: TextOverflow.ellipsis, - ), + child: _isLoadingCategories + ? Center( + child: SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator( + strokeWidth: 2))) + : Text( + selectedCategory ?? + 'selectCategory'.tr(), + style: AppTheme.of(context) + .textMediumMedium, + overflow: TextOverflow.ellipsis, + ), ), Icon(Icons.arrow_drop_down, color: context @@ -90,11 +170,19 @@ class CreateWagerScreen extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - child: Text( - getDisplayText(), - style: AppTheme.of(context).textMediumMedium, - overflow: TextOverflow.ellipsis, - ), + child: _isLoadingHashtags + ? Center( + child: SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator( + strokeWidth: 2))) + : Text( + getDisplayText(), + style: AppTheme.of(context) + .textMediumMedium, + overflow: TextOverflow.ellipsis, + ), ), Icon(Icons.arrow_drop_down, color: context.primaryTextColor), @@ -306,10 +394,7 @@ class CreateWagerScreen extends ConsumerWidget { topRight: Radius.circular(20), ), ), - child: FractionallySizedBox( - heightFactor: 0.6, - child: child, - ), + child: child, ), ); } @@ -396,74 +481,85 @@ class CreateWagerScreen extends ConsumerWidget { } Widget buildBottomSheet(List categories) { - return SizedBox( - width: 500, - child: Column( - children: [ - Container( - padding: const EdgeInsets.only(right: 20, top: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container(), - Container(), - Text( - 'selectCategory'.tr(), - style: AppTheme.of(context).titleExtraLarge24, - ), - IconButton( - icon: Icon( - Icons.close, - size: 24, - color: context.primaryTextColor, + return Padding( + padding: + EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: SizedBox( + width: 500, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + padding: const EdgeInsets.only(right: 20, top: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container(), + Container(), + Text( + 'selectCategory'.tr(), + style: AppTheme.of(context).titleExtraLarge24, ), - onPressed: () => Navigator.pop(context), - ), - ], + IconButton( + icon: Icon( + Icons.close, + size: 24, + color: context.primaryTextColor, + ), + onPressed: () => Navigator.pop(context), + ), + ], + ), ), - ), - verticalSpace(30), - Expanded( - child: SingleChildScrollView( - child: Column( - children: categories.map((category) { - return Column( - children: [ - InkWell( - onTap: () { - Navigator.of(context).pop(category); - ref.read(selectedCategoryProvider.notifier).state = - category; - }, - child: Container( - width: double.infinity, - padding: EdgeInsets.symmetric( - vertical: 15, horizontal: 20), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - category, - style: AppTheme.of(context).textSmallMedium, - ), - SizedBox(width: 5), - if (ref.watch(selectedCategoryProvider) == - category) - SvgPicture.asset(AppIcons.checked), - ], + verticalSpace(20), + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.5, + ), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: categories.map((category) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + onTap: () { + Navigator.of(context).pop(category); + ref + .read(selectedCategoryProvider.notifier) + .state = category; + }, + child: Container( + width: double.infinity, + padding: EdgeInsets.symmetric( + vertical: 15, horizontal: 20), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + category, + style: AppTheme.of(context).textSmallMedium, + ), + SizedBox(width: 5), + if (ref.watch(selectedCategoryProvider) == + category) + SvgPicture.asset(AppIcons.checked), + ], + ), ), ), - ), - if (category != categories[categories.length - 1]) - buildDotedBorder(context), - ], - ); - }).toList(), + if (category != categories[categories.length - 1]) + buildDotedBorder(context), + ], + ); + }).toList(), + ), ), ), - ), - ], + ], + ), ), ); } @@ -585,23 +681,26 @@ class HashtagDialog extends ConsumerWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, + Stack( + alignment: Alignment.center, children: [ - horizontalSpace(140), - Text( - textAlign: TextAlign.center, - 'addHashtag(s)'.tr(), - style: AppTheme.of(context).titleExtraLarge24, + Align( + alignment: Alignment.center, + child: Text( + 'addHashtag(s)'.tr(), + style: AppTheme.of(context).titleExtraLarge24, + ), ), - Spacer(), - IconButton( - icon: Icon( - Icons.close, - size: 24, - color: context.primaryTextColor, + Align( + alignment: Alignment.centerRight, + child: IconButton( + icon: Icon( + Icons.close, + size: 24, + color: context.primaryTextColor, + ), + onPressed: () => Navigator.pop(context), ), - onPressed: () => Navigator.pop(context), ), ], ), @@ -642,30 +741,33 @@ class HashtagBottomSheet extends ConsumerWidget { return Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( - color: context.containerColor, + color: context.primaryBackgroundColor, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, + Stack( + alignment: Alignment.center, children: [ - horizontalSpace(95), - Text( - textAlign: TextAlign.center, - 'addHashtag(s)'.tr(), - style: AppTheme.of(context).titleExtraLarge24, + Align( + alignment: Alignment.center, + child: Text( + 'addHashtag(s)'.tr(), + style: AppTheme.of(context).titleExtraLarge24, + ), ), - Spacer(), - IconButton( - icon: Icon( - Icons.close, - size: 24, - color: context.primaryTextColor, + Align( + alignment: Alignment.centerRight, + child: IconButton( + icon: Icon( + Icons.close, + size: 24, + color: context.primaryTextColor, + ), + onPressed: () => Navigator.pop(context), ), - onPressed: () => Navigator.pop(context), ), ], ), @@ -711,8 +813,12 @@ List _buildHashtagChips(BuildContext context, List hashtags, ), decoration: BoxDecoration( color: isSelected - ? context.primaryTextColor - : context.secondaryTextColor, + ? context.isDarkMode + ? AppColors.buttonColor // Yellow in dark mode + : AppColors.tabSelectedColor // Blue in light mode + : context.isDarkMode + ? AppColors.grayNeutral800 // Dark in dark mode + : AppColors.grayCool100, // Light gray in light mode borderRadius: BorderRadius.circular(20), ), child: Row( @@ -722,7 +828,9 @@ List _buildHashtagChips(BuildContext context, List hashtags, AppIcons.hashTagIcon, colorFilter: ColorFilter.mode( isSelected - ? context.primaryBackgroundColor + ? context.isDarkMode + ? AppColors.grayNeutral800 // Dark text on yellow in dark mode + : AppColors.white // White text on blue in light mode : context.primaryTextColor, BlendMode.srcIn), ), @@ -731,7 +839,9 @@ List _buildHashtagChips(BuildContext context, List hashtags, hashtag, style: AppTheme.of(context).textRegularMedium.copyWith( color: isSelected - ? context.primaryBackgroundColor + ? context.isDarkMode + ? AppColors.grayNeutral800 // Dark text on yellow in dark mode + : AppColors.white // White text on blue in light mode : context.primaryTextColor, ), ), diff --git a/lib/features/create_wager/provider/category_provider.dart b/lib/features/create_wager/provider/category_provider.dart index 3bc2221..0fbdad2 100644 --- a/lib/features/create_wager/provider/category_provider.dart +++ b/lib/features/create_wager/provider/category_provider.dart @@ -1,8 +1,38 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../core/network/api_client.dart'; +import '../../../data/datasources/category_remote_datasource.dart'; +import '../../../data/repositories/category_repository_impl.dart'; +import '../../../domain/usecases/get_all_categories.dart'; +import '../state/category_state.dart'; final selectedCategoryProvider = StateProvider((ref) => null); -final categoriesProvider = Provider>((ref) => [ +// Provider to supply the GetAllCategories use case +final getAllCategoriesUseCaseProvider = Provider((ref) { + final apiClient = ref.read(apiClientProvider); + final remoteDatasource = CategoryRemoteDatasourceImpl(apiClient, ref); + final repository = CategoryRepositoryImpl(remoteDatasource); + return GetAllCategories(repository); +}); + +// State notifier to handle category loading state +final categoriesNotifierProvider = + StateNotifierProvider((ref) { + final getAllCategories = ref.read(getAllCategoriesUseCaseProvider); + return CategoriesNotifier(getAllCategories); +}); + +// Provider to expose just the list of category names for UI consumption +final categoriesProvider = Provider>((ref) { + final state = ref.watch(categoriesNotifierProvider); + + if (state is CategoryLoaded) { + return state.categories.categories + .map((category) => category.name) + .toList(); + } else { + // Fallback if categories haven't loaded yet + return [ 'Sports', 'ESports', 'Politics', @@ -11,4 +41,6 @@ final categoriesProvider = Provider>((ref) => [ 'Entertainment', 'Games', 'Others' - ]); + ]; + } +}); diff --git a/lib/features/create_wager/provider/hashtag_provider.dart b/lib/features/create_wager/provider/hashtag_provider.dart index 9491016..30b20af 100644 --- a/lib/features/create_wager/provider/hashtag_provider.dart +++ b/lib/features/create_wager/provider/hashtag_provider.dart @@ -1,5 +1,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:starkwager/features/create_wager/state/hashtag_state.dart'; +import '../../../core/network/api_client.dart'; +import '../../../data/datasources/hashtag_remote_datasource.dart'; +import '../../../data/repositories/hashtag_repository_impl.dart'; +import '../../../domain/usecases/get_all_hashtags.dart'; +import '../state/hashtag_state.dart'; final selectedHashtagsProvider = StateNotifierProvider>((ref) { @@ -11,15 +15,38 @@ final wagerTitleProvider = return WagerTitleNotifier(); }); +// Provider to supply the GetAllHashtags use case +final getAllHashtagsUseCaseProvider = Provider((ref) { + final apiClient = ref.read(apiClientProvider); + final remoteDatasource = HashtagRemoteDatasourceImpl(apiClient, ref); + final repository = HashtagRepositoryImpl(remoteDatasource); + return GetAllHashtags(repository); +}); + +// State notifier to handle hashtag loading state +final hashtagsApiNotifierProvider = + StateNotifierProvider((ref) { + final getAllHashtags = ref.read(getAllHashtagsUseCaseProvider); + return HashtagApiNotifier(getAllHashtags); +}); + +// Provider to expose just the list of hashtag names for UI consumption final hashtagsListProvider = Provider>((ref) { - return [ - 'Bitcoin', - 'STRKBet', - 'BTCto100k', - 'CryptoBetting', - 'BlockchainWager', - 'CryptoTrends', - 'Web3Challenge', - 'DeFiPrediction' - ]; + final state = ref.watch(hashtagsApiNotifierProvider); + + if (state is HashtagApiLoaded) { + return state.hashtags.hashtags.map((hashtag) => hashtag.name).toList(); + } else { + // Fallback if hashtags haven't loaded yet + return [ + 'Bitcoin', + 'STRKBet', + 'BTCto100k', + 'CryptoBetting', + 'BlockchainWager', + 'CryptoTrends', + 'Web3Challenge', + 'DeFiPrediction' + ]; + } }); diff --git a/lib/features/create_wager/state/category_state.dart b/lib/features/create_wager/state/category_state.dart new file mode 100644 index 0000000..beabc1b --- /dev/null +++ b/lib/features/create_wager/state/category_state.dart @@ -0,0 +1,39 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../domain/models/category.dart'; +import '../../../domain/usecases/get_all_categories.dart'; + +// Category States +abstract class CategoryState {} + +class CategoryInitial extends CategoryState {} + +class CategoryLoading extends CategoryState {} + +class CategoryLoaded extends CategoryState { + final CategoriesResponse categories; + CategoryLoaded(this.categories); +} + +class CategoryError extends CategoryState { + final String message; + CategoryError(this.message); +} + +// Category Notifier +class CategoriesNotifier extends StateNotifier { + final GetAllCategories getAllCategories; + + CategoriesNotifier(this.getAllCategories) : super(CategoryInitial()) { + fetchCategories(); + } + + Future fetchCategories() async { + try { + state = CategoryLoading(); + final categories = await getAllCategories(); + state = CategoryLoaded(categories); + } catch (e) { + state = CategoryError(e.toString()); + } + } +} diff --git a/lib/features/create_wager/state/hashtag_state.dart b/lib/features/create_wager/state/hashtag_state.dart index 915e96d..5d43d20 100644 --- a/lib/features/create_wager/state/hashtag_state.dart +++ b/lib/features/create_wager/state/hashtag_state.dart @@ -1,4 +1,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../domain/models/hashtag.dart'; +import '../../../domain/usecases/get_all_hashtags.dart'; class HashtagsNotifier extends StateNotifier> { HashtagsNotifier() : super({}); @@ -23,3 +25,39 @@ class WagerTitleNotifier extends StateNotifier { state = title; } } + +// API Hashtag States +abstract class HashtagApiState {} + +class HashtagApiInitial extends HashtagApiState {} + +class HashtagApiLoading extends HashtagApiState {} + +class HashtagApiLoaded extends HashtagApiState { + final HashtagsResponse hashtags; + HashtagApiLoaded(this.hashtags); +} + +class HashtagApiError extends HashtagApiState { + final String message; + HashtagApiError(this.message); +} + +// Hashtag API Notifier +class HashtagApiNotifier extends StateNotifier { + final GetAllHashtags getAllHashtags; + + HashtagApiNotifier(this.getAllHashtags) : super(HashtagApiInitial()) { + fetchHashtags(); + } + + Future fetchHashtags() async { + try { + state = HashtagApiLoading(); + final hashtags = await getAllHashtags(); + state = HashtagApiLoaded(hashtags); + } catch (e) { + state = HashtagApiError(e.toString()); + } + } +}