diff --git a/.fandogh/fandogh.yaml b/.fandogh/fandogh.yaml
index 45fc5be..f3f18c9 100644
--- a/.fandogh/fandogh.yaml
+++ b/.fandogh/fandogh.yaml
@@ -1,13 +1,13 @@
kind: ExternalService
-name: app-test
+name: app-dev
spec:
allow_http: false
disable_default_domains: true
- image: app:1.2.0
+ image: app-dev:1.5.11
image_pull_policy: IfNotPresent
path: /
replicas: 1
resources:
- memory: 150Mi
+ memory: 100Mi
domains:
- - name: web.didvan.app
\ No newline at end of file
+ - name: dev.didvan.app
\ No newline at end of file
diff --git a/lib/assets/images/categories/glob-dark.svg b/lib/assets/images/categories/glob-dark.svg
new file mode 100644
index 0000000..a70de82
--- /dev/null
+++ b/lib/assets/images/categories/glob-dark.svg
@@ -0,0 +1,54 @@
+
diff --git a/lib/assets/images/categories/glob-light.svg b/lib/assets/images/categories/glob-light.svg
new file mode 100644
index 0000000..3888540
--- /dev/null
+++ b/lib/assets/images/categories/glob-light.svg
@@ -0,0 +1,54 @@
+
diff --git a/lib/assets/images/categories/steel-dark.svg b/lib/assets/images/categories/steel-dark.svg
new file mode 100644
index 0000000..6b56bc0
--- /dev/null
+++ b/lib/assets/images/categories/steel-dark.svg
@@ -0,0 +1,9 @@
+
diff --git a/lib/assets/images/categories/steel-light.svg b/lib/assets/images/categories/steel-light.svg
new file mode 100644
index 0000000..1c1cc9b
--- /dev/null
+++ b/lib/assets/images/categories/steel-light.svg
@@ -0,0 +1,9 @@
+
diff --git a/lib/assets/images/categories/stock-dark.svg b/lib/assets/images/categories/stock-dark.svg
new file mode 100644
index 0000000..818f6e5
--- /dev/null
+++ b/lib/assets/images/categories/stock-dark.svg
@@ -0,0 +1,42 @@
+
diff --git a/lib/assets/images/categories/stock-light.svg b/lib/assets/images/categories/stock-light.svg
new file mode 100644
index 0000000..22ccde7
--- /dev/null
+++ b/lib/assets/images/categories/stock-light.svg
@@ -0,0 +1,42 @@
+
diff --git a/lib/assets/images/empty_states/studio-dark.svg b/lib/assets/images/empty_states/studio-dark.svg
deleted file mode 100644
index 392039b..0000000
--- a/lib/assets/images/empty_states/studio-dark.svg
+++ /dev/null
@@ -1,48 +0,0 @@
-
diff --git a/lib/assets/images/empty_states/studio-light.svg b/lib/assets/images/empty_states/studio-light.svg
deleted file mode 100644
index 5808670..0000000
--- a/lib/assets/images/empty_states/studio-light.svg
+++ /dev/null
@@ -1,55 +0,0 @@
-
diff --git a/lib/config/theme_data.dart b/lib/config/theme_data.dart
index b1e6db7..5bbab96 100644
--- a/lib/config/theme_data.dart
+++ b/lib/config/theme_data.dart
@@ -192,6 +192,7 @@ extension DidvanColorScheme on ColorScheme {
Color get overlay => brightness == Brightness.dark
? const Color(0xFF0F1011)
: const Color(0xFF292929);
+ Color get yellow => const Color(0XFFEAA92A);
// Error and success colors
Color get errorBack => brightness == Brightness.dark
diff --git a/lib/constants/app_strings.dart b/lib/constants/app_strings.dart
deleted file mode 100644
index e8f4321..0000000
--- a/lib/constants/app_strings.dart
+++ /dev/null
@@ -1 +0,0 @@
-class AppStrings {}
diff --git a/lib/constants/assets.dart b/lib/constants/assets.dart
index e19e386..f582dff 100644
--- a/lib/constants/assets.dart
+++ b/lib/constants/assets.dart
@@ -4,11 +4,12 @@ import 'package:flutter/cupertino.dart';
class Assets {
static const String _basePath = 'lib/assets';
static const String _baseImagesPath = _basePath + '/images';
- static const String _baseThemesPath = _basePath + '/images/themes';
- static const String _baseEmptyStatesPath = _basePath + '/images/empty_states';
+ static const String _baseCategoriesPath = _baseImagesPath + '/categories';
+ static const String _baseThemesPath = _baseImagesPath + '/themes';
+ static const String _baseEmptyStatesPath = _baseImagesPath + '/empty_states';
static const String _baseAnimationsPath = _basePath + '/animations';
- static const String _baseRecordsPath = _basePath + '/images/records';
- static const String _baseLogosPath = _basePath + '/images/logos';
+ static const String _baseRecordsPath = _baseImagesPath + '/records';
+ static const String _baseLogosPath = _baseImagesPath + '/logos';
static String get verticalLogoWithText =>
_baseLogosPath + '/logo-vertical-$_themeSuffix.svg';
@@ -20,17 +21,23 @@ class Assets {
_baseAnimationsPath + '/indicator-$_themeSuffix.riv';
static String get businessCategoryIcon =>
- _baseImagesPath + '/categories/business-$_themeSuffix.svg';
+ _baseCategoriesPath + '/business-$_themeSuffix.svg';
static String get economicCategoryIcon =>
- _baseImagesPath + '/categories/economic-$_themeSuffix.svg';
+ _baseCategoriesPath + '/economic-$_themeSuffix.svg';
static String get enviromentalCategoryIcon =>
- _baseImagesPath + '/categories/enviromental-$_themeSuffix.svg';
+ _baseCategoriesPath + '/enviromental-$_themeSuffix.svg';
static String get politicalCategoryIcon =>
- _baseImagesPath + '/categories/political-$_themeSuffix.svg';
+ _baseCategoriesPath + '/political-$_themeSuffix.svg';
static String get socialCategoryIcon =>
- _baseImagesPath + '/categories/social-$_themeSuffix.svg';
+ _baseCategoriesPath + '/social-$_themeSuffix.svg';
static String get techCategoryIcon =>
- _baseImagesPath + '/categories/tech-$_themeSuffix.svg';
+ _baseCategoriesPath + '/tech-$_themeSuffix.svg';
+ static String get steelCategoryIcon =>
+ _baseCategoriesPath + '/steel-$_themeSuffix.svg';
+ static String get stockCategoryIcon =>
+ _baseCategoriesPath + '/stock-$_themeSuffix.svg';
+ static String get globCategoryIcon =>
+ _baseCategoriesPath + '/glob-$_themeSuffix.svg';
static String get emptyBookmark =>
_baseEmptyStatesPath + '/bookmark-$_themeSuffix.svg';
@@ -42,8 +49,6 @@ class Assets {
_baseEmptyStatesPath + '/connection-$_themeSuffix.svg';
static String get emptyResult =>
_baseEmptyStatesPath + '/result-$_themeSuffix.svg';
- static String get emptyStudio =>
- _baseEmptyStatesPath + '/studio-$_themeSuffix.svg';
static const String lightTheme = _baseThemesPath + '/theme-light.svg';
static const String darkTheme = _baseThemesPath + '/theme-dark.svg';
diff --git a/lib/models/category.dart b/lib/models/category.dart
index 3faf75a..552af8b 100644
--- a/lib/models/category.dart
+++ b/lib/models/category.dart
@@ -1,8 +1,9 @@
class CategoryData {
final int id;
final String label;
+ String? asset;
- const CategoryData({required this.id, required this.label});
+ CategoryData({required this.id, required this.label, this.asset});
factory CategoryData.fromJson(Map json) => CategoryData(
id: json['id'],
diff --git a/lib/models/statistic_data/data.dart b/lib/models/statistic_data/data.dart
new file mode 100644
index 0000000..0e46084
--- /dev/null
+++ b/lib/models/statistic_data/data.dart
@@ -0,0 +1,64 @@
+class Data {
+ final String p;
+ final String h;
+ final String l;
+ final String d;
+ final double dp;
+ final String dt;
+ final String t;
+ final String? tEn;
+ final String tG;
+ final String ts;
+
+ Data({
+ required this.p,
+ required this.h,
+ required this.l,
+ required this.d,
+ required this.dp,
+ required this.dt,
+ required this.t,
+ required this.tEn,
+ required this.tG,
+ required this.ts,
+ });
+
+ factory Data.fromJson(Map json) => Data(
+ p: json['p'],
+ h: json['h'],
+ l: json['l'],
+ d: json['d'],
+ dp: double.parse(json['dp'].toString()),
+ dt: json['dt'],
+ t: json['t'],
+ tEn: json['t_en'],
+ tG: json['t-g'],
+ ts: json['ts'],
+ );
+
+ factory Data.fromList(List list) => Data(
+ p: list[0],
+ h: list[1],
+ l: list[2],
+ d: list[3],
+ dp: double.parse(list[4].toString().replaceAll('-', '0')),
+ dt: list[5],
+ t: list[6],
+ tEn: list[7],
+ tG: '',
+ ts: '',
+ );
+
+ Map toJson() => {
+ 'p': p,
+ 'h': h,
+ 'l': l,
+ 'd': d,
+ 'dp': dp,
+ 'dt': dt,
+ 't': t,
+ 't_en': tEn,
+ 't-g': tG,
+ 'ts': ts,
+ };
+}
diff --git a/lib/models/statistic_data/statistic_data.dart b/lib/models/statistic_data/statistic_data.dart
new file mode 100644
index 0000000..5d720e7
--- /dev/null
+++ b/lib/models/statistic_data/statistic_data.dart
@@ -0,0 +1,29 @@
+import 'data.dart';
+
+class StatisticData {
+ final int id;
+ final String label;
+ final String title;
+ final Data data;
+
+ StatisticData({
+ required this.id,
+ required this.label,
+ required this.title,
+ required this.data,
+ });
+
+ factory StatisticData.fromJson(Map json) => StatisticData(
+ id: json['id'],
+ label: json['label'],
+ title: json['title'],
+ data: Data.fromJson(json['data']),
+ );
+
+ Map toJson() => {
+ 'id': id,
+ 'label': label,
+ 'title': title,
+ 'data': data.toJson(),
+ };
+}
diff --git a/lib/models/view/radar_category.dart b/lib/models/view/radar_category.dart
deleted file mode 100644
index 4312d46..0000000
--- a/lib/models/view/radar_category.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-class RadarCategory {
- final int id;
- final String title;
- final String asset;
-
- RadarCategory({required this.id, required this.title, required this.asset});
-}
diff --git a/lib/providers/server_data.dart b/lib/providers/server_data.dart
index 4ef71bb..4aac55f 100644
--- a/lib/providers/server_data.dart
+++ b/lib/providers/server_data.dart
@@ -9,12 +9,18 @@ class ServerDataProvider {
await _getDirectTypes();
}
- static int labelToTypeId(String label) => label.contains('پشتیبانی')
- ? 7
- : directTypes
+ static int labelToTypeId(String label) {
+ if (label.contains('پشتیبانی اپلیکیشن')) {
+ return 8;
+ } else if (label.contains('پشتیبانی محتوا')) {
+ return 7;
+ } else {
+ return directTypes
.firstWhereOrNull((element) => element.value.contains(label))
?.key ??
7;
+ }
+ }
static Future _getDirectTypes() async {
final service = RequestService(RequestHelper.directTypes);
diff --git a/lib/providers/user.dart b/lib/providers/user.dart
index d87e0ce..f0341e8 100644
--- a/lib/providers/user.dart
+++ b/lib/providers/user.dart
@@ -16,6 +16,7 @@ class UserProvider extends CoreProvier {
static final List _radarMarkQueue = [];
static final List _newsMarkQueue = [];
static final List _studioMarkQueue = [];
+ static final List _statisticMarkQueue = [];
Future setAndGetToken({String? newToken}) async {
if (newToken == null) {
@@ -184,4 +185,20 @@ class UserProvider extends CoreProvier {
_newsMarkQueue.removeWhere((element) => element.key == id);
});
}
+
+ static Future changeStatisticMark(int id, bool value) async {
+ _statisticMarkQueue.add(MapEntry(id, value));
+ Future.delayed(const Duration(milliseconds: 500), () async {
+ final MapEntry? lastChange =
+ _statisticMarkQueue.lastWhereOrNull((item) => item.key == id);
+ if (lastChange == null) return;
+ final service = RequestService(RequestHelper.mark(id, 'statistic'));
+ if (lastChange.value) {
+ await service.post();
+ } else {
+ await service.delete();
+ }
+ _statisticMarkQueue.removeWhere((element) => element.key == id);
+ });
+ }
}
diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart
index 308bc61..95aa9f4 100644
--- a/lib/routes/route_generator.dart
+++ b/lib/routes/route_generator.dart
@@ -14,7 +14,6 @@ import 'package:didvan/views/home/news/news_state.dart';
import 'package:didvan/views/home/radar/radar_details/radar_details.dart';
import 'package:didvan/views/home/radar/radar_details/radar_details_state.dart';
import 'package:didvan/views/home/radar/radar_state.dart';
-import 'package:didvan/views/home/settings/about_us/about_us.dart';
import 'package:didvan/views/home/settings/bookmarks/bookmarks.dart';
import 'package:didvan/views/home/settings/bookmarks/bookmark_state.dart';
import 'package:didvan/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmark.dart';
@@ -24,6 +23,9 @@ import 'package:didvan/views/home/settings/direct_list/direct_list_state.dart';
import 'package:didvan/views/home/settings/general_settings/settings.dart';
import 'package:didvan/views/home/settings/general_settings/settings_state.dart';
import 'package:didvan/views/home/settings/profile/profile.dart';
+import 'package:didvan/views/home/statistic/statistic_details/statistic_details.dart';
+import 'package:didvan/views/home/statistic/statistic_details/statistic_details_state.dart';
+import 'package:didvan/views/home/statistic/statistic_state.dart';
import 'package:didvan/views/home/studio/studio_details/studio_details.mobile.dart'
if (dart.library.io) 'package:didvan/views/home/studio/studio_details/studio_details.mobile.dart'
if (dart.library.html) 'package:didvan/views/home/studio/studio_details/studio_details.web.dart';
@@ -64,6 +66,9 @@ class RouteGenerator {
ChangeNotifierProvider(
create: (context) => StudioState(),
),
+ ChangeNotifierProvider(
+ create: (context) => StatisticState(),
+ ),
],
child: const Home(),
),
@@ -72,10 +77,6 @@ class RouteGenerator {
return _createRoute(
const Profile(),
);
- case Routes.aboutUs:
- return _createRoute(
- const AboutUs(),
- );
case Routes.generalSettings:
return _createRoute(
ChangeNotifierProvider(
@@ -107,6 +108,15 @@ class RouteGenerator {
pageData: settings.arguments as Map,
),
);
+ case Routes.statisticDetails:
+ return _createRoute(
+ ChangeNotifierProvider(
+ create: (context) => StatisticDetailsState(),
+ child: StatisticDetails(
+ pageData: settings.arguments as Map,
+ ),
+ ),
+ );
case Routes.directList:
return _createRoute(
ChangeNotifierProvider(
diff --git a/lib/routes/routes.dart b/lib/routes/routes.dart
index fe94d78..23657ab 100644
--- a/lib/routes/routes.dart
+++ b/lib/routes/routes.dart
@@ -9,6 +9,7 @@ class Routes {
static const String radarDetails = '/radar-details';
static const String newsDetails = '/news-details';
static const String studioDetails = '/studio-details';
+ static const String statisticDetails = '/statistic-details';
static const String directList = '/direct-list';
static const String direct = '/direct';
static const String comments = '/comments';
diff --git a/lib/services/media/media.dart b/lib/services/media/media.dart
index a783e23..2342acd 100644
--- a/lib/services/media/media.dart
+++ b/lib/services/media/media.dart
@@ -14,7 +14,8 @@ class MediaService {
static StudioDetailsData? currentPodcast;
static StudioRequestArgs? podcastPlaylistArgs;
- static Duration? get duration => audioPlayer.current.value?.audio.duration;
+ static Duration? get duration =>
+ audioPlayer.current.valueOrNull?.audio.duration;
static Future handleAudioPlayback({
required dynamic audioSource,
diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart
index 525622e..56ec7da 100644
--- a/lib/services/network/request_helper.dart
+++ b/lib/services/network/request_helper.dart
@@ -8,6 +8,7 @@ class RequestHelper {
static const String _baseRadarUrl = baseUrl + '/radar';
static const String _baseNewsUrl = baseUrl + '/news';
static const String _baseStudioUrl = baseUrl + '/studio';
+ static const String _baseStatisticUrl = baseUrl + '/statistic';
static const String _baseDirectUrl = _baseUserUrl + '/direct';
static const String confirmUsername = _baseUserUrl + '/confirmUsername';
@@ -39,6 +40,8 @@ class RequestHelper {
static String direct(int id) => _baseDirectUrl + '/$id';
static String sendDirectMessage(int id) =>
_baseDirectUrl + '/$id/sendMessage';
+ static String deleteDirect(int id, int messageId) =>
+ _baseDirectUrl + '/$id/message/$messageId';
static String tag({
required List ids,
String? type,
@@ -118,6 +121,21 @@ class RequestHelper {
MapEntry('asc', args.asc),
]);
+ static String statisticOverviews(int? category) =>
+ _baseStatisticUrl +
+ _urlConcatGenerator(
+ [MapEntry('category', category)],
+ );
+ static String statisticDetails(
+ String label,
+ String period,
+ ) =>
+ _baseStatisticUrl +
+ '/$label' +
+ _urlConcatGenerator([
+ MapEntry('period', period),
+ ]);
+
static String mark(int id, String type) => baseUrl + '/$type/$id/mark';
static String tracking(int id, String type) =>
baseUrl + '/$type/$id/tracking';
@@ -127,6 +145,8 @@ class RequestHelper {
baseUrl + '/$type/$id/comments/$commentId/feedback';
static String addComment(int id, String type) =>
baseUrl + '/$type/$id/comments/add';
+ static String deleteComment(int id) => baseUrl + '/comment/$id';
+ static String reportComment(int id) => baseUrl + '/comment/$id/report';
static String _urlConcatGenerator(List> additions) {
String result = '';
diff --git a/lib/utils/date_time.dart b/lib/utils/date_time.dart
index 41be0c6..3e50c93 100644
--- a/lib/utils/date_time.dart
+++ b/lib/utils/date_time.dart
@@ -47,6 +47,13 @@ class DateTimeUtils {
initialDate: initialJalali,
firstDate: firstDate,
lastDate: lastDate,
+ builder: (context, child) => Theme(
+ data: Theme.of(context).copyWith(
+ textTheme: Theme.of(context)
+ .textTheme
+ .copyWith(headline4: Theme.of(context).textTheme.headline3)),
+ child: child!,
+ ),
);
return result?.toDateTime().toString();
diff --git a/lib/views/authentication/screens/username.dart b/lib/views/authentication/screens/username.dart
index 6c5cf8c..cd28681 100644
--- a/lib/views/authentication/screens/username.dart
+++ b/lib/views/authentication/screens/username.dart
@@ -65,14 +65,16 @@ class _UsernameInputState extends State {
text: TextSpan(
style: Theme.of(context).textTheme.caption,
children: [
- const TextSpan(text: 'با و ورود به دیدوان،'),
+ const TextSpan(text: 'با ورود به دیدوان،'),
TextSpan(
text: ' شرایط ',
style: Theme.of(context)
.textTheme
.caption!
.copyWith(color: Theme.of(context).colorScheme.primary),
- recognizer: TapGestureRecognizer()..onTap = _openTermsOfUse,
+ recognizer: TapGestureRecognizer()
+ ..onTap = () =>
+ launch('https://didvan.app/termsOfUse.html#conditions'),
),
const TextSpan(text: 'و\n'),
TextSpan(
@@ -81,7 +83,9 @@ class _UsernameInputState extends State {
.textTheme
.caption!
.copyWith(color: Theme.of(context).colorScheme.primary),
- recognizer: TapGestureRecognizer()..onTap = _openTermsOfUse,
+ recognizer: TapGestureRecognizer()
+ ..onTap = () =>
+ launch('https://didvan.app/termsOfUse.html#privacy'),
),
const TextSpan(text: 'را میپذیرم'),
],
@@ -94,8 +98,4 @@ class _UsernameInputState extends State {
],
);
}
-
- void _openTermsOfUse() {
- launch('https://didvan.app/termsOfUse.html');
- }
}
diff --git a/lib/views/home/comments/comments.dart b/lib/views/home/comments/comments.dart
index 6e03d73..7803230 100644
--- a/lib/views/home/comments/comments.dart
+++ b/lib/views/home/comments/comments.dart
@@ -4,7 +4,7 @@ import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/views/home/comments/comments_state.dart';
-import 'package:didvan/views/home/comments/widgets/comment_item.dart';
+import 'package:didvan/views/home/comments/widgets/comment.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/scaffold.dart';
diff --git a/lib/views/home/comments/comments_state.dart b/lib/views/home/comments/comments_state.dart
index 4b02d00..03e04bc 100644
--- a/lib/views/home/comments/comments_state.dart
+++ b/lib/views/home/comments/comments_state.dart
@@ -4,10 +4,12 @@ import 'package:didvan/models/comment/feedback.dart';
import 'package:didvan/models/comment/reply.dart';
import 'package:didvan/models/comment/user.dart';
import 'package:didvan/models/enums.dart';
+import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/providers/user.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
+import 'package:didvan/utils/action_sheet.dart';
import 'package:provider/provider.dart';
class CommentsState extends CoreProvier {
@@ -158,4 +160,47 @@ class CommentsState extends CoreProvier {
replyingTo = null;
}
}
+
+ void reportComment(int id) {
+ final service = RequestService(RequestHelper.reportComment(id));
+ service.post();
+ ActionSheetUtils.showAlert(
+ AlertData(
+ message: 'گزارش شما با موفقیت ثبت شد و به زودی بررسی میگردد.',
+ aLertType: ALertType.success,
+ ),
+ );
+ }
+
+ void deleteComment(int id, int? rootId) {
+ final service = RequestService(RequestHelper.deleteComment(id));
+ service.delete();
+ if (rootId == null) {
+ final comment = comments.firstWhere((element) => element.id == id);
+ if (comment.replies.isNotEmpty) {
+ comments.insertAll(
+ comments.indexOf(comment),
+ comment.replies.map(
+ (rep) => CommentData(
+ id: rep.id,
+ text: rep.text,
+ createdAt: rep.createdAt,
+ liked: rep.liked,
+ disliked: rep.disliked,
+ feedback: rep.feedback,
+ user: rep.user,
+ replies: [],
+ ),
+ ),
+ );
+ }
+ comments.remove(comment);
+ } else {
+ comments
+ .firstWhere((element) => element.id == rootId)
+ .replies
+ .removeWhere((element) => element.id == id);
+ }
+ notifyListeners();
+ }
}
diff --git a/lib/views/home/comments/widgets/comment_item.dart b/lib/views/home/comments/widgets/comment.dart
similarity index 83%
rename from lib/views/home/comments/widgets/comment_item.dart
rename to lib/views/home/comments/widgets/comment.dart
index 4748031..cc50380 100644
--- a/lib/views/home/comments/widgets/comment_item.dart
+++ b/lib/views/home/comments/widgets/comment.dart
@@ -2,8 +2,13 @@ import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/models/comment/comment.dart';
+import 'package:didvan/models/comment/reply.dart';
+import 'package:didvan/models/view/action_sheet_data.dart';
+import 'package:didvan/providers/user.dart';
+import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/home/comments/comments_state.dart';
+import 'package:didvan/views/home/widgets/menu_item.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
@@ -84,17 +89,24 @@ class CommentState extends State {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DidvanText(
comment.user.fullName,
style: Theme.of(context).textTheme.bodyText1,
),
+ const Spacer(),
DidvanText(
DateTimeUtils.momentGenerator(comment.createdAt),
style: Theme.of(context).textTheme.caption,
color: Theme.of(context).colorScheme.caption,
),
+ const SizedBox(width: 4),
+ DidvanIconButton(
+ size: 18,
+ gestureSize: 24,
+ icon: DidvanIcons.menu_light,
+ onPressed: () => _showCommentActions(comment),
+ ),
],
),
const SizedBox(height: 8),
@@ -171,6 +183,42 @@ class CommentState extends State {
],
),
);
+
+ Future _showCommentActions(comment) async {
+ ActionSheetUtils.showBottomSheet(
+ data: ActionSheetData(
+ title: 'گزینههای نظر',
+ content: Column(
+ children: [
+ if (comment.user.id != context.read().user.id)
+ MenuItem(
+ title: 'گزارش محتوای نامناسب',
+ onTap: () {
+ state.reportComment(comment.id);
+ ActionSheetUtils.pop();
+ },
+ icon: DidvanIcons.alert_regular,
+ ),
+ if (comment.user.id == context.read().user.id)
+ MenuItem(
+ title: 'حذف نظر',
+ color: Theme.of(context).colorScheme.secondary,
+ onTap: () {
+ state.deleteComment(
+ comment.id,
+ comment.runtimeType == Reply ? _comment.id : null,
+ );
+ ActionSheetUtils.pop();
+ },
+ icon: DidvanIcons.trash_solid,
+ ),
+ ],
+ ),
+ hasConfirmButton: false,
+ hasDismissButton: false,
+ ),
+ );
+ }
}
class _FeedbackButtons extends StatefulWidget {
diff --git a/lib/views/home/direct/direct.dart b/lib/views/home/direct/direct.dart
index d47be3c..2c0ba84 100644
--- a/lib/views/home/direct/direct.dart
+++ b/lib/views/home/direct/direct.dart
@@ -1,3 +1,4 @@
+import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/view/app_bar_data.dart';
@@ -6,7 +7,9 @@ import 'package:didvan/services/media/media.dart';
import 'package:didvan/views/home/direct/direct_state.dart';
import 'package:didvan/views/home/direct/widgets/message.dart';
import 'package:didvan/views/home/direct/widgets/message_box.dart';
+import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/scaffold.dart';
+import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/material.dart';
@@ -52,11 +55,14 @@ class _DirectState extends State {
left: 0,
right: 0,
child: DidvanScaffold(
+ padding: EdgeInsets.zero,
reverse: true,
backgroundColor: Theme.of(context).colorScheme.surface,
appBarData: AppBarData(
hasBack: true,
- subtitle: 'ارتباط با سردبیر',
+ subtitle: widget.pageData['type'].contains('پشتیبانی')
+ ? null
+ : 'ارتباط با سردبیر',
title: widget.pageData['type'] ?? 'پشتیبانی اپلیکیشن',
),
slivers: [
@@ -103,6 +109,43 @@ class _DirectState extends State {
left: 0,
child: const MessageBox(),
),
+ if (state.deletionQueue.isNotEmpty)
+ Positioned(
+ left: 0,
+ right: 0,
+ top: d.padding.top,
+ child: Container(
+ height: 72,
+ color: Theme.of(context).colorScheme.surface,
+ child: Row(
+ children: [
+ DidvanIconButton(
+ icon: DidvanIcons.close_solid,
+ size: 32,
+ gestureSize: 48,
+ color: Theme.of(context).colorScheme.secondary,
+ onPressed: () {
+ state.deletionQueue.clear();
+ state.update();
+ },
+ ),
+ DidvanText(
+ '${state.deletionQueue.length} مورد انتخاب شد',
+ style: Theme.of(context).textTheme.subtitle1,
+ color: Theme.of(context).colorScheme.secondary,
+ ),
+ const Spacer(),
+ DidvanIconButton(
+ icon: DidvanIcons.trash_solid,
+ size: 32,
+ gestureSize: 48,
+ color: Theme.of(context).colorScheme.primary,
+ onPressed: state.delete,
+ ),
+ ],
+ ),
+ ),
+ ),
],
),
),
diff --git a/lib/views/home/direct/direct_state.dart b/lib/views/home/direct/direct_state.dart
index 20964d5..ba1e98c 100644
--- a/lib/views/home/direct/direct_state.dart
+++ b/lib/views/home/direct/direct_state.dart
@@ -16,6 +16,7 @@ class DirectState extends CoreProvier {
final List messages = [];
late final int typeId;
final Map> dailyMessages = {};
+ final List deletionQueue = [];
String? text;
RadarAttachment? replyRadar;
@@ -77,6 +78,18 @@ class DirectState extends CoreProvier {
}
}
+ void delete() {
+ for (var i = 0; i < deletionQueue.length; i++) {
+ final service = RequestService(
+ RequestHelper.deleteDirect(typeId, deletionQueue[i]),
+ );
+ service.delete();
+ messages.removeWhere((element) => element.id == deletionQueue[i]);
+ }
+ deletionQueue.clear();
+ notifyListeners();
+ }
+
void _addToDailyGrouped(MessageData message) {
String createdAt = message.createdAt.replaceAll('T', ' ').split(' ').first;
if (!dailyMessages.containsKey(createdAt)) {
diff --git a/lib/views/home/direct/widgets/message.dart b/lib/views/home/direct/widgets/message.dart
index ff90eb1..d1c5e6a 100644
--- a/lib/views/home/direct/widgets/message.dart
+++ b/lib/views/home/direct/widgets/message.dart
@@ -18,85 +18,109 @@ class Message extends StatelessWidget {
@override
Widget build(BuildContext context) {
- final firstMessageOfGroupId = context
- .read()
+ final state = context.read();
+ final firstMessageOfGroupId = state
.dailyMessages[message.createdAt.replaceAll('T', ' ').split(' ').first]!
.last;
- return Column(
- crossAxisAlignment: message.writedByAdmin
- ? CrossAxisAlignment.end
- : CrossAxisAlignment.start,
- children: [
- if (message.id == firstMessageOfGroupId)
- Center(
- child: Container(
- margin: const EdgeInsets.only(bottom: 12),
- padding: const EdgeInsets.all(4),
- decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.splash,
- borderRadius: DesignConfig.lowBorderRadius,
- ),
- child: DidvanText(
- DateTime.parse(message.createdAt).toPersianDateStr(),
- style: Theme.of(context).textTheme.overline,
- color: DesignConfig.isDark
- ? Theme.of(context).colorScheme.white
- : Theme.of(context).colorScheme.black,
- ),
- ),
- ),
- Padding(
- padding: EdgeInsets.only(
- right: message.writedByAdmin ? 20 : 0,
- left: !message.writedByAdmin ? 20 : 0,
- ),
- child: Column(
- crossAxisAlignment: message.writedByAdmin
- ? CrossAxisAlignment.start
- : CrossAxisAlignment.end,
- children: [
- _MessageContainer(
- writedByAdmin: message.writedByAdmin,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- if (message.text != null) DidvanText(message.text!),
- if (message.audio != null || message.audioFile != null)
- AudioWidget(
- audioFile: message.audioFile,
- audioUrl: message.audio,
- id: message.id,
- ),
- if (message.radar != null) const DidvanDivider(),
- if (message.radar != null) const SizedBox(height: 4),
- if (message.radar != null)
- _ReplyRadarOverview(message: message),
- if (message.radar != null) const SizedBox(height: 4),
- ],
+ return GestureDetector(
+ onLongPress: () {
+ if (state.deletionQueue.contains(message.id) || message.writedByAdmin) {
+ return;
+ }
+ state.deletionQueue.add(message.id);
+ state.update();
+ },
+ onTap: () {
+ if (state.deletionQueue.isEmpty || message.writedByAdmin) return;
+ if (!state.deletionQueue.contains(message.id)) {
+ state.deletionQueue.add(message.id);
+ } else {
+ state.deletionQueue.remove(message.id);
+ }
+ state.update();
+ },
+ child: Container(
+ color: state.deletionQueue.contains(message.id)
+ ? Theme.of(context).colorScheme.secondaryDisabled.withOpacity(0.5)
+ : null,
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
+ child: Column(
+ crossAxisAlignment: message.writedByAdmin
+ ? CrossAxisAlignment.end
+ : CrossAxisAlignment.start,
+ children: [
+ if (message.id == firstMessageOfGroupId)
+ Center(
+ child: Container(
+ margin: const EdgeInsets.only(bottom: 12),
+ padding: const EdgeInsets.all(4),
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.splash,
+ borderRadius: DesignConfig.lowBorderRadius,
+ ),
+ child: DidvanText(
+ DateTime.parse(message.createdAt).toPersianDateStr(),
+ style: Theme.of(context).textTheme.overline,
+ color: DesignConfig.isDark
+ ? Theme.of(context).colorScheme.white
+ : Theme.of(context).colorScheme.black,
+ ),
),
),
- const SizedBox(height: 4),
- Row(
- mainAxisSize: MainAxisSize.min,
+ Padding(
+ padding: EdgeInsets.only(
+ right: message.writedByAdmin ? 20 : 0,
+ left: !message.writedByAdmin ? 20 : 0,
+ ),
+ child: Column(
+ crossAxisAlignment: message.writedByAdmin
+ ? CrossAxisAlignment.start
+ : CrossAxisAlignment.end,
children: [
- DidvanText(
- DateTimeUtils.timeWithAmPm(message.createdAt),
- style: Theme.of(context).textTheme.overline,
- color: Theme.of(context).colorScheme.caption,
+ _MessageContainer(
+ writedByAdmin: message.writedByAdmin,
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ if (message.text != null) DidvanText(message.text!),
+ if (message.audio != null || message.audioFile != null)
+ AudioWidget(
+ audioFile: message.audioFile,
+ audioUrl: message.audio,
+ id: message.id,
+ ),
+ if (message.radar != null) const DidvanDivider(),
+ if (message.radar != null) const SizedBox(height: 4),
+ if (message.radar != null)
+ _ReplyRadarOverview(message: message),
+ if (message.radar != null) const SizedBox(height: 4),
+ ],
+ ),
+ ),
+ const SizedBox(height: 4),
+ Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ DidvanText(
+ DateTimeUtils.timeWithAmPm(message.createdAt),
+ style: Theme.of(context).textTheme.overline,
+ color: Theme.of(context).colorScheme.caption,
+ ),
+ if (!message.writedByAdmin)
+ Icon(
+ message.readed
+ ? DidvanIcons.check_double_light
+ : DidvanIcons.check_light,
+ size: 16,
+ )
+ ],
),
- if (!message.writedByAdmin)
- Icon(
- message.readed
- ? DidvanIcons.check_double_light
- : DidvanIcons.check_light,
- size: 16,
- )
],
),
- ],
- ),
+ ),
+ ],
),
- ],
+ ),
);
}
}
@@ -177,7 +201,10 @@ class _MessageContainer extends StatelessWidget {
bottomLeft: writedByAdmin ? Radius.zero : null,
bottomRight: !writedByAdmin ? Radius.zero : null,
),
- color: writedByAdmin ? null : Theme.of(context).colorScheme.focused,
+ color: (writedByAdmin
+ ? Theme.of(context).colorScheme.surface
+ : Theme.of(context).colorScheme.focused)
+ .withOpacity(0.9),
border: Border.all(
color: Theme.of(context).colorScheme.border,
width: 0.5,
diff --git a/lib/views/home/home.dart b/lib/views/home/home.dart
index b9c203e..aec1a56 100644
--- a/lib/views/home/home.dart
+++ b/lib/views/home/home.dart
@@ -3,7 +3,7 @@ import 'package:didvan/views/home/home_state.dart';
import 'package:didvan/views/home/news/news.dart';
import 'package:didvan/views/home/radar/radar.dart';
import 'package:didvan/views/home/settings/settings.dart';
-import 'package:didvan/views/home/statistics/statistics.dart';
+import 'package:didvan/views/home/statistic/statistic.dart';
import 'package:didvan/views/home/studio/studio.dart';
import 'package:didvan/views/widgets/didvan/bnb.dart';
import 'package:flutter/material.dart';
@@ -37,7 +37,7 @@ class _HomeState extends State with SingleTickerProviderStateMixin {
controller: _tabController,
children: const [
News(),
- Statictics(),
+ Statistic(),
Radar(),
Studio(),
Settings(),
diff --git a/lib/views/home/news/news.dart b/lib/views/home/news/news.dart
index 9387dae..afa9c9e 100644
--- a/lib/views/home/news/news.dart
+++ b/lib/views/home/news/news.dart
@@ -52,6 +52,7 @@ class _NewsState extends State {
),
),
SliverStateHandler(
+ centerEmptyState: false,
onRetry: () => state.getNews(page: state.page),
state: state,
builder: (context, state, index) {
@@ -90,7 +91,7 @@ class _NewsState extends State {
void _onChanged(String value) {
final state = context.read();
- if (value.length < 4 && value.isNotEmpty || state.lastSearch == value) {
+ if (value.length < 3 && value.isNotEmpty || state.lastSearch == value) {
return;
}
_timer?.cancel();
diff --git a/lib/views/home/radar/radar.dart b/lib/views/home/radar/radar.dart
index 6384c96..42f3b08 100644
--- a/lib/views/home/radar/radar.dart
+++ b/lib/views/home/radar/radar.dart
@@ -1,17 +1,16 @@
-// ignore_for_file: prefer_const_constructors
-
import 'dart:async';
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/models/category.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/requests/radar.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/views/home/radar/radar_state.dart';
-import 'package:didvan/views/home/radar/widgets/categories_gird.dart';
-import 'package:didvan/views/home/radar/widgets/categories_list.dart';
+import 'package:didvan/views/home/widgets/categories_gird.dart';
+import 'package:didvan/views/home/widgets/categories_list.dart';
import 'package:didvan/views/home/widgets/date_picker_button.dart';
import 'package:didvan/views/home/widgets/logo_app_bar.dart';
import 'package:didvan/utils/action_sheet.dart';
@@ -162,20 +161,49 @@ class _RadarState extends State {
),
],
),
- if (state.appState != AppState.failed) CategoriesRow1(),
- if (state.appState != AppState.failed) CategoriesRow2(),
+ if (state.appState != AppState.failed)
+ CategoriesRow1(
+ topPadding: 300,
+ rightPadding: 124,
+ onSelected: _onCategorySelected,
+ categories: state.categories,
+ isColapsed:
+ state.isColapsed || state.searching || state.filtering,
+ ),
+ if (state.appState != AppState.failed)
+ CategoriesRow2(
+ categories: state.categories,
+ isColapsed:
+ state.isColapsed || state.searching || state.filtering,
+ onSelected: _onCategorySelected,
+ ),
if (state.appState != AppState.failed &&
!state.searching &&
!state.filtering)
- CategoriesList(),
+ CategoriesList(
+ categories: state.categories,
+ isColapsed:
+ state.isColapsed || state.searching || state.filtering,
+ onSelected: (_) => state.getRadars(page: 1),
+ selectedCats: state.selectedCats,
+ ),
],
),
);
}
+ void _onCategorySelected(CategoryData category) {
+ final state = context.read();
+ state.selectedCats.clear();
+ if (category.id != 0) {
+ state.selectedCats.add(category);
+ }
+ state.getRadars(page: 1);
+ }
+
void _onChanged(String value) {
final state = context.read();
- if (value.length < 4 && value.isNotEmpty || state.lastSearch == value) {
+ if (value.length < 3 && value.isNotEmpty || state.lastSearch == value) {
return;
}
_timer?.cancel();
@@ -268,7 +296,7 @@ class _RadarState extends State {
SizedBox(
width: (MediaQuery.of(context).size.width - 40) / 2,
child: DidvanCheckbox(
- title: state.categories[i].title,
+ title: state.categories[i].label,
value: state.selectedCats.contains(state.categories[i]),
onChanged: (value) {
if (value) {
diff --git a/lib/views/home/radar/radar_state.dart b/lib/views/home/radar/radar_state.dart
index fb293a2..2b57c88 100644
--- a/lib/views/home/radar/radar_state.dart
+++ b/lib/views/home/radar/radar_state.dart
@@ -1,8 +1,8 @@
import 'package:didvan/constants/assets.dart';
+import 'package:didvan/models/category.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/overview_data.dart';
import 'package:didvan/models/requests/radar.dart';
-import 'package:didvan/models/view/radar_category.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
@@ -16,8 +16,8 @@ class RadarState extends CoreProvier {
int lastPage = 1;
bool isScrolled = false;
bool shouldColapse = false;
- final List selectedCats = [];
- List categories = [];
+ final List selectedCats = [];
+ List categories = [];
final List radars = [];
bool get filtering =>
@@ -46,11 +46,9 @@ class RadarState extends CoreProvier {
required int page,
}) async {
this.page = page;
- if (this.page == page) {
- radars.clear();
- }
lastSearch = search;
if (page == 1) {
+ radars.clear();
appState = AppState.busy;
}
final RequestService service = RequestService(
@@ -99,34 +97,34 @@ class RadarState extends CoreProvier {
getRadars(page: 1);
});
categories = [
- RadarCategory(
+ CategoryData(
id: 1,
- title: 'اقتصادی',
+ label: 'اقتصادی',
asset: Assets.economicCategoryIcon,
),
- RadarCategory(
+ CategoryData(
id: 2,
- title: 'سیاسی',
+ label: 'سیاسی',
asset: Assets.politicalCategoryIcon,
),
- RadarCategory(
+ CategoryData(
id: 3,
- title: 'فناوری',
+ label: 'فناوری',
asset: Assets.techCategoryIcon,
),
- RadarCategory(
+ CategoryData(
id: 4,
- title: 'کسب و کار',
+ label: 'کسب و کار',
asset: Assets.businessCategoryIcon,
),
- RadarCategory(
+ CategoryData(
id: 5,
- title: 'زیست محیطی',
+ label: 'زیست محیطی',
asset: Assets.enviromentalCategoryIcon,
),
- RadarCategory(
+ CategoryData(
id: 6,
- title: 'اجتماعی',
+ label: 'اجتماعی',
asset: Assets.socialCategoryIcon,
),
];
diff --git a/lib/views/home/radar/widgets/categories_list.dart b/lib/views/home/radar/widgets/categories_list.dart
deleted file mode 100644
index bca0187..0000000
--- a/lib/views/home/radar/widgets/categories_list.dart
+++ /dev/null
@@ -1,130 +0,0 @@
-import 'package:didvan/config/design_config.dart';
-import 'package:didvan/config/theme_data.dart';
-import 'package:didvan/models/view/radar_category.dart';
-import 'package:didvan/views/home/radar/radar_state.dart';
-import 'package:didvan/views/widgets/animated_visibility.dart';
-import 'package:didvan/views/widgets/didvan/text.dart';
-import 'package:flutter/material.dart';
-import 'package:provider/provider.dart';
-
-class CategoriesList extends StatefulWidget {
- const CategoriesList({Key? key}) : super(key: key);
-
- @override
- State createState() => _CategoriesListState();
-}
-
-class _CategoriesListState extends State {
- final _scrollController = ScrollController();
-
- int _lastSelectedCategoryId = 0;
-
- @override
- void didUpdateWidget(covariant CategoriesList oldWidget) {
- final RadarState state = context.read();
- if (state.selectedCats.isNotEmpty &&
- _lastSelectedCategoryId != state.selectedCats.first.id) {
- _lastSelectedCategoryId = state.selectedCats.first.id;
- _scrollController.animateTo(
- state.selectedCats.first.id * 100,
- duration: DesignConfig.lowAnimationDuration,
- curve: Curves.easeIn,
- );
- }
- super.didUpdateWidget(oldWidget);
- }
-
- @override
- Widget build(BuildContext context) {
- final MediaQueryData d = MediaQuery.of(context);
- final state = context.read();
- final isColapsed = state.isColapsed || state.searching || state.filtering;
- return Positioned(
- top: 0,
- left: 0,
- right: 0,
- child: AnimatedCrossFade(
- crossFadeState:
- isColapsed ? CrossFadeState.showSecond : CrossFadeState.showFirst,
- duration: DesignConfig.mediumAnimationDuration,
- reverseDuration: DesignConfig.lowAnimationDuration,
- sizeCurve: Curves.easeIn,
- firstChild: const SizedBox(),
- secondChild: Container(
- height: 60 + d.padding.top,
- decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.surface,
- boxShadow: DesignConfig.defaultShadow,
- ),
- child: AnimatedVisibility(
- isVisible: isColapsed,
- duration: DesignConfig.mediumAnimationDuration,
- child: SingleChildScrollView(
- controller: _scrollController,
- // physics: const BouncingScrollPhysics(),
- scrollDirection: Axis.horizontal,
- padding: EdgeInsets.only(
- top: d.padding.top + 12,
- bottom: 12,
- right: 12,
- ),
- child: Row(
- children: [
- _itemBuilder(
- RadarCategory(title: 'همه', asset: '', id: 0),
- context,
- ),
- for (var i = 0; i < state.categories.length; i++)
- _itemBuilder(state.categories[i], context),
- ],
- ),
- ),
- ),
- ),
- ),
- );
- }
-
- Widget _itemBuilder(RadarCategory category, BuildContext context) {
- final state = context.read();
- return GestureDetector(
- onTap: () async {
- if (state.selectedCats.isNotEmpty &&
- state.selectedCats.first.id == category.id) return;
- state.selectedCats.clear();
- if (category.id != 0) state.selectedCats.add(category);
- await _scrollController.animateTo(
- category.id * 100,
- duration: DesignConfig.lowAnimationDuration,
- curve: Curves.easeIn,
- );
- state.getRadars(page: 1);
- },
- child: Container(
- margin: const EdgeInsets.only(left: 12),
- width: 100,
- padding: const EdgeInsets.all(4),
- alignment: Alignment.center,
- child: FittedBox(
- fit: BoxFit.scaleDown,
- child: DidvanText(
- category.title,
- fontWeight: FontWeight.w600,
- color: Theme.of(context).colorScheme.focusedBorder,
- ),
- ),
- decoration: BoxDecoration(
- color: state.selectedCats.length == 1 &&
- state.selectedCats.contains(category) ||
- category.id == 0 && state.selectedCats.isEmpty
- ? Theme.of(context).colorScheme.focused
- : null,
- border: Border.all(
- color: Theme.of(context).colorScheme.focusedBorder,
- ),
- borderRadius: DesignConfig.lowBorderRadius,
- ),
- ),
- );
- }
-}
diff --git a/lib/views/home/settings/about_us/about_us.dart b/lib/views/home/settings/about_us/about_us.dart
deleted file mode 100644
index 6cc17f9..0000000
--- a/lib/views/home/settings/about_us/about_us.dart
+++ /dev/null
@@ -1,25 +0,0 @@
-import 'package:didvan/models/view/app_bar_data.dart';
-import 'package:didvan/views/widgets/didvan/scaffold.dart';
-import 'package:didvan/views/widgets/didvan/text.dart';
-import 'package:flutter/material.dart';
-
-class AboutUs extends StatelessWidget {
- const AboutUs({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return DidvanScaffold(
- appBarData: AppBarData(
- hasBack: true,
- title: 'معرفی دیدوان',
- ),
- children: const [
- DidvanText(
- '''برای سفر مطمئن در جغرافیای مکانی به ابزارهایی مانند نقشه، قطب نما، سیستم ناوبری، علائم کنار جاده و یا حتی علائم طبیعی نیاز داریم. اما برای سفر در جغرافیای زمانی چه؟
-همه ما آدمیان و همهی سازمانها و شرکتهایمان، لاجرم مسافران جغرافیای زمانی هستند. ما همواره در مسیری رو به مقصدی در آینده در حرکت هستیم. اما فراموش نکنیم که سفر زمانی یک تفاوت بنیادین با طی مسیر در جغرافیای مکانی دارد. جغرافیای زمانی هیچگاه نمیایستد و سفر به آینده یک سفر شگفتانگیز به جایی است که قسمتهایی از آن را قبلاً در گذشته دیدهایم، قسمتهایی از آن را طی مسیر به تدریج میبینیم و قسمتهایی از آن کاملاً بدیع هستند.
-برای سفر مطمئن در جغرافیای زمانی ما به سیستمهای پیشنگر نیاز داریم و یکی از ارکان این سیستمها، سامانههای رصد راهبردی هستند. دیدوان با شعار "چشم همیشه باز مدیران" سامانه رصد راهبردی شرکت فولاد مبارکه است تا سفری مطمئن به آینده را برای این شرکت رقم بزند.\nدر پارسی کهن دیدوان به این شکل معنی شده است: شخصی را گویند که بر جای بلند مانند سر کوه و بالای کشتی نشیند و هرچه از دور بیند خبر دهد. دیدوان چشمی است که آینده را میبیند. ما در دیدوان تلاش داریم که با رویکردی آیندهپژوهانه مسیر پیشروی صنایع را تحلیل کنیم و با تحویل آن به مدیران صنایع، راهنمایی باشیم برای اخذ تصمیمات درست.''',
- ),
- ],
- );
- }
-}
diff --git a/lib/views/home/settings/bookmarks/bookmarks.dart b/lib/views/home/settings/bookmarks/bookmarks.dart
index 81ddeab..01ae4fe 100644
--- a/lib/views/home/settings/bookmarks/bookmarks.dart
+++ b/lib/views/home/settings/bookmarks/bookmarks.dart
@@ -144,7 +144,7 @@ class _BookmarksState extends State {
void _onChanged(String value) {
final state = context.read();
- if (value.length < 4 && value.isNotEmpty || state.lastSearch == value) {
+ if (value.length < 3 && value.isNotEmpty || state.lastSearch == value) {
return;
}
_timer?.cancel();
diff --git a/lib/views/home/settings/settings.dart b/lib/views/home/settings/settings.dart
index e923da5..b205e5a 100644
--- a/lib/views/home/settings/settings.dart
+++ b/lib/views/home/settings/settings.dart
@@ -87,28 +87,32 @@ class Settings extends StatelessWidget {
MenuItem(
icon: DidvanIcons.didvan_solid,
title: 'معرفی دیدوان',
- onTap: () => launch('https://didvan.app/'),
+ onTap: () => launch('https://didvan.app/#info'),
),
const DidvanDivider(),
MenuItem(
icon: DidvanIcons.support_regular,
title: 'پیام به پشتیبانی',
onTap: () {
- launch('mailto:info@didvan.app');
+ Navigator.of(context).pushNamed(
+ Routes.direct,
+ arguments: {'type': 'پشتیبانی اپلیکیشن'},
+ );
},
),
const DidvanDivider(),
MenuItem(
icon: DidvanIcons.alert_regular,
title: 'حریم خصوصی',
- onTap: () => launch('https://didvan.app/'),
+ onTap: () =>
+ launch('https://didvan.app/termsOfUse.html#privacy'),
),
],
),
),
const SizedBox(height: 16),
DidvanText(
- 'نسخه نرمافزار: 1.5.0',
+ 'نسخه نرمافزار: 2.0.0',
style: Theme.of(context).textTheme.caption,
),
],
diff --git a/lib/views/home/statistic/statistic.dart b/lib/views/home/statistic/statistic.dart
new file mode 100644
index 0000000..6496e79
--- /dev/null
+++ b/lib/views/home/statistic/statistic.dart
@@ -0,0 +1,205 @@
+import 'dart:math';
+
+import 'package:didvan/config/design_config.dart';
+import 'package:didvan/config/theme_data.dart';
+import 'package:didvan/models/category.dart';
+import 'package:didvan/models/enums.dart';
+import 'package:didvan/models/statistic_data/statistic_data.dart';
+import 'package:didvan/views/home/statistic/statistic_state.dart';
+import 'package:didvan/views/home/statistic/widgets/statistic_overview.dart';
+import 'package:didvan/views/home/widgets/categories_gird.dart';
+import 'package:didvan/views/home/widgets/categories_list.dart';
+import 'package:didvan/views/home/widgets/logo_app_bar.dart';
+import 'package:didvan/views/widgets/animated_visibility.dart';
+import 'package:didvan/views/widgets/didvan/divider.dart';
+import 'package:didvan/views/widgets/didvan/text.dart';
+import 'package:didvan/views/widgets/state_handlers/empty_list.dart';
+import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+class Statistic extends StatefulWidget {
+ const Statistic({Key? key}) : super(key: key);
+
+ @override
+ State createState() => _StatisticState();
+}
+
+class _StatisticState extends State {
+ final ScrollController _scrollController = ScrollController();
+
+ bool _isAnimating = false;
+
+ @override
+ void initState() {
+ _scrollController.addListener(() {
+ _handleAnimations();
+ });
+ final state = context.read();
+ state.addListener(() {
+ if (state.shouldColapse && mounted) {
+ _handleAnimations(true);
+ state.shouldColapse = false;
+ }
+ });
+ state.init();
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Consumer(
+ builder: (context, state, child) => Stack(
+ children: [
+ CustomScrollView(
+ physics: _isAnimating
+ ? const NeverScrollableScrollPhysics()
+ : const ClampingScrollPhysics(),
+ controller: _scrollController,
+ slivers: [
+ const SliverToBoxAdapter(child: LogoAppBar()),
+ if (state.appState != AppState.failed)
+ const SliverToBoxAdapter(
+ child: SizedBox(height: 180),
+ ),
+ if (state.appState != AppState.failed &&
+ state.markedStatistics.isNotEmpty)
+ SliverPadding(
+ padding: const EdgeInsets.only(right: 16, bottom: 20),
+ sliver: SliverToBoxAdapter(
+ child: Align(
+ alignment: Alignment.centerRight,
+ child: AnimatedVisibility(
+ isVisible: !state.isColapsed,
+ duration: DesignConfig.lowAnimationDuration,
+ child: DidvanText(
+ 'شاخصهای منتخب',
+ style: Theme.of(context).textTheme.subtitle1,
+ color: Theme.of(context).colorScheme.title,
+ ),
+ ),
+ ),
+ ),
+ ),
+ SliverStateHandler(
+ onRetry: state.getStatistic,
+ state: state,
+ itemPadding: const EdgeInsets.only(
+ bottom: 20,
+ left: 16,
+ right: 16,
+ ),
+ emptyState: const EmptyList(),
+ enableEmptyState: _itemCount(state) == 0,
+ placeholder: StatisticOverview.placeHolder,
+ builder: (context, state, index) {
+ if (index == state.markedStatistics.length) {
+ return const DidvanDivider(verticalPadding: 8);
+ }
+ bool isMarked = false;
+ StatisticData statistic;
+ if (index < state.markedStatistics.length) {
+ isMarked = true;
+ statistic = state.markedStatistics[index];
+ } else {
+ index--;
+ statistic =
+ state.statistics[index - state.markedStatistics.length];
+ }
+ return StatisticOverview(
+ statistic: statistic,
+ isMarked: isMarked,
+ onMarkChanged: state.changeMark,
+ );
+ },
+ childCount: _itemCount(state) + 1,
+ ),
+ SliverToBoxAdapter(
+ child: SizedBox(
+ height: state.appState == AppState.busy
+ ? 300
+ : _itemCount(state) == 0
+ ? 150
+ : max(
+ MediaQuery.of(context).size.height -
+ _itemCount(state) * 120,
+ 0),
+ ),
+ ),
+ ],
+ ),
+ if (state.appState != AppState.failed)
+ CategoriesRow1(
+ onSelected: _onCategorySelected,
+ categories: List.from(state.categories)..removeAt(0),
+ isColapsed: state.isColapsed,
+ topPadding: 144,
+ rightPadding: 300,
+ ),
+ if (state.appState != AppState.failed)
+ CategoriesList(
+ categories: state.categories,
+ isColapsed: state.isColapsed,
+ onSelected: (id) {
+ state.selectedCategoryId = id;
+ state.getStatistic();
+ },
+ selectedCats: state.selectedCategory == null
+ ? []
+ : [state.selectedCategory!],
+ ),
+ ],
+ ),
+ );
+ }
+
+ int _itemCount(state) =>
+ state.markedStatistics.length +
+ (state.selectedCategoryId == 1 ? 0 : state.statistics.length);
+
+ void _onCategorySelected(CategoryData category) {
+ final state = context.read();
+ state.selectedCategoryId = 0;
+ if (category.id != 0) {
+ state.selectedCategoryId = category.id;
+ }
+ state.getStatistic();
+ }
+
+ void _handleAnimations([bool forceAnimate = false]) async {
+ final state = context.read();
+ if (_isAnimating) return;
+ final double position = _scrollController.offset;
+ if (position > 5 && !state.isColapsed || forceAnimate) {
+ state.isScrolled = true;
+ _isAnimating = true;
+ setState(() {});
+ await _scrollController.animateTo(
+ 228,
+ duration: DesignConfig.mediumAnimationDuration,
+ curve: Curves.easeIn,
+ );
+ _isAnimating = false;
+ setState(() {});
+ } else if (position <
+ min(_scrollController.position.maxScrollExtent, 228) &&
+ state.isColapsed) {
+ state.isScrolled = false;
+ _isAnimating = true;
+ setState(() {});
+ await _scrollController.animateTo(
+ 0,
+ duration: DesignConfig.mediumAnimationDuration,
+ curve: Curves.easeIn,
+ );
+ _isAnimating = false;
+ setState(() {});
+ }
+ }
+
+ @override
+ void dispose() {
+ _scrollController.dispose();
+ super.dispose();
+ }
+}
diff --git a/lib/views/home/statistic/statistic_details/statistic_details.dart b/lib/views/home/statistic/statistic_details/statistic_details.dart
new file mode 100644
index 0000000..ae78195
--- /dev/null
+++ b/lib/views/home/statistic/statistic_details/statistic_details.dart
@@ -0,0 +1,298 @@
+import 'package:didvan/config/theme_data.dart';
+import 'package:didvan/constants/app_icons.dart';
+import 'package:didvan/models/enums.dart';
+import 'package:didvan/models/view/app_bar_data.dart';
+import 'package:didvan/views/home/statistic/statistic_details/statistic_details_state.dart';
+import 'package:didvan/views/home/widgets/categories_list.dart';
+import 'package:didvan/views/home/widgets/overview/multitype.dart';
+import 'package:didvan/views/home/widgets/tag_item.dart';
+import 'package:didvan/views/widgets/didvan/card.dart';
+import 'package:didvan/views/widgets/didvan/divider.dart';
+import 'package:didvan/views/widgets/didvan/icon_button.dart';
+import 'package:didvan/views/widgets/didvan/scaffold.dart';
+import 'package:didvan/views/widgets/didvan/text.dart';
+import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
+import 'package:fl_chart/fl_chart.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_spinkit/flutter_spinkit.dart';
+import 'package:provider/provider.dart';
+import 'package:intl/intl.dart' as intl;
+
+class StatisticDetails extends StatefulWidget {
+ final Map pageData;
+ const StatisticDetails({Key? key, required this.pageData}) : super(key: key);
+
+ @override
+ State createState() => _StatisticDetailsState();
+}
+
+class _StatisticDetailsState extends State {
+ @override
+ void initState() {
+ final state = context.read();
+ state.label = widget.pageData['label'];
+ state.marked = widget.pageData['marked'];
+ state.currentDateRangeId = 0;
+ Future.delayed(Duration.zero, state.getStatisticDetails);
+ super.initState();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Consumer(
+ builder: (context, state, child) => DidvanScaffold(
+ padding: EdgeInsets.zero,
+ appBarData: AppBarData(
+ title: widget.pageData['title'],
+ hasBack: true,
+ subtitle: 'رادار قیمتها',
+ trailing: DidvanIconButton(
+ icon: state.marked ? Icons.star : Icons.star_border,
+ color: state.marked
+ ? Theme.of(context).colorScheme.yellow
+ : Theme.of(context).colorScheme.focusedBorder,
+ size: 32,
+ onPressed: () {
+ state.marked = !state.marked;
+ state.update();
+ widget.pageData['onMarkChanged'](state.marked);
+ },
+ ),
+ ),
+ children: [
+ StateHandler(
+ topPadding: MediaQuery.of(context).size.height / 3,
+ builder: (context, state) => Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const SizedBox(height: 20),
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 16),
+ child: DidvanText('نمودار تغییرات'),
+ ),
+ const SizedBox(height: 20),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: SizedBox(
+ width: double.infinity,
+ height: 120,
+ child: state.chartState == AppState.busy
+ ? SpinKitThreeBounce(
+ color: Theme.of(context).colorScheme.primary,
+ size: 24,
+ )
+ : LineChart(
+ LineChartData(
+ lineTouchData: LineTouchData(
+ touchTooltipData: LineTouchTooltipData(
+ tooltipBgColor:
+ Theme.of(context).colorScheme.navigation,
+ getTooltipItems: (data) => [
+ LineTooltipItem(
+ state.datas[data.first.spotIndex].tEn! +
+ '\n' +
+ intl.NumberFormat("###,000", "en_US")
+ .format(
+ data.first.bar
+ .spots[data.first.spotIndex].y,
+ ),
+ Theme.of(context)
+ .textTheme
+ .caption!
+ .copyWith(
+ color: Colors.white,
+ ),
+ ),
+ ],
+ ),
+ ),
+ minX: 0,
+ maxX: state.datas.length.toDouble() - 1,
+ maxY: state.maxValue * 1.02,
+ minY: state.minValue! * 0.98,
+ gridData: FlGridData(show: false),
+ borderData: FlBorderData(show: false),
+ titlesData: FlTitlesData(show: false),
+ lineBarsData: [
+ LineChartBarData(
+ spots: [
+ for (var i = 0; i < state.datas.length; i++)
+ FlSpot(
+ i.toDouble(),
+ _stringToDouble(state.datas[i].p),
+ )
+ ],
+ barWidth: 2,
+ dotData: FlDotData(
+ getDotPainter: (p0, p1, p2, p3) =>
+ FlDotCirclePainter(
+ color: Colors.transparent,
+ strokeWidth: 1,
+ strokeColor:
+ Theme.of(context).colorScheme.success,
+ ),
+ ),
+ color: Theme.of(context).colorScheme.success,
+ belowBarData: BarAreaData(
+ show: true,
+ gradient: LinearGradient(
+ begin: Alignment.bottomCenter,
+ end: Alignment.topCenter,
+ tileMode: TileMode.decal,
+ colors: [
+ Theme.of(context)
+ .colorScheme
+ .background,
+ const Color(0XFFF5B763)
+ .withOpacity(0.2),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(height: 20),
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 16),
+ child: DidvanText('بازه نمایش:'),
+ ),
+ const SizedBox(height: 20),
+ CategoriesList(
+ isColapsed: false,
+ isAppBar: false,
+ selectedCats: [state.currentDateRange],
+ categories: state.dateRanges,
+ onSelected: (id) {
+ state.currentDateRangeId = id;
+ state.getStatisticDetails();
+ },
+ ),
+ const SizedBox(height: 16),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: DidvanCard(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ _buildDataItem('قیمت لحظهای', state.data!.p),
+ const DidvanDivider(verticalPadding: 8),
+ _buildDataItem('بالاترین قیمت روز', state.data!.h),
+ const SizedBox(height: 8),
+ _buildDataItem('پایینترین قیمت روز', state.data!.l),
+ const SizedBox(height: 8),
+ _buildDataItem(
+ 'درصد تغییر نسبت به دیروز',
+ '${state.data!.dp}%',
+ icon: _diffIcon(state),
+ color: _diffColor(state),
+ ),
+ const SizedBox(height: 8),
+ _buildDataItem(
+ 'میزان تغییر نسبت به دیروز',
+ state.data!.d,
+ icon: _diffIcon(state),
+ color: _diffColor(state),
+ ),
+ const SizedBox(height: 16),
+ Wrap(
+ spacing: 8,
+ runSpacing: 8,
+ children: [
+ for (var i = 0; i < state.tags.length; i++)
+ TagItem(
+ tag: state.tags[i],
+ onMarkChanged: (_, __) {},
+ type: 'statistic',
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(height: 16),
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16),
+ child: DidvanCard(
+ child: Column(
+ children: [
+ if (state.relatedContents.isEmpty)
+ for (var i = 0; i < 3; i++) ...[
+ MultitypeOverview.placeholder,
+ if (i != 2) const SizedBox(height: 16)
+ ],
+ for (var i = 0;
+ i < state.relatedContents.length;
+ i++) ...[
+ MultitypeOverview(
+ item: state.relatedContents[i],
+ onMarkChanged: (id, value) {},
+ ),
+ if (i != state.relatedContents.length - 1)
+ const SizedBox(height: 16)
+ ]
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(height: 16),
+ ],
+ ),
+ onRetry: state.getStatisticDetails,
+ state: state,
+ )
+ ],
+ ),
+ );
+ }
+
+ double _stringToDouble(String value) =>
+ double.parse(value.replaceAll(',', ''));
+
+ Color? _diffColor(StatisticDetailsState state) {
+ if (state.data!.dp == 0) {
+ return null;
+ }
+ if (state.data!.dt == 'high') {
+ return Theme.of(context).colorScheme.success;
+ } else {
+ return Theme.of(context).colorScheme.error;
+ }
+ }
+
+ IconData? _diffIcon(StatisticDetailsState state) {
+ if (state.data!.dp == 0) {
+ return null;
+ }
+ if (state.data!.dt == 'high') {
+ return DidvanIcons.angle_up_regular;
+ } else {
+ return DidvanIcons.angle_down_regular;
+ }
+ }
+
+ Widget _buildDataItem(
+ String title,
+ String value, {
+ IconData? icon,
+ bool isBold = false,
+ Color? color,
+ }) {
+ return Row(
+ children: [
+ DidvanText(
+ title,
+ style: isBold
+ ? Theme.of(context).textTheme.bodyText1
+ : Theme.of(context).textTheme.bodyText2,
+ ),
+ const Spacer(),
+ if (icon != null) Icon(icon, color: color),
+ DidvanText(value, color: color),
+ ],
+ );
+ }
+}
diff --git a/lib/views/home/statistic/statistic_details/statistic_details_state.dart b/lib/views/home/statistic/statistic_details/statistic_details_state.dart
new file mode 100644
index 0000000..a87ef8c
--- /dev/null
+++ b/lib/views/home/statistic/statistic_details/statistic_details_state.dart
@@ -0,0 +1,169 @@
+import 'package:didvan/models/category.dart';
+import 'package:didvan/models/enums.dart';
+import 'package:didvan/models/overview_data.dart';
+import 'package:didvan/models/statistic_data/data.dart';
+import 'package:didvan/models/tag.dart';
+import 'package:didvan/providers/core.dart';
+import 'package:didvan/services/network/request.dart';
+import 'package:didvan/services/network/request_helper.dart';
+import 'package:collection/collection.dart';
+
+class StatisticDetailsState extends CoreProvier {
+ late bool marked;
+ late String label;
+ String? startDate;
+ String? endDate;
+ int currentDateRangeId = 0;
+ final List datas = [];
+ final List relatedContents = [];
+ final List tags = [];
+ Data? data;
+ double maxValue = 0;
+ double? minValue;
+
+ AppState chartState = AppState.idle;
+
+ final dateRanges = [
+ CategoryData(
+ id: 0,
+ label: 'هفتگی',
+ asset: 'weekly',
+ ),
+ CategoryData(
+ id: 1,
+ label: 'ماهانه',
+ asset: 'monthly',
+ ),
+ CategoryData(
+ id: 2,
+ label: 'شش ماهه',
+ asset: 'semiyearly',
+ ),
+ CategoryData(
+ id: 3,
+ label: 'سالانه',
+ asset: 'yearly',
+ ),
+ ];
+
+ CategoryData get currentDateRange => dateRanges.firstWhere(
+ (element) => element.id == currentDateRangeId,
+ );
+
+ Future getStatisticDetails() async {
+ if (data == null) {
+ final result = await getStatisticCurrentDetails();
+ if (!result) {
+ appState = AppState.failed;
+ return;
+ }
+ }
+ minValue = null;
+ maxValue = 0;
+ if (datas.isEmpty) {
+ appState = AppState.busy;
+ } else {
+ chartState = AppState.busy;
+ notifyListeners();
+ }
+ datas.clear();
+ final service = RequestService(
+ RequestHelper.statisticDetails(
+ label,
+ dateRanges[currentDateRangeId].asset!,
+ ),
+ );
+ await service.httpGet();
+ if (service.isSuccess) {
+ final result = service.result['data'];
+ tags.clear();
+ for (var i = 0; i < service.result['tags'].length; i++) {
+ tags.add(Tag.fromJson(service.result['tags'][i]));
+ }
+ for (var i = 0; i < result.length; i++) {
+ datas.add(Data.fromList(result[i]));
+ }
+ if (currentDateRangeId != 0 && currentDateRangeId != 1) {
+ final grouped =
+ datas.groupListsBy((element) => element.tEn!.split('/')[1]);
+ datas.clear();
+ grouped.forEach((key, value) {
+ datas.add(
+ Data(
+ p: _average(value),
+ h: '',
+ l: '',
+ d: '',
+ dp: 0,
+ dt: '',
+ t: '',
+ tEn: value.first.tEn!.substring(0, 7),
+ tG: '',
+ ts: '',
+ ),
+ );
+ });
+ }
+ for (var i = 0; i < datas.length; i++) {
+ final current = _stringToDouble(datas[i].p);
+ if (maxValue < current) {
+ maxValue = current;
+ }
+ if (minValue == null || minValue! > current) {
+ minValue = current;
+ }
+ }
+ getRelatedContents();
+ datas.replaceRange(0, datas.length, datas.reversed);
+ chartState = AppState.idle;
+ appState = AppState.idle;
+ return;
+ }
+ if (datas.isEmpty) {
+ appState = AppState.failed;
+ } else {
+ chartState = AppState.failed;
+ notifyListeners();
+ }
+ }
+
+ Future getStatisticCurrentDetails() async {
+ final service = RequestService(
+ RequestHelper.statisticDetails(
+ label,
+ 'current',
+ ),
+ );
+ await service.httpGet();
+ if (service.isSuccess) {
+ data = Data.fromJson(service.result['data']);
+ }
+ return service.isSuccess;
+ }
+
+ String _average(List inputs) {
+ double sum = 0;
+ for (var i = 0; i < inputs.length; i++) {
+ sum += _stringToDouble(inputs[i].p);
+ }
+ return (sum / inputs.length).toString();
+ }
+
+ double _stringToDouble(String value) =>
+ double.parse(value.replaceAll(',', ''));
+
+ Future getRelatedContents() async {
+ if (relatedContents.isNotEmpty) return;
+ final service = RequestService(RequestHelper.tag(
+ ids: tags.map((tag) => tag.id).toList(),
+ ));
+ await service.httpGet();
+ if (service.isSuccess) {
+ final relateds = service.result['contents'];
+ for (var i = 0; i < relateds.length; i++) {
+ relatedContents.add(OverviewData.fromJson(relateds[i]));
+ }
+ notifyListeners();
+ }
+ }
+}
diff --git a/lib/views/home/statistic/statistic_state.dart b/lib/views/home/statistic/statistic_state.dart
new file mode 100644
index 0000000..00f6c9f
--- /dev/null
+++ b/lib/views/home/statistic/statistic_state.dart
@@ -0,0 +1,102 @@
+import 'package:didvan/constants/assets.dart';
+import 'package:collection/collection.dart';
+import 'package:didvan/models/category.dart';
+import 'package:didvan/models/enums.dart';
+import 'package:didvan/models/statistic_data/statistic_data.dart';
+import 'package:didvan/providers/core.dart';
+import 'package:didvan/providers/user.dart';
+import 'package:didvan/services/network/request.dart';
+import 'package:didvan/services/network/request_helper.dart';
+
+class StatisticState extends CoreProvier {
+ bool isScrolled = false;
+ bool shouldColapse = false;
+ int selectedCategoryId = -1;
+ List categories = [];
+ final List statistics = [];
+ final List markedStatistics = [];
+
+ bool get isColapsed => (isCategorySelected && isScrolled) || isScrolled;
+
+ CategoryData? get selectedCategory => categories.firstWhereOrNull(
+ (element) => element.id == selectedCategoryId,
+ );
+
+ bool get isCategorySelected => selectedCategoryId != 0;
+
+ Future getStatistic() async {
+ statistics.clear();
+ markedStatistics.clear();
+ appState = AppState.busy;
+ final RequestService service = RequestService(
+ RequestHelper.statisticOverviews(
+ selectedCategoryId == 0 || selectedCategoryId == 1
+ ? null
+ : selectedCategoryId - 1,
+ ),
+ );
+ await service.httpGet();
+ if (service.isSuccess) {
+ final others = service.result['others'];
+ for (var i = 0; i < others.length; i++) {
+ statistics.add(StatisticData.fromJson(others[i]));
+ }
+ final marked = service.result['marked'];
+ for (var i = 0; i < marked.length; i++) {
+ markedStatistics.add(StatisticData.fromJson(marked[i]));
+ }
+ if (isColapsed || isCategorySelected) {
+ shouldColapse = true;
+ }
+ appState = AppState.idle;
+ return;
+ }
+ appState = AppState.failed;
+ }
+
+ Future changeMark(int id, bool value) async {
+ final item = statistics.firstWhereOrNull((element) => element.id == id) ??
+ markedStatistics.firstWhere((element) => element.id == id);
+ if (value) {
+ markedStatistics.add(item);
+ statistics.remove(item);
+ } else {
+ markedStatistics.remove(item);
+ statistics.add(item);
+ }
+ UserProvider.changeStatisticMark(id, value);
+ notifyListeners();
+ }
+
+ void init() {
+ selectedCategoryId = 0;
+ isScrolled = false;
+ markedStatistics.clear();
+ statistics.clear();
+ Future.delayed(Duration.zero, () {
+ getStatistic();
+ });
+ categories = [
+ CategoryData(
+ id: 1,
+ label: 'منتخب',
+ asset: Assets.economicCategoryIcon,
+ ),
+ CategoryData(
+ id: 2,
+ label: 'اقتصاد کلان',
+ asset: Assets.globCategoryIcon,
+ ),
+ CategoryData(
+ id: 3,
+ label: 'صنعت فولاد',
+ asset: Assets.steelCategoryIcon,
+ ),
+ CategoryData(
+ id: 4,
+ label: 'بازار سرمایه',
+ asset: Assets.stockCategoryIcon,
+ ),
+ ];
+ }
+}
diff --git a/lib/views/home/statistic/widgets/statistic_overview.dart b/lib/views/home/statistic/widgets/statistic_overview.dart
new file mode 100644
index 0000000..acb392b
--- /dev/null
+++ b/lib/views/home/statistic/widgets/statistic_overview.dart
@@ -0,0 +1,164 @@
+import 'package:didvan/config/theme_data.dart';
+import 'package:didvan/constants/app_icons.dart';
+import 'package:didvan/models/statistic_data/statistic_data.dart';
+import 'package:didvan/routes/routes.dart';
+import 'package:didvan/views/widgets/didvan/card.dart';
+import 'package:didvan/views/widgets/didvan/text.dart';
+import 'package:didvan/views/widgets/shimmer_placeholder.dart';
+import 'package:flutter/material.dart';
+
+class StatisticOverview extends StatelessWidget {
+ final StatisticData statistic;
+ final bool isMarked;
+ final void Function(int id, bool value) onMarkChanged;
+ const StatisticOverview({
+ Key? key,
+ required this.statistic,
+ required this.isMarked,
+ required this.onMarkChanged,
+ }) : super(key: key);
+
+ Color _diffColor(context) => statistic.data.dt == 'high'
+ ? Theme.of(context).colorScheme.success
+ : Theme.of(context).colorScheme.error;
+
+ bool get _hasDiff => statistic.data.d != '0';
+
+ @override
+ Widget build(BuildContext context) {
+ return DidvanCard(
+ onTap: () =>
+ Navigator.of(context).pushNamed(Routes.statisticDetails, arguments: {
+ 'onMarkChanged': (value) => onMarkChanged(statistic.id, value),
+ 'label': statistic.label,
+ 'title': statistic.title,
+ 'marked': isMarked,
+ }),
+ child: Column(
+ children: [
+ Row(
+ children: [
+ if (isMarked)
+ Icon(
+ Icons.star,
+ color: Theme.of(context).colorScheme.yellow,
+ size: 18,
+ ),
+ DidvanText(
+ statistic.title,
+ style: Theme.of(context).textTheme.bodyText1,
+ ),
+ const Spacer(),
+ if (_hasDiff)
+ DidvanText(
+ '(${statistic.data.d})',
+ color: _diffColor(context),
+ ),
+ if (_hasDiff) const SizedBox(width: 8),
+ DidvanText(
+ statistic.data.p,
+ style: Theme.of(context).textTheme.bodyText1,
+ )
+ ],
+ ),
+ const SizedBox(height: 8),
+ Row(
+ children: [
+ Icon(
+ Icons.trending_down,
+ size: 18,
+ color: Theme.of(context).colorScheme.hint,
+ ),
+ DidvanText(
+ statistic.data.l,
+ style: Theme.of(context).textTheme.caption,
+ color: Theme.of(context).colorScheme.hint,
+ ),
+ const SizedBox(width: 8),
+ Icon(
+ Icons.trending_up,
+ size: 18,
+ color: Theme.of(context).colorScheme.hint,
+ ),
+ DidvanText(
+ statistic.data.h,
+ style: Theme.of(context).textTheme.caption,
+ color: Theme.of(context).colorScheme.hint,
+ ),
+ const Spacer(),
+ if (_hasDiff)
+ Icon(
+ statistic.data.dt == 'high'
+ ? DidvanIcons.angle_up_regular
+ : DidvanIcons.angle_down_regular,
+ size: 18,
+ color: _diffColor(context),
+ ),
+ if (_hasDiff) const SizedBox(width: 4),
+ if (_hasDiff)
+ DidvanText(
+ statistic.data.dp.toString() + '%',
+ style: Theme.of(context).textTheme.caption,
+ color: _diffColor(context),
+ ),
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+
+ static Widget get placeHolder => Column(
+ children: [
+ DidvanCard(
+ child: Column(
+ children: [
+ const SizedBox(height: 4),
+ Row(
+ children: const [
+ ShimmerPlaceholder(width: 80, height: 16),
+ Spacer(),
+ ShimmerPlaceholder(width: 50, height: 14),
+ SizedBox(width: 8),
+ ShimmerPlaceholder(width: 50, height: 16),
+ ],
+ ),
+ const SizedBox(height: 16),
+ Row(
+ children: const [
+ ShimmerPlaceholder(width: 150, height: 12),
+ Spacer(),
+ ShimmerPlaceholder(width: 80, height: 12),
+ ],
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(height: 20),
+ DidvanCard(
+ child: Column(
+ children: [
+ const SizedBox(height: 4),
+ Row(
+ children: const [
+ ShimmerPlaceholder(width: 80, height: 16),
+ Spacer(),
+ ShimmerPlaceholder(width: 50, height: 14),
+ SizedBox(width: 8),
+ ShimmerPlaceholder(width: 50, height: 16),
+ ],
+ ),
+ const SizedBox(height: 16),
+ Row(
+ children: const [
+ ShimmerPlaceholder(width: 150, height: 12),
+ Spacer(),
+ ShimmerPlaceholder(width: 80, height: 12),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ],
+ );
+}
diff --git a/lib/views/home/statistics/statistics.dart b/lib/views/home/statistics/statistics.dart
deleted file mode 100644
index eef8150..0000000
--- a/lib/views/home/statistics/statistics.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-import 'package:didvan/config/theme_data.dart';
-import 'package:didvan/constants/assets.dart';
-import 'package:didvan/views/home/widgets/logo_app_bar.dart';
-import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
-import 'package:flutter/material.dart';
-
-class Statictics extends StatelessWidget {
- const Statictics({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return Column(
- children: [
- const LogoAppBar(),
- Expanded(
- child: EmptyState(
- asset: Assets.emptyChart,
- title: 'قیمتها و شاخصهای اقتصادی',
- subtitle: 'به زودی...',
- titleColor: Theme.of(context).colorScheme.title,
- ),
- ),
- ],
- );
- }
-}
diff --git a/lib/views/home/studio/studio.dart b/lib/views/home/studio/studio.dart
index 296a552..f437719 100644
--- a/lib/views/home/studio/studio.dart
+++ b/lib/views/home/studio/studio.dart
@@ -165,7 +165,7 @@ class _StudioState extends State {
void _onChanged(String value) {
final state = context.read();
- if (value.length < 4 && value.isNotEmpty || state.lastSearch == value) {
+ if (value.length < 3 && value.isNotEmpty || state.lastSearch == value) {
return;
}
_timer?.cancel();
diff --git a/lib/views/home/studio/studio_details/studio_details.mobile.dart b/lib/views/home/studio/studio_details/studio_details.mobile.dart
index 2c34596..b42266c 100644
--- a/lib/views/home/studio/studio_details/studio_details.mobile.dart
+++ b/lib/views/home/studio/studio_details/studio_details.mobile.dart
@@ -88,7 +88,6 @@ class _StudioDetailsState extends State {
),
),
body: SingleChildScrollView(
- physics: const NeverScrollableScrollPhysics(),
child: SizedBox(
height: d.size.height - d.padding.top - 56,
child: Column(
diff --git a/lib/views/home/studio/studio_details/studio_details.web.dart b/lib/views/home/studio/studio_details/studio_details.web.dart
index b944905..95aa28f 100644
--- a/lib/views/home/studio/studio_details/studio_details.web.dart
+++ b/lib/views/home/studio/studio_details/studio_details.web.dart
@@ -87,9 +87,12 @@ class _StudioDetailsState extends State {
height: d.size.height - d.padding.top - 56,
child: Column(
children: [
- const AspectRatio(
+ AspectRatio(
aspectRatio: 16 / 9,
- child: HtmlElementView(viewType: 'video'),
+ child: HtmlElementView(
+ viewType: 'video',
+ key: ValueKey(state.studio.id),
+ ),
),
Expanded(
child: StudioDetailsWidget(
diff --git a/lib/views/home/radar/widgets/categories_gird.dart b/lib/views/home/widgets/categories_gird.dart
similarity index 60%
rename from lib/views/home/radar/widgets/categories_gird.dart
rename to lib/views/home/widgets/categories_gird.dart
index abcc0e2..564ad03 100644
--- a/lib/views/home/radar/widgets/categories_gird.dart
+++ b/lib/views/home/widgets/categories_gird.dart
@@ -1,27 +1,34 @@
import 'package:didvan/config/design_config.dart';
-import 'package:didvan/views/home/radar/radar_state.dart';
-import 'package:didvan/views/home/radar/widgets/category_item.dart';
+import 'package:didvan/models/category.dart';
+import 'package:didvan/views/home/widgets/category_item.dart';
import 'package:flutter/material.dart';
-import 'package:provider/provider.dart';
class CategoriesRow1 extends StatelessWidget {
- const CategoriesRow1({Key? key}) : super(key: key);
+ final List categories;
+ final bool isColapsed;
+ final double topPadding;
+ final double rightPadding;
+ final void Function(CategoryData data) onSelected;
+ const CategoriesRow1({
+ Key? key,
+ required this.categories,
+ required this.isColapsed,
+ required this.onSelected,
+ required this.topPadding,
+ required this.rightPadding,
+ }) : super(key: key);
@override
Widget build(BuildContext context) {
- final state = context.read();
- final isColapsed = state.isColapsed || state.searching || state.filtering;
final MediaQueryData d = MediaQuery.of(context);
return AnimatedPositioned(
curve: Curves.easeIn,
duration: DesignConfig.mediumAnimationDuration,
- top: isColapsed ? -60 : 300 + d.padding.top,
- left: isColapsed ? -80 : 0,
- right: isColapsed ? 124 : 0,
+ top: isColapsed ? -60 : topPadding + d.padding.top,
+ left: isColapsed ? -rightPadding : 0,
+ right: isColapsed ? rightPadding : 0,
child: Row(
- children: context
- .read()
- .categories
+ children: categories
.sublist(0, 3)
.map(
(category) => Expanded(
@@ -30,6 +37,7 @@ class CategoriesRow1 extends StatelessWidget {
child: CategoryItem(
category: category,
isColapsed: isColapsed,
+ onSelected: () => onSelected(category),
),
),
),
@@ -41,14 +49,19 @@ class CategoriesRow1 extends StatelessWidget {
}
class CategoriesRow2 extends StatelessWidget {
+ final List categories;
+ final bool isColapsed;
+ final void Function(CategoryData data) onSelected;
+
const CategoriesRow2({
Key? key,
+ required this.categories,
+ required this.isColapsed,
+ required this.onSelected,
}) : super(key: key);
@override
Widget build(BuildContext context) {
- final state = context.read();
- final isColapsed = state.isColapsed || state.searching || state.filtering;
final MediaQueryData d = MediaQuery.of(context);
return AnimatedPositioned(
curve: Curves.easeIn,
@@ -57,14 +70,13 @@ class CategoriesRow2 extends StatelessWidget {
left: isColapsed ? -d.size.width : 0,
right: isColapsed ? d.size.width : 0,
child: Row(
- children: context
- .read()
- .categories
+ children: categories
.sublist(3, 6)
.map(
(category) => Expanded(
child: CategoryItem(
category: category,
+ onSelected: () => onSelected(category),
isColapsed: isColapsed,
),
),
diff --git a/lib/views/home/widgets/categories_list.dart b/lib/views/home/widgets/categories_list.dart
new file mode 100644
index 0000000..5a718be
--- /dev/null
+++ b/lib/views/home/widgets/categories_list.dart
@@ -0,0 +1,146 @@
+import 'package:didvan/config/design_config.dart';
+import 'package:didvan/config/theme_data.dart';
+import 'package:didvan/models/category.dart';
+import 'package:didvan/views/widgets/animated_visibility.dart';
+import 'package:didvan/views/widgets/didvan/text.dart';
+import 'package:flutter/material.dart';
+
+class CategoriesList extends StatefulWidget {
+ final bool isColapsed;
+ final bool isAppBar;
+ final List selectedCats;
+ final List categories;
+ final void Function(int id) onSelected;
+ const CategoriesList({
+ Key? key,
+ required this.isColapsed,
+ required this.selectedCats,
+ required this.categories,
+ required this.onSelected,
+ this.isAppBar = true,
+ }) : super(key: key);
+
+ @override
+ State createState() => _CategoriesListState();
+}
+
+class _CategoriesListState extends State {
+ final _scrollController = ScrollController();
+
+ int _lastSelectedCategoryId = 0;
+
+ @override
+ void didUpdateWidget(covariant CategoriesList oldWidget) {
+ if (widget.selectedCats.isNotEmpty &&
+ _lastSelectedCategoryId != widget.selectedCats.first.id) {
+ _lastSelectedCategoryId = widget.selectedCats.first.id;
+ _scrollController.animateTo(
+ widget.selectedCats.first.id * 100,
+ duration: DesignConfig.lowAnimationDuration,
+ curve: Curves.easeIn,
+ );
+ }
+ super.didUpdateWidget(oldWidget);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final MediaQueryData d = MediaQuery.of(context);
+ final child = SingleChildScrollView(
+ controller: _scrollController,
+ scrollDirection: Axis.horizontal,
+ padding: widget.isAppBar
+ ? EdgeInsets.only(
+ top: d.padding.top + 12,
+ bottom: 12,
+ )
+ : null,
+ child: Row(
+ children: [
+ const SizedBox(width: 12),
+ if (widget.isAppBar)
+ _itemBuilder(
+ CategoryData(
+ label: 'همه',
+ id: 0,
+ ),
+ context,
+ ),
+ for (var i = 0; i < widget.categories.length; i++)
+ _itemBuilder(widget.categories[i], context),
+ ],
+ ),
+ );
+ if (widget.isAppBar) {
+ return Positioned(
+ top: 0,
+ left: 0,
+ right: 0,
+ child: AnimatedCrossFade(
+ crossFadeState: widget.isColapsed
+ ? CrossFadeState.showSecond
+ : CrossFadeState.showFirst,
+ duration: DesignConfig.mediumAnimationDuration,
+ reverseDuration: DesignConfig.lowAnimationDuration,
+ sizeCurve: Curves.easeIn,
+ firstChild: const SizedBox(),
+ secondChild: Container(
+ height: 60 + d.padding.top,
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surface,
+ boxShadow: DesignConfig.defaultShadow,
+ ),
+ child: AnimatedVisibility(
+ isVisible: widget.isColapsed,
+ duration: DesignConfig.mediumAnimationDuration,
+ child: child,
+ ),
+ ),
+ ),
+ );
+ }
+ return child;
+ }
+
+ Widget _itemBuilder(CategoryData category, BuildContext context) {
+ return GestureDetector(
+ onTap: () async {
+ if (widget.selectedCats.isNotEmpty &&
+ widget.selectedCats.first.id == category.id) return;
+ widget.selectedCats.clear();
+ if (category.id != 0) widget.selectedCats.add(category);
+ await _scrollController.animateTo(
+ category.id * 100,
+ duration: DesignConfig.lowAnimationDuration,
+ curve: Curves.easeIn,
+ );
+ widget.onSelected(category.id);
+ },
+ child: Container(
+ margin: const EdgeInsets.only(left: 12),
+ width: 100,
+ padding: const EdgeInsets.all(4),
+ alignment: Alignment.center,
+ child: FittedBox(
+ fit: BoxFit.scaleDown,
+ child: DidvanText(
+ category.label,
+ fontWeight: FontWeight.w600,
+ color: Theme.of(context).colorScheme.focusedBorder,
+ ),
+ ),
+ decoration: BoxDecoration(
+ color: widget.selectedCats.length == 1 &&
+ widget.selectedCats.contains(category) ||
+ category.id == 0 && widget.selectedCats.isEmpty
+ ? Theme.of(context).colorScheme.splash
+ : null,
+ border: Border.all(
+ color: Theme.of(context).colorScheme.focusedBorder,
+ ),
+ borderRadius: DesignConfig.lowBorderRadius,
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/home/radar/widgets/category_item.dart b/lib/views/home/widgets/category_item.dart
similarity index 83%
rename from lib/views/home/radar/widgets/category_item.dart
rename to lib/views/home/widgets/category_item.dart
index faee630..eaf6cf6 100644
--- a/lib/views/home/radar/widgets/category_item.dart
+++ b/lib/views/home/widgets/category_item.dart
@@ -1,22 +1,22 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
-import 'package:didvan/models/view/radar_category.dart';
-import 'package:didvan/views/home/radar/radar_state.dart';
+import 'package:didvan/models/category.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
-import 'package:provider/provider.dart';
class CategoryItem extends StatelessWidget {
- final RadarCategory category;
+ final CategoryData category;
final bool isColapsed;
+ final VoidCallback onSelected;
const CategoryItem({
Key? key,
required this.isColapsed,
required this.category,
+ required this.onSelected,
}) : super(key: key);
double _width(context) {
@@ -40,14 +40,7 @@ class CategoryItem extends StatelessWidget {
final Size ds = MediaQuery.of(context).size;
return Center(
child: GestureDetector(
- onTap: () {
- final state = context.read();
- state.selectedCats.clear();
- if (category.id != 0) {
- state.selectedCats.add(category);
- }
- state.getRadars(page: 1);
- },
+ onTap: onSelected,
child: AnimatedContainer(
duration: DesignConfig.mediumAnimationDuration,
padding: isColapsed ? const EdgeInsets.all(4) : EdgeInsets.zero,
@@ -77,14 +70,14 @@ class CategoryItem extends StatelessWidget {
borderRadius: DesignConfig.mediumBorderRadius,
),
padding: const EdgeInsets.all(8),
- child: SvgPicture.asset(category.asset),
+ child: SvgPicture.asset(category.asset!),
),
),
const SizedBox(
height: 8,
),
DidvanText(
- category.title,
+ category.label,
style: Theme.of(context).textTheme.subtitle2,
color: Theme.of(context).colorScheme.title,
),
diff --git a/lib/views/home/widgets/search_field.dart b/lib/views/home/widgets/search_field.dart
index 9a979b7..d0fb92d 100644
--- a/lib/views/home/widgets/search_field.dart
+++ b/lib/views/home/widgets/search_field.dart
@@ -78,7 +78,7 @@ class _SearchFieldState extends State {
right: 12,
),
border: InputBorder.none,
- hintText: 'جستجو مطلب در ${widget.title}',
+ hintText: 'جستجو در ${widget.title}',
hintStyle: TextStyle(
color: Theme.of(context).colorScheme.disabledText,
),
diff --git a/lib/views/widgets/didvan/app_bar.dart b/lib/views/widgets/didvan/app_bar.dart
index 81770c8..ab7a949 100644
--- a/lib/views/widgets/didvan/app_bar.dart
+++ b/lib/views/widgets/didvan/app_bar.dart
@@ -29,6 +29,7 @@ class DidvanAppBar extends StatelessWidget {
),
)
: null,
+ color: backgroundColor ?? Theme.of(context).colorScheme.background,
),
child: Row(
children: [
diff --git a/lib/views/widgets/didvan/page_view.dart b/lib/views/widgets/didvan/page_view.dart
index a7cb236..54384af 100644
--- a/lib/views/widgets/didvan/page_view.dart
+++ b/lib/views/widgets/didvan/page_view.dart
@@ -2,10 +2,13 @@ import 'package:carousel_slider/carousel_slider.dart';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
+import 'package:didvan/routes/routes.dart';
+import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/home/widgets/overview/multitype.dart';
import 'package:didvan/views/home/widgets/tag_item.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
+import 'package:didvan/views/widgets/didvan/button.dart';
import 'package:didvan/views/widgets/didvan/card.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
@@ -190,7 +193,35 @@ class _DidvanPageViewState extends State {
if (content.text != null) {
return Html(
data: content.text,
- onAnchorTap: (href, context, map, element) => launch(href!),
+ onAnchorTap: (href, context, map, element) {
+ if (href!.contains('navigate-')) {
+ Navigator.of(ActionSheetUtils.context)
+ .pushNamed(Routes.statisticDetails, arguments: {
+ 'onMarkChanged': (value) {},
+ 'label': href.split('-')[1],
+ 'title': href.split('-').last,
+ 'marked': false,
+ });
+ } else if (href.contains('popup-')) {
+ showDialog(
+ context: ActionSheetUtils.context,
+ builder: (context) => DidvanCard(
+ child: Column(
+ children: [
+ DidvanText(href.split('-').last),
+ const DidvanDivider(),
+ const DidvanButton(
+ title: 'بستن',
+ onPressed: ActionSheetUtils.pop,
+ ),
+ ],
+ ),
+ ),
+ );
+ } else {
+ launch(href);
+ }
+ },
style: {
'*': Style(
direction: TextDirection.rtl,
diff --git a/lib/views/widgets/didvan/scaffold.dart b/lib/views/widgets/didvan/scaffold.dart
index 38f9dde..879e240 100644
--- a/lib/views/widgets/didvan/scaffold.dart
+++ b/lib/views/widgets/didvan/scaffold.dart
@@ -59,11 +59,15 @@ class _DidvanScaffoldState extends State {
SliverAppBar(
toolbarHeight: (widget.appBarData!.isSmall ? 56 : 72) -
statusBarHeight,
- backgroundColor: widget.backgroundColor ??
- Theme.of(context).colorScheme.background,
automaticallyImplyLeading: false,
pinned: true,
- flexibleSpace: DidvanAppBar(appBarData: widget.appBarData!),
+ backgroundColor: widget.backgroundColor ??
+ Theme.of(context).colorScheme.background,
+ flexibleSpace: DidvanAppBar(
+ appBarData: widget.appBarData!,
+ backgroundColor: widget.backgroundColor ??
+ Theme.of(context).colorScheme.background,
+ ),
),
if (widget.children != null && !widget.showSliversFirst)
SliverPadding(
diff --git a/lib/views/widgets/state_handlers/sliver_state_handler.dart b/lib/views/widgets/state_handlers/sliver_state_handler.dart
index 1e70ddb..8338a49 100644
--- a/lib/views/widgets/state_handlers/sliver_state_handler.dart
+++ b/lib/views/widgets/state_handlers/sliver_state_handler.dart
@@ -13,6 +13,7 @@ class SliverStateHandler extends SliverList {
final Widget? placeholder;
final EdgeInsets? itemPadding;
final bool centerEmptyState;
+ final bool hasConstraints;
SliverStateHandler({
Key? key,
required this.state,
@@ -24,14 +25,16 @@ class SliverStateHandler extends SliverList {
this.emptyState,
this.enableEmptyState = false,
this.centerEmptyState = true,
+ this.hasConstraints = false,
}) : super(
key: key,
delegate: SliverChildBuilderDelegate(
(context, index) {
+ final deviceHight = MediaQuery.of(context).size.height;
if (state.appState == AppState.failed) {
return Padding(
padding: EdgeInsets.only(
- top: centerEmptyState ? 120 : 20,
+ top: centerEmptyState ? deviceHight / 4 : deviceHight / 8,
bottom: 20,
),
child: EmptyConnection(onRetry: onRetry),
@@ -40,9 +43,7 @@ class SliverStateHandler extends SliverList {
if (enableEmptyState && state.appState == AppState.idle) {
return Padding(
padding: EdgeInsets.only(
- top: centerEmptyState
- ? MediaQuery.of(context).size.height / 4
- : 20,
+ top: centerEmptyState ? deviceHight / 4 : deviceHight / 8,
bottom: 20,
),
child: emptyState,
diff --git a/lib/views/widgets/state_handlers/state_handler.dart b/lib/views/widgets/state_handlers/state_handler.dart
index 0addd30..c54c651 100644
--- a/lib/views/widgets/state_handlers/state_handler.dart
+++ b/lib/views/widgets/state_handlers/state_handler.dart
@@ -38,10 +38,13 @@ class StateHandler extends StatelessWidget {
case AppState.idle:
return builder(context, state);
case AppState.busy:
- return placeholder ??
- SpinKitSpinningLines(
- color: Theme.of(context).colorScheme.primary,
- );
+ return Padding(
+ padding: EdgeInsets.only(top: topPadding),
+ child: placeholder ??
+ SpinKitSpinningLines(
+ color: Theme.of(context).colorScheme.primary,
+ ),
+ );
case AppState.failed:
return Center(child: EmptyConnection(onRetry: onRetry));
default:
diff --git a/pubspec.lock b/pubspec.lock
index f08bc28..3823c80 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -141,6 +141,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.5"
+ equatable:
+ dependency: transitive
+ description:
+ name: equatable
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "2.0.3"
expandable_bottom_sheet:
dependency: "direct main"
description:
@@ -175,7 +182,7 @@ packages:
name: firebase_core
url: "https://pub.dartlang.org"
source: hosted
- version: "1.14.0"
+ version: "1.15.0"
firebase_core_platform_interface:
dependency: transitive
description:
@@ -189,28 +196,35 @@ packages:
name: firebase_core_web
url: "https://pub.dartlang.org"
source: hosted
- version: "1.6.1"
+ version: "1.6.2"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
url: "https://pub.dartlang.org"
source: hosted
- version: "11.2.12"
+ version: "11.2.14"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
url: "https://pub.dartlang.org"
source: hosted
- version: "3.2.2"
+ version: "3.3.0"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
url: "https://pub.dartlang.org"
source: hosted
- version: "2.2.10"
+ version: "2.2.12"
+ fl_chart:
+ dependency: "direct main"
+ description:
+ name: fl_chart
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.50.1"
flutter:
dependency: "direct main"
description: flutter
@@ -222,7 +236,7 @@ packages:
name: flutter_blurhash
url: "https://pub.dartlang.org"
source: hosted
- version: "0.6.4"
+ version: "0.6.8"
flutter_cache_manager:
dependency: transitive
description:
@@ -236,7 +250,7 @@ packages:
name: flutter_html
url: "https://pub.dartlang.org"
source: hosted
- version: "3.0.0-alpha.2"
+ version: "3.0.0-alpha.3"
flutter_lints:
dependency: "direct dev"
description:
@@ -384,6 +398,13 @@ packages:
name: image_picker
url: "https://pub.dartlang.org"
source: hosted
+ version: "0.8.5"
+ image_picker_android:
+ dependency: transitive
+ description:
+ name: image_picker_android
+ url: "https://pub.dartlang.org"
+ source: hosted
version: "0.8.4+11"
image_picker_for_web:
dependency: transitive
@@ -392,6 +413,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.6"
+ image_picker_ios:
+ dependency: transitive
+ description:
+ name: image_picker_ios
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.8.4+11"
image_picker_platform_interface:
dependency: transitive
description:
@@ -601,7 +629,7 @@ packages:
name: pin_code_fields
url: "https://pub.dartlang.org"
source: hosted
- version: "7.3.0"
+ version: "7.4.0"
platform:
dependency: transitive
description:
@@ -690,14 +718,14 @@ packages:
name: sqflite
url: "https://pub.dartlang.org"
source: hosted
- version: "2.0.2"
+ version: "2.0.2+1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
url: "https://pub.dartlang.org"
source: hosted
- version: "2.2.1"
+ version: "2.2.1+1"
stack_trace:
dependency: transitive
description:
@@ -879,14 +907,14 @@ packages:
name: webview_flutter
url: "https://pub.dartlang.org"
source: hosted
- version: "3.0.1"
+ version: "3.0.2"
webview_flutter_android:
dependency: transitive
description:
name: webview_flutter_android
url: "https://pub.dartlang.org"
source: hosted
- version: "2.8.4"
+ version: "2.8.5"
webview_flutter_platform_interface:
dependency: transitive
description:
@@ -900,7 +928,7 @@ packages:
name: webview_flutter_wkwebview
url: "https://pub.dartlang.org"
source: hosted
- version: "2.7.1"
+ version: "2.7.2"
win32:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index dfc7dfa..31924b7 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 1.5.0+10
+version: 2.0.0+11
environment:
sdk: ">=2.12.0 <3.0.0"
@@ -66,6 +66,7 @@ dependencies:
permission_handler: ^9.2.0
better_player: ^0.0.81
assets_audio_player: ^3.0.4+1
+ fl_chart: ^0.50.1
dev_dependencies: