change hoshan Appbar Header and create Tools and ToolBox page and change AiChatPage,

This commit is contained in:
OkaykOrhmn 2024-11-12 16:54:27 +03:30
parent 3f6957ae15
commit 2f4e049ce0
15 changed files with 1418 additions and 965 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 792 KiB

View File

@ -43,6 +43,8 @@ class Assets {
static String loadingAnimation = '$_baseAnimationsPath/loading.gif';
static String bookmarkAnimation = '$_baseAnimationsPath/bookmark.gif';
static String boxAnimation =
'$_baseAnimationsPath/onlinegiftools-$_themeSuffix.gif';
static String get businessCategoryIcon =>
'$_baseCategoriesPath/business-$_themeSuffix.svg';

View File

@ -18,7 +18,9 @@ import 'package:didvan/services/media/media.dart';
import 'package:didvan/services/notification/firebase_api.dart';
import 'package:didvan/services/notification/notification_service.dart';
import 'package:didvan/utils/my_custom_scroll_behavior.dart';
import 'package:didvan/views/ai/ai_state.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/ai/tools_state.dart';
import 'package:didvan/views/podcasts/podcasts_state.dart';
import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart';
import 'package:firebase_core/firebase_core.dart';
@ -170,6 +172,12 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
ChangeNotifierProvider<HistoryAiChatState>(
create: (context) => HistoryAiChatState(),
),
ChangeNotifierProvider<AiState>(
create: (context) => AiState(),
),
ChangeNotifierProvider<ToolsState>(
create: (context) => ToolsState(),
),
],
child: Consumer<ThemeProvider>(
builder: (context, themeProvider, child) => Container(

View File

@ -0,0 +1,72 @@
import 'package:didvan/models/ai/bots_model.dart';
class ToolsModel {
List<Tools>? tools;
ToolsModel({this.tools});
ToolsModel.fromJson(Map<String, dynamic> json) {
if (json['tools'] != null) {
tools = <Tools>[];
json['tools'].forEach((v) {
tools!.add(Tools.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
if (tools != null) {
data['tools'] = tools!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Tools {
int? id;
String? name;
String? image;
String? description;
String? guide;
int? order;
List<BotsModel>? bots;
Tools(
{this.id,
this.name,
this.image,
this.description,
this.guide,
this.order,
this.bots});
Tools.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
image = json['image'];
description = json['description'];
guide = json['guide'];
order = json['order'];
if (json['bots'] != null) {
bots = <BotsModel>[];
json['bots'].forEach((v) {
bots!.add(BotsModel.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['name'] = name;
data['image'] = image;
data['description'] = description;
data['guide'] = guide;
data['order'] = order;
if (bots != null) {
data['bots'] = bots!.map((v) => v.toJson()).toList();
}
return data;
}
}

View File

@ -205,7 +205,7 @@ class RequestHelper {
static String aiArchived() => '$baseUrl/ai/chat${_urlConcatGenerator([
const MapEntry('archived', true),
])}';
static String aiBots() => '$baseUrl/ai/bot';
static String aiBots() => '$baseUrl/ai/bot/v2';
static String aiSearchBots(String q) =>
'$baseUrl/ai/bot${_urlConcatGenerator([
MapEntry('q', q),
@ -229,6 +229,7 @@ class RequestHelper {
static String deleteAllChats() => '$baseUrl/ai/chat/all';
static String archivedChat(int id) => '$baseUrl/ai/chat/$id/archive';
static String placeholder(int id) => '$baseUrl/ai/chat/$id/placeholder';
static String tools() => '$baseUrl/ai/tool';
static String _urlConcatGenerator(List<MapEntry<String, dynamic>> additions) {
String result = '';

View File

@ -10,7 +10,10 @@ import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/ai/ai_chat_args.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/ai/ai_state.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/ai/tool_screen.dart';
import 'package:didvan/views/ai/tools_screen.dart';
import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:didvan/views/home/home.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
@ -44,255 +47,281 @@ class _AiState extends State<Ai> {
@override
Widget build(BuildContext context) {
return Consumer<HistoryAiChatState>(
builder: (context, state, child) {
if (state.bots.isEmpty) {
return Center(
child: Image.asset(
Assets.loadingAnimation,
width: 60,
height: 60,
),
);
}
final bot = state.bot!;
return Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(bottom: 24),
child: Column(
children: [
const SizedBox(
height: 24,
),
Icon(
DidvanIcons.ai_solid,
size: MediaQuery.sizeOf(context).width / 5,
color: Theme.of(context).colorScheme.title,
),
DidvanText(
'هوشان',
color: Theme.of(context).colorScheme.title,
),
const SizedBox(
height: 24,
),
InkWell(
onTap: () => ActionSheetUtils(context)
.botsDialogSelect(
context: context, state: state),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(360),
border: Border.all(
width: 1,
color:
Theme.of(context).colorScheme.text)),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0, horizontal: 18),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Column(
children: [
Transform.rotate(
angle: 180 * pi / 180,
child: Icon(
DidvanIcons.caret_down_solid,
color: Theme.of(context)
.colorScheme
.title,
),
),
Icon(
DidvanIcons.caret_down_solid,
return Stack(
children: [
Consumer<AiState>(
builder: (BuildContext context, state, Widget? child) {
switch (state.page) {
case 0:
return Consumer<HistoryAiChatState>(
builder: (context, state, child) {
if (state.bots.isEmpty) {
return Center(
child: Image.asset(
Assets.loadingAnimation,
width: 60,
height: 60,
),
);
}
final bot = state.bot!;
return Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(bottom: 24),
child: Column(
children: [
const SizedBox(
height: 24,
),
Icon(
DidvanIcons.ai_solid,
size: MediaQuery.sizeOf(context).width / 5,
color: Theme.of(context).colorScheme.title,
),
DidvanText(
'هوشان',
color: Theme.of(context).colorScheme.title,
),
const SizedBox(
height: 24,
),
InkWell(
onTap: () => ActionSheetUtils(context)
.botsDialogSelect(
context: context, state: state),
child: Container(
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(360),
border: Border.all(
width: 1,
color: Theme.of(context)
.colorScheme
.title,
.text)),
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 8.0, horizontal: 18),
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment:
CrossAxisAlignment.center,
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Column(
children: [
Transform.rotate(
angle: 180 * pi / 180,
child: Icon(
DidvanIcons
.caret_down_solid,
color: Theme.of(context)
.colorScheme
.title,
),
),
Icon(
DidvanIcons
.caret_down_solid,
color: Theme.of(context)
.colorScheme
.title,
),
],
),
const SizedBox(
width: 12,
),
DidvanText(bot.name.toString(),
color: Theme.of(context)
.colorScheme
.title),
],
),
const SizedBox(
width: 12,
),
ClipOval(
child: CachedNetworkImage(
width: 46,
height: 46,
imageUrl: bot.image.toString(),
),
),
],
),
const SizedBox(
width: 12,
),
DidvanText(bot.name.toString(),
color: Theme.of(context)
.colorScheme
.title),
],
),
const SizedBox(
width: 12,
),
ClipOval(
child: CachedNetworkImage(
width: 46,
height: 46,
imageUrl: bot.image.toString(),
),
),
],
),
),
const SizedBox(
height: 24,
),
const Padding(
padding:
EdgeInsets.symmetric(horizontal: 20.0),
child: Text(
"به هوشان؛ هوش مصنوعی دیدوان خوش آمدید. \nبرای شروع گفتگو پیام مورد نظر خود را در کادر زیر بنویسید.",
textAlign: TextAlign.center,
),
)
],
),
),
),
const SizedBox(
height: 24,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Text(
"به هوشان؛ هوش مصنوعی دیدوان خوش آمدید. \nبرای شروع گفتگو پیام مورد نظر خود را در کادر زیر بنویسید.",
textAlign: TextAlign.center,
),
)
],
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 12),
child: InkWell(
onTap: () =>
Navigator.of(context).pushNamed(Routes.aiChat,
arguments: AiChatArgs(
bot: bot,
)),
child: Column(
children: [
Row(
children: [
Expanded(
child: Container(
decoration: BoxDecoration(
boxShadow: DesignConfig.defaultShadow,
color:
Theme.of(context).colorScheme.surface,
border: Border.all(
color: Theme.of(context)
.colorScheme
.border),
borderRadius:
DesignConfig.highBorderRadius),
child: Row(
),
Padding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 12),
child: InkWell(
onTap: () =>
Navigator.of(context).pushNamed(Routes.aiChat,
arguments: AiChatArgs(
bot: bot,
)),
child: Column(
children: [
Row(
children: [
Expanded(
child: Padding(
padding:
const EdgeInsets.symmetric(
horizontal: 8.0,
),
child: Form(
child: Row(
children: [
const MessageBarBtn(
enable: true,
icon: DidvanIcons
.mic_regular),
const SizedBox(
width: 8,
),
Expanded(
child: TextFormField(
textInputAction:
TextInputAction
.newline,
style: Theme.of(context)
.textTheme
.bodyMedium,
minLines: 1,
enabled: false,
decoration:
InputDecoration(
border:
InputBorder.none,
hintText: 'بنویسید...',
hintStyle: Theme.of(
context)
.textTheme
.bodySmall!
.copyWith(
color: Theme.of(
context)
.colorScheme
.disabledText),
child: Container(
decoration: BoxDecoration(
boxShadow:
DesignConfig.defaultShadow,
color: Theme.of(context)
.colorScheme
.surface,
border: Border.all(
color: Theme.of(context)
.colorScheme
.border),
borderRadius: DesignConfig
.highBorderRadius),
child: Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets
.symmetric(
horizontal: 8.0,
),
),
),
const SizedBox(
width: 8,
),
MessageBarBtn(
click: () {
Navigator.of(context)
.pushNamed(
Routes.aiChat,
arguments:
AiChatArgs(
child: Form(
child: Row(
children: [
const MessageBarBtn(
enable: true,
icon: DidvanIcons
.mic_regular),
const SizedBox(
width: 8,
),
Expanded(
child:
TextFormField(
textInputAction:
TextInputAction
.newline,
style: Theme.of(
context)
.textTheme
.bodyMedium,
minLines: 1,
enabled: false,
decoration:
InputDecoration(
border:
InputBorder
.none,
hintText:
'بنویسید...',
hintStyle: Theme.of(
context)
.textTheme
.bodySmall!
.copyWith(
color: Theme.of(context)
.colorScheme
.disabledText),
),
),
),
const SizedBox(
width: 8,
),
MessageBarBtn(
click: () {
Navigator.of(context).pushNamed(
Routes
.aiChat,
arguments: AiChatArgs(
bot:
bot,
attach:
true));
},
enable: false,
icon: Icons
.attach_file_rounded),
],
))))
},
enable: false,
icon: Icons
.attach_file_rounded),
],
))))
],
),
),
),
],
),
),
),
],
),
const Padding(
padding: EdgeInsets.fromLTRB(8, 8, 8, 4),
child: DidvanText(
'مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید.',
fontSize: 12,
),
)
],
)),
const Padding(
padding: EdgeInsets.fromLTRB(8, 8, 8, 4),
child: DidvanText(
'مدل‌های هوش مصنوعی می‌توانند اشتباه کنند، صحت اطلاعات مهم را بررسی کنید.',
fontSize: 12,
),
)
],
)),
),
],
);
},
);
case 1:
return const ToolsScreen();
case 2:
return const ToolScreen();
default:
return const SizedBox();
}
},
),
Positioned(
top: 32,
right: 0,
child: InkWell(
onTap: () => homeScaffKey.currentState!.openDrawer(),
child: Container(
width: 46,
height: 46,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
bottomLeft: Radius.circular(12)),
boxShadow: DesignConfig.defaultShadow),
child: Icon(
DidvanIcons.angle_left_light,
color: Theme.of(context).colorScheme.title,
),
],
),
Positioned(
top: 32,
right: 0,
child: InkWell(
onTap: () => homeScaffKey.currentState!.openDrawer(),
child: Container(
width: 46,
height: 46,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
bottomLeft: Radius.circular(12)),
boxShadow: DesignConfig.defaultShadow),
child: Icon(
DidvanIcons.angle_left_light,
color: Theme.of(context).colorScheme.title,
),
)),
)
],
);
},
)),
)
],
);
}
}

