houshvan version 3 -- 20/06/1403 -- Rhmn

This commit is contained in:
OkaykOrhmn 2024-09-10 14:50:54 +03:30
parent 66b85af8f2
commit 341f9786a5
50 changed files with 1492 additions and 1213 deletions

View File

@ -0,0 +1,8 @@
<svg width="30" height="24" viewBox="0 0 30 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.9541 13.5H14.9541H23.9541V12H5.9541V13.5Z" fill="#195D80"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.4541 10.5C9.61035 10.5 8.9541 9.84375 8.9541 9C8.9541 8.15625 9.61035 7.5 10.4541 7.5C11.251 7.5 11.9541 8.15625 11.9541 9C11.9541 9.79688 11.251 10.5 10.4541 10.5ZM19.4541 10.5C18.6103 10.5 17.9541 9.84375 17.9541 9C17.9541 8.15625 18.6103 7.5 19.4541 7.5C20.251 7.5 20.9541 8.15625 20.9541 9C20.9541 9.79688 20.251 10.5 19.4541 10.5Z" fill="#B8B8B8"/>
<path d="M23.9541 4.5H5.9541V6H23.9541V4.5Z" fill="#195D80"/>
<path d="M2.71972 9.75H4.4541V8.25H2.71972C2.70577 8.23605 2.68663 8.20963 2.66136 8.17478C2.51688 7.97544 2.17228 7.5 1.45411 7.5C-0.473401 7.5 -0.49597 10.4531 1.45411 10.4531C2.12634 10.4531 2.47126 10.0366 2.63113 9.8435C2.67192 9.79424 2.70067 9.75953 2.71972 9.75Z" fill="#195D80"/>
<path d="M29.9541 9C29.9541 8.20312 29.2978 7.5 28.4541 7.5C27.6199 7.5 27.2791 8.03464 27.1623 8.21792L27.1554 8.22869L27.1416 8.25H25.4541V9.75H27.1416L27.1523 9.76636L27.1623 9.78208C27.2791 9.96536 27.6199 10.5 28.4541 10.5C29.2978 10.5 29.9541 9.79688 29.9541 9Z" fill="#195D80"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.4541 16.5V9.75V8.25V3C25.4541 1.35938 24.0947 0 22.4541 0H7.4541C5.7666 0 4.4541 1.35938 4.4541 3V8.25V9.75V16.4531C4.4541 18.0938 5.7666 19.4062 7.4541 19.4062H10.4541V23.3438C10.4541 23.8125 10.6885 24 11.0166 24C11.1103 24 11.2041 24 11.3447 23.9062L17.2041 19.5H22.4541C24.0947 19.5 25.4541 18.1406 25.4541 16.5ZM7.4541 18.0469C6.61035 18.0469 5.9541 17.3906 5.9541 16.5938V13.5V12V6V4.5V3C5.9541 2.20312 6.61035 1.54688 7.4541 1.54688H22.4541C23.251 1.54688 23.9541 2.25 23.9541 3V4.5V6V12V13.5V16.5C23.9541 17.2969 23.251 17.9531 22.4541 17.9531H17.1572C16.8291 17.9531 16.5478 18.0469 16.2666 18.2344L11.9541 21.5625V18.75C11.9541 18.375 11.5791 18.0469 11.2041 18.0469H7.4541Z" fill="#B8B8B8"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,8 @@
<svg width="30" height="24" viewBox="0 0 30 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.9541 13.5H23.9541V12H5.9541V13.5Z" fill="#B20436"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.4541 10.5C9.61035 10.5 8.9541 9.84375 8.9541 9C8.9541 8.15625 9.61035 7.5 10.4541 7.5C11.251 7.5 11.9541 8.15625 11.9541 9C11.9541 9.79688 11.251 10.5 10.4541 10.5ZM19.4541 10.5C18.6103 10.5 17.9541 9.84375 17.9541 9C17.9541 8.15625 18.6103 7.5 19.4541 7.5C20.251 7.5 20.9541 8.15625 20.9541 9C20.9541 9.79688 20.251 10.5 19.4541 10.5Z" fill="#012348"/>
<path d="M23.9541 4.5H5.9541V6H23.9541V4.5Z" fill="#B20436"/>
<path d="M2.71972 9.75H4.4541V8.25H2.71972C2.70577 8.23605 2.68663 8.20963 2.66136 8.17478C2.51688 7.97544 2.17228 7.5 1.45411 7.5C-0.473401 7.5 -0.49597 10.4531 1.45411 10.4531C2.12634 10.4531 2.47126 10.0366 2.63113 9.8435C2.67192 9.79424 2.70067 9.75953 2.71972 9.75Z" fill="#B20436"/>
<path d="M29.9541 9C29.9541 8.20312 29.2978 7.5 28.4541 7.5C27.6199 7.5 27.2791 8.03464 27.1623 8.21792L27.1554 8.22869L27.1416 8.25H25.4541V9.75H27.1416L27.1523 9.76636L27.1623 9.78208C27.2791 9.96536 27.6199 10.5 28.4541 10.5C29.2978 10.5 29.9541 9.79688 29.9541 9Z" fill="#B20436"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M25.4541 16.5V9.75V8.25V3C25.4541 1.35938 24.0947 0 22.4541 0H7.4541C5.7666 0 4.4541 1.35938 4.4541 3V8.25V9.75V16.4531C4.4541 18.0938 5.7666 19.4062 7.4541 19.4062H10.4541V23.3438C10.4541 23.8125 10.6885 24 11.0166 24C11.1103 24 11.2041 24 11.3447 23.9062L17.2041 19.5H22.4541C24.0947 19.5 25.4541 18.1406 25.4541 16.5ZM7.4541 18.0469C6.61035 18.0469 5.9541 17.3906 5.9541 16.5938V13.5V12V6V4.5V3C5.9541 2.20312 6.61035 1.54688 7.4541 1.54688H22.4541C23.251 1.54688 23.9541 2.25 23.9541 3V4.5V6V12V13.5V16.5C23.9541 17.2969 23.251 17.9531 22.4541 17.9531H17.1572C16.8291 17.9531 16.5478 18.0469 16.2666 18.2344L11.9541 21.5625V18.75C11.9541 18.375 11.5791 18.0469 11.2041 18.0469H7.4541Z" fill="#012348"/>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -78,7 +78,7 @@ class Assets {
static String get podcast => '$_baseFeaturesPath/podcast-$_themeSuffix.svg';
static String get risk => '$_baseFeaturesPath/risk-$_themeSuffix.svg';
static String get saha => '$_baseFeaturesPath/saha-$_themeSuffix.svg';
static String get ai => '$_baseFeaturesPath/ai_solid.svg';
static String get ai => '$_baseFeaturesPath/ai-$_themeSuffix.svg';
static String get startup => '$_baseFeaturesPath/startup-$_themeSuffix.svg';
static String get stats => '$_baseFeaturesPath/stats-$_themeSuffix.svg';
static String get tech => '$_baseFeaturesPath/tech-$_themeSuffix.svg';

View File

@ -5,6 +5,7 @@ class BotsModel {
String? description;
List<String>? attachmentType;
int? attachment;
bool? editable;
BotsModel({this.id, this.name, this.image});
@ -20,6 +21,7 @@ class BotsModel {
});
}
attachment = json['attachment'];
editable = json['editable'];
}
Map<String, dynamic> toJson() {
@ -32,6 +34,7 @@ class BotsModel {
data['attachmentType'] = attachmentType!.map((v) => v).toList();
}
data['attachment'] = attachment;
data['editable'] = editable;
return data;
}
}

View File

