friday coffegit config
This commit is contained in:
parent
9531a112d4
commit
6dad9ad68b
|
|
@ -1,5 +1,5 @@
|
|||
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 {
|
||||
int? id;
|
||||
|
|
@ -98,7 +98,6 @@ class Prompts {
|
|||
bool? finished;
|
||||
bool? error;
|
||||
bool? audio;
|
||||
FilesModel? fileLocal;
|
||||
int? duration;
|
||||
|
||||
Prompts(
|
||||
|
|
@ -111,7 +110,6 @@ class Prompts {
|
|||
this.createdAt,
|
||||
this.finished,
|
||||
this.error,
|
||||
this.fileLocal,
|
||||
this.audio,
|
||||
this.duration});
|
||||
|
||||
|
|
@ -147,7 +145,7 @@ class Prompts {
|
|||
String? text,
|
||||
String? file,
|
||||
String? fileName,
|
||||
FilesModel? fileLocal,
|
||||
XFile? fileLocal,
|
||||
String? role,
|
||||
String? createdAt,
|
||||
bool? finished,
|
||||
|
|
@ -161,7 +159,6 @@ class Prompts {
|
|||
text: text ?? this.text,
|
||||
file: file ?? this.file,
|
||||
fileName: fileName ?? this.fileName,
|
||||
fileLocal: fileLocal ?? this.fileLocal,
|
||||
role: role ?? this.role,
|
||||
createdAt: createdAt ?? this.createdAt,
|
||||
finished: finished ?? this.finished,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
enum MyFileType{
|
||||
image,
|
||||
audio,
|
||||
file
|
||||
}
|
||||
|
|
@ -1,45 +1,45 @@
|
|||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
// import 'dart:io';
|
||||
// import 'dart:typed_data';
|
||||
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
// import 'package:mime/mime.dart';
|
||||
// import 'package:path/path.dart' as p;
|
||||
|
||||
class FilesModel {
|
||||
final String path;
|
||||
late String basename;
|
||||
late String extname;
|
||||
late File main;
|
||||
final bool isRecorded;
|
||||
final bool? audio;
|
||||
final bool? image;
|
||||
final bool? network;
|
||||
final Uint8List? bytes;
|
||||
final Duration? duration;
|
||||
// class FilesModel {
|
||||
// final String path;
|
||||
// late String basename;
|
||||
// late String extname;
|
||||
// late File main;
|
||||
// final bool isRecorded;
|
||||
// final bool? audio;
|
||||
// final bool? image;
|
||||
// final bool? network;
|
||||
// final Uint8List? bytes;
|
||||
// final Duration? duration;
|
||||
|
||||
FilesModel(
|
||||
this.path, {
|
||||
final String? name,
|
||||
this.isRecorded = false,
|
||||
this.audio,
|
||||
this.image,
|
||||
this.network,
|
||||
this.bytes,
|
||||
this.duration,
|
||||
}) {
|
||||
basename = name ?? p.basename(path);
|
||||
extname = p.extension(path);
|
||||
main = File(path);
|
||||
}
|
||||
// FilesModel(
|
||||
// this.path, {
|
||||
// final String? name,
|
||||
// this.isRecorded = false,
|
||||
// this.audio,
|
||||
// this.image,
|
||||
// this.network,
|
||||
// this.bytes,
|
||||
// this.duration,
|
||||
// }) {
|
||||
// basename = name ?? p.basename(path);
|
||||
// extname = path.isNotEmpty ? p.extension(path): name !=null ? p.extension(name): '';
|
||||
// main = File(path);
|
||||
// }
|
||||
|
||||
bool isAudio() {
|
||||
return audio ?? lookupMimeType(path)?.startsWith('audio/') ?? false;
|
||||
}
|
||||
// bool isAudio() {
|
||||
// return audio ?? lookupMimeType(path)?.startsWith('audio/') ?? false;
|
||||
// }
|
||||
|
||||
bool isImage() {
|
||||
return image ?? lookupMimeType(path)?.startsWith('image/') ?? false;
|
||||
}
|
||||
// bool isImage() {
|
||||
// return image ?? lookupMimeType(path)?.startsWith('image/') ?? false;
|
||||
// }
|
||||
|
||||
bool isNetwork() {
|
||||
return network ?? path.startsWith('blob:') || path.startsWith('/uploads');
|
||||
}
|
||||
}
|
||||
// bool isNetwork() {
|
||||
// return network ?? path.startsWith('blob:') || path.startsWith('/uploads');
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@
|
|||
|
||||
import 'dart:async';
|
||||
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:flutter/foundation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
|
||||
class AiApiService {
|
||||
static const String baseUrl = 'https://api.didvan.app/ai';
|
||||
|
|
@ -17,7 +16,7 @@ class AiApiService {
|
|||
{required final String url,
|
||||
required final String message,
|
||||
final int? chatId,
|
||||
final FilesModel? file,
|
||||
final XFile? file,
|
||||
final bool? edite}) async {
|
||||
final headers = {
|
||||
"Authorization": "Bearer ${await StorageService.getValue(key: 'token')}",
|
||||
|
|
@ -36,47 +35,61 @@ class AiApiService {
|
|||
}
|
||||
|
||||
if (file != null) {
|
||||
if (file.duration != null) {
|
||||
request.fields['duration'] = file.duration!.inSeconds.toString();
|
||||
}
|
||||
// if (file.duration != null) {
|
||||
// request.fields['duration'] = file.duration!.inSeconds.toString();
|
||||
// }
|
||||
Uint8List bytes;
|
||||
String filename;
|
||||
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
|
||||
// final blob = await html.window
|
||||
// .fetch(file.path.replaceAll('%3A', ':'))
|
||||
// .then((response) => response.blob());
|
||||
bytes = await file.readAsBytes();
|
||||
filename = file.name;
|
||||
mimeType = lookupMimeType(filename) ?? 'application/octet-stream';
|
||||
print("mimeType: $mimeType");
|
||||
|
||||
// // Read the blob as an array buffer
|
||||
// final reader = html.FileReader();
|
||||
// reader.readAsArrayBuffer(blob);
|
||||
// await reader.onLoadEnd.first;
|
||||
// bytes = reader.result as Uint8List;
|
||||
}
|
||||
} else {
|
||||
// For other platforms
|
||||
bytes = await file.main.readAsBytes();
|
||||
}
|
||||
filename = file.basename;
|
||||
|
||||
mimeType = file.isAudio()
|
||||
? file.isRecorded
|
||||
? 'audio/m4a}'
|
||||
: 'audio/${file.extname.replaceAll('.', '')}'
|
||||
: file.isImage()
|
||||
? 'image/png'
|
||||
: 'application/pdf';
|
||||
// switch (file.extname) {
|
||||
// case '.mp3':
|
||||
// mimeType = 'audio/mpeg';
|
||||
// break;
|
||||
// case '.wav':
|
||||
// mimeType = 'audio/wav';
|
||||
// break;
|
||||
// case '.aac':
|
||||
// mimeType = 'audio/aac';
|
||||
// break;
|
||||
// case '.m4a':
|
||||
// mimeType = 'audio/x-m4a'; // or 'audio/aac'
|
||||
// break;
|
||||
// case '.ogg':
|
||||
// mimeType = 'audio/ogg';
|
||||
// break;
|
||||
// case '.flac':
|
||||
// mimeType = 'audio/x-flac';
|
||||
// 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(
|
||||
'file',
|
||||
|
|
@ -86,7 +99,6 @@ class AiApiService {
|
|||
));
|
||||
}
|
||||
|
||||
// print("req: ${request.files}");
|
||||
// print("req: ${request.fields}");
|
||||
|
||||
return request;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// ignore_for_file: avoid_web_libraries_in_flutter
|
||||
|
||||
import 'package:didvan/main.dart';
|
||||
import 'package:didvan/models/ai/file_type.dart';
|
||||
import 'package:didvan/models/notification_message.dart';
|
||||
import 'package:didvan/models/requests/news.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:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class AppInitializer {
|
||||
static String? fcmToken;
|
||||
|
|
@ -204,4 +206,34 @@ class AppInitializer {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import 'package:didvan/services/notification/notification_service.dart';
|
|||
import 'package:didvan/services/storage/storage.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class FirebaseApi {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// 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:didvan/config/design_config.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/models/ai/ai_chat_args.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/view/action_sheet_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/date_time.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_markdown/flutter_markdown.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:provider/provider.dart';
|
||||
|
||||
|
|
@ -268,10 +272,12 @@ class _AiChatPageState extends State<AiChatPage> {
|
|||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
padding: EdgeInsets.only(
|
||||
bottom: state.file != null &&
|
||||
!state.file!.isRecorded
|
||||
bottom:
|
||||
state.file != null &&
|
||||
!state.isRecorded
|
||||
? 150
|
||||
: 100),
|
||||
:
|
||||
100),
|
||||
itemBuilder: (context, mIndex) {
|
||||
final prompts = state.messages[mIndex].prompts;
|
||||
final time = state.messages[mIndex].dateTime;
|
||||
|
|
@ -329,13 +335,12 @@ class _AiChatPageState extends State<AiChatPage> {
|
|||
|
||||
Widget messageBubble(Prompts message, BuildContext context, AiChatState state,
|
||||
int index, int mIndex) {
|
||||
FilesModel? file = message.fileLocal ??
|
||||
(message.file == null
|
||||
? null
|
||||
: FilesModel(message.file.toString(),
|
||||
duration: message.duration != null
|
||||
? Duration(seconds: message.duration!)
|
||||
: null));
|
||||
String? fileUrl = message.file;
|
||||
String? fileName = message.fileName;
|
||||
MyFileType? fileType ;
|
||||
if(fileName != null){
|
||||
fileType = AppInitializer.getFileType(fileName);
|
||||
}
|
||||
|
||||
MarkdownStyleSheet defaultMarkdownStyleSheet = MarkdownStyleSheet(
|
||||
pPadding: const EdgeInsets.all(0.8),
|
||||
|
|
@ -431,22 +436,22 @@ class _AiChatPageState extends State<AiChatPage> {
|
|||
: Column(
|
||||
children: [
|
||||
if (message.role.toString().contains('user') &&
|
||||
file != null)
|
||||
(file.isAudio()
|
||||
fileUrl != null && fileName != null)
|
||||
(fileType == MyFileType.audio
|
||||
// && (!kIsWeb && !Platform.isIOS)
|
||||
)
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16, vertical: 8),
|
||||
child: AudioWave(
|
||||
file: file.path,
|
||||
totalDuration: file.duration,
|
||||
file: fileUrl,
|
||||
// totalDuration: file.duration,
|
||||
),
|
||||
)
|
||||
: file.isImage()
|
||||
: fileType == MyFileType.image
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: messageImage(file),
|
||||
child: messageImage(fileUrl),
|
||||
)
|
||||
: Padding(
|
||||
padding: const EdgeInsets.all(
|
||||
|
|
@ -509,6 +514,7 @@ class _AiChatPageState extends State<AiChatPage> {
|
|||
.remove(message);
|
||||
state.messages.last.prompts.add(
|
||||
message.copyWith(error: false));
|
||||
state.file = state.messages.last.prompts.last.file!= null? XFile(state.messages.last.prompts.last.file!): null;
|
||||
state.update();
|
||||
await state
|
||||
.postMessage(widget.args.bot);
|
||||
|
|
@ -626,7 +632,7 @@ class _AiChatPageState extends State<AiChatPage> {
|
|||
),
|
||||
if (state.file != null && !kIsWeb)
|
||||
FutureBuilder(
|
||||
future: state.file!.main.length(),
|
||||
future: state.file!.length(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const SizedBox();
|
||||
|
|
@ -644,23 +650,23 @@ class _AiChatPageState extends State<AiChatPage> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget messageImage(FilesModel file) {
|
||||
Widget messageImage(String file) {
|
||||
return GestureDetector(
|
||||
onTap: () => ActionSheetUtils(context)
|
||||
.openInteractiveViewer(context, file.path, !file.isNetwork()),
|
||||
child: file.isNetwork()
|
||||
? file.path.startsWith('blob:')
|
||||
.openInteractiveViewer(context, file, !(file.startsWith('blob:') || file.startsWith('/uploads'))),
|
||||
child: (file.startsWith('blob:') || file.startsWith('/uploads'))
|
||||
? file.startsWith('blob:')
|
||||
? ClipRRect(
|
||||
borderRadius: DesignConfig.lowBorderRadius,
|
||||
child: Image.network(file.path))
|
||||
child: Image.network(file))
|
||||
: SkeletonImage(
|
||||
pWidth: MediaQuery.sizeOf(context).width / 1,
|
||||
pHeight: MediaQuery.sizeOf(context).height / 6,
|
||||
imageUrl: file.path,
|
||||
imageUrl: file,
|
||||
)
|
||||
: ClipRRect(
|
||||
borderRadius: DesignConfig.lowBorderRadius,
|
||||
child: Image.file(file.main)),
|
||||
child: Image.file(File(file))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ import 'package:didvan/main.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:persian_number_utility/persian_number_utility.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:didvan/models/ai/bots_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/enums.dart';
|
||||
import 'package:didvan/models/view/alert_data.dart';
|
||||
|
|
@ -33,7 +33,8 @@ class AiChatState extends CoreProvier {
|
|||
final ScrollController scrollController = ScrollController();
|
||||
int? chatId;
|
||||
ChatsModel? chat;
|
||||
FilesModel? file;
|
||||
XFile? file;
|
||||
bool isRecorded = false;
|
||||
TextEditingController message = TextEditingController();
|
||||
|
||||
Future<void> _scrolledEnd() async {
|
||||
|
|
@ -159,6 +160,10 @@ class AiChatState extends CoreProvier {
|
|||
chatId: chatId,
|
||||
file: file,
|
||||
edite: isEdite);
|
||||
file = null;
|
||||
isRecorded = false;
|
||||
update();
|
||||
|
||||
final res = await AiApiService().getResponse(req).catchError((e) {
|
||||
_onError(e);
|
||||
// return e;
|
||||
|
|
@ -166,7 +171,6 @@ class AiChatState extends CoreProvier {
|
|||
|
||||
String responseMessgae = '';
|
||||
String dataMessgae = '';
|
||||
file = null;
|
||||
update();
|
||||
|
||||
final r = res.listen((value) async {
|
||||
|
|
|
|||
|
|
@ -6,11 +6,10 @@ import 'package:didvan/config/theme_data.dart';
|
|||
import 'package:didvan/constants/app_icons.dart';
|
||||
import 'package:didvan/models/ai/bots_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/services/app_initalizer.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/date_time.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:record/record.dart';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class AiMessageBar extends StatefulWidget {
|
||||
|
|
@ -126,8 +126,8 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
ignoring: state.onResponsing,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 24).copyWith(
|
||||
top: openAttach ||
|
||||
(state.file != null && !state.file!.isRecorded)
|
||||
top: openAttach
|
||||
||(state.file != null && !state.isRecorded)
|
||||
? 0
|
||||
: 24),
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -162,25 +162,7 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
FilePickerResult? result =
|
||||
await MediaService.pickPdfFile();
|
||||
if (result != null) {
|
||||
if (kIsWeb) {
|
||||
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);
|
||||
}
|
||||
state.file = result.files.first.xFile;
|
||||
|
||||
openAttach = false;
|
||||
}
|
||||
|
|
@ -234,13 +216,8 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
|
||||
return;
|
||||
}
|
||||
state.file = kIsWeb
|
||||
? FilesModel(pickedFile.path,
|
||||
name: pickedFile.name,
|
||||
image: true,
|
||||
audio: false)
|
||||
: FilesModel(file!.path,
|
||||
image: true, audio: false);
|
||||
state.file =
|
||||
kIsWeb ? pickedFile : XFile(file!.path);
|
||||
openAttach = false;
|
||||
await Future.delayed(
|
||||
Duration.zero,
|
||||
|
|
@ -261,24 +238,8 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
FilePickerResult? result =
|
||||
await MediaService.pickAudioFile();
|
||||
if (result != null) {
|
||||
if (kIsWeb) {
|
||||
Uint8List? bytes = result.files.first
|
||||
.bytes; // Access the bytes property
|
||||
String? name = result.files.first.name;
|
||||
state.file = result.files.first.xFile;
|
||||
|
||||
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;
|
||||
}
|
||||
await Future.delayed(
|
||||
|
|
@ -339,21 +300,29 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
path = await record
|
||||
.stop();
|
||||
|
||||
Duration? duration =
|
||||
await VoiceService
|
||||
.getDuration(
|
||||
src: path ??
|
||||
'');
|
||||
// Duration? duration =
|
||||
// await VoiceService
|
||||
// .getDuration(
|
||||
// src: path ??
|
||||
// '');
|
||||
|
||||
state.file = FilesModel(
|
||||
path.toString(),
|
||||
name:
|
||||
'${DateTime.now().millisecondsSinceEpoch ~/ 1000}.m4a',
|
||||
isRecorded: true,
|
||||
audio: true,
|
||||
image: false,
|
||||
duration:
|
||||
duration);
|
||||
final Uri audioUri =
|
||||
Uri.parse(path!
|
||||
.replaceAll(
|
||||
'%3A',
|
||||
':'));
|
||||
final http.Response
|
||||
audioResponse =
|
||||
await http.get(
|
||||
audioUri);
|
||||
final bytes =
|
||||
audioResponse
|
||||
.bodyBytes;
|
||||
state.file = kIsWeb
|
||||
? XFile.fromData(
|
||||
bytes)
|
||||
: XFile(path!);
|
||||
state.isRecorded = true;
|
||||
_timer.cancel();
|
||||
_countTimer.value = 0;
|
||||
state.update();
|
||||
|
|
@ -409,9 +378,11 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
)
|
||||
: MessageBarBtn(
|
||||
enable: (state.file !=
|
||||
null &&
|
||||
state.file!
|
||||
.isRecorded) ||
|
||||
null
|
||||
&&
|
||||
state
|
||||
.isRecorded
|
||||
) ||
|
||||
(widget.bot
|
||||
.attachment ==
|
||||
1) ||
|
||||
|
|
@ -421,10 +392,11 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
.send_light,
|
||||
click: () async {
|
||||
if ((state.file ==
|
||||
null ||
|
||||
null
|
||||
||
|
||||
!state
|
||||
.file!
|
||||
.isRecorded) &&
|
||||
.isRecorded
|
||||
) &&
|
||||
(widget.bot
|
||||
.attachment !=
|
||||
1) &&
|
||||
|
|
@ -459,10 +431,7 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
?.path,
|
||||
fileName: state
|
||||
.file
|
||||
?.basename,
|
||||
fileLocal:
|
||||
state
|
||||
.file,
|
||||
?.name,
|
||||
finished:
|
||||
true,
|
||||
role: 'user',
|
||||
|
|
@ -490,14 +459,12 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
.text,
|
||||
finished:
|
||||
true,
|
||||
file: state
|
||||
.file
|
||||
?.path,
|
||||
fileName: state
|
||||
.file
|
||||
?.basename,
|
||||
fileLocal:
|
||||
state.file,
|
||||
file: state
|
||||
.file
|
||||
?.path,
|
||||
fileName: state
|
||||
.file
|
||||
?.name,
|
||||
role:
|
||||
'user',
|
||||
createdAt: DateTime.now()
|
||||
|
|
@ -585,9 +552,10 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
],
|
||||
),
|
||||
)
|
||||
: state.file != null &&
|
||||
state.file!
|
||||
.isRecorded
|
||||
: state.file != null
|
||||
&&
|
||||
AppInitializer.getFileType(state.file!.name)
|
||||
== MyFileType.audio && state.isRecorded
|
||||
? audioContainer()
|
||||
: Form(
|
||||
child:
|
||||
|
|
@ -803,9 +771,16 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
}
|
||||
|
||||
AnimatedVisibility fileContainer() {
|
||||
late MyFileType fileType;
|
||||
final state = context.watch<AiChatState>();
|
||||
if(state.file != null ){
|
||||
fileType = AppInitializer.getFileType(state.file!.name);
|
||||
}
|
||||
|
||||
return AnimatedVisibility(
|
||||
isVisible: state.file != null && !state.file!.isRecorded,
|
||||
isVisible: state.file != null
|
||||
&& !state.isRecorded
|
||||
,
|
||||
duration: DesignConfig.lowAnimationDuration,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
|
|
@ -816,19 +791,19 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
margin: const EdgeInsets.only(bottom: 8, left: 12, right: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
state.file != null && state.file!.isImage()
|
||||
state.file != null && (fileType == MyFileType.image)
|
||||
? SizedBox(
|
||||
width: 32,
|
||||
height: 42,
|
||||
child: ClipRRect(
|
||||
borderRadius: DesignConfig.lowBorderRadius,
|
||||
child: state.file!.isNetwork()
|
||||
child: state.file!.path.startsWith('blob:')
|
||||
? Image.network(
|
||||
state.file!.path,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Image.file(
|
||||
state.file!.main,
|
||||
File(state.file!.path),
|
||||
fit: BoxFit.cover,
|
||||
)))
|
||||
: const Icon(Icons.file_copy),
|
||||
|
|
@ -842,14 +817,14 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
SizedBox(
|
||||
height: 24,
|
||||
child: MarqueeText(
|
||||
text: state.file != null ? state.file!.basename : '',
|
||||
text: state.file != null ? state.file!.name : '',
|
||||
style: const TextStyle(fontSize: 14),
|
||||
stop: const Duration(seconds: 3),
|
||||
),
|
||||
),
|
||||
if (state.file != null && !kIsWeb)
|
||||
FutureBuilder(
|
||||
future: state.file!.main.length(),
|
||||
future: state.file!.length(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const SizedBox();
|
||||
|
|
|
|||
|
|
@ -348,7 +348,7 @@ class _ProfilePageState extends State<ProfilePage> {
|
|||
),
|
||||
const SizedBox(height: 16),
|
||||
DidvanText(
|
||||
'نسخه نرمافزار: 3.3.3',
|
||||
'نسخه نرمافزار: 3.3.4',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
# Read more about iOS versioning at
|
||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
version: 3.3.3+3330
|
||||
version: 3.3.4+3340
|
||||
|
||||
environment:
|
||||
sdk: ">=2.19.0 <3.0.0"
|
||||
|
|
|
|||
Loading…
Reference in New Issue