Files
eatvio_app/lib/features/complaining/presentation/complaining_bottom_sheet.dart

609 lines
19 KiB
Dart

import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/enums/complained_content.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/core/widgets/loader.dart';
import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_cubit.dart';
import 'package:eatvio_app/features/complaining/presentation/cubit/complaining_state.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import 'package:collection/collection.dart';
class ComplainingBottomSheet extends StatefulWidget {
const ComplainingBottomSheet({super.key, required this.complainedContentType, this.slug, this.id});
final ComplainedContent complainedContentType;
final String? slug;
final int? id;
@override
State<ComplainingBottomSheet> createState() => _ComplainingBottomSheetState();
}
class _ComplainingBottomSheetState extends State<ComplainingBottomSheet> {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => sl<ComplainingCubit>()..setContentTypeAndIdentifier(contentType: widget.complainedContentType, slug: widget.slug, id: widget.id),
child: Container(
padding: const EdgeInsets.only(top: 8, bottom: 24, left: 16, right: 16),
decoration: const BoxDecoration(
borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
),
child: BlocBuilder<ComplainingCubit, ComplainingState>(
builder: (context, state) {
final category = state.category;
if (state.wasSentSuccessfully == true) {
return const _ComplaintSentWidget();
} else if (state.wasSentSuccessfully == false) {
return const _ComplaintFailedWidget();
}
if (category != null) {
return switch (category) {
ComplainType.spam => const _SpamWidget(),
ComplainType.lawViolation => const _LawViolationWidget(),
ComplainType.deception => const _DeceptionWidget(),
ComplainType.violenceDndHostility => const _ViolenceAndHostility(),
ComplainType.explicitImage => const _ExplicitImageWidget(),
};
}
return const _CategoryWidget();
},
),
),
);
}
}
class _CategoryWidget extends StatelessWidget {
const _CategoryWidget();
final categories = ComplainType.values;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.complaint,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 8),
Text(
s.complaint_question,
textAlign: TextAlign.center,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
const SizedBox(height: 24),
...List.generate(
categories.length,
(index) => Column(
children: [
_CategoryButton(
category: categories[index],
onTap: () {
context.read<ComplainingCubit>().setCategory(categories[index]);
},
),
if (index != categories.length - 1) const SizedBox(height: 20),
],
),
),
const SizedBox(height: 24),
],
);
}
}
class _SpamWidget extends StatelessWidget {
const _SpamWidget();
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.spam,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 8),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.7,
child: Text(
s.spam_description,
textAlign: TextAlign.center,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
const SizedBox(height: 24),
const _ComplainButton(),
],
);
}
}
class _ExplicitImageWidget extends StatelessWidget {
const _ExplicitImageWidget();
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.explicitImages,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 8),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.7,
child: Text(
s.explicitImagesDescription,
textAlign: TextAlign.center,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
const SizedBox(height: 24),
const _ComplainButton(),
],
);
}
}
class _LawViolationWidget extends StatefulWidget {
const _LawViolationWidget();
@override
State<_LawViolationWidget> createState() => _LawViolationWidgetState();
}
class _LawViolationWidgetState extends State<_LawViolationWidget> {
final List<(bool, String)> tags = [];
@override
void didChangeDependencies() {
super.didChangeDependencies();
final s = S.of(context);
if (tags.isEmpty) {
tags.addAll([
(false, s.alcohol),
(false, s.drugs),
(false, s.nicotine),
(false, s.weapon),
(false, s.fake_documents),
(false, s.prostitution),
(false, s.other),
]);
}
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<ComplainingCubit, ComplainingState>(
builder: (context, state) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.lawViolation,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 8),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.7,
child: Text(
s.what_do_you_want_to_complaint_about,
textAlign: TextAlign.center,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
const SizedBox(height: 24),
...tags.mapIndexed((index, element) {
return Row(
children: [
Checkbox(
activeColor: appTheme.blueA400,
side: BorderSide(width: 2, color: tags[index].$1 ? appTheme.blueA400 : appTheme.blueGray),
value: element.$1,
onChanged: (isChecked) {
setState(() {
if (isChecked == null) return;
if (index == tags.length - 1 && !isChecked) {
context.read<ComplainingCubit>().toggleDescription();
}
tags[index] = (isChecked, element.$2);
final activeTags = tags.where((elem) => elem.$1).map((e) => e.$2).toList();
context.read<ComplainingCubit>().setTags(activeTags);
});
},
),
Text(
element.$2,
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
),
],
);
}),
if (tags.last.$1) ...[
const SizedBox(height: 12),
CustomTextFormField(
hintText: s.type_here_your_complaint,
hasError: (state.complainInfo.description?.length ?? 10) < 10,
onChanged: (value) {
context.read<ComplainingCubit>().setDescription(value);
},
),
if ((state.complainInfo.description?.length ?? 10) < 10) ...[
const SizedBox(height: 4),
const ValidationErrorWidget(error: 'Минимум 10 символов'),
]
],
const SizedBox(height: 20),
_ComplainButton(
isDisabled: (state.complainInfo.description?.length ?? 0) < 10,
),
],
);
},
);
}
}
class _ViolenceAndHostility extends StatefulWidget {
const _ViolenceAndHostility();
@override
State<_ViolenceAndHostility> createState() => _ViolenceAndHostilityState();
}
class _ViolenceAndHostilityState extends State<_ViolenceAndHostility> {
final List<(bool, String)> tags = [];
@override
void didChangeDependencies() {
super.didChangeDependencies();
final s = S.of(context);
if (tags.isEmpty) {
tags.addAll([
(false, s.violence_against_people_or_animal),
(false, s.hatred),
(false, s.suicide),
(false, s.hatred_description),
(false, s.extremism),
(false, s.calls_for_bullying),
]);
}
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.lawViolation,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 8),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.7,
child: Text(
s.what_do_you_want_to_complaint_about,
textAlign: TextAlign.center,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
const SizedBox(height: 24),
...tags.mapIndexed((index, element) {
return Row(
children: [
Checkbox(
activeColor: appTheme.blueA400,
side: BorderSide(width: 2, color: tags[index].$1 ? appTheme.blueA400 : appTheme.blueGray),
value: element.$1,
onChanged: (isChecked) {
setState(() {
if (isChecked == null) return;
tags[index] = (isChecked, element.$2);
final activeTags = tags.where((elem) => elem.$1).map((e) => e.$2).toList();
context.read<ComplainingCubit>().setTags(activeTags);
});
},
),
Expanded(
child: Text(
element.$2,
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
),
),
],
);
}),
const SizedBox(height: 20),
const _ComplainButton(),
],
);
}
}
class _DeceptionWidget extends StatefulWidget {
const _DeceptionWidget();
@override
State<_DeceptionWidget> createState() => _DeceptionWidgetState();
}
class _DeceptionWidgetState extends State<_DeceptionWidget> {
final List<(bool, String)> tags = [];
final List<String> descriptions = [];
@override
void didChangeDependencies() {
super.didChangeDependencies();
final s = S.of(context);
if (tags.isEmpty && descriptions.isEmpty) {
tags.addAll([
(false, s.misrepresentation),
(false, s.fraud),
]);
descriptions.addAll([
s.misrepresentation_description,
s.fraud_description,
]);
}
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.lawViolation,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 8),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.7,
child: Text(
s.what_do_you_want_to_complaint_about,
textAlign: TextAlign.center,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
const SizedBox(height: 24),
...tags.mapIndexed((index, element) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Checkbox(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
activeColor: appTheme.blueA400,
side: BorderSide(width: 2, color: tags[index].$1 ? appTheme.blueA400 : appTheme.blueGray),
value: element.$1,
onChanged: (isChecked) {
setState(() {
if (isChecked == null) return;
tags[index] = (isChecked, element.$2);
final activeTags = tags.where((elem) => elem.$1).map((e) => e.$2).toList();
context.read<ComplainingCubit>().setTags(activeTags);
});
},
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 8),
Text(
element.$2,
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.7,
child: Text(
descriptions[index],
style: TextStyles.textSSemibold.copyWith(color: appTheme.lightGray600),
),
),
],
),
],
);
}),
const SizedBox(height: 20),
const _ComplainButton(),
],
);
}
}
class _ComplaintSentWidget extends StatelessWidget {
const _ComplaintSentWidget();
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.complaint_sent,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 8),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.7,
child: Text(
s.complaint_sent_description,
textAlign: TextAlign.center,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
const SizedBox(height: 24),
CustomOutlinedButton(
text: s.ok,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(
side: const WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
),
onPressed: () {
AppRouter.navigatorKey.currentContext?.pop();
},
),
],
);
}
}
class _ComplaintFailedWidget extends StatelessWidget {
const _ComplaintFailedWidget();
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const BottomSheetHandlerBar(),
const SizedBox(height: 20),
Text(
s.could_not_send_complain,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
const SizedBox(height: 8),
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.7,
child: Text(
s.please_try_again_later,
textAlign: TextAlign.center,
style: TextStyles.textMMedium.copyWith(color: appTheme.black),
),
),
const SizedBox(height: 24),
CustomOutlinedButton(
text: s.ok,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(
side: const WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
),
onPressed: () {
AppRouter.navigatorKey.currentContext?.pop();
},
),
],
);
}
}
class _ComplainButton extends StatelessWidget {
const _ComplainButton({this.isDisabled = false});
final bool isDisabled;
@override
Widget build(BuildContext context) {
final s = S.of(context);
return BlocBuilder<ComplainingCubit, ComplainingState>(
builder: (context, state) {
if (state.isLoading) {
return const Loader();
}
return CustomOutlinedButton(
text: s.send_complain,
height: 52,
buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700),
buttonStyle: ButtonStyle(
side: const WidgetStatePropertyAll(BorderSide.none),
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
),
onPressed: () async {
if (isDisabled) {
context.read<ComplainingCubit>().setDescription('');
return;
}
await context.read<ComplainingCubit>().complain();
},
);
},
);
}
}
class _CategoryButton extends StatelessWidget {
const _CategoryButton({required this.category, required this.onTap});
final ComplainType category;
final void Function() onTap;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Ink(
child: Row(
children: [
Text(
category.getTranslatedText(),
style: TextStyles.textLSemibold.copyWith(color: appTheme.black),
),
const Spacer(),
SvgPicture.asset('assets/icons/right_arrow.svg'),
],
),
),
);
}
}
enum ComplainType {
spam,
lawViolation,
deception,
violenceDndHostility,
explicitImage;
String getTranslatedText() => switch (this) {
ComplainType.spam => S.current.spam,
ComplainType.lawViolation => S.current.lawViolation,
ComplainType.deception => S.current.deception,
ComplainType.violenceDndHostility => S.current.violenceAndHostility,
ComplainType.explicitImage => S.current.explicitImage,
};
String getValue() => switch (this) {
ComplainType.spam => 'spam',
ComplainType.lawViolation => 'legalViolation',
ComplainType.deception => 'fraud',
ComplainType.violenceDndHostility => 'violenceAndHatred',
ComplainType.explicitImage => 'explicitImage',
};
}