View File

@ -13,7 +13,6 @@ import 'package:didvan/models/ai/chats_model.dart';
import 'package:didvan/models/ai/files_model.dart';
import 'package:didvan/models/ai/messages_model.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/services/media/media.dart';
@ -25,9 +24,9 @@ import 'package:didvan/views/ai/ai_chat_state.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/ai/widgets/ai_message_bar.dart';
import 'package:didvan/views/ai/widgets/audio_wave.dart';
import 'package:didvan/views/widgets/didvan/button.dart';
import 'package:didvan/views/widgets/didvan/icon_button.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/marquee_text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:didvan/views/widgets/video/chat_video_player.dart';
@ -49,7 +48,9 @@ class AiChatPage extends StatefulWidget {
}
class _AiChatPageState extends State<AiChatPage> {
final GlobalKey<ScaffoldState> scaffKey = GlobalKey<ScaffoldState>();
FocusNode focusNode = FocusNode();
@override
void initState() {
final state = context.read<AiChatState>();
@ -101,196 +102,13 @@ class _AiChatPageState extends State<AiChatPage> {
},
child: Consumer<AiChatState>(
builder: (context, state, child) => Scaffold(
appBar: AppBar(
shadowColor: Theme.of(context).colorScheme.border,
title: Text(widget.args.bot.name.toString()),
leading: Row(
children: [
DidvanIconButton(
icon: DidvanIcons.angle_right_solid,
onPressed: () {
Navigator.of(context).pop();
if (context.read<HistoryAiChatState>().refresh) {
context.read<HistoryAiChatState>().getChats();
context.read<HistoryAiChatState>().refresh = false;
}
},
),
],
),
actions: [
if (state.chatId != null)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: InkWell(
onTap: () {
final TextEditingController placeholder =
TextEditingController(
text: state.chat?.placeholder);
ActionSheetUtils(context).openDialog(
data: ActionSheetData(
hasConfirmButtonClose: false,
hasConfirmButton: false,
hasDismissButton: false,
content: ValueListenableBuilder<bool>(
valueListenable: state.changingPlaceHolder,
builder: (context, value, child) =>
Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.sizeOf(context)
.height /
3),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
Stack(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment
.center,
children: [
DidvanText(
'شخصی‌سازی دستورات',
style: Theme.of(
context)
.textTheme
.titleMedium,
),
],
),
Positioned(
right: 0,
top: 0,
bottom: 0,
child: Center(
child: InkWell(
onTap: () {
ActionSheetUtils(
context)
.pop();
},
child: const Icon(
DidvanIcons
.close_solid,
size: 24,
),
),
)),
],
),
const SizedBox(
height: 12,
),
const DidvanText(
'دوست دارید هوشان چه چیزهایی را درباره شما بداند تا بتواند پاسخ‌های بهتری ارائه دهد؟ '),
const SizedBox(
height: 12,
),
value
? Center(
child: Image.asset(
Assets
.loadingAnimation,
width: 60,
height: 60,
),
)
: TextField(
controller:
placeholder,
style: (Theme.of(
context)
.textTheme
.bodyMedium)!
.copyWith(
fontFamily: DesignConfig
.fontFamily
.padRight(
3)),
minLines: 5,
maxLines: 5,
keyboardType:
TextInputType
.multiline,
decoration:
InputDecoration(
filled: true,
fillColor: Theme.of(
context)
.colorScheme
.secondCTA,
contentPadding:
const EdgeInsets
.fromLTRB(
10,
18,
10,
0),
border: const OutlineInputBorder(
borderRadius:
DesignConfig
.lowBorderRadius),
errorStyle:
const TextStyle(
height:
0.01),
),
),
],
),
),
),
const SizedBox(
height: 12,
),
Row(
children: [
Expanded(
child: DidvanButton(
onPressed: () {
Navigator.of(context).pop();
},
title: 'بازگشت',
style:
ButtonStyleMode.secondary,
),
),
const SizedBox(width: 20),
Expanded(
child: DidvanButton(
style:
ButtonStyleMode.primary,
onPressed: () async {
await state
.changePlaceHolder(
placeholder.text);
Future.delayed(
Duration.zero,
() => ActionSheetUtils(
context)
.pop(),
);
},
title: 'تایید',
),
),
],
),
],
),
),
)));
},
child: const Icon(DidvanIcons.note_regular)),
)
],
centerTitle: true,
automaticallyImplyLeading: false,
appBar: HoshanAppBar(
onBack: () {
Navigator.pop(context);
},
),
key: scaffKey,
drawer: const HoshanDrawer(),
body: state.loading
? Center(
child: Image.asset(
@ -299,85 +117,110 @@ class _AiChatPageState extends State<AiChatPage> {
height: 60,
),
)
: state.messages.isEmpty
? Column(
crossAxisAlignment: CrossAxisAlignment.center,
: Stack(
children: [
Column(
children: [
const SizedBox(
height: 12,
),
Center(
child: Icon(
DidvanIcons.ai_solid,
size: MediaQuery.sizeOf(context).width / 5,
),
),
const DidvanText('هوشان'),
const SizedBox(
height: 24,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
DidvanText(widget.args.bot.name.toString()),
const SizedBox(
width: 12,
height: 24,
),
ClipOval(
child: CachedNetworkImage(
width: 46,
height: 46,
width: 75,
height: 75,
imageUrl: widget.args.bot.image.toString(),
),
),
const SizedBox(
height: 12,
),
DidvanText(
widget.args.bot.name.toString(),
fontSize: 17,
fontWeight: FontWeight.bold,
),
if (state.messages.isEmpty)
Column(
children: [
const SizedBox(
height: 16,
),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20.0),
child: Center(
child: DidvanText(
widget.args.bot.description ??
'')),
)
],
)
],
),
const SizedBox(
height: 24,
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Text(
"به هوشان؛ هوش مصنوعی دیدوان خوش آمدید. \nبرای شروع گفتگو پیام مورد نظر خود را در کادر زیر بنویسید.",
textAlign: TextAlign.center,
),
)
],
)
: SingleChildScrollView(
reverse: true,
controller: state.scrollController,
child: ListView.builder(
itemCount: state.messages.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(
bottom: state.file != null &&
!(state.file!.isRecorded)
? 180
: 100),
itemBuilder: (context, mIndex) {
final prompts = state.messages[mIndex].prompts;
final time = state.messages[mIndex].dateTime;
return Column(
children: [
timeLabel(context, time),
ListView.builder(
itemCount: prompts.length,
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final message = prompts[index];
if (state.messages.isNotEmpty)
SingleChildScrollView(
reverse: true,
controller: state.scrollController,
child: ListView.builder(
itemCount: state.messages.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.only(
bottom: state.file != null &&
!(state.file!.isRecorded)
? 180
: 100),
itemBuilder: (context, mIndex) {
final prompts =
state.messages[mIndex].prompts;
final time =
state.messages[mIndex].dateTime;
return Column(
children: [
timeLabel(context, time),
ListView.builder(
itemCount: prompts.length,
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final message = prompts[index];
return messageBubble(message, context,
state, index, mIndex);
},
),
],
);
}),
return messageBubble(message,
context, state, index, mIndex);
},
),
],
);
}),
),
],
),
Positioned(
top: 32,
right: 0,
child: InkWell(
onTap: () => scaffKey.currentState!.openDrawer(),
child: Container(
width: 46,
height: 46,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
bottomLeft: Radius.circular(12)),
boxShadow: DesignConfig.defaultShadow),
child: Icon(
DidvanIcons.angle_left_light,
color: Theme.of(context).colorScheme.title,
),
)),
)
],
),
bottomSheet: Column(
mainAxisSize: MainAxisSize.min,
children: [

View File

@ -0,0 +1,24 @@
import 'package:didvan/models/ai/tools_model.dart';
import 'package:didvan/providers/core.dart';
class AiState extends CoreProvier {
int page = 0;
Tools? tool;
void goToAi() {
page = 0;
update();
}
void goToTools() {
page = 1;
tool = null;
update();
}
void goToToolBox({required final Tools tool}) {
page = 2;
this.tool = tool;
update();
}
}

View File

@ -0,0 +1,119 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/models/ai/ai_chat_args.dart';
import 'package:didvan/models/ai/tools_model.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/views/ai/ai_state.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
class ToolScreen extends StatefulWidget {
const ToolScreen({Key? key}) : super(key: key);
@override
State<ToolScreen> createState() => _ToolScreenState();
}
class _ToolScreenState extends State<ToolScreen> {
late Tools tool = context.read<AiState>().tool!;
get itemBuilder => null;
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
children: [
const SizedBox(height: 32),
SvgPicture.network(
tool.image!,
width: 64,
height: 64,
),
const SizedBox(
height: 4,
),
DidvanText(
tool.name!,
fontSize: 20,
fontWeight: FontWeight.bold,
color: const Color(0xff1B3C59),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: DidvanText(
tool.guide!,
fontSize: 12,
color: const Color(0xff666666),
),
),
const SizedBox(height: 24),
GridView.builder(
shrinkWrap: true,
itemCount: tool.bots!.length,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 32),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1 / 1,
crossAxisSpacing: 18,
mainAxisSpacing: 18),
itemBuilder: (context, index) {
final bot = tool.bots![index];
return InkWell(
onTap: () => Navigator.of(context).pushNamed(Routes.aiChat,
arguments: AiChatArgs(
bot: bot,
)),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: DesignConfig.lowBorderRadius,
border: Border.all(color: const Color(0xffbbbbbb))),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SkeletonImage(
imageUrl: bot.image!,
width: 72,
height: 72,
borderRadius: BorderRadius.circular(360),
),
const SizedBox(
height: 4,
),
DidvanText(
bot.name!,
fontSize: 16,
fontWeight: FontWeight.bold,
color: const Color(0xff2A282F),
),
if (bot.description != null)
Column(
children: [
const SizedBox(
height: 4,
),
DidvanText(
bot.description!,
fontSize: 14,
fontWeight: FontWeight.bold,
color: const Color(0xffA8A6AC),
),
],
)
],
),
),
);
},
)
],
),
);
}
}

