724 lines
41 KiB
Dart
724 lines
41 KiB
Dart
// ignore_for_file: library_private_types_in_public_api, deprecated_member_use
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:didvan/config/design_config.dart';
|
|
import 'package:didvan/config/theme_data.dart';
|
|
import 'package:didvan/constants/app_icons.dart';
|
|
import 'package:didvan/constants/assets.dart';
|
|
import 'package:didvan/main.dart';
|
|
import 'package:didvan/models/ai/ai_chat_args.dart';
|
|
import 'package:didvan/models/view/action_sheet_data.dart';
|
|
import 'package:didvan/routes/routes.dart';
|
|
import 'package:didvan/utils/action_sheet.dart';
|
|
import 'package:didvan/views/ai/history_ai_chat_state.dart';
|
|
import 'package:didvan/views/ai/widgets/hoshan_drawer.dart';
|
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
|
import 'package:didvan/views/widgets/hoshan_app_bar.dart';
|
|
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
|
|
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
|
|
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_svg/svg.dart';
|
|
import 'package:persian_number_utility/persian_number_utility.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
class HistoryAiChatPage extends StatefulWidget {
|
|
final bool? archived;
|
|
const HistoryAiChatPage({Key? key, required this.archived}) : super(key: key);
|
|
|
|
@override
|
|
_HistoryAiChatPageState createState() => _HistoryAiChatPageState();
|
|
}
|
|
|
|
class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
|
|
final ScrollController scrollController = ScrollController();
|
|
final GlobalKey<ScaffoldState> scaffKey = GlobalKey<ScaffoldState>();
|
|
Timer? _timer;
|
|
|
|
late bool archived = widget.archived ?? false;
|
|
|
|
@override
|
|
void initState() {
|
|
final state = context.read<HistoryAiChatState>();
|
|
Future.delayed(
|
|
Duration.zero,
|
|
() => state.getChats(archived: archived),
|
|
);
|
|
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
|
|
return WillPopScope(
|
|
onWillPop: () async {
|
|
if (context.read<HistoryAiChatState>().refresh) {
|
|
context.read<HistoryAiChatState>().getChats();
|
|
context.read<HistoryAiChatState>().refresh = false;
|
|
}
|
|
return true;
|
|
},
|
|
child: Scaffold(
|
|
key: scaffKey,
|
|
appBar: HoshanAppBar(
|
|
onBack: () {
|
|
if (context.read<HistoryAiChatState>().refresh) {
|
|
context.read<HistoryAiChatState>().getChats();
|
|
context.read<HistoryAiChatState>().refresh = false;
|
|
}
|
|
Navigator.pop(context);
|
|
},
|
|
withActions: false,
|
|
withInfo: true,
|
|
),
|
|
drawer: HoshanDrawer(
|
|
scaffKey: scaffKey,
|
|
),
|
|
body: CustomScrollView(
|
|
physics: const BouncingScrollPhysics(),
|
|
controller: scrollController,
|
|
slivers: [
|
|
SliverAppBar(
|
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
|
scrolledUnderElevation: 0,
|
|
automaticallyImplyLeading: false,
|
|
pinned: true,
|
|
toolbarHeight: archived ? 50 : 110,
|
|
flexibleSpace: Container(
|
|
color: Theme.of(context).colorScheme.surface,
|
|
padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Text('فهرست آرشیوشدهها',
|
|
style: TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.bold,
|
|
color: DesignConfig.isDark
|
|
? const Color.fromARGB(255, 0, 90, 119)
|
|
: const Color.fromARGB(255, 0, 53, 70))),
|
|
),
|
|
// SearchField(
|
|
// title: 'گفتوگوها',
|
|
// onChanged: (value) {
|
|
// final state = context.read<HistoryAiChatState>();
|
|
// if (value.isEmpty) {
|
|
// state.getChats(archived: archived);
|
|
// return;
|
|
// }
|
|
// _timer?.cancel();
|
|
// _timer = Timer(const Duration(seconds: 1), () {
|
|
// state.search = value;
|
|
// state.getSearchChats(q: value, archived: archived);
|
|
// });
|
|
// },
|
|
// focusNode: FocusNode(),
|
|
// ),
|
|
if (!archived)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 0.0),
|
|
child: InkWell(
|
|
onTap: () {
|
|
Navigator.of(context)
|
|
.pushNamed(Routes.aiArchivedHistory);
|
|
},
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
DidvanIcons.ai_regular,
|
|
size: 18,
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
const SizedBox(width: 8),
|
|
DidvanText(
|
|
'گفتوگوهای آرشیو شده',
|
|
style: TextStyle(
|
|
color: theme.colorScheme.primary,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
Consumer<HistoryAiChatState>(
|
|
builder: (context, state, child) {
|
|
return SliverStateHandler(
|
|
paddingEmptyState: 0,
|
|
state: state,
|
|
centerEmptyState: false,
|
|
emptyState: EmptyState(
|
|
asset: Assets.emptyResult,
|
|
height: 400,
|
|
title: 'لیست خالی است',
|
|
titleColor: const Color.fromARGB(255, 2, 126, 167),
|
|
subtitle:
|
|
'در حال حاضر آیتمی در این بخش ثبت نشده است. هر زمان مورد جدیدی اضافه شود، در اینجا نمایش داده میشود.',
|
|
),
|
|
enableEmptyState: archived
|
|
? state.archivedChats.isEmpty
|
|
: state.chats.isEmpty,
|
|
placeholder: const _HistoryPlaceholder(),
|
|
placeholderCount: 8,
|
|
builder: (context, state, index) {
|
|
final chat = archived
|
|
? state.archivedChats[index]
|
|
: state.chats[index];
|
|
TextEditingController title =
|
|
TextEditingController(text: chat.title);
|
|
return Dismissible(
|
|
key: UniqueKey(),
|
|
background: Container(
|
|
color: Theme.of(context).colorScheme.error,
|
|
alignment: Alignment.centerRight,
|
|
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
|
child: Icon(
|
|
DidvanIcons.trash_solid,
|
|
color: Theme.of(context).colorScheme.white,
|
|
),
|
|
),
|
|
secondaryBackground: Container(
|
|
color: Theme.of(context).colorScheme.primary,
|
|
alignment: Alignment.centerLeft,
|
|
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
|
child: Icon(
|
|
archived
|
|
? Icons.folder_delete
|
|
: Icons.create_new_folder_rounded,
|
|
color: Theme.of(context).colorScheme.white),
|
|
),
|
|
movementDuration: const Duration(milliseconds: 600),
|
|
confirmDismiss: (direction) async {
|
|
bool result = false;
|
|
|
|
if (direction == DismissDirection.startToEnd) {
|
|
await ActionSheetUtils(context).openDialog(
|
|
data: ActionSheetData(
|
|
onConfirmed: () async {
|
|
final state =
|
|
context.read<HistoryAiChatState>();
|
|
await state.deleteChat(chat.id!, index,
|
|
archived: archived);
|
|
result = true;
|
|
},
|
|
content: Column(
|
|
children: [
|
|
Row(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.center,
|
|
children: [
|
|
Icon(
|
|
DidvanIcons.trash_solid,
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.error,
|
|
),
|
|
const SizedBox(
|
|
width: 8,
|
|
),
|
|
SizedBox(
|
|
child: DidvanText(
|
|
'پاک کردن گفتوگو',
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.error,
|
|
fontSize: 20,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(
|
|
height: 12,
|
|
),
|
|
SizedBox(
|
|
child: RichText(
|
|
text: TextSpan(
|
|
text:
|
|
'آیا از پاک کردن گفتوگوی ',
|
|
style: TextStyle(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.text,
|
|
fontFamily: DesignConfig
|
|
.fontFamily),
|
|
children: [
|
|
TextSpan(
|
|
text: "\"${chat.title}\"",
|
|
style: TextStyle(
|
|
fontFamily:
|
|
DesignConfig
|
|
.fontFamily,
|
|
fontWeight:
|
|
FontWeight.bold)),
|
|
TextSpan(
|
|
style: TextStyle(
|
|
fontFamily:
|
|
DesignConfig
|
|
.fontFamily),
|
|
text:
|
|
' با هوشان اطمینان دارید؟ '),
|
|
]),
|
|
),
|
|
),
|
|
],
|
|
)));
|
|
} else {
|
|
result = await state.archivedChat(chat.id!, index,
|
|
archived: archived);
|
|
}
|
|
return result;
|
|
},
|
|
child: InkWell(
|
|
onTap: () {
|
|
if (chat.bot != null) {
|
|
navigatorKey.currentState!.pushNamed(
|
|
Routes.aiChat,
|
|
arguments: AiChatArgs(
|
|
bot: chat.bot!,
|
|
chat: chat,
|
|
assistantsName: chat.assistantsName));
|
|
} else {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
const SnackBar(
|
|
content:
|
|
Text('امکان باز کردن این چت وجود ندارد.'),
|
|
),
|
|
);
|
|
}
|
|
},
|
|
onLongPress: () {
|
|
if (archived) {
|
|
state.archivedChats[index] = state
|
|
.archivedChats[index]
|
|
.copyWith(isEditing: true);
|
|
} else {
|
|
state.chats[index] =
|
|
state.chats[index].copyWith(isEditing: true);
|
|
}
|
|
state.update();
|
|
},
|
|
child: Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 12, horizontal: 20),
|
|
decoration: BoxDecoration(
|
|
border: Border(
|
|
bottom: BorderSide(
|
|
color: Theme.of(context).colorScheme.border,
|
|
))),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
// SkeletonImage(
|
|
// imageUrl: chat.bot!.image.toString(),
|
|
// width: 46,
|
|
// height: 46,
|
|
// borderRadius: BorderRadius.circular(360),
|
|
// ),
|
|
// const SizedBox(
|
|
// width: 18,
|
|
// ),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
// Row(
|
|
// mainAxisAlignment:
|
|
// MainAxisAlignment.spaceBetween,
|
|
// children: [
|
|
// // DidvanText(
|
|
// // chat.assistantsName ??
|
|
// // chat.bot!.name.toString(),
|
|
// // fontWeight: FontWeight.bold,
|
|
// // ),
|
|
|
|
// ],
|
|
// ),
|
|
SizedBox(
|
|
child: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
chat.isEditing != null &&
|
|
chat.isEditing!
|
|
? Row(
|
|
children: [
|
|
Expanded(
|
|
child: TextFormField(
|
|
controller: title,
|
|
style:
|
|
const TextStyle(
|
|
fontSize:
|
|
12),
|
|
textAlignVertical:
|
|
TextAlignVertical
|
|
.bottom,
|
|
maxLines: 1,
|
|
decoration:
|
|
const InputDecoration(
|
|
isDense: true,
|
|
contentPadding:
|
|
EdgeInsets.symmetric(
|
|
vertical:
|
|
5,
|
|
horizontal:
|
|
10),
|
|
border:
|
|
OutlineInputBorder(),
|
|
)),
|
|
),
|
|
const SizedBox(
|
|
width: 12,
|
|
),
|
|
state.loadingchangeTitle
|
|
? const SizedBox(
|
|
width: 12,
|
|
height: 12,
|
|
child:
|
|
CircularProgressIndicator())
|
|
: InkWell(
|
|
onTap: () async {
|
|
if (title.text
|
|
.toString() ==
|
|
chat.title
|
|
.toString()) {
|
|
chat.isEditing =
|
|
false;
|
|
state
|
|
.update();
|
|
return;
|
|
}
|
|
if (title.text
|
|
.isNotEmpty) {
|
|
await state.changeNameChat(
|
|
chat.id!,
|
|
index,
|
|
title
|
|
.text);
|
|
title.clear();
|
|
}
|
|
if (chat.isEditing !=
|
|
null) {
|
|
chat.isEditing =
|
|
!chat
|
|
.isEditing!;
|
|
state
|
|
.update();
|
|
return;
|
|
}
|
|
chat.isEditing =
|
|
true;
|
|
|
|
state.update();
|
|
},
|
|
child: const Icon(
|
|
DidvanIcons
|
|
.check_circle_solid),
|
|
)
|
|
],
|
|
)
|
|
: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Column(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment
|
|
.start,
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment
|
|
.start,
|
|
children: [
|
|
DidvanText(
|
|
chat.title
|
|
.toString(),
|
|
maxLines: 1,
|
|
overflow:
|
|
TextOverflow
|
|
.ellipsis,
|
|
),
|
|
DidvanText(
|
|
DateTime.parse(chat
|
|
.updatedAt
|
|
.toString())
|
|
.toPersianDateStr(monthString: ''),
|
|
style:
|
|
const TextStyle(
|
|
fontSize:
|
|
12)),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(
|
|
width: 10,
|
|
),
|
|
PopupMenuButton<String>(
|
|
icon: SvgPicture.asset(
|
|
'lib/assets/icons/more.svg'),
|
|
shape:
|
|
const RoundedRectangleBorder(
|
|
borderRadius:
|
|
BorderRadius
|
|
.only(
|
|
topRight:
|
|
Radius.circular(
|
|
16),
|
|
topLeft:
|
|
Radius.circular(
|
|
0),
|
|
bottomLeft:
|
|
Radius.circular(
|
|
16),
|
|
bottomRight:
|
|
Radius.circular(
|
|
16),
|
|
)),
|
|
color: const Color
|
|
.fromARGB(
|
|
255, 246, 246, 246),
|
|
padding:
|
|
EdgeInsets.zero,
|
|
borderRadius:
|
|
const BorderRadius
|
|
.only(
|
|
topRight:
|
|
Radius.circular(
|
|
16),
|
|
topLeft:
|
|
Radius.circular(
|
|
0),
|
|
bottomLeft:
|
|
Radius.circular(
|
|
16),
|
|
bottomRight:
|
|
Radius.circular(
|
|
16),
|
|
),
|
|
onSelected:
|
|
(value) async {
|
|
switch (value) {
|
|
case 'حذف پیام':
|
|
await state
|
|
.deleteChat(
|
|
chat.id!,
|
|
index,
|
|
archived:
|
|
archived);
|
|
break;
|
|
|
|
case 'آرشیو':
|
|
case 'خارج کردن از آرشیو':
|
|
await state
|
|
.archivedChat(
|
|
chat.id!,
|
|
index,
|
|
archived:
|
|
archived);
|
|
break;
|
|
default:
|
|
}
|
|
},
|
|
itemBuilder:
|
|
(BuildContext
|
|
context) =>
|
|
<PopupMenuEntry<
|
|
String>>[
|
|
PopupMenuItem<String>(
|
|
value:
|
|
'خارج کردن از آرشیو',
|
|
padding:
|
|
const EdgeInsets
|
|
.symmetric(
|
|
horizontal:
|
|
20,
|
|
vertical:
|
|
12),
|
|
child: SizedBox(
|
|
width: 180,
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
children: [
|
|
SvgPicture
|
|
.asset(
|
|
'lib/assets/icons/direct-send.svg',
|
|
height:
|
|
20,
|
|
),
|
|
const SizedBox(
|
|
width:
|
|
16),
|
|
const Text(
|
|
'خارج کردن از آرشیو',
|
|
style: TextStyle(
|
|
fontSize:
|
|
13,
|
|
color: Color.fromARGB(
|
|
255,
|
|
61,
|
|
61,
|
|
61)),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(
|
|
height:
|
|
20),
|
|
const Divider(
|
|
height: 1,
|
|
color: Color
|
|
.fromARGB(
|
|
255,
|
|
227,
|
|
226,
|
|
225),
|
|
)
|
|
],
|
|
),
|
|
),
|
|
),
|
|
PopupMenuItem<String>(
|
|
value: 'حذف پیام',
|
|
padding:
|
|
const EdgeInsets
|
|
.symmetric(
|
|
horizontal:
|
|
20,
|
|
vertical:
|
|
12),
|
|
child: SizedBox(
|
|
width: 180,
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
children: [
|
|
SvgPicture
|
|
.asset(
|
|
'lib/assets/icons/trash.svg',
|
|
height:
|
|
20,
|
|
color: const Color
|
|
.fromARGB(
|
|
255,
|
|
0,
|
|
126,
|
|
167),
|
|
),
|
|
const SizedBox(
|
|
width:
|
|
16),
|
|
const Text(
|
|
'پاک کردن',
|
|
style: TextStyle(
|
|
fontSize: 13,
|
|
color: Color.fromARGB(255, 61, 61, 61))),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
|
|
// if (chat.prompts != null &&
|
|
// chat.prompts!.isNotEmpty &&
|
|
// chat.prompts![0].text != null)
|
|
// DidvanText(
|
|
// chat.prompts![0].text
|
|
// .toString(),
|
|
// maxLines: 1,
|
|
// overflow: TextOverflow.ellipsis,
|
|
// fontSize: 12,
|
|
// ),
|
|
],
|
|
),
|
|
]))
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
childCount: archived
|
|
? state.archivedChats.length
|
|
: state.chats.length,
|
|
onRetry: () => state.getChats(archived: archived));
|
|
},
|
|
)
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _HistoryPlaceholder extends StatelessWidget {
|
|
const _HistoryPlaceholder({Key? key}) : super(key: key);
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return const Padding(
|
|
padding: EdgeInsets.symmetric(vertical: 18.0, horizontal: 20),
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
children: [
|
|
ClipOval(
|
|
child: ShimmerPlaceholder(
|
|
height: 46,
|
|
width: 46,
|
|
),
|
|
),
|
|
SizedBox(width: 16),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
ShimmerPlaceholder(
|
|
height: 20,
|
|
width: 100,
|
|
),
|
|
ShimmerPlaceholder(
|
|
height: 14,
|
|
width: 100,
|
|
),
|
|
],
|
|
),
|
|
SizedBox(height: 12),
|
|
ShimmerPlaceholder(
|
|
height: 16,
|
|
width: double.infinity,
|
|
),
|
|
SizedBox(height: 8),
|
|
ShimmerPlaceholder(
|
|
height: 16,
|
|
width: double.infinity,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|