test one
This commit is contained in:
parent
6c3117a918
commit
9ea80f21fb
|
|
@ -1,5 +1,7 @@
|
|||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:android_intent_plus/android_intent.dart';
|
||||
import 'package:bot_toast/bot_toast.dart';
|
||||
import 'package:didvan/config/theme_data.dart';
|
||||
|
|
@ -12,9 +14,11 @@ import 'package:didvan/providers/user.dart';
|
|||
import 'package:didvan/routes/route_generator.dart';
|
||||
import 'package:didvan/routes/routes.dart';
|
||||
import 'package:didvan/services/app_home_widget/home_widget_repository.dart';
|
||||
import 'package:didvan/services/app_initalizer.dart';
|
||||
import 'package:didvan/services/media/media.dart';
|
||||
import 'package:didvan/services/notification/firebase_api.dart';
|
||||
import 'package:didvan/services/notification/notification_service.dart';
|
||||
import 'package:didvan/utils/my_custom_scroll_behavior.dart';
|
||||
import 'package:didvan/views/ai/history_ai_chat_state.dart';
|
||||
import 'package:didvan/views/podcasts/podcasts_state.dart';
|
||||
import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart';
|
||||
|
|
@ -23,6 +27,7 @@ import 'package:firebase_messaging/firebase_messaging.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:home_widget/home_widget.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
|
@ -56,21 +61,28 @@ Future<void> _backgroundCallbackHomeWidget(Uri? uri) async {
|
|||
}
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
try {
|
||||
HomeWidget.registerBackgroundCallback(_backgroundCallbackHomeWidget);
|
||||
HomeWidget.registerInteractivityCallback(_backgroundCallbackHomeWidget);
|
||||
await runZonedGuarded(() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
await NotificationService.initializeNotification();
|
||||
FirebaseMessaging.onBackgroundMessage(_initPushNotification);
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform);
|
||||
await FirebaseApi().initNotification();
|
||||
} catch (e) {
|
||||
debugPrint;
|
||||
}
|
||||
try {
|
||||
if (!kIsWeb) {
|
||||
HomeWidget.registerBackgroundCallback(_backgroundCallbackHomeWidget);
|
||||
HomeWidget.registerInteractivityCallback(_backgroundCallbackHomeWidget);
|
||||
await NotificationService.initializeNotification();
|
||||
}
|
||||
|
||||
runApp(const Didvan());
|
||||
FirebaseMessaging.onBackgroundMessage(_initPushNotification);
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform);
|
||||
await FirebaseApi().initNotification();
|
||||
} catch (e) {
|
||||
debugPrint;
|
||||
}
|
||||
|
||||
runApp(const Didvan());
|
||||
}, (error, stack) async {
|
||||
error.printError();
|
||||
});
|
||||
}
|
||||
|
||||
class Didvan extends StatefulWidget {
|
||||
|
|
@ -88,10 +100,12 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
NotificationService.startListeningNotificationEvents();
|
||||
if (!kIsWeb) {
|
||||
NotificationService.startListeningNotificationEvents();
|
||||
}
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
@ -108,15 +122,17 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
|
|||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
var r = await HomeWidget.getWidgetData("cRoute", defaultValue: '');
|
||||
if (r!.toString() != Routes.splash) {
|
||||
await HomeWidgetRepository.decideWhereToGo();
|
||||
if (!kIsWeb) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
var r = await HomeWidget.getWidgetData("cRoute", defaultValue: '');
|
||||
if (r!.toString() != Routes.splash) {
|
||||
await HomeWidgetRepository.decideWhereToGo();
|
||||
|
||||
NotificationMessage? data = HomeWidgetRepository.data;
|
||||
NotificationMessage? data = HomeWidgetRepository.data;
|
||||
|
||||
if (data != null) {
|
||||
await HomeWidgetRepository.decideWhereToGoNotif();
|
||||
if (data != null) {
|
||||
await HomeWidgetRepository.decideWhereToGoNotif();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -150,6 +166,7 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
|
|||
color: Theme.of(context).colorScheme.surface,
|
||||
child: SafeArea(
|
||||
child: MaterialApp(
|
||||
scrollBehavior: MyCustomScrollBehavior(),
|
||||
navigatorKey: navigatorKey,
|
||||
debugShowCheckedModeBanner: false,
|
||||
title: 'Didvan',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:didvan/models/ai/bots_model.dart';
|
||||
import 'package:didvan/models/ai/files_model.dart';
|
||||
|
||||
class ChatsModel {
|
||||
int? id;
|
||||
|
|
@ -97,6 +98,7 @@ class Prompts {
|
|||
bool? finished;
|
||||
bool? error;
|
||||
bool? audio;
|
||||
FilesModel? fileLocal;
|
||||
|
||||
Prompts({
|
||||
this.id,
|
||||
|
|
@ -108,8 +110,9 @@ class Prompts {
|
|||
this.createdAt,
|
||||
this.finished,
|
||||
this.error,
|
||||
this.fileLocal,
|
||||
this.audio,
|
||||
});
|
||||
}) {}
|
||||
|
||||
Prompts.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
|
|
@ -141,6 +144,7 @@ class Prompts {
|
|||
String? text,
|
||||
String? file,
|
||||
String? fileName,
|
||||
FilesModel? fileLocal,
|
||||
String? role,
|
||||
String? createdAt,
|
||||
bool? finished,
|
||||
|
|
@ -152,6 +156,7 @@ 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,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
|
@ -9,14 +10,38 @@ class FilesModel {
|
|||
late String extname;
|
||||
late File main;
|
||||
final bool isRecorded;
|
||||
late bool isAudio;
|
||||
late bool isImage;
|
||||
final bool? audio;
|
||||
final bool? image;
|
||||
final bool? network;
|
||||
final Uint8List? bytes;
|
||||
|
||||
FilesModel(this.path, {this.isRecorded = false}) {
|
||||
basename = p.basename(path);
|
||||
FilesModel(
|
||||
this.path, {
|
||||
final String? name,
|
||||
this.isRecorded = false,
|
||||
this.audio,
|
||||
this.image,
|
||||
this.network,
|
||||
this.bytes,
|
||||
}) {
|
||||
basename = name ?? p.basename(path);
|
||||
extname = p.extension(path);
|
||||
main = File(path);
|
||||
isAudio = lookupMimeType(path)?.startsWith('audio/') ?? false;
|
||||
isImage = lookupMimeType(path)?.startsWith('image/') ?? false;
|
||||
print(isAudio());
|
||||
print(isImage());
|
||||
print(isNetwork());
|
||||
print(path);
|
||||
}
|
||||
|
||||
bool isAudio() {
|
||||
return audio ?? lookupMimeType(path)?.startsWith('audio/') ?? false;
|
||||
}
|
||||
|
||||
bool isImage() {
|
||||
return image ?? lookupMimeType(path)?.startsWith('image/') ?? false;
|
||||
}
|
||||
|
||||
bool isNetwork() {
|
||||
return network ?? path.startsWith('blob:') || path.startsWith('/uploads');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,9 @@ import '../views/notification_time/notification_time.dart';
|
|||
|
||||
class RouteGenerator {
|
||||
static Route<dynamic> generateRoute(RouteSettings settings) {
|
||||
HomeWidget.saveWidgetData("cRoute", settings.name!);
|
||||
if (!kIsWeb) {
|
||||
HomeWidget.saveWidgetData("cRoute", settings.name!);
|
||||
}
|
||||
switch (settings.name) {
|
||||
case Routes.splash:
|
||||
return _createRoute(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:didvan/models/ai/files_model.dart';
|
||||
import 'package:didvan/services/storage/storage.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get_connect/http/src/multipart/multipart_file.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
|
@ -17,7 +20,7 @@ class AiApiService {
|
|||
{required final String url,
|
||||
required final String message,
|
||||
final int? chatId,
|
||||
final File? file,
|
||||
final FilesModel? file,
|
||||
final bool? edite}) async {
|
||||
final headers = {
|
||||
"Authorization": "Bearer ${await StorageService.getValue(key: 'token')}",
|
||||
|
|
@ -35,26 +38,69 @@ class AiApiService {
|
|||
request.fields['edit'] = edite.toString().toLowerCase();
|
||||
}
|
||||
if (file != null) {
|
||||
int length = 0;
|
||||
try {
|
||||
length = await file.length();
|
||||
// ...
|
||||
} catch (e) {
|
||||
// Handle the error or return an error response
|
||||
// final file = file;
|
||||
// final filePath = file.path;
|
||||
// final mimeType = filePath != null ? lookupMimeType(filePath) : null;
|
||||
// final contentType = mimeType != null ? MediaType.parse(mimeType) : null;
|
||||
|
||||
// final fileReadStream = file.readStream;
|
||||
// if (fileReadStream == null) {
|
||||
// throw Exception('Cannot read file from null stream');
|
||||
// }
|
||||
// final stream = http.ByteStream(fileReadStream);
|
||||
// final multipartFile = http.MultipartFile(
|
||||
// 'file',
|
||||
// stream,
|
||||
// file.size,
|
||||
// filename: file.name,
|
||||
// contentType: contentType,
|
||||
// );
|
||||
// request.files.add(multipartFile);
|
||||
Uint8List bytes;
|
||||
if (kIsWeb) {
|
||||
if (file.bytes != null) {
|
||||
bytes = file.bytes!;
|
||||
} else {
|
||||
final Uri audioUri = Uri.parse(file.path);
|
||||
final http.Response audioResponse = await http.get(audioUri);
|
||||
bytes = audioResponse.bodyBytes;
|
||||
}
|
||||
request.files.add(http.MultipartFile.fromBytes(
|
||||
'file',
|
||||
bytes,
|
||||
filename: file.isAudio()
|
||||
? 'wav'
|
||||
: file.isImage()
|
||||
? 'png'
|
||||
: 'pdf', // You can set a filename here
|
||||
contentType: parser.MediaType.parse(file.isAudio()
|
||||
? 'audio/mpeg'
|
||||
: file.isImage()
|
||||
? 'image/png'
|
||||
: 'application/pdf'), // Set the MIME type
|
||||
) // Use MediaType.parse to parse the MIME type
|
||||
);
|
||||
} else {
|
||||
int length = 0;
|
||||
try {
|
||||
length = await file.main.length();
|
||||
// ...
|
||||
} catch (e) {
|
||||
// Handle the error or return an error response
|
||||
}
|
||||
String? mimeType = lookupMimeType(
|
||||
file.path); // Use MIME type instead of file extension
|
||||
mimeType ??= 'application/octet-stream';
|
||||
if (mimeType.startsWith('audio')) {
|
||||
mimeType = 'audio/${p.extension(file.path).replaceAll('.', '')}';
|
||||
}
|
||||
request.files.add(
|
||||
http.MultipartFile('file', file.main.readAsBytes().asStream(), length,
|
||||
filename: file.basename,
|
||||
contentType: parser.MediaType.parse(
|
||||
mimeType)), // Use MediaType.parse to parse the MIME type
|
||||
);
|
||||
}
|
||||
String basename = p.basename(file.path);
|
||||
String? mimeType =
|
||||
lookupMimeType(file.path); // Use MIME type instead of file extension
|
||||
mimeType ??= 'application/octet-stream';
|
||||
if (mimeType.startsWith('audio')) {
|
||||
mimeType = 'audio/${p.extension(file.path).replaceAll('.', '')}';
|
||||
}
|
||||
request.files.add(
|
||||
http.MultipartFile('file', file.readAsBytes().asStream(), length,
|
||||
filename: basename,
|
||||
contentType: parser.MediaType.parse(
|
||||
mimeType)), // Use MediaType.parse to parse the MIME type
|
||||
);
|
||||
}
|
||||
|
||||
// print("req: ${request.files}");
|
||||
|
|
@ -76,7 +122,7 @@ class AiApiService {
|
|||
// Handle other non-200 responses
|
||||
throw Exception('Failed to load data');
|
||||
} else {
|
||||
return response.stream;
|
||||
return response.stream.asBroadcastStream();
|
||||
}
|
||||
} catch (e) {
|
||||
// Handle any other errors
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import '../network/request_helper.dart';
|
|||
|
||||
class HomeWidgetRepository {
|
||||
static Future<void> fetchWidget() async {
|
||||
RequestService.token = await StorageService.getValue(key: 'token');
|
||||
// RequestService.token = await StorageService.getValue(key: 'token');
|
||||
final service = RequestService(
|
||||
RequestHelper.widgetNews(),
|
||||
);
|
||||
|
|
@ -90,7 +90,7 @@ class HomeWidgetRepository {
|
|||
);
|
||||
|
||||
if (data.link!.startsWith('http')) {
|
||||
launchUrlString(
|
||||
openInWebView(
|
||||
'${data.link}',
|
||||
mode: LaunchMode.inAppWebView,
|
||||
);
|
||||
|
|
@ -219,7 +219,7 @@ class HomeWidgetRepository {
|
|||
}
|
||||
} else {
|
||||
if (data.link!.startsWith('http')) {
|
||||
launchUrlString(
|
||||
openInWebView(
|
||||
'${data.link}',
|
||||
mode: LaunchMode.inAppWebView,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,10 +13,15 @@ import 'package:flutter/foundation.dart';
|
|||
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';
|
||||
|
||||
enum LaunchMode { inAppWebView }
|
||||
|
||||
void launchUrlString(String src, {dynamic mode}) {
|
||||
void openInWebView(String src, {dynamic mode}) {
|
||||
if (kIsWeb) {
|
||||
launchUrlString(src);
|
||||
return;
|
||||
}
|
||||
navigatorKey.currentState!.pushNamed(Routes.web, arguments: src);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:didvan/constants/assets.dart';
|
||||
import 'package:didvan/main.dart';
|
||||
import 'package:didvan/models/requests/studio.dart';
|
||||
import 'package:didvan/models/studio_details_data.dart';
|
||||
import 'package:didvan/models/view/action_sheet_data.dart';
|
||||
import 'package:didvan/providers/media.dart';
|
||||
import 'package:didvan/services/app_initalizer.dart';
|
||||
import 'package:didvan/services/network/request.dart';
|
||||
import 'package:didvan/services/network/request_helper.dart';
|
||||
import 'package:didvan/services/storage/storage.dart';
|
||||
|
|
@ -121,6 +123,7 @@ class MediaService {
|
|||
return result;
|
||||
} catch (e) {
|
||||
e.printError(info: 'Pick PDF Fail');
|
||||
// navigatorKey.currentState!.pop();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -410,11 +410,13 @@ class ActionSheetUtils {
|
|||
? ClipRRect(
|
||||
borderRadius: DesignConfig.lowBorderRadius,
|
||||
child: Image.file(File(image)))
|
||||
: SkeletonImage(
|
||||
width: min(MediaQuery.of(context).size.width,
|
||||
MediaQuery.of(context).size.height),
|
||||
imageUrl: image,
|
||||
),
|
||||
: image.startsWith('blob:')
|
||||
? ClipRRect(
|
||||
borderRadius: DesignConfig.lowBorderRadius,
|
||||
child: Image.network(image))
|
||||
: SkeletonImage(
|
||||
imageUrl: image,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Get screen media.
|
||||
|
|
@ -7,9 +8,9 @@ final MediaQueryData media =
|
|||
|
||||
/// This extention help us to make widget responsive.
|
||||
extension NumberParsing on num {
|
||||
double w() => this * media.size.width / 100;
|
||||
double w() => this * media.size.width / (kIsWeb ? 250 : 100);
|
||||
|
||||
double h() => this * media.size.height / 100;
|
||||
double h() => this * media.size.height / (kIsWeb ? 550 : 100);
|
||||
}
|
||||
|
||||
///
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MyCustomScrollBehavior extends MaterialScrollBehavior {
|
||||
// Override behavior methods and getters like dragDevices
|
||||
@override
|
||||
Set<PointerDeviceKind> get dragDevices => {
|
||||
PointerDeviceKind.touch,
|
||||
PointerDeviceKind.mouse,
|
||||
};
|
||||
}
|
||||
|
|
@ -63,8 +63,12 @@ class _AiState extends State<Ai> {
|
|||
Icon(
|
||||
DidvanIcons.ai_solid,
|
||||
size: MediaQuery.sizeOf(context).width / 5,
|
||||
color: Theme.of(context).colorScheme.title,
|
||||
),
|
||||
DidvanText(
|
||||
'هوشان',
|
||||
color: Theme.of(context).colorScheme.title,
|
||||
),
|
||||
const DidvanText('هوشان'),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
|
|
@ -74,8 +78,12 @@ class _AiState extends State<Ai> {
|
|||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(DidvanIcons.caret_down_solid),
|
||||
Text(bot.name.toString()),
|
||||
Icon(
|
||||
DidvanIcons.caret_down_solid,
|
||||
color: Theme.of(context).colorScheme.title,
|
||||
),
|
||||
DidvanText(bot.name.toString(),
|
||||
color: Theme.of(context).colorScheme.title),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -177,7 +185,10 @@ class _AiState extends State<Ai> {
|
|||
topLeft: Radius.circular(12),
|
||||
bottomLeft: Radius.circular(12)),
|
||||
boxShadow: DesignConfig.defaultShadow),
|
||||
child: const Icon(DidvanIcons.angle_left_light),
|
||||
child: Icon(
|
||||
DidvanIcons.angle_left_light,
|
||||
color: Theme.of(context).colorScheme.title,
|
||||
),
|
||||
)),
|
||||
)
|
||||
],
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ 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';
|
||||
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
|
|
@ -324,8 +325,8 @@ class _AiChatPageState extends State<AiChatPage> {
|
|||
|
||||
Widget messageBubble(Prompts message, BuildContext context, AiChatState state,
|
||||
int index, int mIndex) {
|
||||
FilesModel? file =
|
||||
message.file == null ? null : FilesModel(message.file.toString());
|
||||
FilesModel? file = message.fileLocal ??
|
||||
(message.file == null ? null : FilesModel(message.file.toString()));
|
||||
|
||||
MarkdownStyleSheet defaultMarkdownStyleSheet = MarkdownStyleSheet(
|
||||
pPadding: const EdgeInsets.all(0.8),
|
||||
|
|
@ -418,13 +419,13 @@ class _AiChatPageState extends State<AiChatPage> {
|
|||
children: [
|
||||
if (message.role.toString().contains('user') &&
|
||||
file != null)
|
||||
file.isAudio
|
||||
file.isAudio()
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16, vertical: 8),
|
||||
child: AudioWave(file: file.path),
|
||||
)
|
||||
: file.isImage
|
||||
: file.isImage()
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: messageImage(file),
|
||||
|
|
@ -605,7 +606,7 @@ class _AiChatPageState extends State<AiChatPage> {
|
|||
stop: const Duration(seconds: 3),
|
||||
),
|
||||
),
|
||||
if (state.file != null)
|
||||
if (state.file != null && !kIsWeb)
|
||||
FutureBuilder(
|
||||
future: state.file!.main.length(),
|
||||
builder: (context, snapshot) {
|
||||
|
|
@ -627,14 +628,18 @@ class _AiChatPageState extends State<AiChatPage> {
|
|||
|
||||
Widget messageImage(FilesModel file) {
|
||||
return GestureDetector(
|
||||
onTap: () => ActionSheetUtils(context).openInteractiveViewer(
|
||||
context, file.path, !file.path.startsWith('/uploads')),
|
||||
child: file.path.startsWith('/uploads')
|
||||
? SkeletonImage(
|
||||
pWidth: MediaQuery.sizeOf(context).width / 1,
|
||||
pHeight: MediaQuery.sizeOf(context).height / 6,
|
||||
imageUrl: file.path,
|
||||
)
|
||||
onTap: () => ActionSheetUtils(context)
|
||||
.openInteractiveViewer(context, file.path, !file.isNetwork()),
|
||||
child: file.isNetwork()
|
||||
? file.path.startsWith('blob:')
|
||||
? ClipRRect(
|
||||
borderRadius: DesignConfig.lowBorderRadius,
|
||||
child: Image.network(file.path))
|
||||
: SkeletonImage(
|
||||
pWidth: MediaQuery.sizeOf(context).width / 1,
|
||||
pHeight: MediaQuery.sizeOf(context).height / 6,
|
||||
imageUrl: file.path,
|
||||
)
|
||||
: ClipRRect(
|
||||
borderRadius: DesignConfig.lowBorderRadius,
|
||||
child: Image.file(file.main)),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:didvan/main.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.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';
|
||||
|
|
@ -14,15 +19,10 @@ import 'package:didvan/services/network/request.dart';
|
|||
import 'package:didvan/services/network/request_helper.dart';
|
||||
import 'package:didvan/utils/action_sheet.dart';
|
||||
import 'package:didvan/views/ai/history_ai_chat_state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:persian_number_utility/persian_number_utility.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class AiChatState extends CoreProvier {
|
||||
ValueNotifier<Stream<String>> messageOnstream =
|
||||
ValueNotifier(const Stream.empty());
|
||||
ValueNotifier(const Stream<String>.empty().asBroadcastStream());
|
||||
List<MessageModel> messages = [];
|
||||
bool onResponsing = false;
|
||||
bool loading = false;
|
||||
|
|
@ -34,6 +34,11 @@ class AiChatState extends CoreProvier {
|
|||
FilesModel? file;
|
||||
TextEditingController message = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _scrolledEnd() async {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await scrollController.animateTo(
|
||||
|
|
@ -53,7 +58,6 @@ class AiChatState extends CoreProvier {
|
|||
|
||||
await ActionSheetUtils(navigatorKey.currentContext!).showAlert(AlertData(
|
||||
message: 'خطا در برقراری ارتباط', aLertType: ALertType.error));
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
@ -94,21 +98,16 @@ class AiChatState extends CoreProvier {
|
|||
messages.add(MessageModel(
|
||||
dateTime: prompt.createdAt.toString(), prompts: [prompt]));
|
||||
}
|
||||
// chats.add("chats: $chats");
|
||||
}
|
||||
appState = AppState.idle;
|
||||
loading = false;
|
||||
onResponsing = false;
|
||||
|
||||
// Add this code to scroll to maxScrollExtent after the ListView is built
|
||||
update();
|
||||
|
||||
return;
|
||||
}
|
||||
appState = AppState.failed;
|
||||
loading = false;
|
||||
onResponsing = false;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
|
|
@ -122,28 +121,22 @@ class AiChatState extends CoreProvier {
|
|||
await service.put();
|
||||
if (service.isSuccess) {
|
||||
appState = AppState.idle;
|
||||
|
||||
// Add this code to scroll to maxScrollExtent after the ListView is built
|
||||
if (chat == null) {
|
||||
chat = ChatsModel(id: chatId, placeholder: placeholder);
|
||||
} else {
|
||||
chat = chat!.copyWith(placeholder: placeholder);
|
||||
}
|
||||
changingPlaceHolder.value = false;
|
||||
|
||||
update();
|
||||
|
||||
return;
|
||||
}
|
||||
appState = AppState.failed;
|
||||
changingPlaceHolder.value = false;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> postMessage(BotsModel bot) async {
|
||||
onResponsing = true;
|
||||
|
||||
update();
|
||||
String message = messages.last.prompts.last.text!;
|
||||
|
||||
|
|
@ -161,27 +154,50 @@ class AiChatState extends CoreProvier {
|
|||
url: '/${bot.id}/${bot.name}'.toLowerCase(),
|
||||
message: message,
|
||||
chatId: chatId,
|
||||
file: file?.main,
|
||||
file: file,
|
||||
edite: isEdite);
|
||||
final res = await AiApiService.getResponse(req).catchError((e) {
|
||||
_onError(e);
|
||||
return e;
|
||||
// return e;
|
||||
});
|
||||
|
||||
String responseMessgae = '';
|
||||
String dataMessgae = '';
|
||||
file = null;
|
||||
update();
|
||||
var r = res.listen((value) async {
|
||||
var str = utf8.decode(value);
|
||||
if (str.contains('{{{')) {
|
||||
dataMessgae += str;
|
||||
update();
|
||||
return;
|
||||
}
|
||||
responseMessgae += str;
|
||||
messageOnstream.value = Stream.value(responseMessgae);
|
||||
|
||||
final stream = res
|
||||
.transform(utf8.decoder)
|
||||
.transform(const LineSplitter()); // <--- Add this line
|
||||
|
||||
final r = stream.listen((str) {
|
||||
// var str = utf8.decode(value);
|
||||
if (!kIsWeb) {
|
||||
if (str.contains('{{{')) {
|
||||
dataMessgae += str;
|
||||
update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
responseMessgae += str;
|
||||
if (kIsWeb) {
|
||||
try {
|
||||
int startIndex = responseMessgae.indexOf('{{{');
|
||||
// + 3 to include the }}} characters
|
||||
|
||||
String slicedText =
|
||||
responseMessgae.substring(startIndex, responseMessgae.length);
|
||||
dataMessgae = slicedText;
|
||||
responseMessgae = responseMessgae.replaceAll(dataMessgae, '');
|
||||
print(
|
||||
"slicedText: $slicedText"); // Output: {{{ HUMAN_MESSAGE_ID: 2028, AI_MESSAGE_ID: 2029 }}}
|
||||
} catch (e) {
|
||||
e.printError();
|
||||
}
|
||||
}
|
||||
messageOnstream.value = Stream.value(responseMessgae);
|
||||
print("responseMessgae: $str");
|
||||
// update();
|
||||
});
|
||||
|
||||
|
|
@ -189,7 +205,6 @@ class AiChatState extends CoreProvier {
|
|||
if (chatId == null) {
|
||||
final service = await getChatId();
|
||||
navigatorKey.currentContext!.read<HistoryAiChatState>().getChats();
|
||||
|
||||
if (!service.isSuccess) {
|
||||
_onError(null);
|
||||
return;
|
||||
|
|
@ -201,7 +216,6 @@ class AiChatState extends CoreProvier {
|
|||
messages.last.prompts.last =
|
||||
messages.last.prompts.last.copyWith(error: true);
|
||||
messageOnstream.value = const Stream.empty();
|
||||
|
||||
update();
|
||||
_scrolledEnd();
|
||||
return;
|
||||
|
|
@ -214,10 +228,8 @@ class AiChatState extends CoreProvier {
|
|||
aiMessageId = data['AI_MESSAGE_ID'];
|
||||
} catch (e) {
|
||||
e.printError();
|
||||
|
||||
return;
|
||||
}
|
||||
// Access the values
|
||||
messages.last.prompts.last = messages.last.prompts.last
|
||||
.copyWith(finished: true, text: responseMessgae, id: aiMessageId);
|
||||
if (messages.last.prompts.length > 2) {
|
||||
|
|
@ -241,7 +253,7 @@ class AiChatState extends CoreProvier {
|
|||
_scrolledEnd();
|
||||
});
|
||||
|
||||
r.onError(_onError);
|
||||
r.onError((e) => _onError(e));
|
||||
}
|
||||
|
||||
Future<void> deleteMessage(int id, int mIndex, int index) async {
|
||||
|
|
@ -253,17 +265,13 @@ class AiChatState extends CoreProvier {
|
|||
} else {
|
||||
messages[mIndex].prompts.removeAt(index);
|
||||
}
|
||||
|
||||
appState = AppState.idle;
|
||||
|
||||
update();
|
||||
|
||||
return;
|
||||
}
|
||||
appState = AppState.failed;
|
||||
await ActionSheetUtils(navigatorKey.currentContext!).showAlert(AlertData(
|
||||
message: 'خطا در برقراری ارتباط', aLertType: ALertType.error));
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import 'package:get/get.dart';
|
|||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:persian_number_utility/persian_number_utility.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:record/record.dart';
|
||||
|
|
@ -80,6 +81,7 @@ class AiMessageBar extends StatefulWidget {
|
|||
class _AiMessageBarState extends State<AiMessageBar> {
|
||||
final ValueNotifier<String> messageText = ValueNotifier('');
|
||||
bool openAttach = false;
|
||||
String? path;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -159,8 +161,24 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
FilePickerResult? result =
|
||||
await MediaService.pickPdfFile();
|
||||
if (result != null) {
|
||||
state.file =
|
||||
FilesModel(result.files.single.path!);
|
||||
// if (kIsWeb) {
|
||||
// Uint8List bytes = result.files.first
|
||||
// .bytes!; // Access the bytes property
|
||||
|
||||
// File file = File.fromRawPath(bytes);
|
||||
// state.file = FilesModel(file.path,
|
||||
// name: result.files.first.name,
|
||||
// bytes: bytes,
|
||||
// audio: false,
|
||||
// image: false);
|
||||
// print(result.files.first.name);
|
||||
// } else {
|
||||
state.file = FilesModel(
|
||||
result.files.single.path!,
|
||||
audio: false,
|
||||
image: false);
|
||||
// }
|
||||
|
||||
openAttach = false;
|
||||
}
|
||||
Future.delayed(
|
||||
|
|
@ -214,8 +232,10 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
return;
|
||||
}
|
||||
state.file = kIsWeb
|
||||
? FilesModel(pickedFile.path)
|
||||
: FilesModel(file!.path);
|
||||
? FilesModel(pickedFile.path,
|
||||
image: true, audio: false)
|
||||
: FilesModel(file!.path,
|
||||
image: true, audio: false);
|
||||
openAttach = false;
|
||||
await Future.delayed(
|
||||
Duration.zero,
|
||||
|
|
@ -231,11 +251,28 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
color: Colors.indigoAccent,
|
||||
click: () async {
|
||||
MediaService.onLoadingPickFile(context);
|
||||
|
||||
FilePickerResult? result =
|
||||
await MediaService.pickAudioFile();
|
||||
if (result != null) {
|
||||
state.file =
|
||||
FilesModel(result.files.single.path!);
|
||||
if (kIsWeb) {
|
||||
Uint8List bytes = result.files.first
|
||||
.bytes!; // Access the bytes property
|
||||
|
||||
File file = File.fromRawPath(bytes);
|
||||
|
||||
state.file = FilesModel(file.path,
|
||||
name: result.files.first.name,
|
||||
bytes: bytes,
|
||||
audio: true,
|
||||
image: false);
|
||||
print(result.files.first.name);
|
||||
} else {
|
||||
state.file = FilesModel(
|
||||
result.files.single.path!,
|
||||
audio: true,
|
||||
image: false);
|
||||
}
|
||||
openAttach = false;
|
||||
}
|
||||
await Future.delayed(
|
||||
|
|
@ -290,13 +327,16 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
icon: DidvanIcons
|
||||
.stop_circle_solid,
|
||||
click: () async {
|
||||
final path =
|
||||
await record
|
||||
.stop();
|
||||
path = await record
|
||||
.stop();
|
||||
print(
|
||||
"pathhhhhhh is: $path");
|
||||
|
||||
state.file = FilesModel(
|
||||
path.toString(),
|
||||
isRecorded: true);
|
||||
isRecorded: true,
|
||||
audio: true,
|
||||
image: false);
|
||||
_timer.cancel();
|
||||
_countTimer.value = 0;
|
||||
state.update();
|
||||
|
|
@ -318,15 +358,29 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
click: () async {
|
||||
if (await record
|
||||
.hasPermission()) {
|
||||
Directory?
|
||||
downloadDir =
|
||||
await getApplicationDocumentsDirectory();
|
||||
try {
|
||||
String path =
|
||||
'';
|
||||
//
|
||||
if (!kIsWeb) {
|
||||
Directory?
|
||||
downloadDir =
|
||||
await getApplicationDocumentsDirectory();
|
||||
path = p.join(
|
||||
downloadDir
|
||||
.path,
|
||||
'${DateTime.now().millisecondsSinceEpoch ~/ 1000}.wav');
|
||||
}
|
||||
|
||||
record.start(
|
||||
const RecordConfig(),
|
||||
path:
|
||||
'${downloadDir.path}/${DateTime.now().millisecondsSinceEpoch ~/ 1000}.m4a');
|
||||
startTimer();
|
||||
record.start(
|
||||
const RecordConfig(),
|
||||
path:
|
||||
path);
|
||||
startTimer();
|
||||
} catch (e) {
|
||||
print(
|
||||
'Error starting recording: $e');
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -381,12 +435,8 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
.file
|
||||
?.path,
|
||||
fileName: state
|
||||
.file ==
|
||||
null
|
||||
? null
|
||||
: p.basename(state
|
||||
.file!
|
||||
.path),
|
||||
.file
|
||||
?.basename,
|
||||
finished:
|
||||
true,
|
||||
role: 'user',
|
||||
|
|
@ -417,10 +467,11 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
file: state
|
||||
.file
|
||||
?.path,
|
||||
fileName: state.file ==
|
||||
null
|
||||
? null
|
||||
: p.basename(state.file!.path),
|
||||
fileName: state
|
||||
.file
|
||||
?.basename,
|
||||
fileLocal:
|
||||
state.file,
|
||||
role:
|
||||
'user',
|
||||
createdAt: DateTime.now()
|
||||
|
|
@ -715,10 +766,6 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
|
||||
AnimatedVisibility fileContainer() {
|
||||
final state = context.watch<AiChatState>();
|
||||
String basename = '';
|
||||
if (state.file != null) {
|
||||
basename = p.basename(state.file!.path);
|
||||
}
|
||||
return AnimatedVisibility(
|
||||
isVisible: state.file != null && !state.file!.isRecorded,
|
||||
duration: DesignConfig.lowAnimationDuration,
|
||||
|
|
@ -731,16 +778,21 @@ 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 && state.file!.isImage()
|
||||
? SizedBox(
|
||||
width: 32,
|
||||
height: 42,
|
||||
child: ClipRRect(
|
||||
borderRadius: DesignConfig.lowBorderRadius,
|
||||
child: Image.file(
|
||||
state.file!.main,
|
||||
fit: BoxFit.cover,
|
||||
)))
|
||||
child: state.file!.isNetwork()
|
||||
? Image.network(
|
||||
state.file!.path,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: Image.file(
|
||||
state.file!.main,
|
||||
fit: BoxFit.cover,
|
||||
)))
|
||||
: const Icon(Icons.file_copy),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
|
|
@ -752,12 +804,12 @@ class _AiMessageBarState extends State<AiMessageBar> {
|
|||
SizedBox(
|
||||
height: 24,
|
||||
child: MarqueeText(
|
||||
text: basename,
|
||||
text: state.file != null ? state.file!.basename : '',
|
||||
style: const TextStyle(fontSize: 14),
|
||||
stop: const Duration(seconds: 3),
|
||||
),
|
||||
),
|
||||
if (state.file != null)
|
||||
if (state.file != null && !kIsWeb)
|
||||
FutureBuilder(
|
||||
future: state.file!.main.length(),
|
||||
builder: (context, snapshot) {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class AudioWave extends StatefulWidget {
|
|||
|
||||
class _AudioWaveState extends State<AudioWave> {
|
||||
final int itemCount = 35;
|
||||
final AudioPlayer audioPlayer = AudioPlayer();
|
||||
final AudioPlayer audioPlayer = AudioPlayer()..setPitch(1);
|
||||
final ValueNotifier<List<double>> randoms = ValueNotifier([]);
|
||||
final ValueNotifier<List<double>> randomsDisable = ValueNotifier([]);
|
||||
|
||||
|
|
@ -40,7 +40,9 @@ class _AudioWaveState extends State<AudioWave> {
|
|||
void initState() {
|
||||
super.initState();
|
||||
try {
|
||||
init();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
await init();
|
||||
});
|
||||
listeners();
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
|
|
@ -54,19 +56,23 @@ class _AudioWaveState extends State<AudioWave> {
|
|||
void setRandoms() {
|
||||
for (var i = 0; i < itemCount; i++) {
|
||||
randoms.value.add(0);
|
||||
randomsDisable.value.add(5.74.w() * Random().nextDouble() + .26.w());
|
||||
randomsDisable.value.add(2 + Random().nextDouble() * (42 - 2));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
try {
|
||||
final path = widget.file;
|
||||
if (widget.file.startsWith('/uploads')) {
|
||||
final audioSource = LockCachingAudioSource(Uri.parse(
|
||||
if (widget.file.startsWith('blob:')) {
|
||||
totalDuration = await audioPlayer
|
||||
.setAudioSource(AudioSource.uri(Uri.parse(path))) ??
|
||||
Duration.zero;
|
||||
} else if (widget.file.startsWith('/uploads')) {
|
||||
final audioSource = AudioSource.uri(Uri.parse(
|
||||
'${RequestHelper.baseUrl + path}?accessToken=${RequestService.token}'));
|
||||
await audioSource.request();
|
||||
totalDuration =
|
||||
await audioPlayer.setAudioSource(audioSource) ?? Duration.zero;
|
||||
totalDuration = await audioPlayer.setUrl(
|
||||
'${RequestHelper.baseUrl + path}?accessToken=${RequestService.token}') ??
|
||||
Duration.zero;
|
||||
} else {
|
||||
totalDuration = await audioPlayer.setFilePath(path) ?? Duration.zero;
|
||||
}
|
||||
|
|
@ -114,7 +120,8 @@ class _AudioWaveState extends State<AudioWave> {
|
|||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
void dispose() async {
|
||||
await audioPlayer.stop();
|
||||
audioPlayer.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
|
@ -215,7 +222,7 @@ class _AudioWaveState extends State<AudioWave> {
|
|||
Opacity(
|
||||
opacity: 0,
|
||||
child: Container(
|
||||
width: 50.5.w(),
|
||||
width: 12,
|
||||
color: Colors.transparent
|
||||
.withOpacity(1),
|
||||
child: Theme(
|
||||
|
|
@ -302,8 +309,8 @@ class _AudioWaveState extends State<AudioWave> {
|
|||
children: values
|
||||
.map(
|
||||
(e) => Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: .2.w()),
|
||||
width: .56.w(),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 1),
|
||||
width: 2,
|
||||
height: e,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(1000),
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:didvan/providers/user.dart';
|
|||
import 'package:didvan/services/network/request.dart';
|
||||
import 'package:didvan/services/network/request_helper.dart';
|
||||
import 'package:didvan/utils/action_sheet.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:home_widget/home_widget.dart';
|
||||
|
||||
class AuthenticationState extends CoreProvier {
|
||||
|
|
@ -52,7 +53,9 @@ class AuthenticationState extends CoreProvier {
|
|||
final token = service.result['token'];
|
||||
await userProvider.setAndGetToken(newToken: token);
|
||||
RequestService.token = token;
|
||||
HomeWidget.saveWidgetData("token", token.toString());
|
||||
if (!kIsWeb) {
|
||||
HomeWidget.saveWidgetData("token", token.toString());
|
||||
}
|
||||
await userProvider.getUserInfo();
|
||||
appState = AppState.idle;
|
||||
return token;
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ class _UsernameInputState extends State<UsernameInput> {
|
|||
.bodySmall!
|
||||
.copyWith(color: Theme.of(context).colorScheme.primary),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => launchUrlString(
|
||||
..onTap = () => openInWebView(
|
||||
'https://didvan.app/terms-of-use#conditions',
|
||||
),
|
||||
),
|
||||
|
|
@ -91,7 +91,7 @@ class _UsernameInputState extends State<UsernameInput> {
|
|||
.bodySmall!
|
||||
.copyWith(color: Theme.of(context).colorScheme.primary),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => launchUrlString(
|
||||
..onTap = () => openInWebView(
|
||||
'https://didvan.app/terms-of-use#privacy',
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:didvan/models/customize_categories/favorites_response.dart';
|
||||
import 'package:didvan/models/enums.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../../models/view/alert_data.dart';
|
||||
import '../../providers/core.dart';
|
||||
|
|
@ -100,7 +101,9 @@ class CustomizeCategoryState extends CoreProvier {
|
|||
);
|
||||
|
||||
appState = AppState.idle;
|
||||
await HomeWidgetRepository.fetchWidget();
|
||||
if (!kIsWeb) {
|
||||
await HomeWidgetRepository.fetchWidget();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -134,7 +137,9 @@ class CustomizeCategoryState extends CoreProvier {
|
|||
);
|
||||
|
||||
appState = AppState.idle;
|
||||
await HomeWidgetRepository.fetchWidget();
|
||||
if (!kIsWeb) {
|
||||
await HomeWidgetRepository.fetchWidget();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import 'package:didvan/views/widgets/didvan/bnb.dart';
|
|||
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
|
||||
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
|
@ -52,7 +53,9 @@ class _HomeState extends State<Home>
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
NotificationService.startListeningNotificationEvents();
|
||||
if (!kIsWeb) {
|
||||
NotificationService.startListeningNotificationEvents();
|
||||
}
|
||||
|
||||
final state = context.read<HomeState>();
|
||||
DesignConfig.updateSystemUiOverlayStyle();
|
||||
|
|
@ -67,16 +70,17 @@ class _HomeState extends State<Home>
|
|||
context.read<HistoryAiChatState>().getBots();
|
||||
}
|
||||
});
|
||||
|
||||
Future.delayed(Duration.zero, () {
|
||||
HomeWidgetRepository.fetchWidget();
|
||||
HomeWidgetRepository.decideWhereToGo();
|
||||
NotificationMessage? data = HomeWidgetRepository.data;
|
||||
if (data != null) {
|
||||
HomeWidgetRepository.decideWhereToGoNotif();
|
||||
}
|
||||
AppInitializer.handleCLick(state, _tabController);
|
||||
});
|
||||
if (!kIsWeb) {
|
||||
Future.delayed(Duration.zero, () {
|
||||
HomeWidgetRepository.fetchWidget();
|
||||
HomeWidgetRepository.decideWhereToGo();
|
||||
NotificationMessage? data = HomeWidgetRepository.data;
|
||||
if (data != null) {
|
||||
HomeWidgetRepository.decideWhereToGoNotif();
|
||||
}
|
||||
AppInitializer.handleCLick(state, _tabController);
|
||||
});
|
||||
}
|
||||
state.refresh();
|
||||
context.read<ThemeProvider>().addListener(() {
|
||||
state.refresh();
|
||||
|
|
@ -120,8 +124,12 @@ class _HomeState extends State<Home>
|
|||
Icon(
|
||||
DidvanIcons.ai_solid,
|
||||
size: MediaQuery.sizeOf(context).width / 5,
|
||||
color: Theme.of(context).colorScheme.title,
|
||||
),
|
||||
DidvanText(
|
||||
'هوشان',
|
||||
color: Theme.of(context).colorScheme.title,
|
||||
),
|
||||
const DidvanText('هوشان'),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
|
|
@ -569,7 +577,7 @@ class _HomeState extends State<Home>
|
|||
Icon(
|
||||
icon,
|
||||
color: enable
|
||||
? null
|
||||
? Theme.of(context).colorScheme.title
|
||||
: Theme.of(context).colorScheme.disabledText,
|
||||
),
|
||||
const SizedBox(
|
||||
|
|
@ -580,7 +588,7 @@ class _HomeState extends State<Home>
|
|||
DidvanText(text,
|
||||
fontSize: 16,
|
||||
color: enable
|
||||
? null
|
||||
? Theme.of(context).colorScheme.title
|
||||
: Theme.of(context).colorScheme.disabledText),
|
||||
// if (!enable) Text('در حال توسعه ...')
|
||||
],
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ class _MainPageSection extends StatelessWidget {
|
|||
|
||||
void _moreHandler(BuildContext context) {
|
||||
if (list.link.startsWith('http')) {
|
||||
launchUrlString(
|
||||
openInWebView(
|
||||
'${list.link}?accessToken=${RequestService.token}',
|
||||
mode: LaunchMode.inAppWebView,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ class MainPageState extends CoreProvier {
|
|||
return;
|
||||
}
|
||||
if (link.startsWith('http')) {
|
||||
launchUrlString(
|
||||
openInWebView(
|
||||
'$link?accessToken=${RequestService.token}',
|
||||
mode: LaunchMode.inAppWebView,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class MainPageBanner extends StatelessWidget {
|
|||
onTap: () => item.link == null || item.link!.isEmpty
|
||||
? ActionSheetUtils(context)
|
||||
.openInteractiveViewer(context, item.image, false)
|
||||
: launchUrlString(item.link!, mode: LaunchMode.inAppWebView),
|
||||
: openInWebView(item.link!, mode: LaunchMode.inAppWebView),
|
||||
child: SkeletonImage(
|
||||
imageUrl: item.image,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ class _NewStatisticState extends State<NewStatistic> {
|
|||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
physics: const ScrollPhysics(),
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: state.contents[id].contents.length,
|
||||
itemBuilder: (context, index) => StatMainCard(
|
||||
id: id,
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class SearchResultItem extends StatelessWidget {
|
|||
return;
|
||||
}
|
||||
if (_targetPageRouteName == null && item.link != null) {
|
||||
launchUrlString(
|
||||
openInWebView(
|
||||
'${item.link!}?accessToken=${RequestService.token}',
|
||||
mode: LaunchMode.inAppWebView,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class MainCategories extends StatelessWidget {
|
|||
|
||||
void _onTap(String link, BuildContext context) {
|
||||
if (link.startsWith('http')) {
|
||||
launchUrlString(
|
||||
openInWebView(
|
||||
'$link?accessToken=${RequestService.token}',
|
||||
mode: LaunchMode.inAppWebView,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class StudioDetailsWidget extends StatelessWidget {
|
|||
key: ValueKey(state.studio.id),
|
||||
data: state.studio.description,
|
||||
onAnchorTap: (href, _, __) =>
|
||||
launchUrlString(href!),
|
||||
openInWebView(href!),
|
||||
style: {
|
||||
'*': Style(
|
||||
direction: TextDirection.rtl,
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ class _ProfilePageState extends State<ProfilePage> {
|
|||
icon: DidvanIcons.info_circle_regular,
|
||||
title: 'معرفی دیدوان',
|
||||
onTap: () =>
|
||||
launchUrlString('https://didvan.app/#info'),
|
||||
openInWebView('https://didvan.app/#info'),
|
||||
),
|
||||
const DidvanDivider(),
|
||||
MenuOption(
|
||||
|
|
@ -338,7 +338,7 @@ class _ProfilePageState extends State<ProfilePage> {
|
|||
MenuOption(
|
||||
icon: DidvanIcons.alert_regular,
|
||||
title: 'حریم خصوصی',
|
||||
onTap: () => launchUrlString(
|
||||
onTap: () => openInWebView(
|
||||
'https://didvan.app/terms-of-use#privacy',
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ class _DidvanPageViewState extends State<DidvanPageView> {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
launchUrlString(href);
|
||||
openInWebView(href);
|
||||
}
|
||||
},
|
||||
style: {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ class MultitypeOverview extends StatelessWidget {
|
|||
return;
|
||||
}
|
||||
if (_targetPageRouteName == null && item.link != null) {
|
||||
launchUrlString(
|
||||
openInWebView(
|
||||
'${item.link!}?accessToken=${RequestService.token}',
|
||||
mode: LaunchMode.inAppWebView,
|
||||
);
|
||||
|
|
|
|||
64
pubspec.lock
64
pubspec.lock
|
|
@ -1138,6 +1138,70 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.0"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "17cd5e205ea615e2c6ea7a77323a11712dffa0720a8a90540db57a01347f9ad9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.2"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.1"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.3"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: "49c10f879746271804767cb45551ec5592cdab00ee105c06dddde1a98f73b185"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ dependencies:
|
|||
mime: ^1.0.2
|
||||
path: any
|
||||
flutter_cache_manager: any
|
||||
url_launcher: ^6.3.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Reference in New Issue