fix audio

This commit is contained in:
OkaykOrhmn 2024-09-25 12:40:51 +03:30
parent 2b1be1989a
commit 74b7f9dd3d
11 changed files with 184 additions and 113 deletions

View File

@ -15,7 +15,7 @@ class MessageData {
final RadarAttachment? radar; final RadarAttachment? radar;
final File? audioFile; final File? audioFile;
final int? audioDuration; final int? audioDuration;
final double? duration; final int? duration;
MessageData({ MessageData({
required this.id, required this.id,
@ -60,4 +60,32 @@ class MessageData {
'news': news?.toJson(), 'news': news?.toJson(),
'radar': radar?.toJson(), 'radar': radar?.toJson(),
}; };
MessageData copyWith({
int? id,
String? text,
String? audio,
bool? writedByAdmin,
bool? readed,
String? createdAt,
NewsAttachment? news,
RadarAttachment? radar,
File? audioFile,
int? audioDuration,
int? duration,
}) {
return MessageData(
id: id ?? this.id,
text: text ?? this.text,
audio: audio ?? this.audio,
writedByAdmin: writedByAdmin ?? this.writedByAdmin,
readed: readed ?? this.readed,
news: news ?? this.news,
radar: radar ?? this.radar,
createdAt: createdAt ?? this.createdAt,
audioFile: audioFile ?? this.audioFile,
audioDuration: audioDuration ?? this.audioDuration,
duration: duration ?? this.duration,
);
}
} }

View File

@ -154,7 +154,7 @@ class _AiState extends State<Ai> {
const Padding( const Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0), padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Text( child: Text(
"به هوشان, هوش مصنوعی دیدوان خوش آمدید. \nبرای شروع گفتگو پیام مورد نظر خود را در کادر زیر بنویسید.", "به هوشان؛ هوش مصنوعی دیدوان خوش آمدید. \nبرای شروع گفتگو پیام مورد نظر خود را در کادر زیر بنویسید.",
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
) )

View File

@ -157,7 +157,7 @@ class _AiChatPageState extends State<AiChatPage> {
height: 12, height: 12,
), ),
const DidvanText( const DidvanText(
'دوست دارید هوشان، چه چیزهایی را درباره شما بداند تا بتواند پاسخ‌های بهتری ارائه دهد؟ \nدستورات و اطلاعات ارائه شما، بر روی تمامی پیام‌هایی که از این به بعد ارسال می‌کنید، اعمال خواهد شد.'), 'دوست دارید هوشان چه چیزهایی را درباره شما بداند تا بتواند پاسخ‌های بهتری ارائه دهد؟ '),
const SizedBox( const SizedBox(
height: 12, height: 12,
), ),
@ -254,7 +254,7 @@ class _AiChatPageState extends State<AiChatPage> {
const Padding( const Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0), padding: EdgeInsets.symmetric(horizontal: 20.0),
child: Text( child: Text(
"به هوشان, هوش مصنوعی دیدوان خوش آمدید. \nبرای شروع گفتگو پیام مورد نظر خود را در کادر زیر بنویسید.", "به هوشان؛ هوش مصنوعی دیدوان خوش آمدید. \nبرای شروع گفتگو پیام مورد نظر خود را در کادر زیر بنویسید.",
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
) )

View File

@ -38,7 +38,6 @@ class _AudioWaveState extends State<AudioWave> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
print(widget.file);
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
if (widget.totalDuration == null) { if (widget.totalDuration == null) {
await VoiceService.getDuration(src: widget.file).then((duration) { await VoiceService.getDuration(src: widget.file).then((duration) {
@ -116,64 +115,60 @@ class _AudioWaveState extends State<AudioWave> {
if (!snapshot.hasData) { if (!snapshot.hasData) {
return const SizedBox(); return const SizedBox();
} }
return totalDuration == null || if ((totalDuration == null ||
(totalDuration != null && (totalDuration != null &&
VoiceService.audioPlayer.playing && (VoiceService.audioPlayer.duration != null &&
VoiceService.audioPlayer.duration!.inSeconds != VoiceService
totalDuration!.inSeconds && .audioPlayer.duration!.inSeconds !=
VoiceService.src == widget.file) totalDuration!.inSeconds))) &&
? Padding( VoiceService.src == widget.file &&
padding: EdgeInsets.symmetric( VoiceService.audioPlayer.playing) {
vertical: widget.loadingPaddingSize), return Padding(
child: Row( padding: EdgeInsets.symmetric(
mainAxisAlignment: MainAxisAlignment.center, vertical: widget.loadingPaddingSize),
children: List.generate( child: Row(
5, mainAxisAlignment: MainAxisAlignment.center,
(index) => SpinKitWave( children: List.generate(
color: Theme.of(context) 5,
.colorScheme (index) => SpinKitWave(
.primary color: Theme.of(context)
.withOpacity(0.4), .colorScheme
size: 32, .primary
itemCount: 10, .withOpacity(0.4),
))), size: 32,
) itemCount: 10,
: Padding( ))),
padding: const EdgeInsets.fromLTRB(24, 12, 12, 0), );
child: ProgressBar( }
thumbColor: Theme.of(context).colorScheme.title, return Padding(
progressBarColor: DesignConfig.isDark padding: EdgeInsets.fromLTRB(
? Theme.of(context).colorScheme.title 24, 12, 12, totalDuration != null ? 0 : 12),
: Theme.of(context).colorScheme.primary, child: ProgressBar(
baseBarColor: thumbColor: Theme.of(context).colorScheme.title,
Theme.of(context).colorScheme.border, progressBarColor: DesignConfig.isDark
bufferedBarColor: ? Theme.of(context).colorScheme.title
Theme.of(context).colorScheme.splash, : Theme.of(context).colorScheme.primary,
total: totalDuration!, baseBarColor: Theme.of(context).colorScheme.border,
progress: VoiceService.src == widget.file bufferedBarColor: Theme.of(context).colorScheme.splash,
? snapshot.data ?? Duration.zero total: totalDuration ?? Duration.zero,
: Duration.zero, progress: VoiceService.src == widget.file
thumbRadius: 6, ? snapshot.data ?? Duration.zero
barHeight: 3, : Duration.zero,
// timeLabelTextStyle: TextStyle( thumbRadius: 6,
// fontSize: showTimer ? null : 0, barHeight: 3,
// height: showTimer ? 3 : 0, timeLabelTextStyle: TextStyle(
// color: fontSize: totalDuration != null ? null : 0,
// Theme.of(context).colorScheme.text, // height: totalDuration != null ? 3 : 0,
// fontFamily: color: Theme.of(context).colorScheme.text,
// DesignConfig.fontFamily.replaceAll( fontFamily: DesignConfig.fontFamily),
// '-FA', onSeek: (value) {
// '', if (VoiceService.src == widget.file) {
// ), VoiceService.audioPlayer.seek(
// ), Duration(milliseconds: value.inMilliseconds));
onSeek: (value) { }
if (VoiceService.src == widget.file) { },
VoiceService.audioPlayer.seek(Duration( ),
milliseconds: value.inMilliseconds)); );
}
},
),
);
}, },
), ),
) )

View File

@ -6,6 +6,7 @@ import 'package:didvan/models/message_data/news_attachment.dart';
import 'package:didvan/models/message_data/radar_attachment.dart'; import 'package:didvan/models/message_data/radar_attachment.dart';
import 'package:didvan/providers/core.dart'; import 'package:didvan/providers/core.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/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -25,7 +26,9 @@ class DirectState extends CoreProvier {
NewsAttachment? replyNews; NewsAttachment? replyNews;
RadarAttachment? replyRadar; RadarAttachment? replyRadar;
File? recordedFile; File? recordedFile;
Uint8List? recordedFileBytes;
int? audioDuration; int? audioDuration;
String? path;
bool isRecording = false; bool isRecording = false;
@ -74,21 +77,18 @@ class DirectState extends CoreProvier {
} }
Future<void> stopRecording({required bool sendImidiately}) async { Future<void> stopRecording({required bool sendImidiately}) async {
final path = await _recorder.stop(); path = await _recorder.stop();
isRecording = false; isRecording = false;
if (path == null) { if (path == null) {
notifyListeners(); notifyListeners();
return; return;
} }
if (kIsWeb) { if (kIsWeb) {
// final uri = Uri.file(path); final Uri audioUri = Uri.parse(path!);
// recordedFile = File.fromUri(uri);
final Uri audioUri = Uri.parse(path);
final http.Response audioResponse = await http.get(audioUri); final http.Response audioResponse = await http.get(audioUri);
final bytes = audioResponse.bodyBytes; recordedFileBytes = audioResponse.bodyBytes;
recordedFile = File.fromRawPath(bytes);
} else { } else {
recordedFile = File(path); recordedFile = File(path!);
} }
if (sendImidiately) { if (sendImidiately) {
await sendMessage(); await sendMessage();
@ -121,7 +121,8 @@ class DirectState extends CoreProvier {
} }
Future<void> sendMessage() async { Future<void> sendMessage() async {
if ((text == null || text!.isEmpty) && recordedFile == null) return; if ((text == null || text!.isEmpty) &&
(recordedFile == null && recordedFileBytes == null)) return;
MediaService.audioPlayer.stop(); MediaService.audioPlayer.stop();
final body = {}; final body = {};
@ -138,17 +139,31 @@ class DirectState extends CoreProvier {
body.addAll({'newsId': replyNews!.id}); body.addAll({'newsId': replyNews!.id});
} }
if (replyNews != null) {
body.addAll({'newsId': replyNews!.id});
}
if (path != null) {
final duration = await VoiceService.getDuration(src: path!);
if (duration != null) {
body.addAll({'duration': duration.inSeconds.toString()});
}
}
final uploadFile = recordedFile; final uploadFile = recordedFile;
final uploadFileBytes = recordedFileBytes;
text = null; text = null;
recordedFile = null; recordedFile = null;
recordedFileBytes = null;
replyRadar = null; replyRadar = null;
replyNews = null; replyNews = null;
path = null;
notifyListeners(); notifyListeners();
final service = final service =
RequestService(RequestHelper.sendDirectMessage(typeId), body: body); RequestService(RequestHelper.sendDirectMessage(typeId), body: body);
if (uploadFile == null) { if (uploadFile == null && uploadFileBytes == null) {
await service.post(); await service.post();
if (service.isSuccess) { if (service.isSuccess) {
@ -171,7 +186,7 @@ class DirectState extends CoreProvier {
createdAt: createdAt:
DateTime.now().subtract(const Duration(minutes: 210)).toString(), DateTime.now().subtract(const Duration(minutes: 210)).toString(),
text: text, text: text,
audio: null, audio: path,
audioFile: uploadFile, audioFile: uploadFile,
radar: replyRadar, radar: replyRadar,
news: replyNews, news: replyNews,
@ -185,20 +200,33 @@ class DirectState extends CoreProvier {
_addToDailyGrouped(messages[i]); _addToDailyGrouped(messages[i]);
} }
await service.multipart( if (kIsWeb) {
file: Platform.isIOS await service.multipartBytes(
? File(uploadFile.path.replaceAll('file://', '')) file: uploadFileBytes!,
: uploadFile, method: 'POST',
method: 'POST', fieldName: 'audio',
fieldName: 'audio', fileName: 'voice-message',
fileName: 'voice-message', mediaExtension: 'm4a',
mediaExtension: 'm4a', mediaFormat: 'audio',
mediaFormat: 'audio', );
); } else {
await service.multipart(
file: Platform.isIOS
? File(uploadFile!.path.replaceAll('file://', ''))
: uploadFile,
method: 'POST',
fieldName: 'audio',
fileName: 'voice-message',
mediaExtension: 'm4a',
mediaFormat: 'audio',
);
}
if (service.isSuccess) { if (service.isSuccess) {
final message = service.result['message']; final message = service.result['message'];
messages[0].id = MessageData.fromJson(message).id; final m = MessageData.fromJson(message);
messages[0] = m;
update();
} }
} }
} }

View File

@ -2,16 +2,13 @@ import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; 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/message_data/message_data.dart'; import 'package:didvan/models/message_data/message_data.dart';
import 'package:didvan/services/media/voice.dart';
import 'package:didvan/utils/date_time.dart'; import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/ai/widgets/audio_wave.dart'; import 'package:didvan/views/ai/widgets/audio_wave.dart';
import 'package:didvan/views/direct/direct_state.dart'; import 'package:didvan/views/direct/direct_state.dart';
import 'package:didvan/views/direct/widgets/audio_widget.dart';
import 'package:didvan/views/widgets/didvan/divider.dart'; import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:persian_number_utility/persian_number_utility.dart';
@ -89,6 +86,9 @@ class Message extends StatelessWidget {
if (message.audio != null || message.audioFile != null) if (message.audio != null || message.audioFile != null)
AudioWave( AudioWave(
file: message.audio ?? message.audioFile!.path, file: message.audio ?? message.audioFile!.path,
totalDuration: message.duration != null
? Duration(seconds: message.duration!)
: null,
), ),
if (message.radar != null || message.news != null) if (message.radar != null || message.news != null)
const DidvanDivider(), const DidvanDivider(),

View File

@ -1,9 +1,9 @@
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';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/views/ai/widgets/audio_wave.dart';
import 'package:didvan/views/ai/widgets/message_bar_btn.dart'; import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:didvan/views/direct/direct_state.dart'; import 'package:didvan/views/direct/direct_state.dart';
import 'package:didvan/views/direct/widgets/audio_widget.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -64,7 +64,9 @@ class MessageBox extends StatelessWidget {
builder: (context, state, child) { builder: (context, state, child) {
if (state.isRecording) { if (state.isRecording) {
return const _Recording(); return const _Recording();
} else if (!state.isRecording && state.recordedFile != null) { } else if (!state.isRecording &&
(state.recordedFile != null ||
state.recordedFileBytes != null)) {
return const _RecordChecking(); return const _RecordChecking();
} }
return const _Typing(); return const _Typing();
@ -223,9 +225,8 @@ class _RecordChecking extends StatelessWidget {
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8), padding: const EdgeInsets.symmetric(vertical: 8),
child: AudioWidget( child: AudioWave(
audioFile: state.recordedFile!, file: state.path!,
id: 0,
// deleteClidk: () => state.deleteRecordedFile, // deleteClidk: () => state.deleteRecordedFile,
), ),
), ),

View File

@ -154,7 +154,7 @@ class _BookmarksState extends State<Bookmarks> {
: Column( : Column(
children: [ children: [
DidvanText( DidvanText(
'در قسمت رصدخانه من، تمامی محتواهایی که در قسمت‌های مختلف سوپراپلیکیشن دیدوان، بوکمارک (نشان‌دار) کرده‌اید، به تفکیک نمایش داده می‌شوند. هم‌چنین امکان درج یادداشت شخصی بصورت ضمیمه برای هر محتوا وجود دارد.', 'در قسمت رصدخانه من، تمامی مطالبی که در قسمت‌های مختلف سوپراپلیکیشن دیدوان، بوکمارک (نشان‌دار) کرده‌اید، به تفکیک نمایش داده می‌شوند. هم‌چنین امکان درج یادداشت شخصی بصورت ضمیمه برای هر محتوا وجود دارد.',
fontSize: 14, fontSize: 14,
color: Theme.of(context).colorScheme.title, color: Theme.of(context).colorScheme.title,
textAlign: TextAlign.justify, textAlign: TextAlign.justify,

View File

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

View File

@ -1,7 +1,8 @@
// ignore_for_file: library_private_types_in_public_api, deprecated_member_use // ignore_for_file: library_private_types_in_public_api, deprecated_member_use
import 'package:didvan/constants/assets.dart'; import 'package:didvan/constants/assets.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart'; import 'package:webview_flutter/webview_flutter.dart';
@ -18,25 +19,33 @@ class _WebViewState extends State<WebView> {
bool loading = true; bool loading = true;
int progress = 0; int progress = 0;
final Set<Factory<VerticalDragGestureRecognizer>> gestureRecognizers = {
Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer())
};
void _onProgress(int progress) {
setState(() {
this.progress = progress;
});
}
void _onPageFinished(String url) async {
await Future.delayed(const Duration(seconds: 2));
setState(() {
if (progress == 100) loading = false;
});
}
@override @override
void initState() { void initState() {
controller = WebViewController() controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted) ..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate( ..setNavigationDelegate(
NavigationDelegate( NavigationDelegate(
onProgress: (int progress) { onProgress: _onProgress,
// Update loading bar.
setState(() {
this.progress = progress;
});
},
onPageStarted: (String url) {}, onPageStarted: (String url) {},
onPageFinished: (String url) async { onPageFinished: _onPageFinished,
await Future.delayed(const Duration(seconds: 2));
setState(() {
if (progress == 100) loading = false;
});
},
onHttpError: (HttpResponseError error) {}, onHttpError: (HttpResponseError error) {},
onWebResourceError: (WebResourceError error) { onWebResourceError: (WebResourceError error) {
// navigatorKey.currentState!.pop(); // navigatorKey.currentState!.pop();
@ -65,11 +74,12 @@ class _WebViewState extends State<WebView> {
} }
}, },
child: Scaffold( child: Scaffold(
appBar: AppBar( // appBar: AppBar(
title: const DidvanText( // title: const DidvanText(
'بازگشت به دیدوان', // 'بازگشت به دیدوان',
), // ),
), // leading: const BackButton(),
// ),
body: loading body: loading
? Center( ? Center(
child: Image.asset( child: Image.asset(
@ -78,7 +88,16 @@ class _WebViewState extends State<WebView> {
height: 60, height: 60,
), ),
) )
: WebViewWidget(controller: controller), : LayoutBuilder(builder: (context, constraints) {
return SizedBox(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: WebViewWidget(
controller: controller,
gestureRecognizers: gestureRecognizers,
),
);
}),
)); ));
} }
} }

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.0+3300 version: 3.3.1+3300
environment: environment:
sdk: ">=2.19.0 <3.0.0" sdk: ">=2.19.0 <3.0.0"