View File

@ -0,0 +1,215 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/constants/assets.dart';
import 'package:didvan/views/ai/ai_state.dart';
import 'package:didvan/views/ai/tools_state.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
class ToolsScreen extends StatefulWidget {
const ToolsScreen({Key? key}) : super(key: key);
@override
State<ToolsScreen> createState() => _ToolsScreenState();
}
class _ToolsScreenState extends State<ToolsScreen> {
@override
void initState() {
super.initState();
context.read<ToolsState>().getTools();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
physics: context.watch<ToolsState>().loading
? const NeverScrollableScrollPhysics()
: const BouncingScrollPhysics(),
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(top: 46, bottom: 24),
child: DidvanText(
'انتخاب بات‌ها',
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xff1B3C59),
),
),
Consumer<ToolsState>(
builder: (BuildContext context, ToolsState state, Widget? child) {
final tools = state.tools;
if (tools != null && tools.isEmpty) {
return EmptyState(
asset: Assets.emptyResult,
title: 'لیست خالی است',
);
}
if (state.loading) {
return toolsPlaceHolder();
}
return ListView.builder(
itemCount: tools!.length,
shrinkWrap: true,
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 24)
.copyWith(bottom: 46),
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final tool = tools[index];
return InkWell(
onTap: () =>
context.read<AiState>().goToToolBox(tool: tool),
child: Container(
decoration: BoxDecoration(
borderRadius: DesignConfig.lowBorderRadius,
border: Border.all(
color: const Color(0xffB8B8B8),
),
),
padding: const EdgeInsets.all(24),
margin: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
SvgPicture.network(
tool.image!,
width: 75,
height: 75,
),
const SizedBox(
width: 8,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
DidvanText(
tool.name!,
fontSize: 16,
fontWeight: FontWeight.bold,
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 2),
decoration: BoxDecoration(
borderRadius:
BorderRadius.circular(4),
color: Theme.of(context)
.colorScheme
.primary),
child: DidvanText(
'${tool.bots!.length} مدل',
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
)
],
),
DidvanText(
'${tool.bots!.map(
(e) => e.name!,
)}'
.replaceAll('(', '')
.replaceAll(')', ''),
fontSize: 12,
color: Theme.of(context).colorScheme.primary,
),
DidvanText(tool.description!,
fontSize: 12, color: const Color(0xffA8A6AC))
],
),
)
],
),
),
);
},
);
},
)
],
),
);
}
ListView toolsPlaceHolder() {
return ListView.builder(
itemCount: 10,
shrinkWrap: true,
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 24),
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Container(
decoration: BoxDecoration(
borderRadius: DesignConfig.lowBorderRadius,
border: Border.all(
color: const Color(0xffB8B8B8),
),
),
padding: const EdgeInsets.all(24),
margin: const EdgeInsets.symmetric(vertical: 8),
child: Row(
children: [
ShimmerPlaceholder(
width: 75,
height: 75,
borderRadius: BorderRadius.circular(360),
),
const SizedBox(
width: 8,
),
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ShimmerPlaceholder(
width: 120,
height: 24,
borderRadius: DesignConfig.mediumBorderRadius,
),
ShimmerPlaceholder(
width: 60,
height: 24,
borderRadius: DesignConfig.mediumBorderRadius,
),
],
),
SizedBox(
height: 8,
),
ShimmerPlaceholder(
width: 120,
height: 24,
borderRadius: DesignConfig.mediumBorderRadius,
),
SizedBox(
height: 8,
),
ShimmerPlaceholder(
width: 240,
height: 46,
borderRadius: DesignConfig.mediumBorderRadius,
),
],
),
)
],
),
);
},
);
}
}