@ -96,6 +96,7 @@ class Prompts {
String? createdAt;
bool? finished;
bool? error;
bool? audio;
Prompts({
this.id,
@ -107,6 +108,7 @@ class Prompts {
this.createdAt,
this.finished,
this.error,
this.audio,
});
Prompts.fromJson(Map<String, dynamic> json) {
@ -117,6 +119,7 @@ class Prompts {
fileName = json['fileName'];
role = json['role'];
createdAt = json['createdAt'];
audio = json['audio'];
}
Map<String, dynamic> toJson() {
@ -128,6 +131,7 @@ class Prompts {
data['fileName'] = fileName;
data['role'] = role;
data['createdAt'] = createdAt;
data['audio'] = audio;
return data;
}
@ -140,7 +144,8 @@ class Prompts {
String? role,
String? createdAt,
bool? finished,
bool? error}) {
bool? error,
bool? audio}) {
return Prompts(
id: id ?? this.id,
chatId: chatId ?? this.chatId,
@ -151,6 +156,7 @@ class Prompts {
createdAt: createdAt ?? this.createdAt,
finished: finished ?? this.finished,
error: error ?? this.error,
audio: audio ?? this.audio,
);
}
}

View File

@ -1,5 +1,6 @@
import 'dart:io';
import 'package:mime/mime.dart';
import 'package:path/path.dart' as p;
class FilesModel {
@ -7,10 +8,15 @@ class FilesModel {
late String basename;
late String extname;
late File main;
final bool isRecorded;
late bool isAudio;
late bool isImage;
FilesModel(this.path) {
FilesModel(this.path, {this.isRecorded = false}) {
basename = p.basename(path);
extname = p.extension(path);
main = File(path);
isAudio = lookupMimeType(path)?.startsWith('audio/') ?? false;
isImage = lookupMimeType(path)?.startsWith('image/') ?? false;
}
}

View File

@ -40,7 +40,7 @@ class NewsDetailsData {
image: json['image'],
createdAt: json['createdAt'],
marked: json['marked'],
liked: json['liked'],
liked: json['liked'] ?? false,
comments: json['comments'],
order: json['order'],
tags: List<Tag>.from(json['tags'].map((tag) => Tag.fromJson(tag))),

View File

@ -53,15 +53,15 @@ class NotificationMessage {
Map<String, String> toPayload() {
final Map<String, String> data = <String, String>{};
data['notificationType'] = notificationType!;
data['title'] = title!;
data['body'] = body!;
data['id'] = id!;
data['type'] = type!;
data['link'] = link!;
data['image'] = image!;
data['photo'] = photo!;
data['userId'] = userId!;
data['notificationType'] = notificationType ?? '';
data['title'] = title ?? '';
data['body'] = body ?? '';
data['id'] = id ?? '';
data['type'] = type ?? '';
data['link'] = link ?? '';
data['image'] = image ?? '';
data['photo'] = photo ?? '';
data['userId'] = userId ?? '';
return data;
}
}

View File

@ -12,7 +12,7 @@ class AppBarData {
AppBarData(
{this.title,
this.subtitle,
this.hasBack = false,
this.hasBack = true,
this.trailing,
this.isSmall = false,
this.hasElevation = true,

View File

@ -56,6 +56,7 @@ import 'package:didvan/views/podcasts/studio_details/studio_details.mobile.dart'
if (dart.library.html) 'package:didvan/views/podcasts/studio_details/studio_details.web.dart';
import 'package:didvan/views/splash/splash.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/views/web/web_view.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:home_widget/home_widget.dart';
@ -310,6 +311,11 @@ class RouteGenerator {
return _createRoute(HistoryAiChatPage(
archived: settings.arguments as bool?,
));
case Routes.web:
return _createRoute(WebView(
src: settings.arguments as String,
));
default:
return _errorRoute(settings.name ?? '');
}

View File

@ -34,4 +34,5 @@ class Routes {
static const String notificationTime = '/notification-time';
static const String widgetSetting = '/widget-setting';
static const String newStatic = '/new-static';
static const String web = '/web';
}

View File

@ -32,7 +32,7 @@ class AiApiService {
request.fields['chatId'] = chatId.toString();
}
if (edite != null) {
request.fields['edite'] = edite.toString().toLowerCase();
request.fields['edit'] = edite.toString().toLowerCase();
}
if (file != null) {
final length = await file.length();
@ -50,6 +50,10 @@ class AiApiService {
mimeType)), // Use MediaType.parse to parse the MIME type
);
}
// print("req: ${request.files}");
// print("req: ${request.fields}");
return request;
}

View File

@ -1,8 +1,10 @@
import 'package:didvan/models/notification_message.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/storage/storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:home_widget/home_widget.dart';
import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:url_launcher/url_launcher_string.dart';
// import 'package:url_launcher/url_launcher_string.dart';
import '../../main.dart';
import '../../models/requests/infography.dart';
@ -15,6 +17,7 @@ import '../network/request_helper.dart';
class HomeWidgetRepository {
static Future<void> fetchWidget() async {
RequestService.token = await StorageService.getValue(key: 'token');
final service = RequestService(
RequestHelper.widgetNews(),
);

View File

@ -14,10 +14,15 @@ import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
enum LaunchMode { inAppWebView }
void launchUrlString(String src, {dynamic mode}) {
navigatorKey.currentState!.pushNamed(Routes.web, arguments: src);
}
class AppInitializer {
static String? fcmToken;
static String? clickAction;
static Future<void> setupServices(BuildContext context) async {
if (!kIsWeb) {
StorageService.appDocsDir =

View File

@ -62,7 +62,7 @@ class MediaService {
},
);
} else {
audioPlayer.setAsset(
audioPlayer.setFilePath(
audioSource,
tag: isVoiceMessage
? null

View File

@ -1,4 +1,6 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:ui';
import 'package:bot_toast/bot_toast.dart';
@ -12,6 +14,7 @@ import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/widgets/didvan/button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -385,6 +388,49 @@ class ActionSheetUtils {
)));
}
static void openInteractiveViewer(
BuildContext context, String image, bool isFile) {
showDialog(
context: context,
barrierDismissible: true,
builder: (context) => Dialog(
backgroundColor: Colors.transparent,
insetPadding: EdgeInsets.zero,
child: Stack(
children: [
Positioned.fill(
child: InteractiveViewer(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: isFile
? ClipRRect(
borderRadius: DesignConfig.lowBorderRadius,
child: Image.file(File(image)))
: SkeletonImage(
width: min(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height),
imageUrl: image,
),
),
),
),
),
Positioned(
right: 24,
top: 24,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surface),
child: const BackButton()),
),
],
),
),
);
}
static void pop() {
DesignConfig.updateSystemUiOverlayStyle();
Navigator.of(context).pop();

29
lib/utils/media.dart Normal file
View File

@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
/// Get screen media.
final MediaQueryData media =
// ignore: deprecated_member_use
MediaQueryData.fromView(WidgetsBinding.instance.window);
/// This extention help us to make widget responsive.
extension NumberParsing on num {
double w() => this * media.size.width / 100;
double h() => this * media.size.height / 100;
}
///
extension StringExtension on String {
String? get appendZeroPrefix {
return length <= 1 ? "0$this" : this;
}
}
/// This extention help us to make widget responsive.
extension DurationExtension on Duration {
String get formattedTime {
int sec = inSeconds % 60;
int min = (inSeconds / 60).floor();
return "${min.toString().appendZeroPrefix}:${sec.toString().appendZeroPrefix}";
}
}

View File

@ -28,7 +28,10 @@ class _AiState extends State<Ai> {
Future.delayed(
Duration.zero,
() {
// state.getChats();
if (context.read<HistoryAiChatState>().refresh) {
context.read<HistoryAiChatState>().getChats();
context.read<HistoryAiChatState>().refresh = false;
}
state.getBots();
},
);

View File

@ -11,23 +11,22 @@ import 'package:didvan/models/ai/files_model.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/services/storage/storage.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/utils/date_time.dart';
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/voice_message_view.dart';
import 'package:didvan/views/ai/widgets/audio_wave.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';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:mime/mime.dart';
import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:provider/provider.dart';
import 'package:voice_message_package/voice_message_package.dart';
class AiChatPage extends StatefulWidget {
final AiChatArgs args;
@ -71,7 +70,10 @@ class _AiChatPageState extends State<AiChatPage> {
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
context.read<HistoryAiChatState>().getChats();
if (context.read<HistoryAiChatState>().refresh) {
context.read<HistoryAiChatState>().getChats();
context.read<HistoryAiChatState>().refresh = false;
}
return true;
},
child: Consumer<AiChatState>(
@ -85,7 +87,10 @@ class _AiChatPageState extends State<AiChatPage> {
icon: DidvanIcons.angle_right_solid,
onPressed: () {
Navigator.of(context).pop();
context.read<HistoryAiChatState>().getChats();
if (context.read<HistoryAiChatState>().refresh) {
context.read<HistoryAiChatState>().getChats();
context.read<HistoryAiChatState>().refresh = false;
}
},
),
],
@ -99,6 +104,7 @@ class _AiChatPageState extends State<AiChatPage> {
final TextEditingController placeholder =
TextEditingController(
text: state.chat?.placeholder);
ActionSheetUtils.context = context;
ActionSheetUtils.openDialog(
data: ActionSheetData(
hasConfirmButtonClose: false,
@ -168,6 +174,10 @@ class _AiChatPageState extends State<AiChatPage> {
fontFamily: DesignConfig
.fontFamily
.padRight(3)),
minLines: 4,
maxLines: 4,
keyboardType:
TextInputType.multiline,
decoration: InputDecoration(
filled: true,
fillColor: Theme.of(context)
@ -249,7 +259,11 @@ class _AiChatPageState extends State<AiChatPage> {
itemCount: state.messages.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.only(bottom: 120),
padding: EdgeInsets.only(
bottom: state.file != null &&
!state.file!.isRecorded
? 150
: 100),
itemBuilder: (context, mIndex) {
final prompts = state.messages[mIndex].prompts;
final time = state.messages[mIndex].dateTime;
@ -263,6 +277,7 @@ class _AiChatPageState extends State<AiChatPage> {
const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final message = prompts[index];
return messageBubble(message, context,
state, index, mIndex);
},
@ -274,13 +289,9 @@ class _AiChatPageState extends State<AiChatPage> {
bottomSheet: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.only(
top: 8, bottom: 24.0, left: 12, right: 12),
child: AiMessageBar(
bot: widget.args.bot,
focusNode: focusNode,
),
AiMessageBar(
bot: widget.args.bot,
focusNode: focusNode,
),
],
)),
@ -308,20 +319,32 @@ class _AiChatPageState extends State<AiChatPage> {
);
}
Padding messageBubble(Prompts message, BuildContext context,
AiChatState state, int index, int mIndex) {
Widget messageBubble(Prompts message, BuildContext context, AiChatState state,
int index, int mIndex) {
FilesModel? file =
message.file == null ? null : FilesModel(message.file.toString());
MarkdownStyleSheet defaultMarkdownStyleSheet = MarkdownStyleSheet(
pPadding: const EdgeInsets.all(0.8),
h1Padding: const EdgeInsets.all(0.8),
h2Padding: const EdgeInsets.all(0.8),
h3Padding: const EdgeInsets.all(0.8),
h4Padding: const EdgeInsets.all(0.8),
h5Padding: const EdgeInsets.all(0.8),
h6Padding: const EdgeInsets.all(0.8),
tablePadding: const EdgeInsets.all(0.8),
blockquotePadding: const EdgeInsets.all(0.8),
listBulletPadding: const EdgeInsets.all(0.8),
tableCellsPadding: const EdgeInsets.all(0.8),
codeblockPadding: const EdgeInsets.all(8),
code: TextStyle(
backgroundColor: Theme.of(context).colorScheme.black,
color: Theme.of(context).colorScheme.white,
),
codeblockPadding: const EdgeInsets.all(8),
codeblockDecoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Theme.of(context).colorScheme.black));
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 4),
child: Column(
@ -336,8 +359,8 @@ class _AiChatPageState extends State<AiChatPage> {
: MainAxisAlignment.end,
children: [
Container(
padding:
const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
constraints: BoxConstraints(
maxWidth: MediaQuery.sizeOf(context).width / 1.5),
decoration: BoxDecoration(
borderRadius: DesignConfig.mediumBorderRadius.copyWith(
bottomLeft: !message.role.toString().contains('user')
@ -359,8 +382,6 @@ class _AiChatPageState extends State<AiChatPage> {
),
),
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.sizeOf(context).width / 1.5),
child: message.finished != null && !message.finished!
? Column(
children: [
@ -394,102 +415,29 @@ class _AiChatPageState extends State<AiChatPage> {
children: [
if (message.role.toString().contains('user') &&
file != null)
lookupMimeType(file.path)?.startsWith('audio/') ??
false
? Directionality(
textDirection: TextDirection.ltr,
child: FutureBuilder(
future: StorageService.getValue(
key: 'token'),
builder: (context, snapshot) {
return MyVoiceMessageView(
size: 32,
controller: VoiceController(
audioSrc: file.path
.startsWith('/uploads')
? 'https://api.didvan.app${file.path}?accessToken=${snapshot.data}'
: file.path,
onComplete: () {
/// do something on complete
},
onPause: () {
/// do something on pause
},
onPlaying: () {
/// do something on playing
},
onError: (err) {
/// do somethin on error
},
isFile: !file.path
.startsWith('/uploads'),
maxDuration:
const Duration(seconds: 10),
),
innerPadding: 0,
cornerRadius: 20,
circlesColor: Theme.of(context)
.colorScheme
.primary,
activeSliderColor:
Theme.of(context)
.colorScheme
.primary,
);
}),
file.isAudio
? Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16, vertical: 8),
child: AudioWave(file: file.path),
)
: Container(
decoration: BoxDecoration(
borderRadius:
DesignConfig.mediumBorderRadius,
color: Theme.of(context)
.colorScheme
.border,
),
constraints:
const BoxConstraints(minWidth: 200),
padding: const EdgeInsets.fromLTRB(
12, 8, 12, 8),
margin: const EdgeInsets.symmetric(
horizontal: 12),
child: Row(
children: [
const Icon(Icons.file_copy),
const SizedBox(
width: 12,
: file.isImage
? Padding(
padding: const EdgeInsets.all(8.0),
child: messageImage(file),
)
: Padding(
padding: const EdgeInsets.all(
8.0,
),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
SizedBox(
width: 200,
child: DidvanText((message
.fileName
.toString())),
),
if (state.file != null)
FutureBuilder(
future: state.file!.main
.length(),
builder:
(context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
return DidvanText(
'File Size ${(snapshot.data! / 1000).round()} KB',
fontSize: 12,
);
})
],
),
)
],
),
),
if (message.text != null)
child: messageFile(
context, message, state),
),
if (message.text != null &&
message.text!.isNotEmpty &&
((message.audio == null ||
(message.audio != null &&
!message.audio!))))
Markdown(
data: message.text.toString(),
selectable: true,
@ -497,24 +445,75 @@ class _AiChatPageState extends State<AiChatPage> {
physics: const NeverScrollableScrollPhysics(),
styleSheet: defaultMarkdownStyleSheet,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (message.role.toString().contains('user') &&
index ==
state.messages[mIndex].prompts.length -
2)
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (message.role
.toString()
.contains('user') &&
index ==
state.messages[mIndex].prompts
.length -
2 &&
(widget.args.bot.editable != null &&
widget.args.bot.editable!))
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () async {
state.isEdite = true;
state.message.text =
message.text.toString();
state.update();
},
child: Icon(
Icons.edit_outlined,
size: 18,
color: Theme.of(context)
.colorScheme
.focusedBorder,
),
),
),
if (message.error != null && message.error!)
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () async {
state.messages.last.prompts
.remove(message);
state.messages.last.prompts.add(
message.copyWith(error: false));
state.update();
await state
.postMessage(widget.args.bot);
},
child: Icon(
DidvanIcons.refresh_solid,
size: 18,
color: Theme.of(context)
.colorScheme
.focusedBorder,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () async {
state.isEdite = true;
state.message.text =
message.text.toString();
state.update();
await Clipboard.setData(ClipboardData(
text: state.messages[mIndex]
.prompts[index].text
.toString()));
ActionSheetUtils.showAlert(AlertData(
message: "متن با موفقیت کپی شد",
aLertType: ALertType.success));
},
child: Icon(
Icons.edit_outlined,
DidvanIcons.copy_regular,
size: 18,
color: Theme.of(context)
.colorScheme
@ -522,21 +521,20 @@ class _AiChatPageState extends State<AiChatPage> {
),
),
),
if (message.error != null && message.error!)
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () async {
state.messages.last.prompts
.remove(message);
state.messages.last.prompts.add(
message.copyWith(error: false));
state.update();
await state
.postMessage(widget.args.bot);
if (message.id != null) {
state.deleteMessage(
message.id!, mIndex, index);
} else {
state.messages[mIndex].prompts
.removeAt(index);
}
},
child: Icon(
DidvanIcons.refresh_solid,
DidvanIcons.trash_solid,
size: 18,
color: Theme.of(context)
.colorScheme
@ -544,50 +542,10 @@ class _AiChatPageState extends State<AiChatPage> {
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () async {
await Clipboard.setData(ClipboardData(
text: state.messages[mIndex]
.prompts[index].text
.toString()));
ActionSheetUtils.showAlert(AlertData(
message: "متن با موفقیت کپی شد",
aLertType: ALertType.success));
},
child: Icon(
DidvanIcons.copy_regular,
size: 18,
color: Theme.of(context)
.colorScheme
.focusedBorder,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () async {
if (message.id != null) {
state.deleteMessage(
message.id!, mIndex, index);
} else {
state.messages[mIndex].prompts
.removeAt(index);
}
},
child: Icon(
DidvanIcons.trash_solid,
size: 18,
color: Theme.of(context)
.colorScheme
.focusedBorder,
),
),
),
],
)
],
),
),
const SizedBox(height: 8),
],
),
),
@ -610,4 +568,67 @@ class _AiChatPageState extends State<AiChatPage> {
),
);
}
Container messageFile(
BuildContext context, Prompts message, AiChatState state) {
return Container(
decoration: BoxDecoration(
borderRadius: DesignConfig.mediumBorderRadius,
color: Theme.of(context).colorScheme.border,
),
padding: const EdgeInsets.fromLTRB(12, 8, 12, 8),
margin: const EdgeInsets.fromLTRB(8, 8, 8, 0),
child: Row(
children: [
const Icon(Icons.file_copy),
const SizedBox(
width: 12,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: MediaQuery.sizeOf(context).width,
child: MarqueeText(
text: message.fileName.toString(),
style: const TextStyle(fontSize: 14),
stop: const Duration(seconds: 3),
),
),
if (state.file != null)
FutureBuilder(
future: state.file!.main.length(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
return DidvanText(
'File Size ${(snapshot.data! / 1000).round()} KB',
fontSize: 12,
);
})
],
),
)
],
),
);
}
Widget messageImage(FilesModel file) {
return GestureDetector(
onTap: () => ActionSheetUtils.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,
)
: ClipRRect(
borderRadius: DesignConfig.lowBorderRadius,
child: Image.file(file.main)),
);
}
}

View File

@ -44,7 +44,8 @@ class AiChatState extends CoreProvier {
Future<void> _onError(e) async {
onResponsing = false;
messages.last.prompts.removeLast();
messages.last.prompts.removeLast();
messages.last.prompts.last =
messages.last.prompts.last.copyWith(error: true);
messageOnstream.value = const Stream.empty();
await ActionSheetUtils.showAlert(AlertData(

View File

@ -51,7 +51,10 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
context.read<HistoryAiChatState>().getChats();
if (context.read<HistoryAiChatState>().refresh) {
context.read<HistoryAiChatState>().getChats();
context.read<HistoryAiChatState>().refresh = false;
}
return true;
},
child: DidvanScaffold(
@ -70,13 +73,17 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
asset: Assets.emptyResult,
title: 'لیست خالی است',
),
enableEmptyState: state.chats.isEmpty,
enableEmptyState: archived
? state.archivedChats.isEmpty
: state.chats.isEmpty,
placeholder: const _HistoryPlaceholder(),
placeholderCount: 8,
// builder: (context, state, index) => _HistoryPlaceholder(),
builder: (context, state, index) {
final chat = state.chats[index];
final chat = archived
? state.archivedChats[index]
: state.chats[index];
TextEditingController title =
TextEditingController(text: state.chats[index].title);
return Dismissible(
@ -349,8 +356,10 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
),
);
},
childCount: state.chats.length,
onRetry: state.getChats);
childCount: archived
? state.archivedChats.length
: state.chats.length,
onRetry: () => state.getChats(archived: archived));
},
)
],
@ -359,7 +368,10 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
hasBack: true,
hasElevation: true,
backClick: () {
context.read<HistoryAiChatState>().getChats();
if (context.read<HistoryAiChatState>().refresh) {
context.read<HistoryAiChatState>().getChats();
context.read<HistoryAiChatState>().refresh = false;
}
},
trailing: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
@ -370,7 +382,7 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
data: ActionSheetData(
onConfirmed: () async {
final state = context.read<HistoryAiChatState>();
await state.deleteAllChat();
archived ? null : await state.deleteAllChat();
},
content: Column(
children: [
@ -385,7 +397,9 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
width: 8,
),
DidvanText(
'پاک کردن همه گفت‌وگوها',
archived
? 'خارج کردن همه آرشیو ها'
: 'پاک کردن همه گفت‌وگوها',
color: Theme.of(context).colorScheme.error,
fontSize: 20,
),

View File

@ -12,12 +12,14 @@ import 'package:flutter/cupertino.dart';
class HistoryAiChatState extends CoreProvier {
final List<ChatsModel> chats = [];
final List<ChatsModel> archivedChats = [];
// final List<int> chatsToDelete = [];
final List<BotsModel> bots = [];
BotsModel? bot;
ValueNotifier<bool> loadingBots = ValueNotifier(false);
bool loadingchangeTitle = false;
bool loadingdeleteAll = false;
bool refresh = false;
Timer? timer;
String search = '';
@ -31,10 +33,12 @@ class HistoryAiChatState extends CoreProvier {
);
await service.httpGet();
if (service.isSuccess) {
chats.clear();
archived != null && archived ? archivedChats.clear() : chats.clear();
final messages = service.result['chats'];
for (var i = 0; i < messages.length; i++) {
chats.add(ChatsModel.fromJson(messages[i]));
archived != null && archived
? archivedChats.add(ChatsModel.fromJson(messages[i]))
: chats.add(ChatsModel.fromJson(messages[i]));
}
appState = AppState.idle;
update();
@ -140,7 +144,8 @@ class HistoryAiChatState extends CoreProvier {
// update();
// }
Future<void> changeNameChat(int id, int index, String title) async {
Future<void> changeNameChat(int id, int index, String title,
{final bool refresh = true}) async {
loadingchangeTitle = true;
update();
final service =
@ -152,7 +157,7 @@ class HistoryAiChatState extends CoreProvier {
appState = AppState.idle;
loadingchangeTitle = false;
update();
this.refresh = refresh;
return;
}
appState = AppState.failed;
@ -163,7 +168,8 @@ class HistoryAiChatState extends CoreProvier {
update();
}
Future<void> deleteChat(int id, int index) async {
Future<void> deleteChat(int id, int index,
{final bool refresh = true}) async {
final service = RequestService(RequestHelper.deleteChat(id));
await service.delete();
if (service.isSuccess) {
@ -173,6 +179,7 @@ class HistoryAiChatState extends CoreProvier {
loadingchangeTitle = false;
update();
this.refresh = refresh;
return;
}
@ -184,7 +191,7 @@ class HistoryAiChatState extends CoreProvier {
update();
}
Future<void> deleteAllChat() async {
Future<void> deleteAllChat({final bool refresh = true}) async {
loadingdeleteAll = true;
update();
final service = RequestService(
@ -198,6 +205,7 @@ class HistoryAiChatState extends CoreProvier {
loadingdeleteAll = false;
update();
this.refresh = refresh;
return;
}
@ -209,7 +217,8 @@ class HistoryAiChatState extends CoreProvier {
update();
}
Future<bool> archivedChat(int id, int index) async {
Future<bool> archivedChat(int id, int index,
{final bool refresh = true}) async {
update();
final service = RequestService(
RequestHelper.archivedChat(id),
@ -221,6 +230,7 @@ class HistoryAiChatState extends CoreProvier {
appState = AppState.idle;
update();
this.refresh = refresh;
return true;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,306 @@
// ignore_for_file: implementation_imports, library_private_types_in_public_api
import 'dart:math';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/utils/media.dart';
import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:just_audio/just_audio.dart';
class AudioWave extends StatefulWidget {
final String file;
final double loadingPaddingSize;
const AudioWave({Key? key, required this.file, this.loadingPaddingSize = 0})
: super(key: key);
@override
_AudioWaveState createState() => _AudioWaveState();
}
class _AudioWaveState extends State<AudioWave> {
final int itemCount = 35;
final AudioPlayer audioPlayer = AudioPlayer();
final ValueNotifier<List<double>> randoms = ValueNotifier([]);
final ValueNotifier<List<double>> randomsDisable = ValueNotifier([]);
Duration totalDuration = Duration.zero;
double currentPosition = 0;
bool loading = true;
bool faile = false;
@override
void initState() {
super.initState();
try {
init();
listeners();
} catch (e) {
if (kDebugMode) {
print('Error occurred: $e');
}
rethrow;
}
}
void setRandoms() {
for (var i = 0; i < itemCount; i++) {
randoms.value.add(0);
randomsDisable.value.add(5.74.w() * Random().nextDouble() + .26.w());
}
}
Future<void> init() async {
try {
final path = widget.file;
if (widget.file.startsWith('/uploads')) {
final audioSource = LockCachingAudioSource(Uri.parse(
'${RequestHelper.baseUrl + path}?accessToken=${RequestService.token}'));
totalDuration =
await audioPlayer.setAudioSource(audioSource) ?? Duration.zero;
} else {
totalDuration = await audioPlayer.setFilePath(path) ?? Duration.zero;
}
setRandoms();
setState(() {
loading = false;
});
} catch (e) {
setState(() {
faile = true;
loading = false;
});
if (kDebugMode) {
print('Error occurred: $e');
}
}
}
Future<void> listeners() async {
audioPlayer.positionStream.listen((position) async {
for (var i = 0; i < itemCount; i++) {
if (i <
((position.inMilliseconds * 40) / totalDuration.inMilliseconds)) {
final ran = randomsDisable.value[i];
randoms.value[i] = ran;
} else {
randoms.value[i] = 0;
}
}
if (position.inMilliseconds >= totalDuration.inMilliseconds) {
audioPlayer.stop();
audioPlayer.seek(Duration.zero);
}
});
}
@override
void dispose() {
audioPlayer.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 46,
child: loading
? Padding(
padding:
EdgeInsets.symmetric(vertical: widget.loadingPaddingSize),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
5,
(index) => SpinKitWave(
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.4),
size: 32,
itemCount: 10,
))),
)
: Directionality(
textDirection: TextDirection.ltr,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
StreamBuilder<PlayerState>(
stream: audioPlayer.playerStateStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
return MessageBarBtn(
enable: true,
icon: faile
? DidvanIcons.refresh_solid
: snapshot.data!.playing
? DidvanIcons.pause_solid
: DidvanIcons.play_solid,
click: () async {
if (faile) {
randoms.value.clear();
randomsDisable.value.clear();
setState(() {
loading = true;
faile = false;
});
init();
return;
}
if (snapshot.data!.playing) {
await audioPlayer.pause();
} else {
await audioPlayer.play();
}
},
);
}),
faile
? const Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0),
child: DidvanText(
'خطا در بارگزاری فایل صوتی',
fontSize: 12,
),
)
: StreamBuilder<Duration>(
stream: audioPlayer.positionStream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox();
}
currentPosition =
snapshot.data!.inMilliseconds.toDouble();
return Expanded(
child: Row(
children: [
ValueListenableBuilder(
valueListenable: randoms,
builder: (context, value, child) {
return Expanded(
child: Stack(
alignment: Alignment.center,
children: [
noise(values: randoms.value),
noise(
values: randomsDisable.value,
color: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.4)),
if (totalDuration != Duration.zero)
Opacity(
opacity: 0,
child: Container(
width: 50.5.w(),
color: Colors.transparent
.withOpacity(1),
child: Theme(
data: Theme.of(context)
.copyWith(
sliderTheme:
SliderThemeData(
thumbShape:
SliderComponentShape
.noThumb,
minThumbSeparation: 0,
),
splashColor:
Colors.transparent,
),
child: Slider(
value: currentPosition,
max: totalDuration
.inMilliseconds
.toDouble() +
const Duration(
milliseconds:
10)
.inMilliseconds
.toDouble(),
onChangeStart: (value) {
// audioPlayer.pause();
},
onChanged: (value) {
for (var i = 0;
i < itemCount;
i++) {
if (i <
((value * 40) /
totalDuration
.inMilliseconds)) {
final ran =
randomsDisable
.value[i];
randoms.value[i] =
ran;
} else {
randoms.value[i] = 0;
}
}
setState(() {
currentPosition = value;
});
},
onChangeEnd: (value) {
audioPlayer.seek(Duration(
milliseconds:
value.round()));
audioPlayer.play();
},
),
),
),
),
],
));
},
),
DidvanText(
DateTimeUtils.normalizeTimeDuration(
snapshot.data! == Duration.zero
? totalDuration
: snapshot.data!)),
],
),
);
},
)
],
),
),
);
}
Row noise({required final List<double> values, final Color? color}) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: values
.map(
(e) => Container(
margin: EdgeInsets.symmetric(horizontal: .2.w()),
width: .56.w(),
height: e,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(1000),
color: color ?? Theme.of(context).colorScheme.primary,
),
),
)
.toList(),
);
}
}

View File

@ -1,294 +0,0 @@
// ignore_for_file: implementation_imports, unused_element, empty_catches
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:flutter/material.dart';
import 'package:voice_message_package/src/helpers/play_status.dart';
import 'package:voice_message_package/src/helpers/utils.dart';
import 'package:voice_message_package/src/voice_controller.dart';
import 'package:voice_message_package/src/widgets/noises.dart';
import 'package:voice_message_package/src/widgets/play_pause_button.dart';
/// A widget that displays a voice message view with play/pause functionality.
///
/// The [VoiceMessageView] widget is used to display a voice message with customizable appearance and behavior.
/// It provides a play/pause button, a progress slider, and a counter for the remaining time.
/// The appearance of the widget can be customized using various properties such as background color, slider color, and text styles.
///
class MyVoiceMessageView extends StatelessWidget {
const MyVoiceMessageView(
{Key? key,
required this.controller,
this.backgroundColor = Colors.transparent,
this.activeSliderColor = Colors.red,
this.notActiveSliderColor,
this.circlesColor = Colors.red,
this.innerPadding = 12,
this.cornerRadius = 20,
// this.playerWidth = 170,
this.size = 38,
this.refreshIcon = const Icon(
Icons.refresh,
color: Colors.white,
),
this.pauseIcon = const Icon(
Icons.pause_rounded,
color: Colors.white,
),
this.playIcon = const Icon(
Icons.play_arrow_rounded,
color: Colors.white,
),
this.stopDownloadingIcon = const Icon(
Icons.close,
color: Colors.white,
),
this.playPauseButtonDecoration,
this.circlesTextStyle = const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
this.counterTextStyle = const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
),
this.playPauseButtonLoadingColor = Colors.white,
this.trashClick,
this.showSpeed = false})
: super(key: key);
/// The controller for the voice message view.
final VoiceController controller;
/// The background color of the voice message view.
final Color backgroundColor;
///
final Color circlesColor;
/// The color of the active slider.
final Color activeSliderColor;
/// The color of the not active slider.
final Color? notActiveSliderColor;
/// The text style of the circles.
final TextStyle circlesTextStyle;
/// The text style of the counter.
final TextStyle counterTextStyle;
/// The padding between the inner content and the outer container.
final double innerPadding;
/// The corner radius of the outer container.
final double cornerRadius;
/// The size of the play/pause button.
final double size;
/// The refresh icon of the play/pause button.
final Widget refreshIcon;
/// The pause icon of the play/pause button.
final Widget pauseIcon;
/// The play icon of the play/pause button.
final Widget playIcon;
/// The stop downloading icon of the play/pause button.
final Widget stopDownloadingIcon;
/// The play Decoration of the play/pause button.
final Decoration? playPauseButtonDecoration;
/// The loading Color of the play/pause button.
final Color playPauseButtonLoadingColor;
final Function()? trashClick;
final bool showSpeed;
@override
/// Build voice message view.
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
final color = circlesColor;
final newTHeme = theme.copyWith(
sliderTheme: SliderThemeData(
trackShape: CustomTrackShape(),
thumbShape: SliderComponentShape.noThumb,
minThumbSeparation: 0,
),
splashColor: Colors.transparent,
);
return Directionality(
textDirection: TextDirection.ltr,
child: Container(
width: 160 + (controller.noiseCount * .72.w()),
padding: EdgeInsets.all(innerPadding),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(cornerRadius),
),
child: ValueListenableBuilder(
/// update ui when change play status
valueListenable: controller.updater,
builder: (context, value, child) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(width: 12),
/// play pause button
PlayPauseButton(
controller: controller,
color: color,
loadingColor: playPauseButtonLoadingColor,
size: size,
refreshIcon: refreshIcon,
pauseIcon: pauseIcon,
playIcon: playIcon,
stopDownloadingIcon: stopDownloadingIcon,
buttonDecoration: playPauseButtonDecoration,
),
const SizedBox(width: 12),
///
/// slider & noises
Expanded(
child: _noises(newTHeme),
),
const SizedBox(width: 12),
Text(controller.remindingTime, style: counterTextStyle),
const SizedBox(width: 12),
///
/// speed button
if (showSpeed) _changeSpeedButton(color),
///
if (trashClick != null)
MessageBarBtn(
enable: true,
icon: DidvanIcons.trash_solid,
color: Theme.of(context).colorScheme.error,
click: () async {
trashClick?.call();
}),
const SizedBox(width: 12),
],
);
},
),
),
);
}
SizedBox _noises(ThemeData newTHeme) => SizedBox(
child: Stack(
alignment: Alignment.center,
children: [
/// noises
Noises(
rList: controller.randoms!,
activeSliderColor: activeSliderColor,
),
/// slider
AnimatedBuilder(
animation: CurvedAnimation(
parent: controller.animController,
curve: Curves.ease,
),
builder: (BuildContext context, Widget? child) {
return Positioned(
left: controller.animController.value,
child: Container(
width: controller.noiseWidth,
height: 6.w(),
color: notActiveSliderColor ?? backgroundColor,
),
);
},
),
Opacity(
opacity: 0,
child: Container(
width: controller.noiseWidth,
color: Colors.transparent.withOpacity(1),
child: Theme(
data: newTHeme,
child: Slider(
value: controller.currentMillSeconds,
max: controller.maxMillSeconds,
onChangeStart: controller.onChangeSliderStart,
onChanged: controller.onChanging,
onChangeEnd: (value) {
controller.onSeek(
Duration(milliseconds: value.toInt()),
);
controller.play();
},
),
),
),
),
],
),
);
Transform _changeSpeedButton(Color color) => Transform.translate(
offset: const Offset(0, -7),
child: GestureDetector(
onTap: () {
controller.changeSpeed();
},
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 3, vertical: 2),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(4),
),
child: Text(
controller.speed.playSpeedStr,
style: circlesTextStyle,
),
),
),
);
}
///
/// A custom track shape for a slider that is rounded rectangular in shape.
/// Extends the [RoundedRectSliderTrackShape] class.
class CustomTrackShape extends RoundedRectSliderTrackShape {
@override
/// Returns the preferred rectangle for the voice message view.
///
/// The preferred rectangle is calculated based on the current state and layout
/// of the voice message view. It represents the area where the view should be
/// displayed on the screen.
///
/// Returns a [Rect] object representing the preferred rectangle.
Rect getPreferredRect({
required RenderBox parentBox,
Offset offset = Offset.zero,
required SliderThemeData sliderTheme,
bool isEnabled = false,
bool isDiscrete = false,
}) {
const double trackHeight = 10;
final double trackLeft = offset.dx,
trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2;
final double trackWidth = parentBox.size.width;
return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight);
}
}

