friday coffegit config

This commit is contained in:
kiainsta23 2024-09-27 15:35:01 +03:30
parent 9531a112d4
commit 6dad9ad68b
11 changed files with 234 additions and 204 deletions

View File

@ -1,5 +1,5 @@
import 'package:didvan/models/ai/bots_model.dart'; import 'package:didvan/models/ai/bots_model.dart';
import 'package:didvan/models/ai/files_model.dart'; import 'package:image_picker/image_picker.dart';
class ChatsModel { class ChatsModel {
int? id; int? id;
@ -98,7 +98,6 @@ class Prompts {
bool? finished; bool? finished;
bool? error; bool? error;
bool? audio; bool? audio;
FilesModel? fileLocal;
int? duration; int? duration;
Prompts( Prompts(
@ -111,7 +110,6 @@ class Prompts {
this.createdAt, this.createdAt,
this.finished, this.finished,
this.error, this.error,
this.fileLocal,
this.audio, this.audio,
this.duration}); this.duration});
@ -147,7 +145,7 @@ class Prompts {
String? text, String? text,
String? file, String? file,
String? fileName, String? fileName,
FilesModel? fileLocal, XFile? fileLocal,
String? role, String? role,
String? createdAt, String? createdAt,
bool? finished, bool? finished,
@ -161,7 +159,6 @@ class Prompts {
text: text ?? this.text, text: text ?? this.text,
file: file ?? this.file, file: file ?? this.file,
fileName: fileName ?? this.fileName, fileName: fileName ?? this.fileName,
fileLocal: fileLocal ?? this.fileLocal,
role: role ?? this.role, role: role ?? this.role,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
finished: finished ?? this.finished, finished: finished ?? this.finished,

View File

@ -0,0 +1,5 @@
enum MyFileType{
image,
audio,
file
}

View File

@ -1,45 +1,45 @@
import 'dart:io'; // import 'dart:io';
import 'dart:typed_data'; // import 'dart:typed_data';
import 'package:mime/mime.dart'; // import 'package:mime/mime.dart';
import 'package:path/path.dart' as p; // import 'package:path/path.dart' as p;
class FilesModel { // class FilesModel {
final String path; // final String path;
late String basename; // late String basename;
late String extname; // late String extname;
late File main; // late File main;
final bool isRecorded; // final bool isRecorded;
final bool? audio; // final bool? audio;
final bool? image; // final bool? image;
final bool? network; // final bool? network;
final Uint8List? bytes; // final Uint8List? bytes;
final Duration? duration; // final Duration? duration;
FilesModel( // FilesModel(
this.path, { // this.path, {
final String? name, // final String? name,
this.isRecorded = false, // this.isRecorded = false,
this.audio, // this.audio,
this.image, // this.image,
this.network, // this.network,
this.bytes, // this.bytes,
this.duration, // this.duration,
}) { // }) {
basename = name ?? p.basename(path); // basename = name ?? p.basename(path);
extname = p.extension(path); // extname = path.isNotEmpty ? p.extension(path): name !=null ? p.extension(name): '';
main = File(path); // main = File(path);
} // }
bool isAudio() { // bool isAudio() {
return audio ?? lookupMimeType(path)?.startsWith('audio/') ?? false; // return audio ?? lookupMimeType(path)?.startsWith('audio/') ?? false;
} // }
bool isImage() { // bool isImage() {
return image ?? lookupMimeType(path)?.startsWith('image/') ?? false; // return image ?? lookupMimeType(path)?.startsWith('image/') ?? false;
} // }
bool isNetwork() { // bool isNetwork() {
return network ?? path.startsWith('blob:') || path.startsWith('/uploads'); // return network ?? path.startsWith('blob:') || path.startsWith('/uploads');
} // }
} // }

View File

@ -2,13 +2,12 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:didvan/models/ai/files_model.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/services/storage/storage.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
class AiApiService { class AiApiService {
static const String baseUrl = 'https://api.didvan.app/ai'; static const String baseUrl = 'https://api.didvan.app/ai';
@ -17,7 +16,7 @@ class AiApiService {
{required final String url, {required final String url,
required final String message, required final String message,
final int? chatId, final int? chatId,
final FilesModel? file, final XFile? file,
final bool? edite}) async { final bool? edite}) async {
final headers = { final headers = {
"Authorization": "Bearer ${await StorageService.getValue(key: 'token')}", "Authorization": "Bearer ${await StorageService.getValue(key: 'token')}",
@ -36,47 +35,61 @@ class AiApiService {
} }
if (file != null) { if (file != null) {
if (file.duration != null) { // if (file.duration != null) {
request.fields['duration'] = file.duration!.inSeconds.toString(); // request.fields['duration'] = file.duration!.inSeconds.toString();
} // }
Uint8List bytes; Uint8List bytes;
String filename; String filename;
String mimeType; String mimeType;
if (kIsWeb) {
// For web platform
if (file.bytes != null) {
bytes = file.bytes!;
} else {
final Uri audioUri = Uri.parse(file.path.replaceAll('%3A', ':'));
final http.Response audioResponse = await http.get(audioUri);
bytes = audioResponse.bodyBytes;
// final f = File.fromUri(Uri.parse(file.path));
// bytes = await f.readAsBytes();
// Fetch the blob using JavaScript interop bytes = await file.readAsBytes();
// final blob = await html.window filename = file.name;
// .fetch(file.path.replaceAll('%3A', ':')) mimeType = lookupMimeType(filename) ?? 'application/octet-stream';
// .then((response) => response.blob()); print("mimeType: $mimeType");
// // Read the blob as an array buffer // switch (file.extname) {
// final reader = html.FileReader(); // case '.mp3':
// reader.readAsArrayBuffer(blob); // mimeType = 'audio/mpeg';
// await reader.onLoadEnd.first; // break;
// bytes = reader.result as Uint8List; // case '.wav':
} // mimeType = 'audio/wav';
} else { // break;
// For other platforms // case '.aac':
bytes = await file.main.readAsBytes(); // mimeType = 'audio/aac';
} // break;
filename = file.basename; // case '.m4a':
// mimeType = 'audio/x-m4a'; // or 'audio/aac'
mimeType = file.isAudio() // break;
? file.isRecorded // case '.ogg':
? 'audio/m4a}' // mimeType = 'audio/ogg';
: 'audio/${file.extname.replaceAll('.', '')}' // break;
: file.isImage() // case '.flac':
? 'image/png' // mimeType = 'audio/x-flac';
: 'application/pdf'; // break;
// case '.wma':
// mimeType = 'audio/x-ms-wma';
// break;
// case '.amr':
// mimeType = 'audio/amr';
// break;
// case '.midi':
// mimeType = 'audio/midi';
// break;
// case '.weba':
// mimeType = 'audio/webm';
// break;
// case '.png':
// mimeType = 'image/png';
// break;
// case '.jpg':
// mimeType = 'image/jpeg';
// break;
// case '.pdf':
// mimeType = 'application/pdf';
// break;
// default:
// mimeType = lookupMimeType(filename) ?? 'application/octet-stream';
// }
request.files.add(http.MultipartFile.fromBytes( request.files.add(http.MultipartFile.fromBytes(
'file', 'file',
@ -86,7 +99,6 @@ class AiApiService {
)); ));
} }
// print("req: ${request.files}");
// print("req: ${request.fields}"); // print("req: ${request.fields}");
return request; return request;

View File

@ -1,6 +1,7 @@
// ignore_for_file: avoid_web_libraries_in_flutter // ignore_for_file: avoid_web_libraries_in_flutter
import 'package:didvan/main.dart'; import 'package:didvan/main.dart';
import 'package:didvan/models/ai/file_type.dart';
import 'package:didvan/models/notification_message.dart'; import 'package:didvan/models/notification_message.dart';
import 'package:didvan/models/requests/news.dart'; import 'package:didvan/models/requests/news.dart';
import 'package:didvan/models/requests/radar.dart'; import 'package:didvan/models/requests/radar.dart';
@ -16,6 +17,7 @@ import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
import 'package:path/path.dart' as p;
class AppInitializer { class AppInitializer {
static String? fcmToken; static String? fcmToken;
@ -204,4 +206,34 @@ class AppInitializer {
Navigator.of(context).pushNamed(Routes.web, arguments: src); Navigator.of(context).pushNamed(Routes.web, arguments: src);
} }
} }
static MyFileType getFileType(String extName) {
MyFileType result;
switch (p.extension(extName)) {
case '.mp3':
case '.wav':
case '.aac':
case '.m4a':
case '.ogg':
case '.flac':
case '.wma':
case '.amr':
case '.midi':
case '.weba':
result = MyFileType.audio;
break;
case '.png':
case '.jpg':
result = MyFileType.image;
break;
case '.pdf':
result = MyFileType.file;
break;
default:
result = MyFileType.file;
}
return result;
}
} }

View File

@ -6,7 +6,6 @@ import 'package:didvan/services/notification/notification_service.dart';
import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/services/storage/storage.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
class FirebaseApi { class FirebaseApi {

View File

@ -1,5 +1,7 @@
// ignore_for_file: library_private_types_in_public_api, deprecated_member_use, depend_on_referenced_packages // ignore_for_file: library_private_types_in_public_api, deprecated_member_use, depend_on_referenced_packages
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
@ -7,10 +9,11 @@ import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart'; import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/ai/ai_chat_args.dart'; import 'package:didvan/models/ai/ai_chat_args.dart';
import 'package:didvan/models/ai/chats_model.dart'; import 'package:didvan/models/ai/chats_model.dart';
import 'package:didvan/models/ai/files_model.dart'; import 'package:didvan/models/ai/file_type.dart';
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/view/action_sheet_data.dart'; import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/models/view/alert_data.dart'; import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/utils/date_time.dart'; import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/ai/ai_chat_state.dart'; import 'package:didvan/views/ai/ai_chat_state.dart';
@ -26,6 +29,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:image_picker/image_picker.dart';
import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -268,10 +272,12 @@ class _AiChatPageState extends State<AiChatPage> {
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
padding: EdgeInsets.only( padding: EdgeInsets.only(
bottom: state.file != null && bottom:
!state.file!.isRecorded state.file != null &&
!state.isRecorded
? 150 ? 150
: 100), :
100),
itemBuilder: (context, mIndex) { itemBuilder: (context, mIndex) {
final prompts = state.messages[mIndex].prompts; final prompts = state.messages[mIndex].prompts;
final time = state.messages[mIndex].dateTime; final time = state.messages[mIndex].dateTime;
@ -329,13 +335,12 @@ class _AiChatPageState extends State<AiChatPage> {
Widget messageBubble(Prompts message, BuildContext context, AiChatState state, Widget messageBubble(Prompts message, BuildContext context, AiChatState state,
int index, int mIndex) { int index, int mIndex) {
FilesModel? file = message.fileLocal ?? String? fileUrl = message.file;
(message.file == null String? fileName = message.fileName;
? null MyFileType? fileType ;
: FilesModel(message.file.toString(), if(fileName != null){
duration: message.duration != null fileType = AppInitializer.getFileType(fileName);
? Duration(seconds: message.duration!) }
: null));
MarkdownStyleSheet defaultMarkdownStyleSheet = MarkdownStyleSheet( MarkdownStyleSheet defaultMarkdownStyleSheet = MarkdownStyleSheet(
pPadding: const EdgeInsets.all(0.8), pPadding: const EdgeInsets.all(0.8),
@ -431,22 +436,22 @@ class _AiChatPageState extends State<AiChatPage> {
: Column( : Column(
children: [ children: [
if (message.role.toString().contains('user') && if (message.role.toString().contains('user') &&
file != null) fileUrl != null && fileName != null)
(file.isAudio() (fileType == MyFileType.audio
// && (!kIsWeb && !Platform.isIOS) // && (!kIsWeb && !Platform.isIOS)
) )
? Padding( ? Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8), horizontal: 16, vertical: 8),
child: AudioWave( child: AudioWave(
file: file.path, file: fileUrl,
totalDuration: file.duration, // totalDuration: file.duration,
), ),
) )
: file.isImage() : fileType == MyFileType.image
? Padding( ? Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: messageImage(file), child: messageImage(fileUrl),
) )
: Padding( : Padding(
padding: const EdgeInsets.all( padding: const EdgeInsets.all(
@ -509,6 +514,7 @@ class _AiChatPageState extends State<AiChatPage> {
.remove(message); .remove(message);
state.messages.last.prompts.add( state.messages.last.prompts.add(
message.copyWith(error: false)); message.copyWith(error: false));
state.file = state.messages.last.prompts.last.file!= null? XFile(state.messages.last.prompts.last.file!): null;
state.update(); state.update();
await state await state
.postMessage(widget.args.bot); .postMessage(widget.args.bot);
@ -626,7 +632,7 @@ class _AiChatPageState extends State<AiChatPage> {
), ),
if (state.file != null && !kIsWeb) if (state.file != null && !kIsWeb)
FutureBuilder( FutureBuilder(
future: state.file!.main.length(), future: state.file!.length(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) { if (!snapshot.hasData) {
return const SizedBox(); return const SizedBox();
@ -644,23 +650,23 @@ class _AiChatPageState extends State<AiChatPage> {
); );
} }
Widget messageImage(FilesModel file) { Widget messageImage(String file) {
return GestureDetector( return GestureDetector(
onTap: () => ActionSheetUtils(context) onTap: () => ActionSheetUtils(context)
.openInteractiveViewer(context, file.path, !file.isNetwork()), .openInteractiveViewer(context, file, !(file.startsWith('blob:') || file.startsWith('/uploads'))),
child: file.isNetwork() child: (file.startsWith('blob:') || file.startsWith('/uploads'))
? file.path.startsWith('blob:') ? file.startsWith('blob:')
? ClipRRect( ? ClipRRect(
borderRadius: DesignConfig.lowBorderRadius, borderRadius: DesignConfig.lowBorderRadius,
child: Image.network(file.path)) child: Image.network(file))
: SkeletonImage( : SkeletonImage(
pWidth: MediaQuery.sizeOf(context).width / 1, pWidth: MediaQuery.sizeOf(context).width / 1,
pHeight: MediaQuery.sizeOf(context).height / 6, pHeight: MediaQuery.sizeOf(context).height / 6,
imageUrl: file.path, imageUrl: file,
) )
: ClipRRect( : ClipRRect(
borderRadius: DesignConfig.lowBorderRadius, borderRadius: DesignConfig.lowBorderRadius,
child: Image.file(file.main)), child: Image.file(File(file))),
); );
} }
} }

View File

@ -6,11 +6,11 @@ import 'package:didvan/main.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:image_picker/image_picker.dart';
import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:didvan/models/ai/bots_model.dart'; import 'package:didvan/models/ai/bots_model.dart';
import 'package:didvan/models/ai/chats_model.dart'; 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/ai/messages_model.dart';
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/view/alert_data.dart'; import 'package:didvan/models/view/alert_data.dart';
@ -33,7 +33,8 @@ class AiChatState extends CoreProvier {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
int? chatId; int? chatId;
ChatsModel? chat; ChatsModel? chat;
FilesModel? file; XFile? file;
bool isRecorded = false;
TextEditingController message = TextEditingController(); TextEditingController message = TextEditingController();
Future<void> _scrolledEnd() async { Future<void> _scrolledEnd() async {
@ -159,6 +160,10 @@ class AiChatState extends CoreProvier {
chatId: chatId, chatId: chatId,
file: file, file: file,
edite: isEdite); edite: isEdite);
file = null;
isRecorded = false;
update();
final res = await AiApiService().getResponse(req).catchError((e) { final res = await AiApiService().getResponse(req).catchError((e) {
_onError(e); _onError(e);
// return e; // return e;
@ -166,7 +171,6 @@ class AiChatState extends CoreProvier {
String responseMessgae = ''; String responseMessgae = '';
String dataMessgae = ''; String dataMessgae = '';
file = null;
update(); update();
final r = res.listen((value) async { final r = res.listen((value) async {

View File

@ -6,11 +6,10 @@ import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/ai/bots_model.dart'; import 'package:didvan/models/ai/bots_model.dart';
import 'package:didvan/models/ai/chats_model.dart'; import 'package:didvan/models/ai/chats_model.dart';
import 'package:didvan/models/ai/files_model.dart'; import 'package:didvan/models/ai/file_type.dart';
import 'package:didvan/models/ai/messages_model.dart'; import 'package:didvan/models/ai/messages_model.dart';
import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/media/media.dart'; import 'package:didvan/services/media/media.dart';
import 'package:didvan/services/media/voice.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/utils/date_time.dart'; import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/ai/ai_chat_state.dart'; import 'package:didvan/views/ai/ai_chat_state.dart';
@ -33,6 +32,7 @@ import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:record/record.dart'; import 'package:record/record.dart';
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
class AiMessageBar extends StatefulWidget { class AiMessageBar extends StatefulWidget {
@ -126,8 +126,8 @@ class _AiMessageBarState extends State<AiMessageBar> {
ignoring: state.onResponsing, ignoring: state.onResponsing,
child: Container( child: Container(
padding: const EdgeInsets.symmetric(vertical: 24).copyWith( padding: const EdgeInsets.symmetric(vertical: 24).copyWith(
top: openAttach || top: openAttach
(state.file != null && !state.file!.isRecorded) ||(state.file != null && !state.isRecorded)
? 0 ? 0
: 24), : 24),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -162,25 +162,7 @@ class _AiMessageBarState extends State<AiMessageBar> {
FilePickerResult? result = FilePickerResult? result =
await MediaService.pickPdfFile(); await MediaService.pickPdfFile();
if (result != null) { if (result != null) {
if (kIsWeb) { state.file = result.files.first.xFile;
Uint8List? bytes = result.files.first
.bytes; // Access the bytes property
String? name = result.files.first.name;
// Store bytes and file name directly in your state or model
state.file = FilesModel(
'', // No need for a file path on web
name: name,
bytes: bytes,
audio: false,
image: false,
);
} else {
state.file = FilesModel(
result.files.single.path!,
audio: false,
image: false);
}
openAttach = false; openAttach = false;
} }
@ -234,13 +216,8 @@ class _AiMessageBarState extends State<AiMessageBar> {
return; return;
} }
state.file = kIsWeb state.file =
? FilesModel(pickedFile.path, kIsWeb ? pickedFile : XFile(file!.path);
name: pickedFile.name,
image: true,
audio: false)
: FilesModel(file!.path,
image: true, audio: false);
openAttach = false; openAttach = false;
await Future.delayed( await Future.delayed(
Duration.zero, Duration.zero,
@ -261,24 +238,8 @@ class _AiMessageBarState extends State<AiMessageBar> {
FilePickerResult? result = FilePickerResult? result =
await MediaService.pickAudioFile(); await MediaService.pickAudioFile();
if (result != null) { if (result != null) {
if (kIsWeb) { state.file = result.files.first.xFile;
Uint8List? bytes = result.files.first
.bytes; // Access the bytes property
String? name = result.files.first.name;
state.file = FilesModel(
'', // No need for a file path on web
name: name,
bytes: bytes,
audio: true,
image: false,
);
} else {
state.file = FilesModel(
result.files.single.path!,
audio: true,
image: false);
}
openAttach = false; openAttach = false;
} }
await Future.delayed( await Future.delayed(
@ -339,21 +300,29 @@ class _AiMessageBarState extends State<AiMessageBar> {
path = await record path = await record
.stop(); .stop();
Duration? duration = // Duration? duration =
await VoiceService // await VoiceService
.getDuration( // .getDuration(
src: path ?? // src: path ??
''); // '');
state.file = FilesModel( final Uri audioUri =
path.toString(), Uri.parse(path!
name: .replaceAll(
'${DateTime.now().millisecondsSinceEpoch ~/ 1000}.m4a', '%3A',
isRecorded: true, ':'));
audio: true, final http.Response
image: false, audioResponse =
duration: await http.get(
duration); audioUri);
final bytes =
audioResponse
.bodyBytes;
state.file = kIsWeb
? XFile.fromData(
bytes)
: XFile(path!);
state.isRecorded = true;
_timer.cancel(); _timer.cancel();
_countTimer.value = 0; _countTimer.value = 0;
state.update(); state.update();
@ -409,9 +378,11 @@ class _AiMessageBarState extends State<AiMessageBar> {
) )
: MessageBarBtn( : MessageBarBtn(
enable: (state.file != enable: (state.file !=
null && null
state.file! &&
.isRecorded) || state
.isRecorded
) ||
(widget.bot (widget.bot
.attachment == .attachment ==
1) || 1) ||
@ -421,10 +392,11 @@ class _AiMessageBarState extends State<AiMessageBar> {
.send_light, .send_light,
click: () async { click: () async {
if ((state.file == if ((state.file ==
null || null
||
!state !state
.file! .isRecorded
.isRecorded) && ) &&
(widget.bot (widget.bot
.attachment != .attachment !=
1) && 1) &&
@ -459,10 +431,7 @@ class _AiMessageBarState extends State<AiMessageBar> {
?.path, ?.path,
fileName: state fileName: state
.file .file
?.basename, ?.name,
fileLocal:
state
.file,
finished: finished:
true, true,
role: 'user', role: 'user',
@ -495,9 +464,7 @@ class _AiMessageBarState extends State<AiMessageBar> {
?.path, ?.path,
fileName: state fileName: state
.file .file
?.basename, ?.name,
fileLocal:
state.file,
role: role:
'user', 'user',
createdAt: DateTime.now() createdAt: DateTime.now()
@ -585,9 +552,10 @@ class _AiMessageBarState extends State<AiMessageBar> {
], ],
), ),
) )
: state.file != null && : state.file != null
state.file! &&
.isRecorded AppInitializer.getFileType(state.file!.name)
== MyFileType.audio && state.isRecorded
? audioContainer() ? audioContainer()
: Form( : Form(
child: child:
@ -803,9 +771,16 @@ class _AiMessageBarState extends State<AiMessageBar> {
} }
AnimatedVisibility fileContainer() { AnimatedVisibility fileContainer() {
late MyFileType fileType;
final state = context.watch<AiChatState>(); final state = context.watch<AiChatState>();
if(state.file != null ){
fileType = AppInitializer.getFileType(state.file!.name);
}
return AnimatedVisibility( return AnimatedVisibility(
isVisible: state.file != null && !state.file!.isRecorded, isVisible: state.file != null
&& !state.isRecorded
,
duration: DesignConfig.lowAnimationDuration, duration: DesignConfig.lowAnimationDuration,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -816,19 +791,19 @@ class _AiMessageBarState extends State<AiMessageBar> {
margin: const EdgeInsets.only(bottom: 8, left: 12, right: 12), margin: const EdgeInsets.only(bottom: 8, left: 12, right: 12),
child: Row( child: Row(
children: [ children: [
state.file != null && state.file!.isImage() state.file != null && (fileType == MyFileType.image)
? SizedBox( ? SizedBox(
width: 32, width: 32,
height: 42, height: 42,
child: ClipRRect( child: ClipRRect(
borderRadius: DesignConfig.lowBorderRadius, borderRadius: DesignConfig.lowBorderRadius,
child: state.file!.isNetwork() child: state.file!.path.startsWith('blob:')
? Image.network( ? Image.network(
state.file!.path, state.file!.path,
fit: BoxFit.cover, fit: BoxFit.cover,
) )
: Image.file( : Image.file(
state.file!.main, File(state.file!.path),
fit: BoxFit.cover, fit: BoxFit.cover,
))) )))
: const Icon(Icons.file_copy), : const Icon(Icons.file_copy),
@ -842,14 +817,14 @@ class _AiMessageBarState extends State<AiMessageBar> {
SizedBox( SizedBox(
height: 24, height: 24,
child: MarqueeText( child: MarqueeText(
text: state.file != null ? state.file!.basename : '', text: state.file != null ? state.file!.name : '',
style: const TextStyle(fontSize: 14), style: const TextStyle(fontSize: 14),
stop: const Duration(seconds: 3), stop: const Duration(seconds: 3),
), ),
), ),
if (state.file != null && !kIsWeb) if (state.file != null && !kIsWeb)
FutureBuilder( FutureBuilder(
future: state.file!.main.length(), future: state.file!.length(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) { if (!snapshot.hasData) {
return const SizedBox(); return const SizedBox();

View File

@ -348,7 +348,7 @@ class _ProfilePageState extends State<ProfilePage> {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
DidvanText( DidvanText(
'نسخه نرم‌افزار: 3.3.3', 'نسخه نرم‌افزار: 3.3.4',
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall,
), ),
], ],

View File

@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 3.3.3+3330 version: 3.3.4+3340
environment: environment:
sdk: ">=2.19.0 <3.0.0" sdk: ">=2.19.0 <3.0.0"