View File

@ -0,0 +1,29 @@
import 'package:didvan/models/ai/tools_model.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
class ToolsState extends CoreProvier {
bool loading = true;
List<Tools>? tools;
void getTools() async {
final service = RequestService(
RequestHelper.tools(),
);
await service.httpGet();
if (service.isSuccess) {
final ToolsModel toolsModel = ToolsModel.fromJson(service.result);
tools = toolsModel.tools!;
tools ??= [];
appState = AppState.idle;
loading = false;
update();
return;
}
appState = AppState.failed;
loading = false;
update();
}
}

View File

@ -0,0 +1,475 @@
import 'package:cached_network_image/cached_network_image.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/ai/chats_model.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/models/view/alert_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/home/home.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:provider/provider.dart';
class HoshanDrawer extends StatefulWidget {
const HoshanDrawer({Key? key}) : super(key: key);
@override
State<HoshanDrawer> createState() => _HoshanDrawerState();
}
class _HoshanDrawerState extends State<HoshanDrawer> {
@override
Widget build(BuildContext context) {
return Drawer(
child: Consumer<HistoryAiChatState>(
builder: (context, state, child) {
return Column(
children: [
const SizedBox(
height: 8,
),
Padding(
padding: const EdgeInsets.only(left: 20.0, top: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
onTap: () => homeScaffKey.currentState!.closeDrawer(),
child: const Icon(
DidvanIcons.close_regular,
),
)
],
),
),
Icon(
DidvanIcons.ai_solid,
size: MediaQuery.sizeOf(context).width / 5,
color: Theme.of(context).colorScheme.title,
),
DidvanText(
'هوشان',
color: Theme.of(context).colorScheme.title,
),
const SizedBox(
height: 24,
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
children: [
drawerBtn(
icon: Icons.handshake_rounded,
text: 'ساخت دستیار شخصی',
crossAxisAlignment: CrossAxisAlignment.start,
enable: false),
const DidvanDivider(),
drawerBtn(
icon: CupertinoIcons.doc_text_search,
text: 'جستجو در مدل‌ها',
click: () {
ActionSheetUtils(context).botsDialogSelect(
context: context, state: state);
homeScaffKey.currentState!.closeDrawer();
},
enable: false),
const DidvanDivider(),
drawerBtn(
icon: DidvanIcons.chats_regular,
text: 'تاریخچه همه گفتگوها',
label: 'حذف همه',
click: () {
Navigator.of(context)
.pushNamed(Routes.aiHistory);
},
labelClick: state.chats.isEmpty
? null
: () async {
await ActionSheetUtils(context)
.openDialog(
data: ActionSheetData(
onConfirmed: () async {
await state.deleteAllChat(
refresh: false);
},
content: Column(
children: [
Row(
crossAxisAlignment:
CrossAxisAlignment
.center,
children: [
Icon(
DidvanIcons
.trash_solid,
color: Theme.of(
context)
.colorScheme
.error,
),
const SizedBox(
width: 8,
),
DidvanText(
'پاک کردن همه گفت‌وگوها',
color: Theme.of(
context)
.colorScheme
.error,
fontSize: 20,
),
],
),
const SizedBox(
height: 12,
),
const DidvanText(
'آیا از پاک کردن تمامی گفت‌وگوهای انجام شده با هوشان اطمینان دارید؟'),
],
)));
},
),
const SizedBox(
height: 12,
),
// SearchField(
// title: 'title',
// onChanged: (value) {},
// focusNode: FocusNode()),
// SizedBox(
// height: 12,
// ),
Expanded(
child: state.loadingdeleteAll ||
state.appState == AppState.busy
? ListView.builder(
shrinkWrap: true,
itemCount: 10,
padding: const EdgeInsets.symmetric(
horizontal: 12),
physics:
const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return const Padding(
padding: EdgeInsets.symmetric(
vertical: 12.0),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
ClipOval(
child: ShimmerPlaceholder(
height: 24,
width: 24,
),
),
SizedBox(width: 12),
Expanded(
child: ShimmerPlaceholder(
height: 24,
),
),
SizedBox(width: 12),
ShimmerPlaceholder(
height: 24,
width: 24,
),
SizedBox(width: 8),
ShimmerPlaceholder(
height: 24,
width: 12,
),
],
),
);
},
)
: state.chats.isEmpty
? Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
SvgPicture.asset(
Assets.emptyResult,
height:
MediaQuery.sizeOf(context)
.height /
10,
),
const DidvanText(
'لیست خالی است',
fontSize: 14,
fontWeight: FontWeight.bold,
)
],
),
)
: ListView.builder(
shrinkWrap: true,
itemCount: state.chats.length,
padding: const EdgeInsets.symmetric(
horizontal: 12),
physics:
const BouncingScrollPhysics(),
itemBuilder: (context, index) {
final chat = state.chats[index];
TextEditingController title =
TextEditingController(
text: chat.title);
return chatRow(
chat, title, state, index);
},
),
),
// SizedBox(
// height: 12,
// ),
// Text('نمایش قدیمی‌ترها')
],
),
),
Column(
children: [
const DidvanDivider(),
drawerBtn(
icon: Icons.folder_copy_outlined,
text: 'گفت‌وگوهای آرشیو شده',
click: () {
Navigator.of(context)
.pushNamed(Routes.aiHistory, arguments: true);
},
),
const SizedBox(
height: 12,
),
drawerBtn(
icon: DidvanIcons.support_regular,
text: 'پیام به پشتیبانی',
click: () {
Navigator.of(context).pushNamed(
Routes.direct,
arguments: {'type': 'پشتیبانی اپلیکیشن'},
);
},
),
],
)
],
),
),
),
const SizedBox(
height: 32,
)
],
);
},
),
);
}
Widget drawerBtn(
{final CrossAxisAlignment? crossAxisAlignment,
required final IconData icon,
required final String text,
final bool enable = true,
final String? label,
final Function()? labelClick,
final Function()? click}) {
return InkWell(
onTap: enable
? click
: () {
ActionSheetUtils(context).showAlert(
AlertData(message: 'درحال توسعه', aLertType: ALertType.info));
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
crossAxisAlignment:
crossAxisAlignment ?? CrossAxisAlignment.center,
children: [
Icon(
icon,
color: enable
? Theme.of(context).colorScheme.title
: Theme.of(context).colorScheme.disabledText,
),
const SizedBox(
width: 8,
),
Column(
children: [
DidvanText(text,
fontSize: 16,
color: enable
? Theme.of(context).colorScheme.title
: Theme.of(context).colorScheme.disabledText),
// if (!enable) Text('در حال توسعه ...')
],
)
],
),
if (label != null)
InkWell(
onTap: labelClick,
child: DidvanText(
label,
color: Theme.of(context).colorScheme.primary,
fontSize: 12,
),
)
],
),
),
);
}
Padding chatRow(ChatsModel chat, TextEditingController title,
HistoryAiChatState state, int index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: InkWell(
onTap: () {
navigatorKey.currentState!.pushNamed(Routes.aiChat,
arguments: AiChatArgs(bot: chat.bot!, chat: chat));
},
child: Row(
children: [
ClipOval(
child: CachedNetworkImage(
imageUrl: chat.bot!.image.toString(),
width: 24,
height: 24,
)),
const SizedBox(
width: 12,
),
Expanded(
child: Text(
chat.title.toString(),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
const SizedBox(
width: 24,
),
Row(
children: [
InkWell(
onTap: () async {
ActionSheetUtils(context).openDialog(
data: ActionSheetData(
content: Center(
child: TextFormField(
controller: title,
style: const TextStyle(fontSize: 12),
textAlignVertical: TextAlignVertical.bottom,
maxLines: 3,
decoration: const InputDecoration(
isDense: true,
contentPadding: EdgeInsets.symmetric(
vertical: 5, horizontal: 10),
border: OutlineInputBorder(),
)),
),
title: 'تغییر نام',
onConfirmed: () async {
if (title.text.isNotEmpty) {
await state.changeNameChat(
chat.id!, index, title.text,
refresh: false);
title.clear();
}
if (chat.isEditing != null) {
chat.isEditing = !chat.isEditing!;
state.update();
return;
}
state.update();
},
));
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.edit_outlined,
size: 20,
),
),
),
const SizedBox(
width: 4,
),
PopupMenuButton(
onSelected: (value) async {
switch (value) {
case 'حذف پیام':
await state.deleteChat(chat.id!, index, refresh: false);
break;
case 'آرشیو':
await state.archivedChat(chat.id!, index,
refresh: false);
break;
default:
}
state.update();
},
itemBuilder: (BuildContext context) {
return <PopupMenuEntry>[
ActionSheetUtils.popUpBtns(
value: 'حذف پیام',
icon: DidvanIcons.trash_regular,
color: Theme.of(context).colorScheme.error,
height: 32,
size: 16),
ActionSheetUtils.popUpBtns(
value: 'آرشیو',
icon: Icons.folder_copy,
height: 32,
size: 16,
),
];
},
offset: const Offset(0, 0),
position: PopupMenuPosition.under,
useRootNavigator: true,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.more_vert,
size: 20,
),
),
),
],
)
],
),
),
);
}
}

