Houshan-Basa/lib/ui/screens/chat/chat_page.dart

2261 lines
127 KiB
Dart

// ignore_for_file: deprecated_member_use_from_same_package, use_build_context_synchronously
import 'dart:math';
import 'package:cross_file/cross_file.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:go_router/go_router.dart';
import 'package:hoshan/core/gen/assets.gen.dart';
import 'package:hoshan/core/gen/my_flutter_app_icons.dart';
import 'package:hoshan/core/routes/route_generator.dart';
import 'package:hoshan/core/services/file_manager/download_file_services.dart';
import 'package:hoshan/core/services/file_manager/pick_file_services.dart';
import 'package:hoshan/core/utils/date_time.dart';
import 'package:hoshan/core/utils/strings.dart';
import 'package:hoshan/data/model/ai/chats_history_model.dart';
import 'package:hoshan/data/model/ai/credit_model.dart';
import 'package:hoshan/data/model/ai/messages_model.dart';
import 'package:hoshan/data/model/ai/send_message_model.dart';
import 'package:hoshan/data/model/chat_args.dart';
import 'package:hoshan/data/model/empty_states_enum.dart';
import 'package:hoshan/ui/screens/chat/bloc/messages_bloc.dart';
import 'package:hoshan/ui/screens/chat/bloc/related_questions_bloc.dart';
import 'package:hoshan/ui/screens/chat/cubit/receive_message_cubit.dart';
import 'package:hoshan/ui/screens/library/bloc/chats_history_bloc.dart';
import 'package:hoshan/ui/screens/library/library_screen.dart';
import 'package:hoshan/ui/screens/splash/cubit/user_info_cubit.dart';
import 'package:hoshan/ui/theme/colors.dart';
import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart';
import 'package:hoshan/ui/theme/responsive.dart';
import 'package:hoshan/ui/theme/text.dart';
import 'package:hoshan/ui/widgets/components/animations/animated_visibility.dart';
import 'package:hoshan/ui/widgets/components/audio/player.dart';
import 'package:hoshan/ui/widgets/components/audio/recorder.dart';
import 'package:hoshan/ui/widgets/components/button/circle_icon_btn.dart';
import 'package:hoshan/ui/screens/chat/cubit/like_message_cubit.dart';
import 'package:hoshan/ui/widgets/components/dialog/bottom_sheets.dart';
import 'package:hoshan/ui/widgets/components/dialog/dialog_handler.dart';
import 'package:hoshan/ui/widgets/components/dropdown/hint_tooltip.dart';
import 'package:hoshan/ui/widgets/components/dropdown/more_popup_menu.dart';
import 'package:hoshan/ui/widgets/components/image/custome_image.dart';
import 'package:hoshan/ui/widgets/components/image/network_image.dart';
import 'package:hoshan/ui/widgets/components/snackbar/snackbar_manager.dart';
import 'package:hoshan/ui/widgets/components/text/default_markdown_text.dart';
import 'package:hoshan/ui/widgets/components/video/video_thumbnail.dart';
import 'package:hoshan/ui/widgets/sections/empty/empty_states.dart';
import 'package:hoshan/ui/widgets/sections/loading/chat_screen_placeholder.dart';
import 'package:hoshan/ui/widgets/sections/loading/default_placeholder.dart';
class ChatPage extends StatefulWidget {
final ChatArgs chatArgs;
const ChatPage({super.key, required this.chatArgs});
@override
State<ChatPage> createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
late int? chatId = widget.chatArgs.chatId;
late final bot = widget.chatArgs.bot;
late final ValueNotifier<bool> visibleAttach =
ValueNotifier(widget.chatArgs.bot.attachment == 3);
final ValueNotifier<bool> showRecorder = ValueNotifier(false);
final ValueNotifier<bool> isGhost = ValueNotifier(false);
final ValueNotifier<bool> recording = ValueNotifier(false);
final ValueNotifier<bool> refreshQuestions = ValueNotifier(true);
final ValueNotifier<bool> webSearch = ValueNotifier(false);
final ValueNotifier<bool> showInfo = ValueNotifier(false);
final ValueNotifier<XFile?> selectedFile = ValueNotifier(null);
final TextEditingController messageText = TextEditingController();
ValueNotifier<int?> maxLines = ValueNotifier(5);
void sendRequest(
{required final String? message,
final bool retry = false,
final XFile? file,
final bool withOutNewMessage = false}) {
final creditState = CreditModel(
credit: UserInfoCubit.userInfoModel.credit ?? 0,
freeCredit: UserInfoCubit.userInfoModel.freeCredit ?? 0);
int credit = (creditState.freeCredit ?? 0) + (creditState.credit ?? 0);
if (credit < widget.chatArgs.bot.cost!) {
DialogHandler(context: context).showUpgradeCredit();
messageText.text = message ?? '';
return;
}
if (!withOutNewMessage) {
context.read<MessagesBloc>().add(AddMessage(
message: Messages(
role: 'human',
id: 'hero',
content: [
if (message != null && message.isNotEmpty)
Content(type: 'text', text: message)
],
query: message,
file: file,
retry: retry)));
}
context.read<ReceiveMessageCubit>().execute(
request: SendMessageModel(
botId: bot.id,
file: file,
id: chatId,
messageId: 'hero',
query: message,
ghost: isGhost.value,
tool: bot.tool,
webSearch: webSearch.value,
retry: retry));
if (widget.chatArgs.bot.attachment != 3 && refreshQuestions.value) {
context.read<RelatedQuestionsBloc>().add(ClearAllRelatedQuestions());
}
selectedFile.value = null;
showRecorder.value = false;
messageText.clear();
}
void _popUpMenu(
Messages message, GlobalKey<State<StatefulWidget>> containerKey) {
String copyText = '';
List<String> urls = [];
if (message.content != null && message.content!.isNotEmpty) {
for (var content in message.content!) {
if (content.imageUrl != null) {
if (content.imageUrl!.url != null) {
urls.add(content.imageUrl!.url!);
}
copyText += content.imageUrl!.query ?? '';
copyText += ' ';
}
if (content.audioUrl != null) {
if (content.audioUrl!.url != null) {
urls.add(content.audioUrl!.url!);
}
copyText += content.audioUrl!.query ?? '';
copyText += ' ';
}
if (content.pdfUrl != null) {
if (content.pdfUrl!.url != null) {
urls.add(content.pdfUrl!.url!);
}
copyText += content.pdfUrl!.query ?? '';
copyText += ' ';
}
if (content.pdfUrl == null &&
content.imageUrl == null &&
content.audioUrl == null) {
copyText += content.text ?? '';
copyText += ' ';
}
}
}
final items = [
PopUpMenuItemModel(
popupMenuItem: PopupMenuItem(
value: 0,
child: MorePopupMenuHandler.morePopUpItem(
color: message.fromBot!
? Theme.of(context).colorScheme.primary
: Colors.white,
icon: Assets.icon.outline.trash,
title: 'حذف')),
click: () {
try {
context
.read<MessagesBloc>()
.add(DeleteMessage(chatId: chatId!, message: message));
} catch (e) {
if (kDebugMode) {
print("Error when delete message: $e");
}
}
},
),
if ((bot.deleted != null && !bot.deleted!))
if (!message.fromBot!)
PopUpMenuItemModel(
popupMenuItem: PopupMenuItem(
value: 1,
child: MorePopupMenuHandler.morePopUpItem(
color: message.fromBot!
? Theme.of(context).colorScheme.primary
: Colors.white,
icon: Assets.icon.outline.bitcoinRefresh,
title: 'دوباره بپرس')),
click: () {
if (message.content != null &&
message.content!
.firstWhere(
(element) => element.type == 'text',
)
.text !=
null) {
refreshQuestions.value = false;
sendRequest(
file: message.file,
message: message.content!
.firstWhere(
(element) => element.type == 'text',
)
.text!,
retry: true);
}
},
),
if (copyText.replaceAll(' ', '').isNotEmpty)
PopUpMenuItemModel(
popupMenuItem: PopupMenuItem(
value: 2,
child: MorePopupMenuHandler.morePopUpItem(
color: message.fromBot!
? Theme.of(context).colorScheme.primary
: Colors.white,
icon: Assets.icon.outline.copy,
title: 'کپی')),
click: () async {
await Clipboard.setData(ClipboardData(text: copyText));
if (mounted) {
SnackBarManager(context, id: 'Copy').show(
status: SnackBarStatus.success,
message: 'پیام با موفقیت کپی شد 😃',
);
}
},
),
];
MorePopupMenuHandler(context: context).showMorePopupMenu(
right: message.fromBot!,
color: message.error!
? AppColors.red.defaultShade
: message.fromBot!
? Theme.of(context).colorScheme.surface
: AppColors.primaryColor.defaultShade,
containerKey: containerKey,
items: [
...items,
if (urls.isNotEmpty)
for (var i = 0; i < urls.length; i++)
PopUpMenuItemModel(
popupMenuItem: PopupMenuItem(
value: i + items.length,
child: MorePopupMenuHandler.morePopUpItem(
color: message.fromBot!
? Theme.of(context).colorScheme.primary
: Colors.white,
icon: Assets.icon.outline.download,
title:
'دانلود ${urls[i].isVideo() ? "ویدیو" : urls[i].isImage() ? 'عکس' : urls[i].isAudio() ? 'فایل صوتی' : 'فایل'}')),
click: () async {
DownloadFileService.getFile(url: urls[i]).then((value) {
SnackBarManager(context).show(
message: 'فایل با موفقیت در پوشه Downloads نشست.',
status: SnackBarStatus.success);
});
},
),
]);
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
context.read<ChatsHistoryBloc>().add(RestartChatsHistory());
context.read<ChatsHistoryBloc>().add(
GetAllChats(type: widget.chatArgs.isPerson ? 'character' : 'llm'));
});
}
late double maxWidthDesktop;
@override
Widget build(BuildContext context) {
return Theme(
data: Theme.of(context).copyWith(
bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent)),
child: Scaffold(
drawer: Drawer(
shape: const BeveledRectangleBorder(borderRadius: BorderRadius.zero),
child: LibraryScreen(
type: widget.chatArgs.isPerson ? 'character' : 'llm',
onTap: (chat) async {
context.push(Routes.chat,
extra: ChatArgs(
bot: chat.bot!,
chatId: chat.id,
isPerson: widget.chatArgs.isPerson));
},
),
),
appBar: AppBar(
// actions: [
// InkWell(
// onTap: () {
// DialogHandler(context: context).showPrivateBots();
// },
// child: CircleIconBtn(
// size: Responsive(context).isMobile() ? 32 : 46,
// iconPadding: const EdgeInsets.all(8),
// icon: Assets.icon.outline.crown,
// color: context.read<ThemeModeCubit>().isDark()
// ? AppColors.black[900]
// : AppColors.secondryColor[50],
// iconColor: Theme.of(context).colorScheme.secondary,
// ),
// ),
// const SizedBox(
// width: 16,
// ),
// ],
leading: Builder(builder: (context) {
return IconButton(
icon: const Icon(Icons.menu),
onPressed: () {
Scaffold.of(context).openDrawer();
},
);
}),
),
body: Stack(
children: [
SizedBox(
width: double.infinity,
height: MediaQuery.sizeOf(context).height,
child: Assets.image.chatBack.image(fit: BoxFit.cover),
),
Responsive(context).maxWidthInDesktop(
maxWidth: 800,
child: (contxet, maxWidth) {
maxWidthDesktop = maxWidth;
return SingleChildScrollView(
controller: ReceiveMessageCubit.scrollController,
reverse: true,
physics:
context.watch<MessagesBloc>().state is MessagesLoading
? const NeverScrollableScrollPhysics()
: const BouncingScrollPhysics(),
child: Column(
children: [
messages(),
aNewMessage(),
if (widget.chatArgs.bot.attachment != 3)
relatedQuestions(),
const SizedBox(
height: 120,
),
ValueListenableBuilder(
valueListenable: selectedFile,
builder: (context, value, child) => SizedBox(
height: value != null ? 70 : 0,
),
)
],
),
);
}),
],
),
bottomSheet: messageBar(),
),
);
}
Widget messages() {
return BlocConsumer<MessagesBloc, MessagesState>(
listener: (context, state) {
if (state is MessagesSuccess) {
if (state.isGetAll) {
if (chatId != null &&
widget.chatArgs.bot.attachment != 3 &&
(widget.chatArgs.bot.tool != null &&
!widget.chatArgs.bot.tool!)) {
try {
if (refreshQuestions.value) {
context
.read<RelatedQuestionsBloc>()
.add(GetAllRelatedQuestions(
chatId: chatId!,
messageId: state.messages
.lastWhere(
(element) => element.role == 'human',
)
.id!,
content: state.messages.last.query ?? '',
));
}
} catch (e) {
if (kDebugMode) {
print("Error while get Related Questions is: $e");
}
}
}
}
}
},
builder: (context, state) {
if (state is MessagesFail) {
return Padding(
padding:
EdgeInsets.only(top: MediaQuery.sizeOf(context).height * 0.1),
child: EmptyStates.getEmptyState(status: EmptyStatesEnum.server),
);
}
if (state is MessagesLoading) {
return const ChatScreenPlaceholder();
}
if (state is MessagesSuccess) {
return ListView.builder(
shrinkWrap: true,
itemCount: state.messages.length,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final message = state.messages[index];
final GlobalKey containerKey = GlobalKey();
final GlobalKey<DefaultMarkdownTextState> markdownKey =
GlobalKey();
ValueNotifier<String> directionName = ValueNotifier('RTL');
return GestureDetector(
onLongPress: () {
_popUpMenu(message, containerKey);
},
child: Container(
alignment: message.fromBot!
? Alignment.centerLeft
: Alignment.centerRight,
padding: const EdgeInsets.all(16),
child: Directionality(
textDirection: message.fromBot!
? TextDirection.ltr
: TextDirection.rtl,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Container(
constraints: BoxConstraints(
minWidth: Responsive(context).isMobile()
? maxWidthDesktop * 0.4
: maxWidthDesktop * 0.2,
maxWidth: Responsive(context).isMobile()
? maxWidthDesktop * 0.8
: maxWidthDesktop * 0.6),
decoration: BoxDecoration(
color: message.error!
? AppColors.red.defaultShade
: message.fromBot!
? Theme.of(context)
.colorScheme
.surface
: AppColors.primaryColor.defaultShade,
borderRadius: BorderRadius.circular(16)
.copyWith(
topRight: message.fromBot!
? const Radius.circular(10)
: const Radius.circular(0),
bottomLeft: message.fromBot!
? const Radius.circular(0)
: const Radius.circular(10))),
padding: const EdgeInsets.all(8),
child: message.content != null
? Column(
crossAxisAlignment:
CrossAxisAlignment.end,
children: [
if (message.file != null)
message.file!.name.isVideo()
? GestureDetector(
onTap: () => DialogHandler(
context: context)
.showVideoHero(
url: message
.file!.path),
child: Container(
constraints:
const BoxConstraints(
maxWidth: double.infinity,
),
child: VideoThumbnailWidget(
videoUrl:
message.file!.path),
),
)
: message.file!.name.isImage()
? GestureDetector(
onTap: () =>
DialogHandler(
context:
context)
.showImageHero(
image: message
.file!
.path),
child: AspectRatio(
aspectRatio: 1 / 1,
child: Container(
constraints:
const BoxConstraints(
maxWidth:
double.infinity,
),
child: ClipRRect(
borderRadius:
BorderRadius
.circular(
8),
child:
CustomeImage(
src: message
.file!.path,
fit: BoxFit
.cover,
)),
),
),
)
: message.file!.name.isAudio()
? Player(
fileUrl: message
.file!.path,
inMessages: true,
)
: Container(
decoration: BoxDecoration(
color: context
.read<
ThemeModeCubit>()
.isDark()
? AppColors
.black[
900]
: AppColors
.gray
.defaultShade,
borderRadius:
BorderRadius
.circular(
10)),
padding:
const EdgeInsets
.all(8),
constraints:
const BoxConstraints(
minHeight:
64),
child: Row(
children: [
SizedBox(
child: message
.file!
.name
.isDocument()
? const Icon(
CupertinoIcons
.doc)
: const SizedBox
.shrink(),
),
Expanded(
child:
Padding(
padding: const EdgeInsets
.symmetric(
horizontal:
12.0),
child: Text(
message.file!
.name,
textDirection: message
.file!
.name
.startsWithEnglish()
? TextDirection
.ltr
: TextDirection
.rtl,
style: const TextStyle(
fontSize:
16),
overflow:
TextOverflow
.ellipsis,
maxLines: 2,
),
)),
],
),
),
...List.generate(
message.content!.length,
(index) {
final content =
message.content![index];
return Column(
children: [
if (content.audioUrl != null)
Player(
fileUrl:
content.audioUrl!.url ??
'',
inMessages: true,
),
if (content.imageUrl != null)
Container(
constraints:
const BoxConstraints(
maxWidth: double.infinity,
),
child: AspectRatio(
aspectRatio: 1 / 1,
child: ImageNetwork(
url: content.imageUrl
?.url ??
'',
showHero: true,
radius: 10,
),
),
),
if (content.pdfUrl != null)
Container(
decoration: BoxDecoration(
color: context
.read<
ThemeModeCubit>()
.isDark()
? AppColors
.black[900]
: AppColors.gray
.defaultShade,
borderRadius:
BorderRadius
.circular(10)),
padding:
const EdgeInsets.all(8),
constraints:
const BoxConstraints(
minHeight: 64),
child: Row(
children: [
const SizedBox(
child: Icon(
CupertinoIcons
.doc),
),
Expanded(
child: Padding(
padding:
const EdgeInsets
.symmetric(
horizontal:
12.0),
child: Text(
content.pdfUrl!.url
?.split('/')
.last ??
'',
textDirection: (content
.pdfUrl!
.url
?.split(
'/')
.last ??
'')
.startsWithEnglish()
? TextDirection
.ltr
: TextDirection
.rtl,
style:
const TextStyle(
fontSize:
16),
overflow:
TextOverflow
.ellipsis,
maxLines: 2,
),
)),
],
),
),
if (content.audioUrl == null &&
content.imageUrl == null &&
content.pdfUrl == null)
Padding(
padding: const EdgeInsets
.symmetric(
horizontal: 8.0),
child: Builder(
builder: (context) {
directionName
.value = content
.text !=
null &&
content.text!
.startsWithEnglish()
? "LTR"
: 'RTL';
return DefaultMarkdownText(
key: markdownKey,
text: content.text ??
'',
fromBot:
message.fromBot!,
color: message
.fromBot!
? Theme.of(
context)
.colorScheme
.onSurface
: Colors.white);
}),
)
],
);
},
),
if (message.fromBot!)
Padding(
padding: const EdgeInsets.only(
top: 8.0, left: 8.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
ValueListenableBuilder(
valueListenable:
directionName,
builder: (context, dir, _) {
return InkWell(
onTap: () {
directionName
.value = markdownKey
.currentState
?.changeDirection() ??
'RTL';
},
child: Container(
padding:
const EdgeInsets
.symmetric(
horizontal:
12,
vertical: 4),
decoration: BoxDecoration(
borderRadius:
BorderRadius
.circular(
4),
color: context
.read<
ThemeModeCubit>()
.isDark()
? AppColors
.black[
900]
: AppColors
.primaryColor[
50]),
child: Text(
dir,
style: TextStyle(
color: Theme.of(
context)
.colorScheme
.primary),
),
),
);
}),
Row(
mainAxisSize:
MainAxisSize.min,
children: [
BlocProvider<
LikeMessageCubit>(
create: (context) =>
LikeMessageCubit()
..getLike(
like: message
.like),
child: BlocBuilder<
LikeMessageCubit,
LikeMessageState>(
builder:
(context, state) {
return DefaultPlaceHolder(
enabled: state
is LikeMessageLoading,
child: Row(
children: [
GestureDetector(
onTap:
() async {
await context.read<LikeMessageCubit>().setLike(
like: state
is LikeMessageLiked
? null
: true,
chatId:
chatId!,
messageId:
message.id!);
},
child:
Padding(
padding: const EdgeInsets
.only(
right:
20.0),
child:
SizedBox(
width: 16,
height:
16,
child: state
is LikeMessageLiked
? Assets
.icon
.bold
.like
.svg(color: AppColors.green.defaultShade)
: Assets.icon.outline.like.svg(color: Theme.of(context).colorScheme.primary),
),
),
),
GestureDetector(
onTap:
() async {
await context.read<LikeMessageCubit>().setLike(
like: state
is LikeMessageDisLiked
? null
: false,
chatId:
chatId!,
messageId:
message.id!);
},
child:
Padding(
padding: const EdgeInsets
.only(
right:
20.0),
child:
SizedBox(
width: 16,
height:
16,
child: state
is LikeMessageDisLiked
? Assets
.icon
.bold
.dislike
.svg(color: AppColors.red.defaultShade)
: Assets.icon.outline.dislike.svg(color: Theme.of(context).colorScheme.primary),
),
),
),
],
),
);
},
),
),
if (widget.chatArgs.bot
.tool !=
null &&
!widget
.chatArgs.bot.tool!)
MorePopupMenuHandler(
context: context)
.morePopUpMenu(
child: Padding(
padding:
const EdgeInsets
.only(
right:
20.0),
child:
CircleIconBtn(
icon: Assets
.icon
.outline
.magicpen,
color: context
.read<
ThemeModeCubit>()
.isDark()
? AppColors
.black[
900]
: AppColors
.primaryColor[50],
iconColor: Theme.of(
context)
.colorScheme
.primary,
size: 28,
iconPadding:
const EdgeInsets
.all(
6),
),
),
items: [
PopUpMenuItemModel(
click: () {
try {
refreshQuestions
.value =
false;
sendRequest(
file: message
.file,
message:
'خلاصه‌تر بنویس');
} catch (e) {
if (kDebugMode) {
print(
'Error is: $e');
}
}
},
popupMenuItem: PopupMenuItem(
value: 0,
child: MorePopupMenuHandler.morePopUpItem(
icon: Assets
.icon
.outline
.eraser,
title:
'خلاصه‌تر بنویس')),
),
PopUpMenuItemModel(
click: () {
try {
refreshQuestions
.value =
false;
sendRequest(
file: message
.file,
message:
'کامل بنویس');
} catch (e) {
if (kDebugMode) {
print(
'Error is: $e');
}
}
},
popupMenuItem: PopupMenuItem(
value: 1,
child: MorePopupMenuHandler.morePopUpItem(
icon: Assets
.icon
.outline
.edit2,
title:
'کامل بنویس')),
),
PopUpMenuItemModel(
popupMenuItem: PopupMenuItem(
value: 2,
child: MorePopupMenuHandler.morePopUpItem(
icon: Assets
.icon
.outline
.voiceCricle,
title:
'لحن نوشته را تغییر بده')),
click: () async {
await BottomSheetHandler(
context)
.showStringList(
onSelect:
(value) {
try {
refreshQuestions.value =
false;
sendRequest(
file: message.file,
message: 'لحن نوشته را تغییر بده به $value');
} catch (e) {
if (kDebugMode) {
print('Error is: $e');
}
}
},
title: 'انتخاب لحن نوشته',
values: [
'رسمی',
'عامیانه',
'دوستانه',
'حرفه ای',
'محاوره ای',
'طنز',
'جدی'
]);
},
),
PopUpMenuItemModel(
popupMenuItem: PopupMenuItem(
value: 3,
child: MorePopupMenuHandler.morePopUpItem(
icon: Assets
.icon
.outline
.translate,
title:
'ترجمه کن')),
click: () async {
await BottomSheetHandler(
context)
.showStringList(
onSelect:
(value) {
try {
refreshQuestions.value =
false;
sendRequest(
file: message.file,
message: 'زبان نوشته را تغییر بده به $value');
} catch (e) {
if (kDebugMode) {
print('Error is: $e');
}
}
},
title:
'انتخاب زبان',
values: [
'🇮🇷 فارسی',
'Arabic 🇸🇦',
'Bengali 🇧🇩',
'English 🇬🇧',
'French 🇫🇷',
'German 🇩🇪',
'Hindi 🇮🇳',
'Italian 🇮🇹'
]);
},
),
]),
],
),
],
),
)
],
)
: null,
),
const SizedBox(
width: 4,
),
message.error!
? CircleIconBtn(
key: containerKey,
icon: Assets.icon.outline.bitcoinRefresh,
iconColor: AppColors.red.defaultShade,
iconPadding: const EdgeInsets.all(6),
onTap: () async {
if (message.query != null) {
refreshQuestions.value = true;
sendRequest(
file: message.file,
withOutNewMessage: true,
message: message.query!,
);
context.read<MessagesBloc>().add(
ChangeMessage(
oldMessage: message,
newMessage: message.copyWith(
error: false)));
}
},
)
: Transform.rotate(
angle: pi / 2,
child: CircleIconBtn(
color: message.fromBot!
? context
.read<ThemeModeCubit>()
.isDark()
? AppColors.black[900]
: AppColors.primaryColor[50]
: AppColors.primaryColor.defaultShade,
iconColor: message.fromBot!
? Theme.of(context)
.colorScheme
.primary
: Colors.white,
key: containerKey,
icon: Assets.icon.outline.more,
iconPadding: const EdgeInsets.all(6),
onTap: () async {
_popUpMenu(message, containerKey);
},
),
)
],
),
Padding(
padding: const EdgeInsets.all(4.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (message.fromBot!)
// Row(
// children: [
// ImageNetwork(
// url: bot.image ?? '',
// width: 16,
// height: 16,
// radius: 360,
// ),
// const SizedBox(
// width: 4,
// ),
// Text(bot.name ?? '',
// textDirection: TextDirection.rtl,
// style: AppTextStyles.body5.copyWith(
// fontWeight: FontWeight.bold,
// color: Theme.of(context)
// .colorScheme
// .onSurface)),
// const SizedBox(
// width: 8,
// ),
// ],
// ),
if (message.createdAt != null)
Text(
DateTimeUtils.convertToSentTime(
message.createdAt!),
style: AppTextStyles.body5.copyWith(
color: Theme.of(context)
.colorScheme
.onSurface)),
],
),
)
],
),
),
),
);
},
);
}
return chatScreen();
},
);
}
Widget aNewMessage() {
return BlocConsumer<ReceiveMessageCubit, ReceiveMessageState>(
builder: (context, state) {
if (state is ReceiveMessageOnResponsing) {
return Container(
alignment: Alignment.centerLeft,
child: Container(
padding: const EdgeInsets.all(16),
child: Container(
constraints: BoxConstraints(
minWidth: Responsive(context).isMobile()
? maxWidthDesktop * 0.4
: maxWidthDesktop * 0.2,
maxWidth: Responsive(context).isMobile()
? maxWidthDesktop * 0.8
: maxWidthDesktop * 0.6),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(10)
.copyWith(bottomLeft: const Radius.circular(0))),
padding: const EdgeInsets.all(16),
child: DefaultMarkdownText(
text: '${state.text}...',
color: Theme.of(context).colorScheme.onSurface,
),
),
),
);
}
if (state is ReceiveMessageLoading) {
return Container(
alignment: Alignment.centerLeft,
child: Container(
padding: const EdgeInsets.all(16),
child: Container(
width: Responsive(context).isMobile()
? maxWidthDesktop * 0.8
: maxWidthDesktop * 0.6,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(10)
.copyWith(bottomLeft: const Radius.circular(0))),
padding: const EdgeInsets.all(16),
child: Center(
child: SpinKitThreeBounce(
color: Theme.of(context).colorScheme.primary,
size: 32,
)),
),
),
);
}
return const SizedBox.shrink();
},
listener: (context, state) {
if (state is ReceiveMessageDone) {
context.read<MessagesBloc>().add(AddMessage(message: state.message));
if (state.model.chatId != null) {
if (chatId == null && !isGhost.value) {
context.read<ChatsHistoryBloc>().add(AddChat(
chats: Chats(
bot: bot,
title: state.model.chatTitle,
createdAt: DateTime.now().toIso8601String(),
id: state.model.chatId)));
}
chatId = state.model.chatId;
try {
if (widget.chatArgs.bot.attachment != 3 &&
(widget.chatArgs.bot.tool != null &&
!widget.chatArgs.bot.tool! &&
refreshQuestions.value)) {
context.read<RelatedQuestionsBloc>().add(GetAllRelatedQuestions(
chatId: chatId!,
messageId: state.message.id!,
content: state.message.query!));
}
} catch (e) {
if (kDebugMode) {
print('Error is: $e');
}
}
}
final humanMessage =
context.read<MessagesBloc>().state.messages.firstWhere(
(element) => element.id == state.oldHumanMessageId,
);
if (state.model.humanMessageId != null) {
context.read<MessagesBloc>().add(ChangeMessage(
oldMessage: humanMessage,
newMessage:
humanMessage.copyWith(id: state.model.humanMessageId)));
}
context.read<UserInfoCubit>().changeCredit(CreditModel(
credit: state.model.credit, freeCredit: state.model.freeCredit));
} else if (state is ReceiveMessageOnFail) {
SnackBarManager(context, id: 'ReceiveMessageOnFail').show(
status: SnackBarStatus.error,
message: state.detail,
);
final humanMessage =
context.read<MessagesBloc>().state.messages.firstWhere(
(element) => element.id == state.oldHumanMessageId,
);
context.read<MessagesBloc>().add(ChangeMessage(
oldMessage: humanMessage,
newMessage: humanMessage.copyWith(error: true)));
}
},
);
}
Widget relatedQuestions() {
return BlocBuilder<RelatedQuestionsBloc, RelatedQuestionsState>(
builder: (context, state) {
if (state is RelatedQuestionsSuccess &&
state.relatedQuestionsModel.questions != null &&
state.relatedQuestionsModel.questions!.isNotEmpty) {
return Directionality(
textDirection: TextDirection.rtl,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Column(
children: [
Row(
children: [
Assets.icon.outline.messageQuestion.svg(
color: Theme.of(context).colorScheme.primary),
const SizedBox(
width: 4,
),
Text(
'سوالات مرتبط:',
style: AppTextStyles.body3.copyWith(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.primary),
),
],
)
],
),
const SizedBox(
height: 12,
),
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: state.relatedQuestionsModel.questions!.length,
itemBuilder: (context, index) {
final question =
state.relatedQuestionsModel.questions![index];
return GestureDetector(
onTap: () {
refreshQuestions.value = true;
sendRequest(message: question);
},
child: Directionality(
textDirection: question.startsWithEnglish()
? TextDirection.ltr
: TextDirection.rtl,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0),
child: Text(
question,
textDirection: question.startsWithEnglish()
? TextDirection.ltr
: TextDirection.rtl,
style: AppTextStyles.body4.copyWith(
color: AppColors.gray[context
.read<ThemeModeCubit>()
.isDark()
? 600
: 900]),
),
),
if (index !=
state.relatedQuestionsModel.questions!
.length -
1)
Padding(
padding:
const EdgeInsets.symmetric(vertical: 4.0),
child: Divider(
color: AppColors.gray.defaultShade,
),
)
],
),
),
);
},
),
],
),
),
);
}
return const SizedBox();
},
);
}
Widget chatScreen() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
widget.chatArgs.isPerson
? Column(
children: [
const SizedBox(
height: 16,
),
ImageNetwork(
url: widget.chatArgs.bot.image ?? '',
width: 120,
height: 120,
radius: 16,
),
const SizedBox(
height: 16,
),
Text(
widget.chatArgs.bot.name ?? '',
style: AppTextStyles.headline6.copyWith(
color: Theme.of(context).colorScheme.onSurface),
),
if (widget.chatArgs.bot.description != null)
Container(
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Theme.of(context).colorScheme.surface),
child: Center(
child: Text(
widget.chatArgs.bot.description!,
textDirection: TextDirection.rtl,
textAlign: TextAlign.justify,
style: AppTextStyles.body5.copyWith(
color: Theme.of(context).colorScheme.onSurface),
),
),
),
],
)
: bot.tool ?? false
? Column(
children: [
const SizedBox(
height: 24,
),
Center(
child: ImageNetwork(
url: bot.image ?? '',
width: 64,
height: 64,
radius: 360,
color:
bot.image != null && bot.image!.contains('/llm')
? Theme.of(context).colorScheme.onSurface
: null,
),
),
const SizedBox(
height: 8,
),
Text(
bot.name ?? '',
textDirection: TextDirection.rtl,
style: AppTextStyles.headline6.copyWith(
color: Theme.of(context).colorScheme.onSurface),
),
if (bot.description != null)
LayoutBuilder(builder: (context, constraints) {
return ValueListenableBuilder(
valueListenable: maxLines,
builder: (context, lines, _) {
final span = TextSpan(
text: bot.description,
style: AppTextStyles.body4.copyWith(
color: AppColors.gray[context
.read<ThemeModeCubit>()
.isDark()
? 600
: 900]));
final tp = TextPainter(
text: span,
textDirection: TextDirection.ltr);
tp.layout(maxWidth: constraints.maxWidth);
final numLines =
tp.computeLineMetrics().length;
return Padding(
padding:
const EdgeInsets.fromLTRB(4, 12, 4, 4),
child: Stack(
children: [
Column(
children: [
Text(
bot.description!,
textDirection: TextDirection.rtl,
style: AppTextStyles.body4.copyWith(
color: AppColors.gray[context
.read<
ThemeModeCubit>()
.isDark()
? 600
: 900]),
maxLines: (lines),
textAlign: TextAlign.justify,
),
if (lines == null && numLines >= 5)
Transform.rotate(
angle: -pi / 2,
child: CircleIconBtn(
onTap: () {
if (maxLines.value ==
null) {
maxLines.value = 5;
return;
}
maxLines.value = null;
},
size: 46,
color: Theme.of(context)
.colorScheme
.primary,
iconColor: Colors.white,
icon: Assets.icon.outline
.arrowRight)),
],
),
if (lines != null && numLines > lines)
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
height: 64,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context)
.scaffoldBackgroundColor,
Theme.of(context)
.scaffoldBackgroundColor
.withAlpha(140)
],
begin:
Alignment.bottomCenter,
end: Alignment.topCenter,
),
),
alignment:
Alignment.bottomCenter,
child: SizedBox(
child: Transform.rotate(
angle: pi / 2,
child: CircleIconBtn(
onTap: () {
if (maxLines
.value ==
null) {
maxLines.value =
5;
return;
}
maxLines.value =
null;
},
size: 46,
color:
Theme.of(context)
.colorScheme
.primary,
iconColor:
Colors.white,
icon: Assets
.icon
.outline
.arrowRight)),
),
))
],
),
);
});
}),
const SizedBox(
height: 8,
),
],
)
: Container(
padding: const EdgeInsets.all(12),
margin: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Theme.of(context).colorScheme.surface),
child: Center(
child: Text(
'سلام! به هوشان خوش اومدی. من اینجا هستم تا پاسخگوی سوالاتت باشم. امیدوارم در استفاده از هوشان تجربه خوبی داشته باشی!',
textDirection: TextDirection.rtl,
textAlign: TextAlign.justify,
style: AppTextStyles.body5.copyWith(
color: Theme.of(context).colorScheme.onSurface),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ValueListenableBuilder(
valueListenable: isGhost,
builder: (context, g, _) {
return Transform.scale(
scale: 0.8,
child: Switch.adaptive(
value: g,
thumbIcon: WidgetStateProperty.resolveWith<Icon?>(
(Set<WidgetState> states) {
if (states.contains(WidgetState.selected)) {
return Icon(CustomIcons.ghost,
color: Theme.of(context)
.colorScheme
.onSurface);
}
return Icon(Icons.close,
color: Theme.of(context)
.colorScheme
.onSurface);
},
),
onChanged: (value) {
isGhost.value = value;
},
),
);
}),
const SizedBox(
width: 8,
),
Text(
'حالت ناشناس',
style: AppTextStyles.body4.copyWith(
color: Theme.of(context).colorScheme.onSurface,
fontWeight: FontWeight.bold),
),
const SizedBox(
width: 8,
),
Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: HintTooltip(
hint:
'با فعال کردن این گزینه؛ چت‌های شما در قسمت تاریخچه، ذخیره نمی‌شوند و اطلاعاتتان ناشناس باقی می‌ماند.',
iconColor: Theme.of(context).colorScheme.onSurface,
),
)
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(12)),
child: Row(
children: [
Text(
bot.cost == 0 || bot.cost == null
? 'رایگان'
: bot.cost.toString(),
style: AppTextStyles.body3.copyWith(color: Colors.white),
),
const SizedBox(
width: 4,
),
Assets.icon.outline.coin
.svg(color: Colors.white, width: 18, height: 18)
],
),
),
],
),
],
),
);
}
Widget messageBar() {
return Directionality(
textDirection: TextDirection.rtl,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ValueListenableBuilder(
valueListenable: selectedFile,
builder: (context, value, child) {
if (value != null && !showRecorder.value) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(10)),
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(8),
constraints: const BoxConstraints(minHeight: 64),
child: Row(
children: [
SizedBox(
child: value.name.isImage()
? GestureDetector(
onTap: () => DialogHandler(context: context)
.showImageHero(image: value.path),
child: SizedBox(
width: 46,
child: AspectRatio(
aspectRatio: 3 / 4,
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: CustomeImage(
src: value.path,
fit: BoxFit.cover,
)),
),
),
)
: value.path.isDocument()
? const Icon(CupertinoIcons.doc)
: value.path.isAudio()
? Player(
fileUrl: value.path,
inMessages: true,
)
: const SizedBox.shrink(),
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Text(
value.name,
textDirection: value.name.startsWithEnglish()
? TextDirection.ltr
: TextDirection.rtl,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).colorScheme.onSurface),
overflow: TextOverflow.ellipsis,
maxLines: 2,
),
)),
CircleIconBtn(
icon: Assets.icon.outline.trash,
color: AppColors.red[50],
iconColor: AppColors.red.defaultShade,
iconPadding: const EdgeInsets.all(6),
onTap: () {
selectedFile.value = null;
},
)
],
),
);
}
return const SizedBox.shrink();
},
),
Container(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
margin: Responsive(context).isMobile()
? EdgeInsets.zero
: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(32), topRight: Radius.circular(32)),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
offset: const Offset(0, 0),
blurRadius: 12,
spreadRadius: 0,
),
],
),
child: Column(
children: [
ValueListenableBuilder(
valueListenable: showRecorder,
builder: (context, inRecord, child) {
return inRecord
? Recorder(
play: true,
onDelete: () {
selectedFile.value = null;
showRecorder.value = false;
},
onError: (e) {
showRecorder.value = false;
recording.value = false;
},
onRecordFinish: (file) {
selectedFile.value = file;
recording.value = false;
},
)
: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: widget.chatArgs.bot.attachment == 3
? const SizedBox.shrink()
: SizedBox(
child: ValueListenableBuilder(
valueListenable: selectedFile,
builder: (context, file, child) {
return ValueListenableBuilder(
valueListenable: messageText,
builder:
(context, text, child) {
return Directionality(
textDirection: text.text
.startsWithEnglish()
? TextDirection.ltr
: TextDirection.rtl,
child: TextField(
controller: messageText,
onChanged: (value) {},
enabled: (bot.deleted !=
null &&
!bot
.deleted!) &&
(context
.watch<
ReceiveMessageCubit>()
.state
is! ReceiveMessageOnResponsing &&
context
.watch<
ReceiveMessageCubit>()
.state
is! ReceiveMessageLoading) &&
!(bot.attachment ==
1 &&
file != null) &&
(widget.chatArgs.bot
.attachment !=
3),
minLines: 1,
maxLines: 6, // Set this
keyboardType:
TextInputType
.multiline,
style: AppTextStyles
.body4
.copyWith(
color: Theme.of(
context)
.colorScheme
.onSurface),
decoration:
InputDecoration(
contentPadding:
const EdgeInsets
.fromLTRB(
0, 12, 0, 12),
filled: true,
hintText: (bot.deleted !=
null &&
bot.deleted!)
? 'دستیار مورد نظر توسط سازنده حذف شده است!'
: 'چیزی بنویسید ...',
hintStyle:
AppTextStyles
.body4,
fillColor: Colors
.transparent,
border:
const OutlineInputBorder(
borderSide:
BorderSide.none,
),
),
),
);
});
},
),
),
),
const SizedBox(
width: 16,
),
],
);
}),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
if ((bot.deleted != null && !bot.deleted!))
ValueListenableBuilder(
valueListenable: selectedFile,
builder: (context, file, child) {
return ValueListenableBuilder(
valueListenable: recording,
builder: (context, inRecording, child) {
return ValueListenableBuilder(
valueListenable: showRecorder,
builder:
(context, showRecord, child) {
return ValueListenableBuilder(
valueListenable: messageText,
builder: (context, message,
child) {
return inRecording
? const SizedBox
.shrink()
: bot.attachmentType !=
null &&
bot.attachmentType!
.contains(
'audio') &&
message.text
.replaceAll(
' ', '')
.isEmpty &&
!showRecord &&
file == null
? GestureDetector(
onTap: () {
showRecorder
.value =
true;
recording
.value =
true;
},
child: Assets
.icon
.outline
.microphoneChat
.svg(
width:
24,
height:
24,
color: Theme.of(context)
.colorScheme
.primary))
: GestureDetector(
onTap: () {
if ((messageText
.text
.replaceAll(' ',
'')
.isEmpty &&
widget.chatArgs.bot.attachment !=
3) &&
selectedFile
.value ==
null) {
return;
}
if (selectedFile
.value !=
null &&
bot.attachment ==
1) {
return;
}
refreshQuestions
.value =
true;
sendRequest(
file: selectedFile
.value,
message:
messageText
.text);
},
child: Assets
.icon
.bold
.send
.svg(
width:
24,
height: 24));
},
);
});
});
}),
const SizedBox(
width: 8,
),
// CircleIconBtn(
// icon: Assets.icon.outline.infoCircle,
// color: Theme.of(context).colorScheme.surface,
// iconColor: Theme.of(context).colorScheme.onSurface,
// onTap: () {
// showInfo.value = !showInfo.value;
// },
// ),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ValueListenableBuilder(
valueListenable: webSearch,
builder: (context, isWebSearchEnabled, _) {
return GestureDetector(
onTap: () {
webSearch.value = !webSearch.value;
},
child: HintTooltip(
hint: 'جستجو در وب',
child: Assets.icon.outline.globalSearch.svg(
width: 24,
height: 24,
color: isWebSearchEnabled
? Theme.of(context)
.colorScheme
.primary
: Theme.of(context)
.colorScheme
.onSurface
.withOpacity(0.4),
),
),
);
},
),
// ValueListenableBuilder(
// valueListenable: webSearch,
// builder: (context, canWebSearch, _) {
// return ChoiceChip(
// padding: EdgeInsets.zero,
// showCheckmark: false,
// labelPadding:
// const EdgeInsets.symmetric(horizontal: 8),
// selectedColor: Theme.of(context)
// .colorScheme
// .primary, // Change selected color
// backgroundColor:
// Theme.of(context).colorScheme.surface,
// surfaceTintColor: Colors.transparent,
// selectedShadowColor: Colors.transparent,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(360)),
// onSelected: (value) {
// webSearch.value = value;
// },
// label: Row(
// children: [
// Assets.icon.outline.globalSearch.svg(
// color: canWebSearch
// ? Colors.white
// : Theme.of(context)
// .colorScheme
// .onSurface),
// const SizedBox(
// width: 4,
// ),
// Text(
// 'جستجو در وب',
// style: AppTextStyles.body5.copyWith(
// color: canWebSearch
// ? Colors.white
// : Theme.of(context)
// .colorScheme
// .onSurface),
// ),
// ],
// ),
// selected: canWebSearch);
// }),
const SizedBox(
width: 12,
),
if ((bot.deleted != null && !bot.deleted!))
if (bot.attachmentType != null &&
bot.attachmentType!.isNotEmpty)
ValueListenableBuilder(
valueListenable: visibleAttach,
builder: (context, value, child) {
return AnimatedVisibility(
isVisible: value,
duration:
const Duration(milliseconds: 300),
fadeMode: FadeMode.horizontal,
child: Padding(
padding:
const EdgeInsets.only(left: 12.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.end,
children: [
if (bot.attachmentType!
.contains('image'))
Padding(
padding:
const EdgeInsets.only(
right: 8.0),
child: CircleIconBtn(
icon: Assets.icon.outline
.galleryAdd,
color: Theme.of(context)
.colorScheme
.primary,
iconColor: Colors.white,
onTap: () async {
await BottomSheetHandler(
context)
.showPickImage(
onSelect: (file) {
selectedFile.value =
file;
if (widget
.chatArgs
.bot
.attachment !=
3) {
visibleAttach
.value =
false;
}
},
);
}),
),
if (bot.attachmentType!
.contains('audio'))
Padding(
padding:
const EdgeInsets.only(
right: 8.0),
child: CircleIconBtn(
icon: Assets
.icon.outline.musicnote,
color: Theme.of(context)
.colorScheme
.primary,
iconColor: Colors.white,
onTap: () async {
final file =
await PickFileService(
context)
.getFile(
fileType:
FileType
.audio);
if (file != null) {
selectedFile.value =
file.single;
if (widget.chatArgs.bot
.attachment !=
3) {
visibleAttach.value =
false;
}
}
},
),
),
if (bot.attachmentType!
.contains('pdf'))
Padding(
padding:
const EdgeInsets.only(
right: 8.0),
child: CircleIconBtn(
icon: Assets
.icon.outline.cardAdd,
color: Theme.of(context)
.colorScheme
.primary,
iconColor: Colors.white,
onTap: () async {
final file =
await PickFileService(
context)
.getFile(
fileType:
FileType
.custom,
allowedExtensions: [
'pdf',
'doc',
'docx',
'xls',
'xlsx',
'xlsm',
'xlsb',
'xlt',
'xltx',
'xltm'
]);
if (file != null) {
selectedFile.value =
file.single;
if (widget
.chatArgs
.bot
.attachment !=
3) {
visibleAttach
.value = false;
}
}
}),
)
],
),
));
},
),
if ((bot.deleted != null && !bot.deleted!))
if (bot.attachment != 0 &&
bot.attachmentType != null &&
bot.attachmentType!.isNotEmpty &&
bot.attachment != 3)
GestureDetector(
onTap: () {
if (widget.chatArgs.bot.attachment != 3) {
visibleAttach.value =
!visibleAttach.value;
}
},
child: Assets.icon.outline.elementPlus.svg(
width: 24,
height: 24,
color: Theme.of(context)
.colorScheme
.primary)),
],
),
],
),
SizedBox(
height: 10,
),
],
),
const SizedBox(
height: 4,
),
ValueListenableBuilder(
valueListenable: showInfo,
builder: (context, show, _) {
if (show) {
return Text(
'مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید و از وارد کردن اطلاعات حساس بپرهیزید.',
style: AppTextStyles.body6.copyWith(
color: Theme.of(context).colorScheme.onSurface),
);
}
return const SizedBox.shrink();
})
],
),
),
],
),
);
}
}