From ede59d0477c44e9626b2c3e8f6859b74cc663cbe Mon Sep 17 00:00:00 2001 From: Username1548 Date: Wed, 5 Mar 2025 17:25:07 +0700 Subject: [PATCH] added diet complaints and token interceptor for auth handling --- ios/Runner.xcodeproj/project.pbxproj | 21 +--- ios/Runner/Base.lproj/Main.storyboard | 13 +- lib/core/app_router.dart | 27 +--- lib/core/interceptors/token_interceptor.dart | 31 +++++ lib/core/locator_service.dart | 5 +- lib/core/token_storage.dart | 13 +- .../complaining_bottom_sheet.dart | 8 +- .../presentation/cubit/complaining_cubit.dart | 7 +- .../repositories/complaining_repository.dart | 20 ++- .../services/complaining_service.dart | 7 ++ .../services/complaining_service.g.dart | 23 ++++ .../presentation/widgets/more_options.dart | 1 + .../feed/models/diets_pagination_model.g.dart | 115 +++++++++++------- .../presentation/pages/profile_screen.dart | 1 + 14 files changed, 180 insertions(+), 112 deletions(-) create mode 100644 lib/core/interceptors/token_interceptor.dart diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index a87c031c..beb32d21 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -364,11 +364,9 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 16; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = VL68C36HYS; + DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -389,7 +387,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Eatvio distribution profile"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -516,11 +513,9 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 16; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = VL68C36HYS; + DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -541,7 +536,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Eatvio distribution profile"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -556,11 +550,9 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CODE_SIGN_STYLE = Manual; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 16; - DEVELOPMENT_TEAM = ""; - "DEVELOPMENT_TEAM[sdk=iphoneos*]" = VL68C36HYS; + DEVELOPMENT_TEAM = VL68C36HYS; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -581,7 +573,6 @@ PRODUCT_BUNDLE_IDENTIFIER = com.eatvioapp.app; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "Eatvio distribution profile"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard index f3c28516..5817610d 100644 --- a/ios/Runner/Base.lproj/Main.storyboard +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -1,8 +1,10 @@ - - + + + - + + @@ -14,13 +16,14 @@ - + - + + diff --git a/lib/core/app_router.dart b/lib/core/app_router.dart index bf988f64..78d7d25f 100644 --- a/lib/core/app_router.dart +++ b/lib/core/app_router.dart @@ -1,10 +1,5 @@ -import 'dart:developer'; - -import 'package:dio/dio.dart'; import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/core/locator_service.dart'; -import 'package:eatvio_app/core/token_storage.dart'; -import 'package:eatvio_app/core/websockets/ws_service.dart'; import 'package:eatvio_app/features/auth/presentation/pages/auth_screen.dart'; import 'package:eatvio_app/features/blog_post/presentation/pages/blog_post_screen.dart'; import 'package:eatvio_app/features/blog_post_create/presentation/pages/blog_post_create_screen.dart'; @@ -65,30 +60,12 @@ class AppRouter { static GlobalKey navigatorKey = GlobalKey(); static GoRouter appRouter = GoRouter( - initialLocation: '/auth-screen', + initialLocation: '/my-profile-screen', navigatorKey: navigatorKey, routes: [ GoRoute( path: '/auth-screen', - name: 'authScreen', - redirect: (context, state) async { - // TokenStorage.removeTokenInStorage(); - // await TokenStorage.saveTokenInStorage('Bearer 3|laravel_sanctum_zoubsO9NPXAqgLZEdHV9R3XbuDUHJJMJyXcd77mhbb649f04'); - final token = await TokenStorage.getTokenFromStorage(); - if (token != null) { - try { - await sl().getWebsocket(); - return '/my-profile-screen'; - } on DioException catch (e, st) { - if (e.response?.statusCode == 401 && e.response?.statusMessage == 'Unauthenticated.') { - TokenStorage.removeTokenInStorage(); - } - log(e.toString(), stackTrace: st); - } - } - - return null; - }, + name: 'authScreen', builder: (context, state) => const AuthScreen(), routes: [ GoRoute( diff --git a/lib/core/interceptors/token_interceptor.dart b/lib/core/interceptors/token_interceptor.dart new file mode 100644 index 00000000..b2cd9207 --- /dev/null +++ b/lib/core/interceptors/token_interceptor.dart @@ -0,0 +1,31 @@ +import 'package:dio/dio.dart'; +import 'package:eatvio_app/core/app_router.dart'; +import 'package:eatvio_app/core/token_storage.dart'; +import 'package:go_router/go_router.dart'; + +class TokenInterceptor extends Interceptor { + @override + Future onRequest( + RequestOptions options, + RequestInterceptorHandler handler, + ) async { + final token = await TokenStorage.getTokenFromStorage(); + if (token != null && token.isNotEmpty) { + options.headers['Authorization'] = 'Bearer $token'; + } + + super.onRequest(options, handler); + } + + @override + Future onError( + DioException err, + ErrorInterceptorHandler handler, + ) async { + if (err.response?.statusCode == 401) { + await TokenStorage.removeTokenInStorage(); + AppRouter.navigatorKey.currentContext?.go('/auth-screen'); + } + super.onError(err, handler); + } +} diff --git a/lib/core/locator_service.dart b/lib/core/locator_service.dart index f5caadfb..7e79faf4 100644 --- a/lib/core/locator_service.dart +++ b/lib/core/locator_service.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/core/const.dart'; +import 'package:eatvio_app/core/interceptors/token_interceptor.dart'; import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/core/websockets/ws_service.dart'; import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit.dart'; @@ -166,13 +167,13 @@ Future init() async { sl.registerLazySingleton( () => Dio( BaseOptions( - baseUrl: baseUrl, + baseUrl: baseUrl, headers: { 'accept': 'application/json', 'content-type': 'application/json', }, ), - ), + )..interceptors.add(TokenInterceptor()), ); sl.registerLazySingleton(() async => await FlutterSecureStorage()); } diff --git a/lib/core/token_storage.dart b/lib/core/token_storage.dart index 017931c4..5b28ffc7 100644 --- a/lib/core/token_storage.dart +++ b/lib/core/token_storage.dart @@ -1,18 +1,12 @@ -import 'package:dio/dio.dart'; -import 'package:eatvio_app/core/locator_service.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; abstract class TokenStorage { - static _addTokenToDioHeader(String token) { - sl().options.headers['Authorization'] = 'Bearer $token'; - } static Future haveTokenInStorage() async { try { if (await FlutterSecureStorage().read(key: 'token') == null) { return false; - } else { - _addTokenToDioHeader((await FlutterSecureStorage().read(key: 'token'))!); + } else { return true; } } catch (e) { @@ -23,9 +17,8 @@ abstract class TokenStorage { static Future getTokenFromStorage() async => await FlutterSecureStorage().read(key: 'token'); static saveTokenInStorage(String token) async { - await FlutterSecureStorage().write(key: 'token', value: token); - _addTokenToDioHeader(token); + await FlutterSecureStorage().write(key: 'token', value: token); } - static removeTokenInStorage() async => (await FlutterSecureStorage().delete(key: 'token')); + static Future removeTokenInStorage() async => (await FlutterSecureStorage().delete(key: 'token')); } diff --git a/lib/features/complaining/presentation/complaining_bottom_sheet.dart b/lib/features/complaining/presentation/complaining_bottom_sheet.dart index 68770acc..eff3b154 100644 --- a/lib/features/complaining/presentation/complaining_bottom_sheet.dart +++ b/lib/features/complaining/presentation/complaining_bottom_sheet.dart @@ -319,9 +319,11 @@ class _ViolenceAndHostilityState extends State<_ViolenceAndHostility> { }); }, ), - Text( - element.$2, - style: TextStyles.textLSemibold.copyWith(color: appTheme.black), + Expanded( + child: Text( + element.$2, + style: TextStyles.textLSemibold.copyWith(color: appTheme.black), + ), ), ], ); diff --git a/lib/features/complaining/presentation/cubit/complaining_cubit.dart b/lib/features/complaining/presentation/cubit/complaining_cubit.dart index 61843c04..0a2827f3 100644 --- a/lib/features/complaining/presentation/cubit/complaining_cubit.dart +++ b/lib/features/complaining/presentation/cubit/complaining_cubit.dart @@ -43,8 +43,11 @@ class ComplainingCubit extends Cubit { if (category != null && contentType != null) { switch (contentType) { case ComplainedContent.diet: - emit(state.copyWith(wasSentSuccessfully: true)); - return; + if (id != null) { + await repository.sendDietComplain(id: id, category: category.getValue(), body: state.complainInfo); + emit(state.copyWith(wasSentSuccessfully: true)); + return; + } case ComplainedContent.plan: if (slug != null) { await repository.sendPlanComplain(slug: slug, category: category.getValue(), body: state.complainInfo); diff --git a/lib/features/complaining/repositories/complaining_repository.dart b/lib/features/complaining/repositories/complaining_repository.dart index af3a10b6..1b1a5d14 100644 --- a/lib/features/complaining/repositories/complaining_repository.dart +++ b/lib/features/complaining/repositories/complaining_repository.dart @@ -11,7 +11,7 @@ class ComplainingRepository { required ComplainModel body, }) async { try { - service.sendPlanComplain(slug: slug, category: category, body: body); + await service.sendPlanComplain(slug: slug, category: category, body: body); } catch (e) { rethrow; } @@ -23,7 +23,7 @@ class ComplainingRepository { required ComplainModel body, }) async { try { - service.sendRecipeComplain(slug: slug, category: category, body: body); + await service.sendRecipeComplain(slug: slug, category: category, body: body); } catch (e) { rethrow; } @@ -35,7 +35,7 @@ class ComplainingRepository { required ComplainModel body, }) async { try { - service.sendBlogPostsComplain(id: id, category: category, body: body); + await service.sendBlogPostsComplain(id: id, category: category, body: body); } catch (e) { rethrow; } @@ -47,7 +47,19 @@ class ComplainingRepository { required ComplainModel body, }) async { try { - service.sendProfileComplain(slug: slug, category: category, body: body); + await service.sendProfileComplain(slug: slug, category: category, body: body); + } catch (e) { + rethrow; + } + } + + Future sendDietComplain({ + required int id, + required String category, + required ComplainModel body, + }) async { + try { + await service.sendDietComplain(id: id, category: category, body: body); } catch (e) { rethrow; } diff --git a/lib/features/complaining/services/complaining_service.dart b/lib/features/complaining/services/complaining_service.dart index cf214149..242545ad 100644 --- a/lib/features/complaining/services/complaining_service.dart +++ b/lib/features/complaining/services/complaining_service.dart @@ -35,5 +35,12 @@ abstract class ComplainingService { @Path() required String category, @Body() required ComplainModel body, }); + + @POST('/api/diets/{id}/complain/{category}') + Future sendDietComplain({ + @Path() required int id, + @Path() required String category, + @Body() required ComplainModel body, + }); } diff --git a/lib/features/complaining/services/complaining_service.g.dart b/lib/features/complaining/services/complaining_service.g.dart index 8b3d6595..de76a860 100644 --- a/lib/features/complaining/services/complaining_service.g.dart +++ b/lib/features/complaining/services/complaining_service.g.dart @@ -109,6 +109,29 @@ class _ComplainingService implements ComplainingService { await _dio.fetch(_options); } + @override + Future sendDietComplain({ + required int id, + required String category, + required ComplainModel body, + }) async { + final _extra = {}; + final queryParameters = {}; + final _headers = {}; + final _data = body; + final _options = _setStreamType( + Options(method: 'POST', headers: _headers, extra: _extra) + .compose( + _dio.options, + '/api/diets/${id}/complain/${category}', + queryParameters: queryParameters, + data: _data, + ) + .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)), + ); + await _dio.fetch(_options); + } + RequestOptions _setStreamType(RequestOptions requestOptions) { if (T != dynamic && !(requestOptions.responseType == ResponseType.bytes || diff --git a/lib/features/diets/presentation/widgets/more_options.dart b/lib/features/diets/presentation/widgets/more_options.dart index 3347144c..3d2d54d6 100644 --- a/lib/features/diets/presentation/widgets/more_options.dart +++ b/lib/features/diets/presentation/widgets/more_options.dart @@ -90,6 +90,7 @@ class MoreOptions extends StatelessWidget { if (complainedContentType != null) { showModalBottomSheet( context: context, + isScrollControlled: true, builder: (context) => ComplainingBottomSheet( complainedContentType: complainedContentType!, id: id, diff --git a/lib/features/feed/models/diets_pagination_model.g.dart b/lib/features/feed/models/diets_pagination_model.g.dart index 3832c00d..3e7099a5 100644 --- a/lib/features/feed/models/diets_pagination_model.g.dart +++ b/lib/features/feed/models/diets_pagination_model.g.dart @@ -6,49 +6,57 @@ part of 'diets_pagination_model.dart'; // JsonSerializableGenerator // ************************************************************************** -_$DietsPaginationImpl _$$DietsPaginationImplFromJson(Map json) => _$DietsPaginationImpl( - data: (json['data'] as List).map((e) => Diet.fromJson(e as Map)).toList(), +_$DietsPaginationImpl _$$DietsPaginationImplFromJson( + Map json) => + _$DietsPaginationImpl( + data: (json['data'] as List) + .map((e) => Diet.fromJson(e as Map)) + .toList(), links: Links.fromJson(json['links'] as Map), meta: Meta.fromJson(json['meta'] as Map), ); -Map _$$DietsPaginationImplToJson(_$DietsPaginationImpl instance) => { +Map _$$DietsPaginationImplToJson( + _$DietsPaginationImpl instance) => + { 'data': instance.data, 'links': instance.links, 'meta': instance.meta, }; -_$DietImpl _$$DietImplFromJson(Map json) { - return _$DietImpl( - id: (json['id'] as num).toInt(), - name: json['name'] as String, - image: json['image'] as String, - duration: (json['duration'] as num).toInt(), - state: $enumDecode(_$DietStateEnumMap, json['state']), - type: DietType.fromJson(json['type'] as Map), - details: json['details'] as String, - dailySchedule: (json['daily_schedule'] as Map).map( - (k, e) => MapEntry( - int.parse(k), - (e as Map).map( - (k, e) => MapEntry( - int.parse(k), - (e as Map).map( - (k, e) => MapEntry(int.parse(k), DailySchedule.fromJson(e as Map)), - )), - )), - ), - author: Author.fromJson(json['author'] as Map), - totalLikes: (json['total_likes'] as num).toInt(), - totalViews: (json['total_views'] as num).toInt(), - totalComments: (json['total_comments'] as num).toInt(), - wasLikedByMe: json['was_liked_by_me'] as bool, - isFavorite: json['is_favorite'] as bool, - dailyNutrition: (json['daily_nutrition'] as List).map((e) => DailyNutrition.fromJson(e as Map)).toList(), - ); -} +_$DietImpl _$$DietImplFromJson(Map json) => _$DietImpl( + id: (json['id'] as num).toInt(), + name: json['name'] as String, + image: json['image'] as String, + duration: (json['duration'] as num).toInt(), + state: $enumDecode(_$DietStateEnumMap, json['state']), + type: DietType.fromJson(json['type'] as Map), + details: json['details'] as String, + dailySchedule: (json['daily_schedule'] as Map).map( + (k, e) => MapEntry( + int.parse(k), + (e as Map).map( + (k, e) => MapEntry( + int.parse(k), + (e as Map).map( + (k, e) => MapEntry(int.parse(k), + DailySchedule.fromJson(e as Map)), + )), + )), + ), + author: Author.fromJson(json['author'] as Map), + totalLikes: (json['total_likes'] as num).toInt(), + totalViews: (json['total_views'] as num).toInt(), + totalComments: (json['total_comments'] as num).toInt(), + wasLikedByMe: json['was_liked_by_me'] as bool, + isFavorite: json['is_favorite'] as bool, + dailyNutrition: (json['daily_nutrition'] as List) + .map((e) => DailyNutrition.fromJson(e as Map)) + .toList(), + ); -Map _$$DietImplToJson(_$DietImpl instance) => { +Map _$$DietImplToJson(_$DietImpl instance) => + { 'id': instance.id, 'name': instance.name, 'image': instance.image, @@ -56,8 +64,10 @@ Map _$$DietImplToJson(_$DietImpl instance) => 'state': _$DietStateEnumMap[instance.state]!, 'type': instance.type, 'details': instance.details, - 'daily_schedule': - instance.dailySchedule.map((k, e) => MapEntry(k.toString(), e.map((k, e) => MapEntry(k.toString(), e.map((k, e) => MapEntry(k.toString(), e)))))), + 'daily_schedule': instance.dailySchedule.map((k, e) => MapEntry( + k.toString(), + e.map((k, e) => MapEntry( + k.toString(), e.map((k, e) => MapEntry(k.toString(), e)))))), 'author': instance.author, 'total_likes': instance.totalLikes, 'total_views': instance.totalViews, @@ -73,11 +83,13 @@ const _$DietStateEnumMap = { DietState.personal: 'personal', }; -_$CreatedDietImpl _$$CreatedDietImplFromJson(Map json) => _$CreatedDietImpl( +_$CreatedDietImpl _$$CreatedDietImplFromJson(Map json) => + _$CreatedDietImpl( id: (json['id'] as num).toInt(), ); -Map _$$CreatedDietImplToJson(_$CreatedDietImpl instance) => { +Map _$$CreatedDietImplToJson(_$CreatedDietImpl instance) => + { 'id': instance.id, }; @@ -90,7 +102,8 @@ _$AuthorImpl _$$AuthorImplFromJson(Map json) => _$AuthorImpl( authorPlaceInRating: (json['author_place_in_rating'] as num?)?.toInt(), ); -Map _$$AuthorImplToJson(_$AuthorImpl instance) => { +Map _$$AuthorImplToJson(_$AuthorImpl instance) => + { 'author_slug': instance.authorSlug, 'author_name': instance.authorName, 'author_profile_icon': instance.authorProfileIcon, @@ -99,18 +112,23 @@ Map _$$AuthorImplToJson(_$AuthorImpl instance) => json) => _$DailyScheduleImpl( +_$DailyScheduleImpl _$$DailyScheduleImplFromJson(Map json) => + _$DailyScheduleImpl( name: json['name'] as String, portion: $enumDecode(_$PortionEnumMap, json['portion']), quantity: _parseQuantity(json['quantity']), - dailyNutrition: json['nutrition'] == null ? null : DailyNutrition.fromJson(json['nutrition'] as Map), + dailyNutrition: json['nutrition'] == null + ? null + : DailyNutrition.fromJson(json['nutrition'] as Map), ); -Map _$$DailyScheduleImplToJson(_$DailyScheduleImpl instance) => { +Map _$$DailyScheduleImplToJson(_$DailyScheduleImpl instance) => + { 'name': instance.name, 'portion': _$PortionEnumMap[instance.portion]!, 'quantity': instance.quantity, - if (instance.productNutritionalValueId case final value?) 'product_nutritional_value_id': value, + if (instance.productNutritionalValueId case final value?) + 'product_nutritional_value_id': value, }; const _$PortionEnumMap = { @@ -119,28 +137,33 @@ const _$PortionEnumMap = { Portion.PCS: 'pcs', }; -_$DietTypeImpl _$$DietTypeImplFromJson(Map json) => _$DietTypeImpl( +_$DietTypeImpl _$$DietTypeImplFromJson(Map json) => + _$DietTypeImpl( light: json['light'] as bool?, veg: json['veg'] as bool?, carb: json['carb'] as bool?, meat: json['meat'] as bool?, ); -Map _$$DietTypeImplToJson(_$DietTypeImpl instance) => { +Map _$$DietTypeImplToJson(_$DietTypeImpl instance) => + { 'light': instance.light, 'veg': instance.veg, 'carb': instance.carb, 'meat': instance.meat, }; -_$DailyNutritionImpl _$$DailyNutritionImplFromJson(Map json) => _$DailyNutritionImpl( +_$DailyNutritionImpl _$$DailyNutritionImplFromJson(Map json) => + _$DailyNutritionImpl( energy: (json['energy'] as num).toDouble(), proteins: (json['proteins'] as num).toDouble(), fats: (json['fats'] as num).toDouble(), carbohydrates: (json['carbohydrates'] as num).toDouble(), ); -Map _$$DailyNutritionImplToJson(_$DailyNutritionImpl instance) => { +Map _$$DailyNutritionImplToJson( + _$DailyNutritionImpl instance) => + { 'energy': instance.energy, 'proteins': instance.proteins, 'fats': instance.fats, diff --git a/lib/features/profile/presentation/pages/profile_screen.dart b/lib/features/profile/presentation/pages/profile_screen.dart index 7aba6e23..07eb536a 100644 --- a/lib/features/profile/presentation/pages/profile_screen.dart +++ b/lib/features/profile/presentation/pages/profile_screen.dart @@ -1021,6 +1021,7 @@ class _ProfileScreenState extends State { } else { showModalBottomSheet( context: context, + isScrollControlled: true, builder: (context) => ComplainingBottomSheet( complainedContentType: ComplainedContent.profile, slug: widget.author?.authorSlug,