View File

@ -1,40 +1,33 @@
// ignore_for_file: deprecated_member_use
import 'package:cached_network_image/cached_network_image.dart';
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/ai/chats_model.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/notification_message.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/providers/theme.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/notification/notification_service.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/ai/ai.dart';
import 'package:didvan/views/ai/ai_state.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/ai/widgets/hoshan_drawer.dart';
import 'package:didvan/views/home/categories/categories_page.dart';
import 'package:didvan/views/home/main/main_page.dart';
import 'package:didvan/views/home/home_state.dart';
import 'package:didvan/views/home/new_statistic/new_statistic.dart';
import 'package:didvan/views/home/search/search.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/hoshan_app_bar.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/logo_app_bar.dart';
import 'package:didvan/views/widgets/didvan/bnb.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import '../../services/app_home_widget/home_widget_repository.dart';
@ -172,290 +165,32 @@ class _HomeState extends State<Home>
super.initState();
}
PreferredSizeWidget getAppBar() {
PreferredSizeWidget result = const LogoAppBar();
if (context.watch<HomeState>().tabController.index == 2) {
result = HoshanAppBar(
onBack: () {
final state = context.read<AiState>();
if (state.page == 1) {
state.goToAi();
} else {
state.goToTools();
}
},
);
}
return result;
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: homeScaffKey,
appBar: LogoAppBar(
canSearch: context.watch<HomeState>().tabController.index != 2,
),
appBar: getAppBar(),
resizeToAvoidBottomInset: false,
drawer: context.watch<HomeState>().tabController.index == 2
? Drawer(
child: Consumer<HistoryAiChatState>(
builder: (context, state, child) {
return Column(
children: [
const SizedBox(
height: 8,
),
Padding(
padding: const EdgeInsets.only(left: 20.0, top: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
onTap: () =>
homeScaffKey.currentState!.closeDrawer(),
child: const Icon(
DidvanIcons.close_regular,
),
)
],
),
),
Icon(
DidvanIcons.ai_solid,
size: MediaQuery.sizeOf(context).width / 5,
color: Theme.of(context).colorScheme.title,
),
DidvanText(
'هوشان',
color: Theme.of(context).colorScheme.title,
),
const SizedBox(
height: 24,
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
children: [
drawerBtn(
icon: Icons.handshake_rounded,
text: 'ساخت دستیار شخصی',
crossAxisAlignment:
CrossAxisAlignment.start,
enable: false),
const DidvanDivider(),
drawerBtn(
icon: CupertinoIcons.doc_text_search,
text: 'جستجو در مدل‌ها',
click: () {
ActionSheetUtils(context)
.botsDialogSelect(
context: context,
state: state);
homeScaffKey.currentState!
.closeDrawer();
},
enable: false),
const DidvanDivider(),
drawerBtn(
icon: DidvanIcons.chats_regular,
text: 'تاریخچه همه گفتگوها',
label: 'حذف همه',
click: () {
Navigator.of(context)
.pushNamed(Routes.aiHistory);
},
labelClick: state.chats.isEmpty
? null
: () async {
await ActionSheetUtils(context)
.openDialog(
data: ActionSheetData(
onConfirmed:
() async {
await state
.deleteAllChat(
refresh:
false);
},
content: Column(
children: [
Row(
crossAxisAlignment:
CrossAxisAlignment
.center,
children: [
Icon(
DidvanIcons
.trash_solid,
color: Theme.of(
context)
.colorScheme
.error,
),
const SizedBox(
width: 8,
),
DidvanText(
'پاک کردن همه گفت‌وگوها',
color: Theme.of(
context)
.colorScheme
.error,
fontSize:
20,
),
],
),
const SizedBox(
height: 12,
),
const DidvanText(
'آیا از پاک کردن تمامی گفت‌وگوهای انجام شده با هوشان اطمینان دارید؟'),
],
)));
},
),
const SizedBox(
height: 12,
),
// SearchField(
// title: 'title',
// onChanged: (value) {},
// focusNode: FocusNode()),
// SizedBox(
// height: 12,
// ),
Expanded(
child: state.loadingdeleteAll ||
state.appState == AppState.busy
? ListView.builder(
shrinkWrap: true,
itemCount: 10,
padding:
const EdgeInsets.symmetric(
horizontal: 12),
physics:
const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return const Padding(
padding: EdgeInsets.symmetric(
vertical: 12.0),
child: Row(
crossAxisAlignment:
CrossAxisAlignment
.start,
children: [
ClipOval(
child:
ShimmerPlaceholder(
height: 24,
width: 24,
),
),
SizedBox(width: 12),
Expanded(
child:
ShimmerPlaceholder(
height: 24,
),
),
SizedBox(width: 12),
ShimmerPlaceholder(
height: 24,
width: 24,
),
SizedBox(width: 8),
ShimmerPlaceholder(
height: 24,
width: 12,
),
],
),
);
},
)
: state.chats.isEmpty
? Padding(
padding: const EdgeInsets.all(
12.0),
child: Column(
children: [
SvgPicture.asset(
Assets.emptyResult,
height:
MediaQuery.sizeOf(
context)
.height /
10,
),
const DidvanText(
'لیست خالی است',
fontSize: 14,
fontWeight:
FontWeight.bold,
)
],
),
)
: ListView.builder(
shrinkWrap: true,
itemCount: state.chats.length,
padding: const EdgeInsets
.symmetric(
horizontal: 12),
physics:
const BouncingScrollPhysics(),
itemBuilder:
(context, index) {
final chat =
state.chats[index];
TextEditingController
title =
TextEditingController(
text: chat.title);
return chatRow(chat, title,
state, index);
},
),
),
// SizedBox(
// height: 12,
// ),
// Text('نمایش قدیمی‌ترها')
],
),
),
Column(
children: [
const DidvanDivider(),
drawerBtn(
icon: Icons.folder_copy_outlined,
text: 'گفت‌وگوهای آرشیو شده',
click: () {
Navigator.of(context).pushNamed(
Routes.aiHistory,
arguments: true);
},
),
const SizedBox(
height: 12,
),
drawerBtn(
icon: DidvanIcons.support_regular,
text: 'پیام به پشتیبانی',
click: () {
Navigator.of(context).pushNamed(
Routes.direct,
arguments: {
'type': 'پشتیبانی اپلیکیشن'
},
);
},
),
],
)
],
),
),
),
const SizedBox(
height: 32,
)
],
);
},
),
)
? const HoshanDrawer()
: null,
body: WillPopScope(
onWillPop: () async {
@ -519,193 +254,4 @@ class _HomeState extends State<Home>
),
);
}
Padding chatRow(ChatsModel chat, TextEditingController title,
HistoryAiChatState state, int index) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: InkWell(
onTap: () {
navigatorKey.currentState!.pushNamed(Routes.aiChat,
arguments: AiChatArgs(bot: chat.bot!, chat: chat));
},
child: Row(
children: [
ClipOval(
child: CachedNetworkImage(
imageUrl: chat.bot!.image.toString(),
width: 24,
height: 24,
)),
const SizedBox(
width: 12,
),
Expanded(
child: Text(
chat.title.toString(),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
const SizedBox(
width: 24,
),
Row(
children: [
InkWell(
onTap: () async {
ActionSheetUtils(context).openDialog(
data: ActionSheetData(
content: Center(
child: TextFormField(
controller: title,
style: const TextStyle(fontSize: 12),
textAlignVertical: TextAlignVertical.bottom,
maxLines: 3,
decoration: const InputDecoration(
isDense: true,
contentPadding: EdgeInsets.symmetric(
vertical: 5, horizontal: 10),
border: OutlineInputBorder(),
)),
),
title: 'تغییر نام',
onConfirmed: () async {
if (title.text.isNotEmpty) {
await state.changeNameChat(
chat.id!, index, title.text,
refresh: false);
title.clear();
}
if (chat.isEditing != null) {
chat.isEditing = !chat.isEditing!;
state.update();
return;
}
state.update();
},
));
},
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.edit_outlined,
size: 20,
),
),
),
const SizedBox(
width: 4,
),
PopupMenuButton(
onSelected: (value) async {
switch (value) {
case 'حذف پیام':
await state.deleteChat(chat.id!, index, refresh: false);
break;
case 'آرشیو':
await state.archivedChat(chat.id!, index,
refresh: false);
break;
default:
}
state.update();
},
itemBuilder: (BuildContext context) {
return <PopupMenuEntry>[
ActionSheetUtils.popUpBtns(
value: 'حذف پیام',
icon: DidvanIcons.trash_regular,
color: Theme.of(context).colorScheme.error,
height: 32,
size: 16),
ActionSheetUtils.popUpBtns(
value: 'آرشیو',
icon: Icons.folder_copy,
height: 32,
size: 16,
),
];
},
offset: const Offset(0, 0),
position: PopupMenuPosition.under,
useRootNavigator: true,
child: const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(
Icons.more_vert,
size: 20,
),
),
),
],
)
],
),
),
);
}
Widget drawerBtn(
{final CrossAxisAlignment? crossAxisAlignment,
required final IconData icon,
required final String text,
final bool enable = true,
final String? label,
final Function()? labelClick,
final Function()? click}) {
return InkWell(
onTap: enable
? click
: () {
ActionSheetUtils(context).showAlert(
AlertData(message: 'درحال توسعه', aLertType: ALertType.info));
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
crossAxisAlignment:
crossAxisAlignment ?? CrossAxisAlignment.center,
children: [
Icon(
icon,
color: enable
? Theme.of(context).colorScheme.title
: Theme.of(context).colorScheme.disabledText,
),
const SizedBox(
width: 8,
),
Column(
children: [
DidvanText(text,
fontSize: 16,
color: enable
? Theme.of(context).colorScheme.title
: Theme.of(context).colorScheme.disabledText),
// if (!enable) Text('در حال توسعه ...')
],
)
],
),
if (label != null)
InkWell(
onTap: labelClick,
child: DidvanText(
label,
color: Theme.of(context).colorScheme.primary,
fontSize: 12,
),
)
],
),
),
);
}
}