View File

@ -1,3 +1,4 @@
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/views/authentication/authentication_state.dart';
import 'package:didvan/views/authentication/widgets/authentication_layout.dart';
import 'package:didvan/views/widgets/didvan/button.dart';
@ -6,7 +7,7 @@ import 'package:didvan/views/widgets/didvan/text_field.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
// import 'package:url_launcher/url_launcher.dart';
class UsernameInput extends StatefulWidget {
const UsernameInput({
@ -78,10 +79,8 @@ class _UsernameInputState extends State<UsernameInput> {
.bodySmall!
.copyWith(color: Theme.of(context).colorScheme.primary),
recognizer: TapGestureRecognizer()
..onTap = () => launchUrl(
Uri.parse(
'https://didvan.app/terms-of-use#conditions',
),
..onTap = () => launchUrlString(
'https://didvan.app/terms-of-use#conditions',
),
),
const TextSpan(text: 'و\n'),
@ -92,10 +91,8 @@ class _UsernameInputState extends State<UsernameInput> {
.bodySmall!
.copyWith(color: Theme.of(context).colorScheme.primary),
recognizer: TapGestureRecognizer()
..onTap = () => launchUrl(
Uri.parse(
'https://didvan.app/terms-of-use#privacy',
),
..onTap = () => launchUrlString(
'https://didvan.app/terms-of-use#privacy',
),
),
const TextSpan(text: 'را می‌پذیرم'),

View File

@ -51,6 +51,7 @@ class DirectState extends CoreProvier {
recordedFile!.delete();
recordedFile = null;
notifyListeners();
update();
}
Future<void> startRecording() async {

View File

@ -1,5 +1,3 @@
// ignore_for_file: unused_element
import 'dart:io';
import 'package:didvan/config/theme_data.dart';
@ -7,60 +5,62 @@ import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/requests/studio.dart';
import 'package:didvan/models/studio_details_data.dart';
import 'package:didvan/services/media/media.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/views/ai/widgets/voice_message_view.dart';
import 'package:didvan/views/widgets/audio/audio_slider.dart';
import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:provider/provider.dart';
import 'package:voice_message_package/voice_message_package.dart';
class AudioWidget extends StatelessWidget {
final String? audioUrl;
final File? audioFile;
final int id;
final StudioDetailsData? audioMetaData;
final Function()? deleteClidk;
const AudioWidget({
Key? key,
this.audioUrl,
this.audioFile,
required this.id,
this.audioMetaData,
this.deleteClidk,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MyVoiceMessageView(
size: 32,
controller: VoiceController(
audioSrc: audioUrl != null
? '${RequestHelper.baseUrl + audioUrl!}?accessToken=${RequestService.token}'
: audioFile!.path,
onComplete: () {
/// do something on complete
},
onPause: () {
/// do something on pause
},
onPlaying: () {
/// do something on playing
},
onError: (err) {
/// do somethin on error
},
isFile: audioFile != null,
maxDuration: const Duration(seconds: 10),
),
innerPadding: 0,
cornerRadius: 20,
circlesColor: Theme.of(context).colorScheme.primary,
activeSliderColor: Theme.of(context).colorScheme.primary,
backgroundColor: Colors.transparent,
trashClick: deleteClidk,
return StreamBuilder<bool>(
stream: MediaService.audioPlayer.playingStream,
builder: (context, snapshot) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 12),
child: Row(
children: [
Expanded(
child: AudioSlider(
tag: audioMetaData != null
? '${audioMetaData!.type}-$id'
: 'message-$id',
duration: audioMetaData?.duration,
showTimer: true,
),
),
],
),
),
),
const SizedBox(width: 12),
_AudioControllerButton(
audioFile: audioFile,
audioUrl: audioUrl,
id: id,
audioMetaData: audioMetaData,
),
],
);
},
);
}
}

View File

@ -0,0 +1,30 @@
// ignore_for_file: unused_element
import 'dart:io';
import 'package:didvan/models/studio_details_data.dart';
import 'package:didvan/views/ai/widgets/audio_wave.dart';
import 'package:flutter/material.dart';
class DownloadableAudioWidget extends StatelessWidget {
final String? audioUrl;
final File? audioFile;
final int id;
final StudioDetailsData? audioMetaData;
final Function()? deleteClidk;
const DownloadableAudioWidget({
Key? key,
this.audioUrl,
this.audioFile,
required this.id,
this.audioMetaData,
this.deleteClidk,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.sizeOf(context).width / 1.6,
child: AudioWave(file: audioUrl != null ? audioUrl! : audioFile!.path));
}
}

View File

@ -4,7 +4,7 @@ import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/message_data/message_data.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/direct/direct_state.dart';
import 'package:didvan/views/direct/widgets/audio_widget.dart';
import 'package:didvan/views/direct/widgets/downloadable_audio_widget.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
@ -84,7 +84,7 @@ class Message extends StatelessWidget {
children: [
if (message.text != null) DidvanText(message.text!),
if (message.audio != null || message.audioFile != null)
AudioWidget(
DownloadableAudioWidget(
audioFile: message.audioFile,
audioUrl: message.audio,
id: message.id,

View File

@ -1,8 +1,9 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:didvan/views/direct/direct_state.dart';
import 'package:didvan/views/direct/widgets/audio_widget.dart';
import 'package:didvan/views/direct/widgets/downloadable_audio_widget.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/foundation.dart';
@ -219,21 +220,30 @@ class _RecordChecking extends StatelessWidget {
},
color: Theme.of(context).colorScheme.focusedBorder,
),
const SizedBox(
width: 12,
),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: AudioWidget(
child: DownloadableAudioWidget(
audioFile: state.recordedFile!,
id: 0,
deleteClidk: () => state.deleteRecordedFile,
// deleteClidk: () => state.deleteRecordedFile,
),
),
),
// DidvanIconButton(
// icon: DidvanIcons.trash_solid,
// color: Theme.of(context).colorScheme.secondary,
// onPressed: state.deleteRecordedFile,
// ),
const SizedBox(
width: 12,
),
MessageBarBtn(
enable: true,
icon: DidvanIcons.trash_solid,
color: Theme.of(context).colorScheme.error,
click: () => state.deleteRecordedFile()),
const SizedBox(
width: 12,
),
],
);
}

View File

@ -61,7 +61,10 @@ class _HomeState extends State<Home>
_tabController.addListener(() {
state.currentPageIndex = _tabController.index;
if (_tabController.index == 2) {
homeScaffKey.currentState!.closeDrawer();
context.read<HistoryAiChatState>().getChats();
context.read<HistoryAiChatState>().getBots();
}
});
@ -166,7 +169,8 @@ class _HomeState extends State<Home>
data: ActionSheetData(
onConfirmed: () async {
await state
.deleteAllChat();
.deleteAllChat(
refresh: false);
},
content: Column(
children: [
@ -463,7 +467,8 @@ class _HomeState extends State<Home>
onTap: () async {
if (title.text.isNotEmpty) {
await state.changeNameChat(
chat.id!, index, title.text);
chat.id!, index, title.text,
refresh: false);
title.clear();
}
if (chat.isEditing != null) {
@ -489,12 +494,12 @@ class _HomeState extends State<Home>
onSelected: (value) async {
switch (value) {
case 'حذف پیام':
await state.deleteChat(chat.id!, index);
await state.deleteChat(chat.id!, index, refresh: false);
break;
case 'آرشیو':
await state.archivedChat(chat.id!, index);
await state.getChats();
await state.archivedChat(chat.id!, index,
refresh: false);
break;
default:
}

View File

@ -196,7 +196,7 @@ class HomeState extends CoreProvier {
MenuItemType(
label: 'سها',
asset: Assets.saha,
link: 'https://saha.didvan.app',
link: 'https://saha.didvan.app/app',
),
MenuItemType(
label: 'هوشان',

View File

@ -2,6 +2,7 @@ import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/home_page_content/home_page_list.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/views/home/main/main_page_state.dart';
import 'package:didvan/views/home/main/widgets/banner.dart';
import 'package:didvan/views/home/main/widgets/general_item.dart';
@ -12,7 +13,7 @@ import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
// import 'package:url_launcher/url_launcher_string.dart';
import 'package:didvan/services/network/request.dart';
class MainPage extends StatefulWidget {

View File

@ -5,11 +5,12 @@ import 'package:didvan/models/requests/news.dart';
import 'package:didvan/models/requests/radar.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/routes/routes.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/utils/action_sheet.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher_string.dart';
// import 'package:url_launcher/url_launcher_string.dart';
class MainPageState extends CoreProvier {
late MainPageContent content;

View File

@ -1,16 +1,12 @@
import 'dart:math';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/home/main/main_page_state.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/slider.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
// import 'package:url_launcher/url_launcher_string.dart';
class MainPageBanner extends StatelessWidget {
final bool isFirst;
@ -26,7 +22,8 @@ class MainPageBanner extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 4),
child: GestureDetector(
onTap: () => item.link == null || item.link!.isEmpty
? _openInteractiveViewer(context, item.image)
? ActionSheetUtils.openInteractiveViewer(
context, item.image, false)
: launchUrlString(item.link!, mode: LaunchMode.inAppWebView),
child: SkeletonImage(
imageUrl: item.image,
@ -40,62 +37,4 @@ class MainPageBanner extends StatelessWidget {
height: (MediaQuery.of(context).size.width - 8) * 9.0 / 16.0,
);
}
void _openInteractiveViewer(BuildContext context, String image) {
showDialog(
context: context,
builder: (context) => Stack(
children: [
Positioned.fill(
child: InteractiveViewer(
child: Center(
child: SkeletonImage(
width: min(MediaQuery.of(context).size.width,
MediaQuery.of(context).size.height),
imageUrl: image,
),
),
),
),
const Positioned(
right: 24,
top: 24,
child: _BackButton(),
),
],
),
);
}
}
class _BackButton extends StatefulWidget {
const _BackButton({Key? key}) : super(key: key);
@override
__BackButtonState createState() => __BackButtonState();
}
class __BackButtonState extends State<_BackButton> {
@override
Widget build(BuildContext context) {
return AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: true,
child: InkWrapper(
borderRadius: DesignConfig.lowBorderRadius,
onPressed: Navigator.of(context).pop,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.splash,
border: Border.all(color: Theme.of(context).colorScheme.border),
borderRadius: DesignConfig.lowBorderRadius,
),
child: const Icon(
DidvanIcons.back_regular,
size: 32,
),
),
),
);
}
}

View File

@ -69,26 +69,28 @@ class _MainPagePodcastItemState extends State<MainPagePodcastItem> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width:
MediaQuery.of(context).size.width * 2 / 3 - 60,
child: AudioWidget(
id: widget.content.id,
audioUrl: widget.content.link,
audioMetaData: StudioDetailsData(
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 12.0),
child: SizedBox(
child: AudioWidget(
id: widget.content.id,
duration: widget.content.duration!,
title: widget.content.title,
description: '',
image: widget.content.image,
link: widget.content.link,
iframe: null,
createdAt: '',
order: 1,
marked: widget.content.marked,
comments: 0,
tags: [],
type: 'podcast',
audioUrl: widget.content.link,
audioMetaData: StudioDetailsData(
id: widget.content.id,
duration: widget.content.duration!,
title: widget.content.title,
description: '',
image: widget.content.image,
link: widget.content.link,
iframe: null,
createdAt: '',
order: 1,
marked: widget.content.marked,
comments: 0,
tags: [],
type: 'podcast',
),
),
),
),

View File

@ -96,6 +96,9 @@ class _NewStatisticState extends State<NewStatistic> {
itemsInStatics(context, state, 3),
itemsInStatics(context, state, 4),
itemsInStatics(context, state, 5, hasDivider: false),
const SizedBox(
height: 24,
),
],
),
),
@ -135,6 +138,7 @@ class _NewStatisticState extends State<NewStatistic> {
physics: const ScrollPhysics(),
itemCount: state.contents[id].contents.length,
itemBuilder: (context, index) => StatMainCard(
id: id,
statistic: state.contents[id].contents[index],
),
),

View File

@ -15,7 +15,7 @@ import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:didvan/services/app_initalizer.dart';
class SearchResultItem extends StatelessWidget {
final OverviewData item;

View File

@ -6,7 +6,7 @@ import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:didvan/services/app_initalizer.dart';
class MainCategories extends StatelessWidget {
const MainCategories({super.key});

View File

@ -17,7 +17,7 @@ import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:didvan/services/app_initalizer.dart';
class StudioDetailsWidget extends StatelessWidget {
final void Function(int id, bool value) onMarkChanged;
@ -65,7 +65,7 @@ class StudioDetailsWidget extends StatelessWidget {
key: ValueKey(state.studio.id),
data: state.studio.description,
onAnchorTap: (href, _, __) =>
launchUrl(Uri.parse(href!)),
launchUrlString(href!),
style: {
'*': Style(
direction: TextDirection.rtl,

View File

@ -22,7 +22,7 @@ import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:didvan/services/app_initalizer.dart';
class ProfilePage extends StatefulWidget {
const ProfilePage({Key? key}) : super(key: key);
@ -222,7 +222,7 @@ class _ProfilePageState extends State<ProfilePage> {
icon: DidvanIcons.info_circle_regular,
title: 'معرفی دیدوان',
onTap: () =>
launchUrl(Uri.parse('https://didvan.app/#info')),
launchUrlString('https://didvan.app/#info'),
),
const DidvanDivider(),
MenuOption(
@ -232,12 +232,16 @@ class _ProfilePageState extends State<ProfilePage> {
state.showContactUs = !state.showContactUs;
state.update();
},
trailing: Icon(
state.showContactUs
? DidvanIcons.angle_up_regular
: DidvanIcons.angle_down_regular,
size: 18,
color: Theme.of(context).colorScheme.title,
trailing: Row(
children: [
Icon(
state.showContactUs
? DidvanIcons.angle_up_regular
: DidvanIcons.angle_down_regular,
size: 18,
color: Theme.of(context).colorScheme.title,
),
],
)),
AnimatedVisibility(
isVisible: state.showContactUs,
@ -316,9 +320,8 @@ class _ProfilePageState extends State<ProfilePage> {
MenuOption(
icon: DidvanIcons.alert_regular,
title: 'حریم خصوصی',
onTap: () => launchUrl(
Uri.parse(
'https://didvan.app/terms-of-use#privacy'),
onTap: () => launchUrlString(
'https://didvan.app/terms-of-use#privacy',
),
),
],

View File

@ -0,0 +1,75 @@
// ignore_for_file: library_private_types_in_public_api, deprecated_member_use
import 'package:didvan/constants/assets.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebView extends StatefulWidget {
final String src;
const WebView({Key? key, required this.src}) : super(key: key);
@override
_WebViewState createState() => _WebViewState();
}
class _WebViewState extends State<WebView> {
late WebViewController controller;
bool loading = true;
int progress = 0;
@override
void initState() {
controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
// Update loading bar.
setState(() {
this.progress = progress;
});
},
onPageStarted: (String url) {},
onPageFinished: (String url) async {
await Future.delayed(const Duration(seconds: 2));
setState(() {
if (progress == 100) loading = false;
});
},
onHttpError: (HttpResponseError error) {},
onWebResourceError: (WebResourceError error) {
// navigatorKey.currentState!.pop();
},
onNavigationRequest: (NavigationRequest request) {
// if (request.url.startsWith('https://www.youtube.com/')) {
// return NavigationDecision.prevent;
// }
return NavigationDecision.navigate;
},
),
)
..loadRequest(Uri.parse(widget.src));
super.initState();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
if (await controller.canGoBack()) {
await controller.goBack();
return false;
} else {
return true;
}
},
child: loading
? Center(
child: Image.asset(
Assets.loadingAnimation,
width: 60,
height: 60,
),
)
: WebViewWidget(controller: controller));
}
}

View File

@ -0,0 +1,40 @@
// ignore_for_file: library_private_types_in_public_api
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:flutter/material.dart';
class BackButton extends StatefulWidget {
const BackButton({Key? key}) : super(key: key);
@override
__BackButtonState createState() => __BackButtonState();
}
class __BackButtonState extends State<BackButton> {
@override
Widget build(BuildContext context) {
return AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: true,
child: InkWrapper(
borderRadius: DesignConfig.lowBorderRadius,
onPressed: Navigator.of(context).pop,
child: Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.splash,
border: Border.all(color: Theme.of(context).colorScheme.border),
borderRadius: DesignConfig.lowBorderRadius,
),
child: const Icon(
DidvanIcons.back_regular,
size: 32,
),
),
),
);
}
}

View File

@ -23,7 +23,7 @@ import 'package:didvan/views/widgets/item_title.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:didvan/services/app_initalizer.dart';
class DidvanPageView extends StatefulWidget {
final List items;
@ -309,7 +309,7 @@ class _DidvanPageViewState extends State<DidvanPageView> {
),
);
} else {
launchUrl(Uri.parse(href));
launchUrlString(href);
}
},
style: {

View File

@ -33,7 +33,7 @@ class LogoAppBar extends StatelessWidget implements PreferredSizeWidget {
decoration: BoxDecoration(
borderRadius: const BorderRadius.only(bottomLeft: Radius.circular(20)),
color: Theme.of(context).colorScheme.surface,
boxShadow: state.currentPageIndex == 1 || state.filtering
boxShadow: state.filtering
? null
: [
BoxShadow(

View File

@ -19,7 +19,7 @@ import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:didvan/services/app_initalizer.dart';
class MultitypeOverview extends StatelessWidget {
final OverviewData item;

View File

@ -11,6 +11,8 @@ class SkeletonImage extends StatelessWidget {
final String imageUrl;
final double? width;
final double? height;
final double? pWidth;
final double? pHeight;
final BorderRadius? borderRadius;
final double? aspectRatio;
const SkeletonImage({
@ -20,6 +22,8 @@ class SkeletonImage extends StatelessWidget {
this.aspectRatio,
this.width,
this.height,
this.pWidth,
this.pHeight,
}) : super(key: key);
@override
@ -39,7 +43,10 @@ class SkeletonImage extends StatelessWidget {
imageUrl: imageUrl.startsWith('http')
? imageUrl
: RequestHelper.baseUrl + imageUrl,
placeholder: (context, _) => const ShimmerPlaceholder(),
placeholder: (context, _) => ShimmerPlaceholder(
width: pWidth,
height: pHeight,
),
),
),
);

View File

@ -359,7 +359,7 @@ packages:
source: sdk
version: "0.0.0"
flutter_cache_manager:
dependency: transitive
dependency: "direct main"
description:
name: flutter_cache_manager
sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
@ -1074,22 +1074,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.2.0"
syncfusion_flutter_core:
dependency: transitive
description:
name: syncfusion_flutter_core
sha256: e17dcc7a1d0701e84d0a83c0040503cdcc6c72e44db0d733ab4c706dd5b8b9f8
url: "https://pub.dev"
source: hosted
version: "25.2.7"
syncfusion_flutter_sliders:
dependency: transitive
description:
name: syncfusion_flutter_sliders
sha256: "842a452fd73fd61fbebbff72d726ffea4cdaacf088ca2738aeaf7f4454b3861e"
url: "https://pub.dev"
source: hosted
version: "25.2.7"
synchronized:
dependency: transitive
description:
@ -1154,70 +1138,6 @@ 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: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89"
url: "https://pub.dev"
source: hosted
version: "6.3.0"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
url: "https://pub.dev"
source: hosted
version: "3.1.1"
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: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7
url: "https://pub.dev"
source: hosted
version: "3.1.1"
uuid:
dependency: transitive
description:
@ -1306,14 +1226,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "13.0.0"
voice_message_package:
dependency: "direct main"
description:
name: voice_message_package
sha256: cd7a717751e60908d37624309c6995c1c62c1bab6b54f7d15c2d71289f0b0cb6
url: "https://pub.dev"
source: hosted
version: "2.2.1"
wakelock_plus:
dependency: transitive
description:

View File

@ -57,12 +57,12 @@ dependencies:
bot_toast: ^4.0.1
flutter_secure_storage: ^8.0.0
flutter_html: ^3.0.0-alpha.2
url_launcher: ^6.0.18
# url_launcher: ^6.0.18
audio_video_progress_bar: ^2.0.0
image_cropper: ^1.5.0
firebase_core: ^3.1.0
firebase_messaging: ^15.0.1
webview_flutter: ^4.2.0
webview_flutter: ^4.8.0
expandable_bottom_sheet: ^1.1.1+1
permission_handler: ^11.0.0
# better_player:
@ -89,9 +89,10 @@ dependencies:
flutter_markdown: ^0.7.3+1
file_picker: ^8.0.5
marquee: ^2.2.3
voice_message_package: ^2.2.1
mime: ^1.0.2
path: any
flutter_cache_manager: any
dev_dependencies:
flutter_test:
sdk: flutter