fixed story index bug

This commit is contained in:
mohamadmahdi jebeli 2025-07-12 15:30:37 +03:30
parent 283938d759
commit 5f02802ad1
35 changed files with 296 additions and 223 deletions

View File

@ -17,6 +17,7 @@
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="Didvan" android:label="Didvan"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true">
<receiver <receiver
android:name=".FavWidget" android:name=".FavWidget"

View File

@ -6,7 +6,6 @@ class MainPageBannerType {
factory MainPageBannerType.fromJson(Map<String, dynamic> json) { factory MainPageBannerType.fromJson(Map<String, dynamic> json) {
return MainPageBannerType( return MainPageBannerType(
// fix if it null
image: json['image'] ?? '', image: json['image'] ?? '',
link: json['link'], link: json['link'],
); );

View File

@ -1,9 +1,11 @@
class MainPageContentType { class MainPageContentType {
final int id; final int id;
final String title; final String title;
final String image; final String image;
final String link; final String link;
bool marked; bool marked;
bool isViewed;
final List<String> subtitles; final List<String> subtitles;
final int? duration; final int? duration;
@ -13,6 +15,7 @@ class MainPageContentType {
required this.image, required this.image,
required this.link, required this.link,
required this.marked, required this.marked,
required this.isViewed,
required this.subtitles, required this.subtitles,
this.duration, this.duration,
}); });
@ -24,6 +27,7 @@ class MainPageContentType {
image: json['image'], image: json['image'],
link: json['link'], link: json['link'],
marked: json['marked'], marked: json['marked'],
isViewed: json['isViewed'] ?? false,
subtitles: List<String>.from(json['subtitles']), subtitles: List<String>.from(json['subtitles']),
duration: json['duration'], duration: json['duration'],
); );

View File

@ -1,14 +1,16 @@
class SliderData { class SliderData {
final int id; final int id;
final String title; final String title;
final String image; final String image;
final String link; final String link;
final bool isViewed;
const SliderData({ const SliderData({
required this.id, required this.id,
required this.title, required this.title,
required this.image, required this.image,
required this.link, required this.link,
required this.isViewed,
}); });
factory SliderData.fromJson(Map<String, dynamic> json) => SliderData( factory SliderData.fromJson(Map<String, dynamic> json) => SliderData(
@ -16,11 +18,14 @@ class SliderData {
title: json['title'], title: json['title'],
image: json['image'], image: json['image'],
link: json['link'], link: json['link'],
isViewed: json['isViewed'] ?? false,
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'id': id, 'id': id,
'title': title, 'title': title,
'image': image, 'image': image,
'link': link,
'isViewed': isViewed,
}; };
} }

View File

@ -1,3 +1,5 @@
// ignore_for_file: avoid_print
import 'package:didvan/models/ai/ai_chat_args.dart'; import 'package:didvan/models/ai/ai_chat_args.dart';
import 'package:didvan/models/story_model.dart'; import 'package:didvan/models/story_model.dart';
import 'package:didvan/views/ai/ai_chat_page.dart'; import 'package:didvan/views/ai/ai_chat_page.dart';
@ -494,6 +496,7 @@ class RouteGenerator {
textScaler: const TextScaler.linear(1.0), textScaler: const TextScaler.linear(1.0),
), ),
child: Container( child: Container(
// ignore: deprecated_member_use
color: Theme.of(context).colorScheme.background, color: Theme.of(context).colorScheme.background,
alignment: Alignment.center, alignment: Alignment.center,
child: AspectRatio(aspectRatio: 9 / 16, child: page), child: AspectRatio(aspectRatio: 9 / 16, child: page),

View File

@ -1,4 +1,4 @@
// ignore_for_file: depend_on_referenced_packages, avoid_web_libraries_in_flutter // ignore_for_file: depend_on_referenced_packages, avoid_web_libraries_in_flutter, avoid_print
import 'dart:async'; import 'dart:async';

View File

@ -1,5 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:didvan/constants/assets.dart'; import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/requests/studio.dart'; import 'package:didvan/models/requests/studio.dart';
import 'package:didvan/models/studio_details_data.dart'; import 'package:didvan/models/studio_details_data.dart';
@ -9,7 +10,6 @@ import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/services/storage/storage.dart'; import 'package:didvan/services/storage/storage.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -90,17 +90,7 @@ class MediaService {
} }
} }
await audioPlayer.play(); await audioPlayer.play();
// await audioPlayer.open(
// audio,
// showNotification: !isVoiceMessage,
// notificationSettings: NotificationSettings(
// customStopAction: (_) => resetAudioPlayer(),
// customNextAction: (_) => onTrackChanged?.call(true),
// customPrevAction: (_) => onTrackChanged?.call(false),
// ),
// );
} catch (e) { } catch (e) {
// resetAudioPlayer();
// rethrow; // rethrow;
} }
} }
@ -133,7 +123,6 @@ class MediaService {
return result; return result;
} catch (e) { } catch (e) {
e.printError(info: 'Pick PDF Fail'); e.printError(info: 'Pick PDF Fail');
// navigatorKey.currentState!.pop();
return null; return null;
} }
} }
@ -143,30 +132,11 @@ class MediaService {
return await FilePicker.platform return await FilePicker.platform
.pickFiles( .pickFiles(
type: FileType.any, type: FileType.any,
// allowedExtensions: [
// 'pdf',
// 'doc',
// 'docx',
// 'xls',
// 'xlsx',
// 'ppt',
// 'pptx',
// 'txt',
// 'mp3',
// 'wav',
// 'aac',
// 'ogg',
// 'flac'
// ], // You can specify allowed extensions if needed
allowMultiple: true, allowMultiple: true,
// Note: The maxFiles parameter is not directly supported by FilePicker.
// You will need to handle the limit after selection if necessary.
) )
.then((result) { .then((result) {
if (result != null && result.files.length > 3) { if (result != null && result.files.length > 3) {
// Handle the case where the selected files exceed the max limit return null;
// You can show an error message or return null
return null; // or show a message to the user
} }
return result; return result;
}); });
@ -189,47 +159,75 @@ class MediaService {
} }
static Future<String?> downloadFile(String url, {final String? name}) async { static Future<String?> downloadFile(String url, {final String? name}) async {
debugPrint("Attempting to download file from URL: $url");
final basename = name ?? p.basename(url).split('?accessToken=').first; final basename = name ?? p.basename(url).split('?accessToken=').first;
PermissionStatus status;
if (Platform.isAndroid) {
final androidInfo = await DeviceInfoPlugin().androidInfo;
if (androidInfo.version.sdkInt >= 33) {
debugPrint("Android 13+ detected. Requesting Photos permission.");
status = await Permission.photos.request();
} else {
debugPrint("Older Android version detected. Requesting Storage permission.");
status = await Permission.storage.request();
}
} else if (Platform.isIOS) {
debugPrint("iOS detected. Requesting Photos permission.");
status = await Permission.photos.request();
} else {
status = PermissionStatus.granted;
}
debugPrint("Permission status after request: $status");
if (status.isGranted) {
Directory? dir; Directory? dir;
try { try {
if (Platform.isIOS) { if (Platform.isIOS) {
dir = await getApplicationDocumentsDirectory(); // for iOS dir = await getApplicationDocumentsDirectory();
} else { } else {
dir = Directory('/storage/emulated/0/Download/'); // for android dir = await getExternalStorageDirectory();
if (!await dir.exists()) dir = (await getExternalStorageDirectory())!;
} }
} catch (err) {
if (kDebugMode) { if (dir == null) {
print("Cannot get download folder path $err"); debugPrint("Error: Could not determine download directory.");
return null;
} }
}
String path = "${dir?.path}${Platform.isIOS ? '/' : ''}"; final path = dir.path;
final status = await Permission.storage.request(); debugPrint("Download directory determined: $path");
if (status.isGranted) {
try { final taskId = await FlutterDownloader.enqueue(
await FlutterDownloader.enqueue(
url: url, url: url,
savedDir: path, savedDir: path,
fileName: basename, fileName: basename,
showNotification: true, showNotification: true,
openFileFromNotification: true, openFileFromNotification: true,
saveInPublicStorage: true,
); );
debugPrint("Download successfully enqueued with taskId: $taskId");
return path;
} catch (e) { } catch (e) {
if (kDebugMode) { debugPrint("Error during download process: $e");
print("Exception$e");
}
return null; return null;
} }
} else if (status.isPermanentlyDenied) {
debugPrint("Storage permission is permanently denied. Opening app settings.");
await openAppSettings();
return null;
} else { } else {
debugPrint("Storage permission was denied.");
return null; return null;
} }
}
return path;
}
static String downloadFileFromWeb(String url) { static String downloadFileFromWeb(String url) {
final filename = url.split('/').last.split('?').first;
html.AnchorElement anchorElement = html.AnchorElement(href: url); html.AnchorElement anchorElement = html.AnchorElement(href: url);
anchorElement.download = url; anchorElement.download = filename;
anchorElement.click(); anchorElement.click();
return anchorElement.pathname!; return anchorElement.pathname!;
} }