View File

@ -0,0 +1,90 @@
import 'dart:math';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart';
import 'package:didvan/views/ai/ai_state.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget {
final Function()? onBack;
const HoshanAppBar({Key? key, this.onBack}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20)),
color: Theme.of(context).colorScheme.surface,
boxShadow: [
BoxShadow(
color: const Color(0XFF1B3C59).withOpacity(0.15),
blurRadius: 8,
spreadRadius: 0,
offset: const Offset(0, 8),
)
],
),
padding: const EdgeInsets.all(16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Row(
children: [
Icon(
DidvanIcons.ai_solid,
size: 40,
),
DidvanText(
'هوشان',
fontSize: 14,
fontWeight: FontWeight.bold,
),
],
),
Row(
children: [
DidvanIconButton(
icon: DidvanIcons.info_circle_light,
size: 32,
onPressed: () {}),
DidvanIconButton(
icon: DidvanIcons.antenna_light,
size: 32,
onPressed: () {},
),
context.watch<AiState>().page != 0
? Transform.rotate(
angle: 180 * pi / 180,
child: DidvanIconButton(
icon: DidvanIcons.back_light,
size: 32,
onPressed: () => onBack?.call(),
),
)
: InkWell(
onTap: () {
context.read<AiState>().goToTools();
},
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Image.asset(
Assets.boxAnimation,
width: 38,
height: 38,
),
),
),
],
)
],
));
}
@override
Size get preferredSize => const Size(double.infinity, 144);
}