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/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,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
enum MyFileType{
|
||||||
|
image,
|
||||||
|
audio,
|
||||||
|
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');
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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))),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
|
|
@ -490,14 +459,12 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
||||||
.text,
|
.text,
|
||||||
finished:
|
finished:
|
||||||
true,
|
true,
|
||||||
file: state
|
file: state
|
||||||
.file
|
.file
|
||||||
?.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();
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue