last version 3.3.0 houshvan

This commit is contained in:
OkaykOrhmn 2024-09-16 17:05:18 +03:30
parent 9ea80f21fb
commit 1708bcffc8
37 changed files with 582 additions and 462 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

View File

@ -42,6 +42,7 @@ class Assets {
static String get studioLogo => '$_baseLogosPath/studio-$_themeSuffix.svg'; static String get studioLogo => '$_baseLogosPath/studio-$_themeSuffix.svg';
static String loadingAnimation = '$_baseAnimationsPath/loading.gif'; static String loadingAnimation = '$_baseAnimationsPath/loading.gif';
static String bookmarkAnimation = '$_baseAnimationsPath/bookmark.gif';
static String get businessCategoryIcon => static String get businessCategoryIcon =>
'$_baseCategoriesPath/business-$_themeSuffix.svg'; '$_baseCategoriesPath/business-$_themeSuffix.svg';

View File

@ -61,7 +61,6 @@ Future<void> _backgroundCallbackHomeWidget(Uri? uri) async {
} }
void main() async { void main() async {
await runZonedGuarded(() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
try { try {
@ -80,9 +79,6 @@ void main() async {
} }
runApp(const Didvan()); runApp(const Didvan());
}, (error, stack) async {
error.printError();
});
} }
class Didvan extends StatefulWidget { class Didvan extends StatefulWidget {

View File

@ -23,6 +23,7 @@ class Content {
final String createdAt; final String createdAt;
bool marked; bool marked;
bool liked; bool liked;
int likes;
final List<Tag> tags; final List<Tag> tags;
@ -30,6 +31,7 @@ class Content {
{required this.id, {required this.id,
required this.marked, required this.marked,
required this.liked, required this.liked,
required this.likes,
required this.createdAt, required this.createdAt,
required this.title, required this.title,
required this.image, required this.image,
@ -41,6 +43,7 @@ class Content {
id: json['id'], id: json['id'],
marked: json['marked'], marked: json['marked'],
liked: json['liked'], liked: json['liked'],
likes: json['likes'],
createdAt: json['createdAt'], createdAt: json['createdAt'],
title: json['title'], title: json['title'],
image: json['image'], image: json['image'],

View File

@ -10,6 +10,7 @@ class NewsDetailsData {
final String createdAt; final String createdAt;
bool marked; bool marked;
bool liked; bool liked;
int likes;
int comments; int comments;
final int order; final int order;
final List<Tag> tags; final List<Tag> tags;
@ -25,6 +26,7 @@ class NewsDetailsData {
required this.createdAt, required this.createdAt,
required this.marked, required this.marked,
required this.liked, required this.liked,
required this.likes,
required this.comments, required this.comments,
required this.tags, required this.tags,
required this.contents, required this.contents,
@ -41,6 +43,7 @@ class NewsDetailsData {
createdAt: json['createdAt'], createdAt: json['createdAt'],
marked: json['marked'], marked: json['marked'],
liked: json['liked'] ?? false, liked: json['liked'] ?? false,
likes: json['likes'],
comments: json['comments'], comments: json['comments'],
order: json['order'], order: json['order'],
tags: List<Tag>.from(json['tags'].map((tag) => Tag.fromJson(tag))), tags: List<Tag>.from(json['tags'].map((tag) => Tag.fromJson(tag))),
@ -59,6 +62,7 @@ class NewsDetailsData {
'createdAt': createdAt, 'createdAt': createdAt,
'marked': marked, 'marked': marked,
'liked': liked, 'liked': liked,
'likes': likes,
'comments': comments, 'comments': comments,
'tags': tags.map((e) => e.toJson()).toList(), 'tags': tags.map((e) => e.toJson()).toList(),
'contents': contents.map((e) => e.toJson()).toList(), 'contents': contents.map((e) => e.toJson()).toList(),

View File

@ -20,6 +20,7 @@ class OverviewData {
int comments; int comments;
bool marked; bool marked;
bool liked; bool liked;
int likes;
final List<CategoryData>? categories; final List<CategoryData>? categories;
OverviewData({ OverviewData({
@ -31,6 +32,7 @@ class OverviewData {
required this.type, required this.type,
required this.marked, required this.marked,
required this.liked, required this.liked,
required this.likes,
required this.comments, required this.comments,
required this.forManagers, required this.forManagers,
this.category, this.category,
@ -87,6 +89,7 @@ class OverviewData {
type: json['type'] ?? '', type: json['type'] ?? '',
marked: json['marked'] ?? true, marked: json['marked'] ?? true,
liked: json['liked'] ?? true, liked: json['liked'] ?? true,
likes: json['likes'] ?? 0,
link: json['link'], link: json['link'],
iframe: json['iframe'], iframe: json['iframe'],
categories: json['categories'] != null categories: json['categories'] != null

View File

@ -4,8 +4,8 @@ class ActionSheetData {
final Widget content; final Widget content;
final String? confrimTitle; final String? confrimTitle;
final String? dismissTitle; final String? dismissTitle;
final VoidCallback? onConfirmed; final Function()? onConfirmed;
final VoidCallback? onDismissed; final Function()? onDismissed;
final String? title; final String? title;
final bool hasPadding; final bool hasPadding;
final IconData? titleIcon; final IconData? titleIcon;

View File

@ -132,7 +132,9 @@ class RouteGenerator {
ChangeNotifierProvider<NewStatisticState>( ChangeNotifierProvider<NewStatisticState>(
create: (context) => NewStatisticState()) create: (context) => NewStatisticState())
], ],
child: const Home(), child: Home(
showDialogs: settings.arguments as bool?,
),
), ),
); );
case Routes.editProfile: case Routes.editProfile:

View File

@ -20,7 +20,7 @@ class FirebaseApi {
e.printError(); e.printError();
} }
await _firebaseMessaging.requestPermission( _firebaseMessaging.requestPermission(
alert: true, alert: true,
announcement: true, announcement: true,
badge: true, badge: true,

View File

@ -169,8 +169,8 @@ class ActionSheetUtils {
child: DidvanButton( child: DidvanButton(
style: ButtonStyleMode.primary, style: ButtonStyleMode.primary,
onPressed: () { onPressed: () {
Navigator.of(context).pop();
data.onConfirmed?.call(); data.onConfirmed?.call();
pop();
}, },
title: data.confrimTitle ?? 'تایید', title: data.confrimTitle ?? 'تایید',
), ),
@ -251,7 +251,10 @@ class ActionSheetUtils {
if (data.hasDismissButton) if (data.hasDismissButton)
Expanded( Expanded(
child: DidvanButton( child: DidvanButton(
onPressed: data.onDismissed ?? () => pop(), onPressed: () {
data.onDismissed?.call();
pop();
},
title: data.dismissTitle ?? 'بازگشت', title: data.dismissTitle ?? 'بازگشت',
style: ButtonStyleMode.flat, style: ButtonStyleMode.flat,
), ),

View File

@ -58,7 +58,7 @@ class _AiState extends State<Ai> {
Column( Column(
children: [ children: [
const SizedBox( const SizedBox(
height: 12, height: 24,
), ),
Icon( Icon(
DidvanIcons.ai_solid, DidvanIcons.ai_solid,
@ -134,16 +134,6 @@ class _AiState extends State<Ai> {
const SizedBox( const SizedBox(
width: 8, width: 8,
), ),
Container(
width: 32,
height: 32,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.border),
child: const Icon(
DidvanIcons.mic_regular,
),
),
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(

View File

@ -409,9 +409,13 @@ class _AiChatPageState extends State<AiChatPage> {
}, },
); );
}), }),
SpinKitThreeBounce( Padding(
padding:
const EdgeInsets.symmetric(vertical: 8.0),
child: SpinKitThreeBounce(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
size: 18, size: 18,
),
) )
], ],
) )

View File

@ -34,11 +34,6 @@ class AiChatState extends CoreProvier {
FilesModel? file; FilesModel? file;
TextEditingController message = TextEditingController(); TextEditingController message = TextEditingController();
@override
void dispose() {
super.dispose();
}
Future<void> _scrolledEnd() async { Future<void> _scrolledEnd() async {
WidgetsBinding.instance.addPostFrameCallback((_) async { WidgetsBinding.instance.addPostFrameCallback((_) async {
await scrollController.animateTo( await scrollController.animateTo(
@ -166,12 +161,8 @@ class AiChatState extends CoreProvier {
file = null; file = null;
update(); update();
final stream = res final r = res.listen((value) async {
.transform(utf8.decoder) var str = utf8.decode(value);
.transform(const LineSplitter()); // <--- Add this line
final r = stream.listen((str) {
// var str = utf8.decode(value);
if (!kIsWeb) { if (!kIsWeb) {
if (str.contains('{{{')) { if (str.contains('{{{')) {
dataMessgae += str; dataMessgae += str;
@ -197,6 +188,7 @@ class AiChatState extends CoreProvier {
} }
} }
messageOnstream.value = Stream.value(responseMessgae); messageOnstream.value = Stream.value(responseMessgae);
print("responseMessgae: $str"); print("responseMessgae: $str");
// update(); // update();
}); });

View File

@ -27,7 +27,6 @@ import 'package:get/get.dart';
import 'package:image_cropper/image_cropper.dart'; import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:persian_number_utility/persian_number_utility.dart'; import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:record/record.dart'; import 'package:record/record.dart';
@ -161,23 +160,25 @@ class _AiMessageBarState extends State<AiMessageBar> {
FilePickerResult? result = FilePickerResult? result =
await MediaService.pickPdfFile(); await MediaService.pickPdfFile();
if (result != null) { if (result != null) {
// if (kIsWeb) { if (kIsWeb) {
// Uint8List bytes = result.files.first Uint8List? bytes = result.files.first
// .bytes!; // Access the bytes property .bytes; // Access the bytes property
String? name = result.files.first.name;
// File file = File.fromRawPath(bytes); // Store bytes and file name directly in your state or model
// state.file = FilesModel(file.path, state.file = FilesModel(
// name: result.files.first.name, '', // No need for a file path on web
// bytes: bytes, name: name,
// audio: false, bytes: bytes,
// image: false); audio: false,
// print(result.files.first.name); image: false,
// } else { );
} else {
state.file = FilesModel( state.file = FilesModel(
result.files.single.path!, result.files.single.path!,
audio: false, audio: false,
image: false); image: false);
// } }
openAttach = false; openAttach = false;
} }
@ -243,6 +244,7 @@ class _AiMessageBarState extends State<AiMessageBar> {
); );
}, },
), ),
if (!kIsWeb || !Platform.isIOS)
if (historyState.bot!.attachmentType! if (historyState.bot!.attachmentType!
.contains('audio')) .contains('audio'))
attachBtn( attachBtn(
@ -319,7 +321,8 @@ class _AiMessageBarState extends State<AiMessageBar> {
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
bottom: 8.0), bottom: 8.0),
child: (snapshot.hasData && child: (!kIsWeb &&
snapshot.hasData &&
snapshot.data! != snapshot.data! !=
RecordState.stop) RecordState.stop)
? MessageBarBtn( ? MessageBarBtn(
@ -342,7 +345,9 @@ class _AiMessageBarState extends State<AiMessageBar> {
state.update(); state.update();
}, },
) )
: widget.bot.attachmentType! : (!kIsWeb || !Platform.isIOS) &&
widget.bot
.attachmentType!
.contains( .contains(
'audio') && 'audio') &&
value.isEmpty && value.isEmpty &&
@ -523,11 +528,23 @@ class _AiMessageBarState extends State<AiMessageBar> {
mainAxisAlignment: mainAxisAlignment:
MainAxisAlignment MainAxisAlignment
.center, .center,
children: List children: List.generate(
.generate( 4,
3, (index) => snapshot.data! == RecordState.pause
(index) => ? Row(
SpinKitWave( mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
8,
(index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 1.0, vertical: 12),
child: Container(
width: 3,
height: 8,
decoration: BoxDecoration(color: Theme.of(context).colorScheme.primary.withOpacity(0.4)),
),
)),
)
: SpinKitWave(
color: Theme.of(context).colorScheme.primary.withOpacity(0.4), color: Theme.of(context).colorScheme.primary.withOpacity(0.4),
size: 32, size: 32,
itemCount: 10, itemCount: 10,

View File

@ -11,6 +11,7 @@ import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
@ -35,6 +36,7 @@ class _AudioWaveState extends State<AudioWave> {
double currentPosition = 0; double currentPosition = 0;
bool loading = true; bool loading = true;
bool faile = false; bool faile = false;
bool onChanging = false;
@override @override
void initState() { void initState() {
@ -56,7 +58,7 @@ class _AudioWaveState extends State<AudioWave> {
void setRandoms() { void setRandoms() {
for (var i = 0; i < itemCount; i++) { for (var i = 0; i < itemCount; i++) {
randoms.value.add(0); randoms.value.add(0);
randomsDisable.value.add(2 + Random().nextDouble() * (42 - 2)); randomsDisable.value.add(1 + Random().nextDouble() * (38 - 1));
} }
} }
@ -94,7 +96,7 @@ class _AudioWaveState extends State<AudioWave> {
Future<void> listeners() async { Future<void> listeners() async {
audioPlayer.positionStream.listen((position) async { audioPlayer.positionStream.listen((position) async {
if (randomsDisable.value.isEmpty) return; if (randomsDisable.value.isEmpty || onChanging) return;
try { try {
for (var i = 0; i < itemCount; i++) { for (var i = 0; i < itemCount; i++) {
@ -219,12 +221,9 @@ class _AudioWaveState extends State<AudioWave> {
.primary .primary
.withOpacity(0.4)), .withOpacity(0.4)),
if (totalDuration != Duration.zero) if (totalDuration != Duration.zero)
Opacity( Positioned.fill(
child: Opacity(
opacity: 0, opacity: 0,
child: Container(
width: 12,
color: Colors.transparent
.withOpacity(1),
child: Theme( child: Theme(
data: Theme.of(context) data: Theme.of(context)
.copyWith( .copyWith(
@ -252,22 +251,22 @@ class _AudioWaveState extends State<AudioWave> {
// audioPlayer.pause(); // audioPlayer.pause();
}, },
onChanged: (value) { onChanged: (value) {
for (var i = 0; // for (var i = 0;
i < itemCount; // i < itemCount;
i++) { // i++) {
if (i < // if (i <
((value * 40) / // ((value * 40) /
totalDuration // totalDuration
.inMilliseconds)) { // .inMilliseconds)) {
final ran = // final ran =
randomsDisable // randomsDisable
.value[i]; // .value[i];
randoms.value[i] = // randoms.value[i] =
ran; // ran;
} else { // } else {
randoms.value[i] = 0; // randoms.value[i] = 0;
} // }
} // }
setState(() { setState(() {
currentPosition = value; currentPosition = value;
}); });

View File

@ -80,78 +80,11 @@ class _PasswordInputState extends State<PasswordInput> {
await ServerDataProvider.getData(); await ServerDataProvider.getData();
if (mounted) { if (mounted) {
Future.delayed(Duration.zero, await Future.delayed(
() => Navigator.of(context).pushReplacementNamed(Routes.home)); Duration.zero,
} () => Navigator.of(context)
.pushReplacementNamed(Routes.home, arguments: true));
_showResetPasswordDialog();
} }
} }
void _showResetPasswordDialog() {
ActionSheetUtils(context).openDialog(
data: ActionSheetData(
content: const DidvanText(
'خوش آمدید!\nبرای امنیت بیشتر، رمز عبور خود را تغییر دهید.',
),
onConfirmed: () => Navigator.of(navigatorKey.currentContext!).pushNamed(
Routes.authenticaion,
arguments: true,
),
isBackgroundDropBlur: false,
confrimTitle: 'تغییر رمز عبور',
onDismissed: Navigator.of(navigatorKey.currentContext!).pop,
dismissTitle: 'بعدا',
),
);
_showCustomizeDialog();
}
void _showCustomizeDialog() {
ActionSheetUtils(context).openDialog(
data: ActionSheetData(
backgroundColor: Theme.of(context).colorScheme.background,
isBackgroundDropBlur: true,
content: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWrapper(
onPressed: Navigator.of(navigatorKey.currentContext!).pop,
child: const Icon(
DidvanIcons.close_solid,
size: 24,
),
),
DidvanText(
'شخصی سازی محتوا',
style: Theme.of(context).textTheme.displaySmall,
color: Theme.of(context).colorScheme.text,
),
const InkWrapper(
child: Icon(
DidvanIcons.close_regular,
size: 24,
color: Colors.transparent,
),
),
],
),
const SizedBox(
height: 12,
),
const DidvanText(
"کاربر گرامی\nلطفا جهت شخصی‌سازی و استفاده بهتر از برنامه، دسته‌بندی‌های مورد علاقه خود و زمان دریافت اعلانات را انتخاب نمایید.")
],
),
hasDismissButton: false,
onConfirmed: () => Navigator.of(navigatorKey.currentContext!).pushNamed(
Routes.favouritesStep,
arguments: {"toTimer": true},
),
confrimTitle: 'تایید',
),
);
} }
} }

View File

@ -51,7 +51,7 @@ class _UsernameInputState extends State<UsernameInput> {
Padding( Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: DidvanText( child: DidvanText(
'نام کاربری می‌تواند شامل کاراکترهای کوچک و بزرگ انگلیسی و اعداد باشد.', 'نام کاربری می‌تواند شامل کاراکترهای انگلیسی و اعداد باشد.',
style: Theme.of(context).textTheme.labelSmall, style: Theme.of(context).textTheme.labelSmall,
), ),
), ),

View File

@ -74,6 +74,7 @@ class _HashtagState extends State<Hashtag> {
liked: item.liked, liked: item.liked,
onLikedChanged: (_, value, __) => onLikedChanged: (_, value, __) =>
_changeLiked(item.id, value, 'banner'), _changeLiked(item.id, value, 'banner'),
likes: item.likes,
); );
case 'radar': case 'radar':

View File

@ -1,11 +1,14 @@
import 'dart:async'; import 'dart:async';
import 'package:didvan/config/design_config.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/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/routes/routes.dart'; import 'package:didvan/routes/routes.dart';
import 'package:didvan/views/home/bookmarks/bookmark_state.dart'; import 'package:didvan/views/home/bookmarks/bookmark_state.dart';
import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/menu_item.dart'; import 'package:didvan/views/widgets/menu_item.dart';
import 'package:didvan/views/widgets/overview/multitype.dart'; import 'package:didvan/views/widgets/overview/multitype.dart';
// import 'package:didvan/views/widgets/search_field.dart'; // import 'package:didvan/views/widgets/search_field.dart';
@ -124,7 +127,9 @@ class _BookmarksState extends State<Bookmarks> {
), ),
), ),
SliverPadding( SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(
horizontal: 16,
),
sliver: SliverStateHandler<BookmarksState>( sliver: SliverStateHandler<BookmarksState>(
state: state, state: state,
centerEmptyState: state.searching, centerEmptyState: state.searching,
@ -144,9 +149,24 @@ class _BookmarksState extends State<Bookmarks> {
}, },
placeholder: MultitypeOverview.placeholder, placeholder: MultitypeOverview.placeholder,
itemPadding: const EdgeInsets.only(bottom: 8), itemPadding: const EdgeInsets.only(bottom: 8),
paddingEmptyState: 0,
emptyState: state.searching emptyState: state.searching
? EmptyResult(onNewSearch: _focuseNode.requestFocus) ? EmptyResult(onNewSearch: _focuseNode.requestFocus)
: const EmptyList(), : Column(
children: [
DidvanText(
'در قسمت رصدخانه من، تمامی محتواهایی که در قسمت‌های مختلف سوپراپلیکیشن دیدوان، بوکمارک (نشان‌دار) کرده‌اید، به تفکیک نمایش داده می‌شوند. هم‌چنین امکان درج یادداشت شخصی بصورت ضمیمه برای هر محتوا وجود دارد.',
fontSize: 14,
color: Theme.of(context).colorScheme.title,
textAlign: TextAlign.justify,
),
Image.asset(
Assets.bookmarkAnimation,
width: MediaQuery.sizeOf(context).width,
height: 180,
),
],
),
enableEmptyState: state.bookmarks.isEmpty, enableEmptyState: state.bookmarks.isEmpty,
childCount: childCount:
state.bookmarks.length + (state.page != state.lastPage ? 1 : 0), state.bookmarks.length + (state.page != state.lastPage ? 1 : 0),

View File

@ -27,6 +27,7 @@ import 'package:didvan/views/home/new_statistic/new_statistic.dart';
import 'package:didvan/views/home/search/search.dart'; import 'package:didvan/views/home/search/search.dart';
import 'package:didvan/views/widgets/didvan/divider.dart'; import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/logo_app_bar.dart'; import 'package:didvan/views/widgets/logo_app_bar.dart';
import 'package:didvan/views/widgets/didvan/bnb.dart'; import 'package:didvan/views/widgets/didvan/bnb.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart';
@ -35,13 +36,15 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../services/app_home_widget/home_widget_repository.dart'; import '../../services/app_home_widget/home_widget_repository.dart';
final GlobalKey<ScaffoldState> homeScaffKey = GlobalKey<ScaffoldState>(); final GlobalKey<ScaffoldState> homeScaffKey = GlobalKey<ScaffoldState>();
class Home extends StatefulWidget { class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key); final bool? showDialogs;
const Home({Key? key, this.showDialogs}) : super(key: key);
@override @override
State<Home> createState() => _HomeState(); State<Home> createState() => _HomeState();
@ -51,8 +54,90 @@ class _HomeState extends State<Home>
with SingleTickerProviderStateMixin, WidgetsBindingObserver { with SingleTickerProviderStateMixin, WidgetsBindingObserver {
late final TabController _tabController; late final TabController _tabController;
Future<void> _showDialog(BuildContext context) async {
WidgetsBinding.instance?.addPostFrameCallback((_) {
ActionSheetUtils(context)
.openDialog(
data: ActionSheetData(
content: const DidvanText(
'خوش آمدید!\nبرای امنیت بیشتر، رمز عبور خود را تغییر دهید.',
),
onConfirmed: () {
Future.delayed(
Duration.zero,
() => Navigator.of(context)
.pushNamed(Routes.authenticaion, arguments: true),
);
},
isBackgroundDropBlur: false,
confrimTitle: 'تغییر رمز عبور',
dismissTitle: 'بعدا',
),
)
.then((value) => ActionSheetUtils(context).openDialog(
data: ActionSheetData(
backgroundColor: Theme.of(context).colorScheme.background,
isBackgroundDropBlur: true,
content: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWrapper(
onPressed: () {
Future.delayed(
Duration.zero,
() => Navigator.of(context).pop(),
);
},
child: const Icon(
DidvanIcons.close_solid,
size: 24,
),
),
DidvanText(
'شخصی سازی محتوا',
style: Theme.of(context).textTheme.displaySmall,
color: Theme.of(context).colorScheme.text,
),
const InkWrapper(
child: Icon(
DidvanIcons.close_regular,
size: 24,
color: Colors.transparent,
),
),
],
),
const SizedBox(
height: 12,
),
const DidvanText(
"کاربر گرامی\nلطفا جهت شخصی‌سازی و استفاده بهتر از برنامه، دسته‌بندی‌های مورد علاقه خود و زمان دریافت اعلانات را انتخاب نمایید.")
],
),
// hasDismissButton: false,
onConfirmed: () {
Future.delayed(
Duration.zero,
() =>
Navigator.of(navigatorKey.currentContext!).pushNamed(
Routes.favouritesStep,
arguments: {"toTimer": true},
),
);
},
confrimTitle: 'تایید',
),
));
});
}
@override @override
void initState() { void initState() {
if (widget.showDialogs ?? false) {
_showDialog(context);
}
if (!kIsWeb) { if (!kIsWeb) {
NotificationService.startListeningNotificationEvents(); NotificationService.startListeningNotificationEvents();
} }

View File

@ -142,6 +142,7 @@ class _InfographyScreenState extends State<InfographyScreen> {
Wrap( Wrap(
children: [ children: [
for (var i = 0; i < state.categories.length; i++) for (var i = 0; i < state.categories.length; i++)
if (state.categories[i].label != 'هوشان')
SizedBox( SizedBox(
width: (MediaQuery.of(context).size.width - 40) / 2, width: (MediaQuery.of(context).size.width - 40) / 2,
child: DidvanCheckbox( child: DidvanCheckbox(
@ -232,6 +233,7 @@ class _InfographyScreenState extends State<InfographyScreen> {
liked: state.contents[index].liked, liked: state.contents[index].liked,
onLikedChanged: (id, value, _) => onLikedChanged: (id, value, _) =>
state.changeLiked(id, value), state.changeLiked(id, value),
likes: state.contents[index].likes,
), ),
), ),
), ),

View File

@ -58,6 +58,7 @@ class InfographyItem extends StatelessWidget {
final int id; final int id;
final bool marked; final bool marked;
final bool liked; final bool liked;
final int likes;
final void Function(int id, bool value, bool shouldUpdate) onMarkChanged; final void Function(int id, bool value, bool shouldUpdate) onMarkChanged;
final void Function(int id, bool value, bool shouldUpdate) onLikedChanged; final void Function(int id, bool value, bool shouldUpdate) onLikedChanged;
@ -72,6 +73,7 @@ class InfographyItem extends StatelessWidget {
required this.id, required this.id,
required this.marked, required this.marked,
required this.liked, required this.liked,
required this.likes,
required this.onLikedChanged}); required this.onLikedChanged});
void _openInteractiveViewer(BuildContext context, String image) { void _openInteractiveViewer(BuildContext context, String image) {
@ -152,6 +154,17 @@ class InfographyItem extends StatelessWidget {
), ),
Row( Row(
children: [ children: [
LikedButton(
itemId: id,
type: 'infography',
gestureSize: 32,
value: liked,
onMarkChanged: (value) => onLikedChanged(id, value, true),
likes: likes,
),
const SizedBox(
width: 4.0,
),
DidvanIconButton( DidvanIconButton(
gestureSize: 32, gestureSize: 32,
onPressed: () => Navigator.of(context).pushNamed( onPressed: () => Navigator.of(context).pushNamed(
@ -164,19 +177,6 @@ class InfographyItem extends StatelessWidget {
), ),
icon: DidvanIcons.mention_icon, icon: DidvanIcons.mention_icon,
), ),
const SizedBox(
width: 8.0,
),
LikedButton(
itemId: id,
type: 'infography',
gestureSize: 32,
value: liked,
onMarkChanged: (value) => onLikedChanged(id, value, true),
),
const SizedBox(
width: 8.0,
),
BookmarkButton( BookmarkButton(
itemId: id, itemId: id,
type: 'infography', type: 'infography',

View File

@ -50,6 +50,7 @@ class SearchPage extends StatelessWidget {
isColapsed: state.selectedCats.length <= 1, isColapsed: state.selectedCats.length <= 1,
selectedCats: state.selectedCats, selectedCats: state.selectedCats,
categories: state.categoryFilters, categories: state.categoryFilters,
disableHoushan: true,
onSelected: (id) { onSelected: (id) {
state.selectedCats.clear(); state.selectedCats.clear();
final cat = state.categoryFilters final cat = state.categoryFilters

View File

@ -70,7 +70,18 @@ class _MentionsState extends State<Mentions> {
} }
} }
_bottomPadding = bottomViewInset; _bottomPadding = bottomViewInset;
return Material( return WillPopScope(
onWillPop: () async {
if (mentionsState.showUsersForMentionsLayout) {
mentionsState.showUsersForMentionsLayout = false;
mentionsState.searchUsers.text = '';
mentionsState.update();
return false;
}
return true;
},
child: Material(
child: Stack( child: Stack(
children: [ children: [
DidvanScaffold( DidvanScaffold(
@ -119,9 +130,12 @@ class _MentionsState extends State<Mentions> {
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0), filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: color: Theme.of(context)
Theme.of(context).colorScheme.focused.withOpacity(0.5)), .colorScheme
.focused
.withOpacity(0.5)),
child: DidvanScaffold( child: DidvanScaffold(
hidePlayer: true,
appBarData: null, appBarData: null,
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 16, right: 16, top: 16, bottom: 92), left: 16, right: 16, top: 16, bottom: 92),
@ -182,7 +196,8 @@ class _MentionsState extends State<Mentions> {
DidvanIconButton( DidvanIconButton(
icon: DidvanIcons.close_regular, icon: DidvanIcons.close_regular,
onPressed: () { onPressed: () {
mentionsState.showUsersForMentionsLayout = false; mentionsState.showUsersForMentionsLayout =
false;
mentionsState.searchUsers.text = ''; mentionsState.searchUsers.text = '';
mentionsState.update(); mentionsState.update();
@ -215,6 +230,7 @@ class _MentionsState extends State<Mentions> {
), ),
], ],
), ),
),
); );
} }
} }

View File

@ -80,11 +80,9 @@ class _StudioSliderState extends State<StudioSlider> {
horizontal: 8, horizontal: 8,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
color: (state.videosSelected color: Theme.of(context)
? Theme.of(context)
.colorScheme .colorScheme
.secondaryDisabled .focused
: Theme.of(context).colorScheme.focused)
.withOpacity(0.9), .withOpacity(0.9),
borderRadius: const BorderRadius.vertical( borderRadius: const BorderRadius.vertical(
bottom: Radius.circular(10), bottom: Radius.circular(10),
@ -103,9 +101,11 @@ class _StudioSliderState extends State<StudioSlider> {
width: 52, width: 52,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: Theme.of(context) color: (state.videosSelected
? Theme.of(context).colorScheme.secondary
: Theme.of(context)
.colorScheme .colorScheme
.secondary .focusedBorder)
.withOpacity(0.7), .withOpacity(0.7),
), ),
child: Icon( child: Icon(
@ -155,13 +155,6 @@ class _SliderIndicator extends StatelessWidget {
required this.isVideo, required this.isVideo,
}) : super(key: key); }) : super(key: key);
Color _color(BuildContext context) {
if (isVideo) {
return Theme.of(context).colorScheme.secondary;
}
return Theme.of(context).colorScheme.focusedBorder;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AnimatedContainer( return AnimatedContainer(
@ -171,10 +164,11 @@ class _SliderIndicator extends StatelessWidget {
margin: const EdgeInsets.only(left: 4), margin: const EdgeInsets.only(left: 4),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(
color: _color(context), color: Theme.of(context).colorScheme.focusedBorder,
), ),
shape: BoxShape.circle, shape: BoxShape.circle,
color: isCurrentIndex ? _color(context) : null, color:
isCurrentIndex ? Theme.of(context).colorScheme.focusedBorder : null,
), ),
); );
} }

View File

@ -29,7 +29,7 @@ class StudioTabBar extends StatelessWidget {
Expanded( Expanded(
child: _StudioTypeButton( child: _StudioTypeButton(
icon: DidvanIcons.video_solid, icon: DidvanIcons.video_solid,
selectedColor: Theme.of(context).colorScheme.secondary, selectedColor: Theme.of(context).colorScheme.focusedBorder,
title: 'ویدیو', title: 'ویدیو',
onTap: () => state.videosSelected = true, onTap: () => state.videosSelected = true,
isSelected: state.videosSelected, isSelected: state.videosSelected,

View File

@ -347,7 +347,7 @@ class _ProfilePageState extends State<ProfilePage> {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
DidvanText( DidvanText(
'نسخه نرم‌افزار: 3.2.2', 'نسخه نرم‌افزار: 3.3.0',
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall,
), ),
], ],

View File

@ -1,6 +1,7 @@
// ignore_for_file: library_private_types_in_public_api, deprecated_member_use // ignore_for_file: library_private_types_in_public_api, deprecated_member_use
import 'package:didvan/constants/assets.dart'; import 'package:didvan/constants/assets.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart'; import 'package:webview_flutter/webview_flutter.dart';
@ -62,7 +63,18 @@ class _WebViewState extends State<WebView> {
return true; return true;
} }
}, },
child: loading child: Scaffold(
appBar: AppBar(
title: const Padding(
padding: EdgeInsets.only(top: 12.0),
child: DidvanText(
'بازگشت به دیدوان',
fontSize: 18,
),
),
toolbarHeight: 32,
),
body: loading
? Center( ? Center(
child: Image.asset( child: Image.asset(
Assets.loadingAnimation, Assets.loadingAnimation,
@ -70,6 +82,7 @@ class _WebViewState extends State<WebView> {
height: 60, height: 60,
), ),
) )
: WebViewWidget(controller: controller)); : WebViewWidget(controller: controller),
));
} }
} }

View File

@ -10,6 +10,7 @@ class CategoriesList extends StatefulWidget {
final bool isAppBar; final bool isAppBar;
final List<CategoryData> selectedCats; final List<CategoryData> selectedCats;
final List<CategoryData> categories; final List<CategoryData> categories;
final bool disableHoushan;
final void Function(int id) onSelected; final void Function(int id) onSelected;
final double top; final double top;
const CategoriesList({ const CategoriesList({
@ -19,6 +20,7 @@ class CategoriesList extends StatefulWidget {
required this.categories, required this.categories,
required this.onSelected, required this.onSelected,
this.isAppBar = true, this.isAppBar = true,
this.disableHoushan = false,
required this.top, required this.top,
}) : super(key: key); }) : super(key: key);
@ -69,7 +71,9 @@ class _CategoriesListState extends State<CategoriesList> {
context, context,
), ),
for (var i = 0; i < widget.categories.length; i++) for (var i = 0; i < widget.categories.length; i++)
_itemBuilder(widget.categories[i], context), widget.disableHoushan && widget.categories[i].label == 'هوشان'
? const SizedBox()
: _itemBuilder(widget.categories[i], context),
], ],
), ),
); );

View File

@ -5,6 +5,7 @@ import 'package:didvan/providers/user.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class LikedButton extends StatefulWidget { class LikedButton extends StatefulWidget {
@ -15,6 +16,7 @@ class LikedButton extends StatefulWidget {
final double gestureSize; final double gestureSize;
final String type; final String type;
final int itemId; final int itemId;
final int likes;
const LikedButton({ const LikedButton({
Key? key, Key? key,
required this.value, required this.value,
@ -22,6 +24,7 @@ class LikedButton extends StatefulWidget {
required this.gestureSize, required this.gestureSize,
required this.type, required this.type,
required this.itemId, required this.itemId,
required this.likes,
this.askForConfirmation = false, this.askForConfirmation = false,
this.color, this.color,
}) : super(key: key); }) : super(key: key);
@ -45,16 +48,23 @@ class _LikedButtonState extends State<LikedButton> {
super.initState(); super.initState();
} }
late int likes = widget.likes;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DidvanIconButton( return Row(
gestureSize: widget.gestureSize, crossAxisAlignment: CrossAxisAlignment.end,
color: widget.color ?? children: [
(DesignConfig.isDark || !_value if (likes != 0)
? null SizedBox(
: Theme.of(context).colorScheme.primary), height: 16,
icon: _value ? DidvanIcons.like_solid : DidvanIcons.like_regular, child: DidvanText(
onPressed: () async { likes.toString(),
fontSize: 14,
),
),
InkWell(
onTap: () async {
bool confirm = false; bool confirm = false;
if (widget.askForConfirmation) { if (widget.askForConfirmation) {
await ActionSheetUtils(context).openDialog( await ActionSheetUtils(context).openDialog(
@ -72,11 +82,24 @@ class _LikedButtonState extends State<LikedButton> {
if (!widget.askForConfirmation || confirm) { if (!widget.askForConfirmation || confirm) {
setState(() { setState(() {
_value = !_value; _value = !_value;
if (_value) {
likes += 1;
} else {
likes -= 1;
}
}); });
widget.onMarkChanged(_value); widget.onMarkChanged(_value);
UserProvider.changeItemLiked(widget.type, widget.itemId, _value); UserProvider.changeItemLiked(widget.type, widget.itemId, _value);
} }
}, },
child: Icon(
_value ? CupertinoIcons.heart_fill : CupertinoIcons.heart,
size: 24,
color: widget.color ??
(!_value ? null : Theme.of(context).colorScheme.error),
),
),
],
); );
} }
} }

View File

@ -193,12 +193,13 @@ class LogoAppBar extends StatelessWidget implements PreferredSizeWidget {
Wrap( Wrap(
children: [ children: [
for (var i = 0; i < state.categoryFilters.length; i++) for (var i = 0; i < state.categoryFilters.length; i++)
if (state.categoryFilters[i].label != 'هوشان')
SizedBox( SizedBox(
width: (MediaQuery.of(context).size.width - 40) / 2, width: (MediaQuery.of(context).size.width - 40) / 2,
child: DidvanCheckbox( child: DidvanCheckbox(
title: state.categoryFilters[i].label, title: state.categoryFilters[i].label,
value: value: state.selectedCats
state.selectedCats.contains(state.categoryFilters[i]), .contains(state.categoryFilters[i]),
onChanged: (value) { onChanged: (value) {
if (value) { if (value) {
state.selectedCats.add(state.categoryFilters[i]); state.selectedCats.add(state.categoryFilters[i]);

View File

@ -74,6 +74,7 @@ class MultitypeOverview extends StatelessWidget {
case 'podcast': case 'podcast':
return DidvanIcons.podcast_light; return DidvanIcons.podcast_light;
case 'delphi': case 'delphi':
case 'survey':
return DidvanIcons.saha_light; return DidvanIcons.saha_light;
case 'infography': case 'infography':
return DidvanIcons.infography_regular; return DidvanIcons.infography_regular;

View File

@ -93,6 +93,10 @@ class NewsOverview extends StatelessWidget {
onMarkChanged: (value) => onMarkChanged: (value) =>
onLikedChanged(news.id, value, false), onLikedChanged(news.id, value, false),
askForConfirmation: hasUnmarkConfirmation, askForConfirmation: hasUnmarkConfirmation,
likes: news.likes,
),
const SizedBox(
width: 4.0,
), ),
BookmarkButton( BookmarkButton(
itemId: news.id, itemId: news.id,

View File

@ -132,6 +132,10 @@ class RadarOverview extends StatelessWidget {
onMarkChanged: (value) => onMarkChanged: (value) =>
onLikedChanged(radar.id, value, false), onLikedChanged(radar.id, value, false),
askForConfirmation: hasUnmarkConfirmation, askForConfirmation: hasUnmarkConfirmation,
likes: radar.likes,
),
const SizedBox(
width: 4.0,
), ),
BookmarkButton( BookmarkButton(
itemId: radar.id, itemId: radar.id,

View File

@ -133,12 +133,13 @@ class SearchAppBar extends StatelessWidget implements PreferredSizeWidget {
Wrap( Wrap(
children: [ children: [
for (var i = 0; i < state.categoryFilters.length; i++) for (var i = 0; i < state.categoryFilters.length; i++)
if (state.categoryFilters[i].label != 'هوشان')
SizedBox( SizedBox(
width: (MediaQuery.of(context).size.width - 40) / 2, width: (MediaQuery.of(context).size.width - 40) / 2,
child: DidvanCheckbox( child: DidvanCheckbox(
title: state.categoryFilters[i].label, title: state.categoryFilters[i].label,
value: value: state.selectedCats
state.selectedCats.contains(state.categoryFilters[i]), .contains(state.categoryFilters[i]),
onChanged: (value) { onChanged: (value) {
if (value) { if (value) {
state.selectedCats.add(state.categoryFilters[i]); state.selectedCats.add(state.categoryFilters[i]);

View File

@ -15,6 +15,7 @@ class SliverStateHandler<T extends CoreProvier> extends SliverList {
final bool centerEmptyState; final bool centerEmptyState;
final bool hasConstraints; final bool hasConstraints;
final int placeholderCount; final int placeholderCount;
final double? paddingEmptyState;
SliverStateHandler({ SliverStateHandler({
Key? key, Key? key,
required this.state, required this.state,
@ -28,6 +29,7 @@ class SliverStateHandler<T extends CoreProvier> extends SliverList {
this.centerEmptyState = true, this.centerEmptyState = true,
this.hasConstraints = false, this.hasConstraints = false,
this.placeholderCount = 3, this.placeholderCount = 3,
this.paddingEmptyState,
}) : super( }) : super(
key: key, key: key,
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
@ -45,7 +47,8 @@ class SliverStateHandler<T extends CoreProvier> extends SliverList {
if (enableEmptyState && state.appState == AppState.idle) { if (enableEmptyState && state.appState == AppState.idle) {
return Padding( return Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
top: centerEmptyState ? deviceHight / 4 : deviceHight / 8, top: paddingEmptyState ??
(centerEmptyState ? deviceHight / 4 : deviceHight / 8),
bottom: 20, bottom: 20,
), ),
child: emptyState, child: emptyState,

View File

@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 3.2.2+3220 version: 3.3.0+3300
environment: environment:
sdk: ">=2.19.0 <3.0.0" sdk: ">=2.19.0 <3.0.0"