View File

@ -6,7 +6,7 @@ import 'package:didvan/models/requests/studio.dart';
class RequestHelper { class RequestHelper {
static const String baseUrl = 'https://api.didvan.app'; static const String baseUrl = 'https://api.didvan.app';
static const String baseUrl2 = 'http://opportunity-threat.didvan.com'; static const String baseUrl2 = 'https://opportunity-threat.didvan.com';
static const String storiesV1 = '$baseUrl2/api/v1/stories'; static const String storiesV1 = '$baseUrl2/api/v1/stories';
static const String storyActivity = '$baseUrl2/api/v1/stories/activity'; static const String storyActivity = '$baseUrl2/api/v1/stories/activity';
static const String _baseUserUrl = '$baseUrl/user'; static const String _baseUserUrl = '$baseUrl/user';

View File

@ -1,8 +1,11 @@
// ignore_for_file: avoid_print
import 'dart:convert'; import 'dart:convert';
import 'package:didvan/models/note.dart'; import 'package:didvan/models/note.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
// ignore: library_prefixes
import 'package:http/http.dart' as RequestServices; import 'package:http/http.dart' as RequestServices;
class NoteService { class NoteService {

View File

@ -1,3 +1,5 @@
// ignore_for_file: avoid_print
import 'dart:convert'; import 'dart:convert';
import 'package:didvan/models/home_page_content/swot.dart'; import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';

View File

@ -1,3 +1,5 @@
// ignore_for_file: avoid_print
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
class NativeWebViewLauncher { class NativeWebViewLauncher {

View File

@ -20,6 +20,7 @@
const Ai({Key? key}) : super(key: key); const Ai({Key? key}) : super(key: key);
@override @override
// ignore: library_private_types_in_public_api
_AiState createState() => _AiState(); _AiState createState() => _AiState();
} }

View File

@ -1,8 +1,5 @@
// lib/views/ai/ai_chat_page.dart
// ignore_for_file: library_private_types_in_public_api, deprecated_member_use, depend_on_referenced_packages, unnecessary_import // ignore_for_file: library_private_types_in_public_api, deprecated_member_use, depend_on_referenced_packages, unnecessary_import
import 'dart:io' show Platform;
import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter/foundation.dart' show kIsWeb;
@ -599,15 +596,15 @@ class _AiChatPageState extends State<AiChatPage> {
), ),
), ),
if (message.file != null && !kIsWeb) if (message.file != null && !kIsWeb)
!Platform.isIOS Padding(
? Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: InkWell( child: InkWell(
onTap: () async { onTap: () async {
debugPrint(
"Download button tapped on iOS");
final url = final url =
'${RequestHelper.baseUrl + message.file.toString()}?accessToken=${RequestService.token}'; '${RequestHelper.baseUrl + message.file.toString()}?accessToken=${RequestService.token}';
await MediaService.downloadFile( await MediaService.downloadFile(url,
url,
name: message.fileName); name: message.fileName);
}, },
child: Icon( child: Icon(
@ -618,8 +615,7 @@ class _AiChatPageState extends State<AiChatPage> {
.focusedBorder, .focusedBorder,
), ),
), ),
) ),
: const SizedBox.shrink(),
if (message.error != null && message.error!) if (message.error != null && message.error!)
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
@ -645,14 +641,7 @@ class _AiChatPageState extends State<AiChatPage> {
), ),
), ),
), ),
if (state.messages[mIndex].prompts[index] if (message.text != null && message.text!.isNotEmpty && (file == null || !file.isImage()))
.text !=
null ||
(state.messages[mIndex].prompts[index]
.text !=
null &&
state.messages[mIndex].prompts[index]
.text!.isNotEmpty))
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: InkWell( child: InkWell(

View File

@ -1,4 +1,4 @@
// ignore_for_file: body_might_complete_normally_catch_error // ignore_for_file: body_might_complete_normally_catch_error, avoid_print
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';

View File

@ -9,7 +9,6 @@ import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/models/view/alert_data.dart'; import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/routes/routes.dart'; import 'package:didvan/routes/routes.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart'; import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/widgets/didvan/divider.dart'; import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';

View File

@ -1,4 +1,3 @@
// lib/views/ai_section/ai_section_page.dart
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
@ -185,14 +184,7 @@ class _AiSectionPageState extends State<AiSectionPage> {
key: _aiSectionScaffoldKey, key: _aiSectionScaffoldKey,
appBar: HoshanAppBar( appBar: HoshanAppBar(
onBack: () { onBack: () {
if (aiState.isChatting) {
aiState.endChat();
setState(() {
_currentTabIndex = 2;
});
} else {
Navigator.of(context).pop(); Navigator.of(context).pop();
}
}, },
withActions: true, withActions: true,
), ),

View File

@ -11,6 +11,7 @@ import 'package:didvan/services/media/voice.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_sound/public/flutter_sound_player.dart'; import 'package:flutter_sound/public/flutter_sound_player.dart';
import 'package:flutter_sound/public/flutter_sound_recorder.dart'; import 'package:flutter_sound/public/flutter_sound_recorder.dart';
import 'package:flutter_sound_platform_interface/flutter_sound_platform_interface.dart'; import 'package:flutter_sound_platform_interface/flutter_sound_platform_interface.dart';
@ -24,7 +25,7 @@ class DirectState extends CoreProvier {
final Map<String, List<int>> dailyMessages = {}; final Map<String, List<int>> dailyMessages = {};
final List<int> deletionQueue = []; final List<int> deletionQueue = [];
String? text; final TextEditingController textController = TextEditingController();
NewsAttachment? replyNews; NewsAttachment? replyNews;
RadarAttachment? replyRadar; RadarAttachment? replyRadar;
int? audioDuration; int? audioDuration;
@ -104,6 +105,7 @@ class DirectState extends CoreProvier {
@override @override
void dispose() { void dispose() {
textController.dispose();
super.dispose(); super.dispose();
mPlayer!.closePlayer(); mPlayer!.closePlayer();
mPlayer = null; mPlayer = null;
@ -172,7 +174,7 @@ class DirectState extends CoreProvier {
} }
Future<void> startRecording() async { Future<void> startRecording() async {
text = null; textController.text.isEmpty;
// await _recorder.hasPermission(); // await _recorder.hasPermission();
record(); record();
@ -228,13 +230,14 @@ class DirectState extends CoreProvier {
} }
Future<void> sendMessage() async { Future<void> sendMessage() async {
if ((text == null || text!.isEmpty) && path == null) return;
if ((textController.text.isEmpty) && path == null) return;
VoiceService.audioPlayer.stop(); VoiceService.audioPlayer.stop();
final body = {}; final body = {};
if (text != null) { if (textController.text.isNotEmpty) {
body.addAll({'text': text}); body.addAll({'text': textController.text});
} }
if (replyRadar != null) { if (replyRadar != null) {
@ -253,7 +256,7 @@ class DirectState extends CoreProvier {
body.addAll({'duration': countTimer.value.inSeconds.toString()}); body.addAll({'duration': countTimer.value.inSeconds.toString()});
} }
text = null; textController.clear();
notifyListeners(); notifyListeners();

View File

@ -1,3 +1,4 @@
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
@ -9,16 +10,21 @@ import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class MessageBox extends StatelessWidget { class MessageBox extends StatefulWidget {
const MessageBox({Key? key}) : super(key: key); const MessageBox({Key? key}) : super(key: key);
@override
State<MessageBox> createState() => _MessageBoxState();
}
class _MessageBoxState extends State<MessageBox> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
children: [ children: [
Consumer<DirectState>( Consumer<DirectState>(
builder: (context, state, child) => state.replyRadar != null || builder: (context, state, child) =>
state.replyNews != null state.replyRadar != null || state.replyNews != null
? _MessageBoxContainer( ? _MessageBoxContainer(
isMessage: false, isMessage: false,
child: Padding( child: Padding(
@ -38,7 +44,8 @@ class MessageBox extends StatelessWidget {
: state.replyNews!.title, : state.replyNews!.title,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: 1, maxLines: 1,
color: Theme.of(context).colorScheme.primary, color:
Theme.of(context).colorScheme.primary,
), ),
], ],
), ),
@ -111,7 +118,21 @@ class _Typing extends StatefulWidget {
} }
class _TypingState extends State<_Typing> { class _TypingState extends State<_Typing> {
final _formKey = GlobalKey<FormState>(); @override
void initState() {
super.initState();
context.read<DirectState>().textController.addListener(_rebuild);
}
@override
void dispose() {
context.read<DirectState>().textController.removeListener(_rebuild);
super.dispose();
}
void _rebuild() {
setState(() {});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -126,13 +147,11 @@ class _TypingState extends State<_Typing> {
scale: animation, scale: animation,
child: child, child: child,
), ),
child: state.text != null && state.text!.isNotEmpty child: state.textController.text.isNotEmpty
? DidvanIconButton( ? DidvanIconButton(
icon: DidvanIcons.send_solid, icon: DidvanIcons.send_solid,
onPressed: () async { onPressed: () async {
await state.sendMessage(); await state.sendMessage();
state.update();
_formKey.currentState!.reset();
}, },
size: 32, size: 32,
color: Theme.of(context).colorScheme.focusedBorder, color: Theme.of(context).colorScheme.focusedBorder,
@ -147,23 +166,17 @@ class _TypingState extends State<_Typing> {
), ),
Expanded( Expanded(
flex: 15, flex: 15,
child: Form(
key: _formKey,
child: TextFormField( child: TextFormField(
controller: state.textController,
textInputAction: TextInputAction.send, textInputAction: TextInputAction.send,
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
decoration: InputDecoration( decoration: InputDecoration(
border: InputBorder.none, border: InputBorder.none,
hintText: 'بنویسید یا پیام صوتی بگذارید...', hintText: 'بنویسید یا پیام صوتی بگذارید...',
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( hintStyle: Theme.of(context)
color: Theme.of(context).colorScheme.disabledText), .textTheme
), .bodySmall!
onChanged: (value) { .copyWith(color: Theme.of(context).colorScheme.disabledText),
if (value.length <= 1) {
setState(() {});
}
state.text = value;
},
), ),
), ),
), ),
@ -227,7 +240,6 @@ class _RecordChecking extends StatelessWidget {
child: AudioWave( child: AudioWave(
file: state.path!, file: state.path!,
totalDuration: state.countTimer.value, totalDuration: state.countTimer.value,
// deleteClidk: () => state.deleteRecordedFile,
), ),
), ),
), ),

View File

@ -1,9 +1,10 @@
// ignore_for_file: avoid_print
import 'dart:convert'; import 'dart:convert';
import 'package:didvan/models/home_page_content/swot.dart'; import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:http/http.dart' as RequestServicess;
class BookmarkService { class BookmarkService {
static Future<List<int>> fetchBookmarks() async { static Future<List<int>> fetchBookmarks() async {
@ -69,7 +70,7 @@ class BookmarkService {
} }
static Future<List<SwotItem>> fetchBookmarkedSwotItems() async { static Future<List<SwotItem>> fetchBookmarkedSwotItems() async {
final postIds = await fetchBookmarks(); // استفاده از همان لیست بوکمارکها final postIds = await fetchBookmarks();
final List<SwotItem> items = []; final List<SwotItem> items = [];
for (final postId in postIds) { for (final postId in postIds) {

View File

@ -44,6 +44,7 @@ class _BookmarksState extends State<Bookmarks> {
} }
// ignore: unused_element
void _onSearchChanged(String value) { void _onSearchChanged(String value) {
final state = context.read<BookmarksState>(); final state = context.read<BookmarksState>();
if (value.length < 3 && value.isNotEmpty) { if (value.length < 3 && value.isNotEmpty) {

View File

@ -78,6 +78,7 @@ class _HomeState extends State<Home>
) )
.then((value) => ActionSheetUtils(context).openDialog( .then((value) => ActionSheetUtils(context).openDialog(
data: ActionSheetData( data: ActionSheetData(
// ignore: deprecated_member_use
backgroundColor: Theme.of(context).colorScheme.background, backgroundColor: Theme.of(context).colorScheme.background,
isBackgroundDropBlur: true, isBackgroundDropBlur: true,
content: Stack( content: Stack(
@ -208,6 +209,7 @@ class _HomeState extends State<Home>
scaffKey: homeScaffKey, scaffKey: homeScaffKey,
) )
: null, : null,
// ignore: deprecated_member_use
body: WillPopScope( body: WillPopScope(
onWillPop: () async { onWillPop: () async {
if (context.read<HomeState>().tabController.index == 0) { if (context.read<HomeState>().tabController.index == 0) {

View File

@ -3,7 +3,6 @@ import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/main.dart'; import 'package:didvan/main.dart';
import 'package:didvan/models/home_page_content/home_page_list.dart'; import 'package:didvan/models/home_page_content/home_page_list.dart';
import 'package:didvan/models/home_page_content/swot.dart'; import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/services/swot_service.dart';
import 'package:didvan/routes/routes.dart'; import 'package:didvan/routes/routes.dart';
import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/views/home/main/main_page_state.dart'; import 'package:didvan/views/home/main/main_page_state.dart';
@ -48,10 +47,8 @@ class _MainPageState extends State<MainPage> {
children: [ children: [
if (state.stories.isNotEmpty) StorySection(stories: state.stories), if (state.stories.isNotEmpty) StorySection(stories: state.stories),
const SizedBox(height: 12), const SizedBox(height: 12),
// Main page content
const MainPageMainContent(), const MainPageMainContent(),
// START: MODIFIED SECTION
Builder(builder: (context) { Builder(builder: (context) {
final List<Widget> pageContent = []; final List<Widget> pageContent = [];
if (state.content != null && state.content!.lists.isNotEmpty) { if (state.content != null && state.content!.lists.isNotEmpty) {

View File

@ -1,5 +1,6 @@
import 'package:didvan/main.dart'; import 'package:didvan/main.dart';
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/home_page_content/content.dart';
import 'package:didvan/models/home_page_content/home_page_content.dart'; import 'package:didvan/models/home_page_content/home_page_content.dart';
import 'package:didvan/models/home_page_content/swot.dart'; import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/models/requests/infography.dart'; import 'package:didvan/models/requests/infography.dart';
@ -33,7 +34,12 @@ class MainPageState extends CoreProvier {
} }
} }
Future<void> _getSwotItems() async { // این متد را اضافه کنید int getStoryStartIndex(List<MainPageContentType> stories) {
final firstUnreadIndex = stories.indexWhere((story) => !story.isViewed);
return firstUnreadIndex != -1 ? firstUnreadIndex : 0;
}
Future<void> _getSwotItems() async {
try { try {
swotItems = await SwotService.fetchSwotItems(); swotItems = await SwotService.fetchSwotItems();
} catch (e) { } catch (e) {
@ -44,6 +50,7 @@ class MainPageState extends CoreProvier {
Future<void> _fetchStories() async { Future<void> _fetchStories() async {
try { try {
stories = await StoryService.getStories(); stories = await StoryService.getStories();
// ignore: avoid_print
print("Fetched ${stories.length} stories."); print("Fetched ${stories.length} stories.");
} catch (e) { } catch (e) {
stories = []; stories = [];

View File

@ -1,3 +1,5 @@
// ignore_for_file: avoid_print
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/views/home/bookmarks/bookmark_service.dart'; import 'package:didvan/views/home/bookmarks/bookmark_service.dart';

View File

@ -80,6 +80,7 @@ class _BookmarkedIconState extends State<BookmarkedIcon> {
widget.onBookmarkChanged?.call(newBookmarkStatus); widget.onBookmarkChanged?.call(newBookmarkStatus);
} }
} catch (e) { } catch (e) {
// ignore: avoid_print
print("Error toggling bookmark for post ${widget.postId}: $e"); print("Error toggling bookmark for post ${widget.postId}: $e");
} finally { } finally {
if (mounted) { if (mounted) {

View File

@ -23,11 +23,14 @@ class StorySection extends StatelessWidget {
child: _StoryCircle( child: _StoryCircle(
userStories: userStories, userStories: userStories,
onTap: () { onTap: () {
bool allStoriesViewed = stories.every((userStories) =>
userStories.stories.every((story) => story.isViewed.value));
Navigator.of(context).pushNamed( Navigator.of(context).pushNamed(
Routes.storyViewer, Routes.storyViewer,
arguments: { arguments: {
'stories': stories, 'stories': stories,
'tappedIndex': index, 'tappedIndex': allStoriesViewed ? 0 : index,
}, },
); );
}, },

View File

@ -57,6 +57,7 @@ class _SwotBookmark extends State<SwotBookmark> {
} }
void saveOrUpdateNote(String content) async { void saveOrUpdateNote(String content) async {
// ignore: avoid_print
print('Processing note for PostId: ${widget.item.id}, Content: $content'); print('Processing note for PostId: ${widget.item.id}, Content: $content');
setState(() { setState(() {
isLoading = true; isLoading = true;

View File

@ -19,6 +19,7 @@ class MainCategories extends StatelessWidget {
'$link?accessToken=${RequestService.token}', '$link?accessToken=${RequestService.token}',
mode: LaunchMode.inAppWebView, mode: LaunchMode.inAppWebView,
); );
// ignore: avoid_print
print("your goddamn token is :${RequestService.token}"); print("your goddamn token is :${RequestService.token}");
} else if (link.startsWith('tab-')) { } else if (link.startsWith('tab-')) {
final state = context.read<HomeState>(); final state = context.read<HomeState>();

View File

@ -1,3 +1,5 @@
// ignore_for_file: unnecessary_this
import 'dart:async'; import 'dart:async';
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
@ -22,9 +24,7 @@ class StudioDetailsState extends CoreProvier {
int _trackingTimerCounter = 0; int _trackingTimerCounter = 0;
Timer? _trackingTimer; Timer? _trackingTimer;
// BEGIN: ADD THIS LINE
StreamSubscription? _positionSubscription; StreamSubscription? _positionSubscription;
// END: ADD THIS LINE
int _selectedDetailsIndex = 0; int _selectedDetailsIndex = 0;
Timer? timer; Timer? timer;

View File

@ -41,11 +41,18 @@ class _StudioSliderState extends State<StudioSlider> {
padding: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.symmetric(horizontal: 4),
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
final firstUnreadIndex =
state.sliders.indexWhere((story) => !story.isViewed);
final startId = (firstUnreadIndex == -1)
? state.sliders.first.id
: state.sliders[firstUnreadIndex].id;
if (state.videosSelected) { if (state.videosSelected) {
Navigator.of(context) Navigator.of(context)
.pushNamed(Routes.studioDetails, arguments: { .pushNamed(Routes.studioDetails, arguments: {
'onMarkChanged': state.changeMark, 'onMarkChanged': state.changeMark,
'id': state.sliders[i].id, 'id': startId,
'args': 'args':
const StudioRequestArgs(page: 0, type: 'video'), const StudioRequestArgs(page: 0, type: 'video'),
'hasUnmarkConfirmation': false, 'hasUnmarkConfirmation': false,
@ -54,7 +61,7 @@ class _StudioSliderState extends State<StudioSlider> {
return; return;
} }
context.read<StudioDetailsState>().getStudioDetails( context.read<StudioDetailsState>().getStudioDetails(
state.sliders[i].id, startId,
args: const StudioRequestArgs( args: const StudioRequestArgs(
page: 0, page: 0,
type: 'podcast', type: 'podcast',

View File

@ -1,8 +1,8 @@
// ignore_for_file: avoid_print
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:didvan/models/story_model.dart'; import 'package:didvan/models/story_model.dart';
import 'package:didvan/services/story_service.dart'; import 'package:didvan/services/story_service.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart'; import 'package:video_player/video_player.dart';
@ -92,7 +92,24 @@ class _UserStoryViewerState extends State<UserStoryViewer>
void initState() { void initState() {
super.initState(); super.initState();
_animationController = AnimationController(vsync: this); _animationController = AnimationController(vsync: this);
_loadStory(story: widget.userStories.stories.first);
final allStoriesInGroupViewed =
widget.userStories.stories.every((story) => story.isViewed.value);
if (allStoriesInGroupViewed) {
for (final story in widget.userStories.stories) {
story.isViewed.value = false;
}
}
_currentStoryIndex =
widget.userStories.stories.indexWhere((story) => !story.isViewed.value);
if (_currentStoryIndex == -1) {
_currentStoryIndex = 0;
}
_loadStory(story: widget.userStories.stories[_currentStoryIndex]);
_animationController.addStatusListener((status) { _animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) { if (status == AnimationStatus.completed) {
@ -314,6 +331,7 @@ class _AnimatedBar extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: position < currentIndex color: position < currentIndex
? Colors.white ? Colors.white
// ignore: deprecated_member_use
: Colors.white.withOpacity(0.5), : Colors.white.withOpacity(0.5),
border: Border.all(color: Colors.black26, width: 0.8), border: Border.all(color: Colors.black26, width: 0.8),
borderRadius: BorderRadius.circular(30.0), borderRadius: BorderRadius.circular(30.0),

View File

@ -1,3 +1,5 @@
// ignore_for_file: unused_element
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart'; import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -7,8 +9,6 @@ import 'package:provider/provider.dart';
import '../../../config/design_config.dart'; import '../../../config/design_config.dart';
import '../../../constants/app_icons.dart'; import '../../../constants/app_icons.dart';
import '../../../models/enums.dart'; import '../../../models/enums.dart';
import '../../../models/requests/radar.dart';
import '../../../routes/routes.dart';
import '../../../services/media/media.dart'; import '../../../services/media/media.dart';
import '../../../utils/action_sheet.dart'; import '../../../utils/action_sheet.dart';
import '../../podcasts/podcasts_state.dart'; import '../../podcasts/podcasts_state.dart';

View File

@ -1,13 +1,8 @@
import 'dart:math';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/views/ai/ai_state.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget { class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget {
final Function()? onBack; final Function()? onBack;
@ -55,14 +50,14 @@ class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget {
), ),
Row( Row(
children: [ children: [
if (withInfo) // if (withInfo)
DidvanIconButton( // DidvanIconButton(
icon: DidvanIcons.info_circle_light, // icon: DidvanIcons.info_circle_light,
size: 32, // size: 32,
onPressed: () { // onPressed: () {
Navigator.pushNamed(context, Routes.info); // Navigator.pushNamed(context, Routes.info);
}), // }),
if (withActions) // if (withActions)
// Stack( // Stack(
// children: [ // children: [
// DidvanIconButton( // DidvanIconButton(
@ -81,15 +76,13 @@ class HoshanAppBar extends StatelessWidget implements PreferredSizeWidget {
// ) // )
// ], // ],
// ), // ),
if (context.watch<AiState>().page != 0 || !withActions) if (withInfo)
Transform.rotate( DidvanIconButton(
angle: 180 * pi / 180, icon: DidvanIcons.angle_left_light,
child: DidvanIconButton(
icon: DidvanIcons.back_light,
size: 32, size: 32,
onPressed: () => onBack?.call(), onPressed: () => onBack?.call(),
), ),
),
], ],
) )
], ],

View File

@ -201,6 +201,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.11" version: "0.7.11"
device_info_plus:
dependency: "direct main"
description:
name: device_info_plus
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
url: "https://pub.dev"
source: hosted
version: "11.5.0"
device_info_plus_platform_interface:
dependency: transitive
description:
name: device_info_plus_platform_interface
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
url: "https://pub.dev"
source: hosted
version: "7.0.3"
equatable: equatable:
dependency: transitive dependency: transitive
description: description:
@ -1650,6 +1666,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.12.0" version: "5.12.0"
win32_registry:
dependency: transitive
description:
name: win32_registry
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -1668,4 +1692,4 @@ packages:
version: "6.5.0" version: "6.5.0"
sdks: sdks:
dart: ">=3.7.0 <4.0.0" dart: ">=3.7.0 <4.0.0"
flutter: ">=3.27.0" flutter: ">=3.29.0"

View File

@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 3.3.8+3380 version: 4.0.1+4380
environment: environment:
sdk: ">=2.19.0 <3.0.0" sdk: ">=2.19.0 <3.0.0"
@ -112,6 +112,8 @@ dependencies:
flutter_inappwebview: ^6.1.5 flutter_inappwebview: ^6.1.5
sms_autofill: ^2.4.1 sms_autofill: ^2.4.1
shimmer: ^3.0.0 shimmer: ^3.0.0
device_info_plus: ^11.5.0
# image_gallery_saver: ^2.0.3
# fading_edge_scrollview: ^4.1.1 # fading_edge_scrollview: ^4.1.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: