diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index f4ee5ba..beb7c61 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -77,6 +77,10 @@ PODS:
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
+ - flutter_sound (9.6.0):
+ - Flutter
+ - flutter_sound_core (= 9.6.0)
+ - flutter_sound_core (9.6.0)
- flutter_vibrate (0.0.1):
- Flutter
- GoogleDataTransport (9.4.1):
@@ -164,6 +168,7 @@ DEPENDENCIES:
- flutter_background_service_ios (from `.symlinks/plugins/flutter_background_service_ios/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
+ - flutter_sound (from `.symlinks/plugins/flutter_sound/ios`)
- flutter_vibrate (from `.symlinks/plugins/flutter_vibrate/ios`)
- home_widget (from `.symlinks/plugins/home_widget/ios`)
- image_cropper (from `.symlinks/plugins/image_cropper/ios`)
@@ -189,6 +194,7 @@ SPEC REPOS:
- FirebaseCoreInternal
- FirebaseInstallations
- FirebaseMessaging
+ - flutter_sound_core
- GoogleDataTransport
- GoogleUtilities
- IosAwnCore
@@ -217,6 +223,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
+ flutter_sound:
+ :path: ".symlinks/plugins/flutter_sound/ios"
flutter_vibrate:
:path: ".symlinks/plugins/flutter_vibrate/ios"
home_widget:
@@ -265,6 +273,8 @@ SPEC CHECKSUMS:
flutter_background_service_ios: e30e0d3ee69e4cee66272d0c78eacd48c2e94aac
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
+ flutter_sound: dde9a913063b65a27ba8fdc2039036b99b136c79
+ flutter_sound_core: 0c6eb9d5268adc70ff159b3d65fd3d98a82d3a27
flutter_vibrate: 9f4c2ab57008965f78969472367c329dd77eb801
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 65f0b71..3ca83d6 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -59,7 +59,7 @@
NSAppleMusicUsageDescription
This app requires access to Apple Music to [explain specific reason].
NSMicrophoneUsageDescription
- ... explain why the app uses the microphone here ...
+ To send voice text this app requires Mic's permission.
NSAppTransportSecurity
NSAllowsArbitraryLoads
@@ -71,9 +71,9 @@
NSAppTransportSecurity
- NSAllowsArbitraryLoads
-
+ NSAllowsArbitraryLoads
+
-
+
\ No newline at end of file
diff --git a/lib/models/ai/ai_chat_args.dart b/lib/models/ai/ai_chat_args.dart
index 1445425..194681d 100644
--- a/lib/models/ai/ai_chat_args.dart
+++ b/lib/models/ai/ai_chat_args.dart
@@ -5,6 +5,7 @@ class AiChatArgs {
final BotsModel bot;
final ChatsModel? chat;
final Prompts? prompts;
+ final bool? attach;
- AiChatArgs({required this.bot, this.chat, this.prompts});
+ AiChatArgs({required this.bot, this.chat, this.prompts, this.attach});
}
diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart
index 39ed646..e313283 100644
--- a/lib/services/media/media.dart
+++ b/lib/services/media/media.dart
@@ -163,7 +163,7 @@ class MediaService {
print("Cannot get download folder path $err");
}
}
- String path = "${dir?.path}$basename";
+ String path = "${dir?.path}${Platform.isIOS ? '/' : ''}$basename";
File file = File(path);
diff --git a/lib/utils/action_sheet.dart b/lib/utils/action_sheet.dart
index bfa0ee3..401c964 100644
--- a/lib/utils/action_sheet.dart
+++ b/lib/utils/action_sheet.dart
@@ -382,7 +382,12 @@ class ActionSheetUtils {
),
),
const SizedBox(width: 12),
- Text(bot.name.toString())
+ Expanded(
+ child: DidvanText(
+ bot.name.toString(),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ ))
],
),
),
diff --git a/lib/views/ai/ai.dart b/lib/views/ai/ai.dart
index e27a244..02a85e5 100644
--- a/lib/views/ai/ai.dart
+++ b/lib/views/ai/ai.dart
@@ -235,7 +235,18 @@ class _AiState extends State {
const SizedBox(
width: 8,
),
- const MessageBarBtn(
+ MessageBarBtn(
+ click: () {
+ Navigator.of(context)
+ .pushNamed(
+ Routes.aiChat,
+ arguments:
+ AiChatArgs(
+ bot:
+ bot,
+ attach:
+ true));
+ },
enable: false,
icon: Icons
.attach_file_rounded),
diff --git a/lib/views/ai/ai_chat_page.dart b/lib/views/ai/ai_chat_page.dart
index d634e40..b1fba3e 100644
--- a/lib/views/ai/ai_chat_page.dart
+++ b/lib/views/ai/ai_chat_page.dart
@@ -23,6 +23,7 @@ import 'package:didvan/views/ai/ai_chat_state.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/ai/widgets/ai_message_bar.dart';
import 'package:didvan/views/ai/widgets/audio_wave.dart';
+import 'package:didvan/views/widgets/didvan/button.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/marquee_text.dart';
@@ -125,96 +126,160 @@ class _AiChatPageState extends State {
ActionSheetUtils(context).openDialog(
data: ActionSheetData(
hasConfirmButtonClose: false,
- onConfirmed: () async {
- final state = context.read();
- await state
- .changePlaceHolder(placeholder.text);
- Future.delayed(
- Duration.zero,
- () => ActionSheetUtils(context).pop(),
- );
- },
+ hasConfirmButton: false,
+ hasDismissButton: false,
content: ValueListenableBuilder(
valueListenable: state.changingPlaceHolder,
- builder: (context, value, child) => Column(
- children: [
- Stack(
- children: [
- Row(
- mainAxisAlignment:
- MainAxisAlignment.center,
- children: [
- DidvanText(
- 'شخصیسازی دستورات',
- style: Theme.of(context)
- .textTheme
- .titleMedium,
- ),
- ],
- ),
- Positioned(
- right: 0,
- top: 0,
- bottom: 0,
- child: Center(
- child: InkWell(
- onTap: () {
- ActionSheetUtils(context)
- .pop();
- },
- child: const Icon(
- DidvanIcons.close_solid,
- size: 24,
- ),
+ builder: (context, value, child) =>
+ Container(
+ constraints: BoxConstraints(
+ maxHeight: MediaQuery.sizeOf(context)
+ .height /
+ 3),
+ child: Column(
+ children: [
+ Expanded(
+ child: SingleChildScrollView(
+ child: Column(
+ children: [
+ Stack(
+ children: [
+ Row(
+ mainAxisAlignment:
+ MainAxisAlignment
+ .center,
+ children: [
+ DidvanText(
+ 'شخصیسازی دستورات',
+ style: Theme.of(
+ context)
+ .textTheme
+ .titleMedium,
+ ),
+ ],
+ ),
+ Positioned(
+ right: 0,
+ top: 0,
+ bottom: 0,
+ child: Center(
+ child: InkWell(
+ onTap: () {
+ ActionSheetUtils(
+ context)
+ .pop();
+ },
+ child: const Icon(
+ DidvanIcons
+ .close_solid,
+ size: 24,
+ ),
+ ),
+ )),
+ ],
),
- )),
- ],
- ),
- const SizedBox(
- height: 12,
- ),
- const DidvanText(
- 'دوست دارید هوشان چه چیزهایی را درباره شما بداند تا بتواند پاسخهای بهتری ارائه دهد؟ '),
- const SizedBox(
- height: 12,
- ),
- value
- ? Center(
- child: Image.asset(
- Assets.loadingAnimation,
- width: 60,
- height: 60,
+ const SizedBox(
+ height: 12,
+ ),
+ const DidvanText(
+ 'دوست دارید هوشان چه چیزهایی را درباره شما بداند تا بتواند پاسخهای بهتری ارائه دهد؟ '),
+ const SizedBox(
+ height: 12,
+ ),
+ value
+ ? Center(
+ child: Image.asset(
+ Assets
+ .loadingAnimation,
+ width: 60,
+ height: 60,
+ ),
+ )
+ : TextField(
+ controller:
+ placeholder,
+ style: (Theme.of(
+ context)
+ .textTheme
+ .bodyMedium)!
+ .copyWith(
+ fontFamily: DesignConfig
+ .fontFamily
+ .padRight(
+ 3)),
+ minLines: 5,
+ maxLines: 5,
+ keyboardType:
+ TextInputType
+ .multiline,
+ decoration:
+ InputDecoration(
+ filled: true,
+ fillColor: Theme.of(
+ context)
+ .colorScheme
+ .secondCTA,
+ contentPadding:
+ const EdgeInsets
+ .fromLTRB(
+ 10,
+ 18,
+ 10,
+ 0),
+ border: const OutlineInputBorder(
+ borderRadius:
+ DesignConfig
+ .lowBorderRadius),
+ errorStyle:
+ const TextStyle(
+ height:
+ 0.01),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(
+ height: 12,
+ ),
+ Row(
+ children: [
+ Expanded(
+ child: DidvanButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ title: 'بازگشت',
+ style:
+ ButtonStyleMode.secondary,
),
- )
- : TextField(
- controller: placeholder,
- style: (Theme.of(context)
- .textTheme
- .bodyMedium)!
- .copyWith(
- fontFamily: DesignConfig
- .fontFamily
- .padRight(3)),
- minLines: 4,
- maxLines: 4,
- keyboardType:
- TextInputType.multiline,
- decoration: InputDecoration(
- filled: true,
- fillColor: Theme.of(context)
- .colorScheme
- .secondCTA,
- contentPadding:
- const EdgeInsets.fromLTRB(
- 10, 10, 10, 0),
- border: const OutlineInputBorder(
- borderRadius: DesignConfig
- .lowBorderRadius),
- errorStyle: const TextStyle(
- height: 0.01),
+ ),
+ const SizedBox(width: 20),
+ Expanded(
+ child: DidvanButton(
+ style:
+ ButtonStyleMode.primary,
+ onPressed: () async {
+ final state = context
+ .read();
+ await state
+ .changePlaceHolder(
+ placeholder.text);
+ Future.delayed(
+ Duration.zero,
+ () => ActionSheetUtils(
+ context)
+ .pop(),
+ );
+ },
+ title: 'تایید',
),
- )
- ],
+ ),
+ ],
+ ),
+ ],
+ ),
),
)));
},
@@ -321,6 +386,7 @@ class _AiChatPageState extends State {
// :
AiMessageBar(
bot: widget.args.bot,
+ attch: widget.args.attach,
),
],
)),
@@ -394,7 +460,7 @@ class _AiChatPageState extends State {
children: [
Container(
constraints: BoxConstraints(
- maxWidth: MediaQuery.sizeOf(context).width / 1.5),
+ maxWidth: MediaQuery.sizeOf(context).width / 1.3),
decoration: BoxDecoration(
borderRadius: DesignConfig.mediumBorderRadius.copyWith(
bottomLeft: !message.role.toString().contains('user')
@@ -513,24 +579,43 @@ class _AiChatPageState extends State {
value: historyAiChatState
.bots[index],
height: 72,
- child: Row(
- children: [
- ClipOval(
- child: CachedNetworkImage(
- imageUrl:
+ child: Container(
+ constraints:
+ const BoxConstraints(
+ maxWidth: 200),
+ child: Row(
+ children: [
+ ClipOval(
+ child:
+ CachedNetworkImage(
+ imageUrl:
+ historyAiChatState
+ .bots[index]
+ .image
+ .toString(),
+ width: 42,
+ height: 42,
+ ),
+ ),
+ const SizedBox(width: 12),
+ Expanded(
+ child: Directionality(
+ textDirection:
+ TextDirection.ltr,
+ child: DidvanText(
historyAiChatState
.bots[index]
- .image
+ .name
.toString(),
- width: 42,
- height: 42,
+ maxLines: 1,
+ overflow:
+ TextOverflow
+ .ellipsis,
+ ),
+ ),
),
- ),
- const SizedBox(width: 12),
- DidvanText(
- '${historyAiChatState.bots[index].name}X',
- ),
- ],
+ ],
+ ),
),
),
)
@@ -542,7 +627,7 @@ class _AiChatPageState extends State {
padding: const EdgeInsets.symmetric(
horizontal: 8),
constraints: const BoxConstraints(
- maxWidth: 120),
+ maxWidth: 100),
decoration: BoxDecoration(
borderRadius:
DesignConfig.lowBorderRadius,
@@ -553,11 +638,15 @@ class _AiChatPageState extends State {
child: Row(
children: [
Expanded(
- child: DidvanText(
- '${widget.args.bot.name}',
- maxLines: 1,
- overflow:
- TextOverflow.ellipsis,
+ child: Directionality(
+ textDirection:
+ TextDirection.ltr,
+ child: DidvanText(
+ '${widget.args.bot.name}',
+ maxLines: 1,
+ overflow:
+ TextOverflow.ellipsis,
+ ),
),
),
const Icon(
diff --git a/lib/views/ai/widgets/ai_message_bar.dart b/lib/views/ai/widgets/ai_message_bar.dart
index 7aaa2ec..112c6b4 100644
--- a/lib/views/ai/widgets/ai_message_bar.dart
+++ b/lib/views/ai/widgets/ai_message_bar.dart
@@ -1,6 +1,7 @@
// ignore_for_file: library_private_types_in_public_api, avoid_web_libraries_in_flutter
import 'dart:async';
+import 'package:record/record.dart';
import 'package:universal_html/html.dart' as html;
import 'dart:io';
import 'package:audio_session/audio_session.dart';
@@ -39,7 +40,9 @@ typedef _Fn = void Function();
class AiMessageBar extends StatefulWidget {
final BotsModel bot;
- const AiMessageBar({Key? key, required this.bot}) : super(key: key);
+ final bool? attch;
+ const AiMessageBar({Key? key, required this.bot, this.attch})
+ : super(key: key);
@override
_AiMessageBarState createState() => _AiMessageBarState();
@@ -55,7 +58,7 @@ class _AiMessageBarState extends State {
bool _mPlayerIsInited = false;
bool _mRecorderIsInited = false;
bool _mplaybackReady = false;
- bool openAttach = false;
+ late bool openAttach = widget.attch ?? false;
Timer? _timer;
final theSource = AudioSource.microphone;
@@ -91,9 +94,13 @@ class _AiMessageBarState extends State {
Future openTheRecorder() async {
if (!kIsWeb) {
- var status = await Permission.microphone.request();
+ var status = await Permission.microphone.status;
+ await AudioRecorder().hasPermission();
if (status != PermissionStatus.granted) {
- throw RecordingPermissionException('Microphone permission not granted');
+ if (!Platform.isIOS) {
+ throw RecordingPermissionException(
+ 'Microphone permission not granted');
+ }
}
}
await _mRecorder!.openRecorder();
@@ -253,7 +260,7 @@ class _AiMessageBarState extends State {
builder: (context, value, child) => Padding(
padding: const EdgeInsets.fromLTRB(8, 0, 8, 8),
child: SizedBox(
- width: 46,
+ width: 50,
child: Center(
child: DidvanText(
DateTimeUtils.normalizeTimeDuration(value)),
diff --git a/lib/views/profile/profile.dart b/lib/views/profile/profile.dart
index 3932a46..6f3ec85 100644
--- a/lib/views/profile/profile.dart
+++ b/lib/views/profile/profile.dart
@@ -348,7 +348,7 @@ class _ProfilePageState extends State {
),
const SizedBox(height: 16),
DidvanText(
- 'نسخه نرمافزار: 3.3.4',
+ 'نسخه نرمافزار: 3.3.5',
style: Theme.of(context).textTheme.bodySmall,
),
],
diff --git a/pubspec.yaml b/pubspec.yaml
index 233402b..b7970d1 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -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.4+3340
+version: 3.3.5+3350
environment:
sdk: ">=2.19.0 <3.0.0"