Merge branch 'dev' into 'master'
Dev See merge request Didvan/didvan-app!13
This commit is contained in:
commit
ce00f619a9
|
|
@ -1,13 +1,13 @@
|
||||||
kind: ExternalService
|
kind: ExternalService
|
||||||
name: app-test
|
name: app-dev
|
||||||
spec:
|
spec:
|
||||||
allow_http: false
|
allow_http: false
|
||||||
disable_default_domains: true
|
disable_default_domains: true
|
||||||
image: app:1.1.1
|
image: app-dev:1.1.4
|
||||||
image_pull_policy: IfNotPresent
|
image_pull_policy: IfNotPresent
|
||||||
path: /
|
path: /
|
||||||
replicas: 1
|
replicas: 1
|
||||||
resources:
|
resources:
|
||||||
memory: 150Mi
|
memory: 100Mi
|
||||||
domains:
|
domains:
|
||||||
- name: web.didvan.app
|
- name: dev.didvan.app
|
||||||
|
|
@ -88,6 +88,8 @@ PODS:
|
||||||
- TOCropViewController (2.6.1)
|
- TOCropViewController (2.6.1)
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- webview_flutter_wkwebview (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
- audio_session (from `.symlinks/plugins/audio_session/ios`)
|
||||||
|
|
@ -103,6 +105,7 @@ DEPENDENCIES:
|
||||||
- record (from `.symlinks/plugins/record/ios`)
|
- record (from `.symlinks/plugins/record/ios`)
|
||||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
|
|
@ -145,6 +148,8 @@ EXTERNAL SOURCES:
|
||||||
:path: ".symlinks/plugins/sqflite/ios"
|
:path: ".symlinks/plugins/sqflite/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
webview_flutter_wkwebview:
|
||||||
|
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
|
audio_session: 4f3e461722055d21515cf3261b64c973c062f345
|
||||||
|
|
@ -171,6 +176,7 @@ SPEC CHECKSUMS:
|
||||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||||
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
|
TOCropViewController: edfd4f25713d56905ad1e0b9f5be3fbe0f59c863
|
||||||
url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af
|
url_launcher_ios: 02f1989d4e14e998335b02b67a7590fa34f971af
|
||||||
|
webview_flutter_wkwebview: 005fbd90c888a42c5690919a1527ecc6649e1162
|
||||||
|
|
||||||
PODFILE CHECKSUM: fe0e1ee7f3d1f7d00b11b474b62dd62134535aea
|
PODFILE CHECKSUM: fe0e1ee7f3d1f7d00b11b474b62dd62134535aea
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,7 @@ class DesignConfig {
|
||||||
static SystemUiOverlayStyle get systemUiOverlayStyle => SystemUiOverlayStyle(
|
static SystemUiOverlayStyle get systemUiOverlayStyle => SystemUiOverlayStyle(
|
||||||
statusBarIconBrightness:
|
statusBarIconBrightness:
|
||||||
brightness == Brightness.dark ? Brightness.light : Brightness.dark,
|
brightness == Brightness.dark ? Brightness.light : Brightness.dark,
|
||||||
statusBarColor:
|
statusBarColor: Theme.of(context!).colorScheme.background,
|
||||||
Theme.of(context!).colorScheme.background.withOpacity(0.5),
|
|
||||||
systemNavigationBarColor: Theme.of(context!).colorScheme.surface,
|
systemNavigationBarColor: Theme.of(context!).colorScheme.surface,
|
||||||
systemNavigationBarDividerColor: Colors.transparent,
|
systemNavigationBarDividerColor: Colors.transparent,
|
||||||
systemNavigationBarIconBrightness:
|
systemNavigationBarIconBrightness:
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@ class OverviewData {
|
||||||
final String image;
|
final String image;
|
||||||
final String description;
|
final String description;
|
||||||
final int? timeToRead;
|
final int? timeToRead;
|
||||||
|
final int? duration;
|
||||||
final String? reference;
|
final String? reference;
|
||||||
|
final String? media;
|
||||||
final bool forManagers;
|
final bool forManagers;
|
||||||
final String createdAt;
|
final String createdAt;
|
||||||
final String type;
|
final String type;
|
||||||
|
|
@ -24,6 +26,8 @@ class OverviewData {
|
||||||
required this.marked,
|
required this.marked,
|
||||||
required this.comments,
|
required this.comments,
|
||||||
required this.forManagers,
|
required this.forManagers,
|
||||||
|
this.media,
|
||||||
|
this.duration,
|
||||||
this.timeToRead,
|
this.timeToRead,
|
||||||
this.reference,
|
this.reference,
|
||||||
this.categories,
|
this.categories,
|
||||||
|
|
@ -39,11 +43,17 @@ class OverviewData {
|
||||||
forManagers: json['forManagers'] ?? false,
|
forManagers: json['forManagers'] ?? false,
|
||||||
comments: json['comments'] ?? 0,
|
comments: json['comments'] ?? 0,
|
||||||
createdAt: json['createdAt'],
|
createdAt: json['createdAt'],
|
||||||
|
duration: json['duration'],
|
||||||
type: json['type'] ?? '',
|
type: json['type'] ?? '',
|
||||||
marked: json['marked'] ?? false,
|
marked: json['marked'] ?? false,
|
||||||
categories: (json['categories'] as List<dynamic>?)
|
media: json['media'],
|
||||||
?.map((e) => CategoryData.fromJson(e as Map<String, dynamic>))
|
categories: json['categories'] != null
|
||||||
.toList(),
|
? List<CategoryData>.from(
|
||||||
|
json['categories'].map(
|
||||||
|
(e) => CategoryData.fromJson(e),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
class StudioRequestArgs {
|
||||||
|
final int page;
|
||||||
|
final String? search;
|
||||||
|
final String? order;
|
||||||
|
final String? type;
|
||||||
|
|
||||||
|
const StudioRequestArgs({
|
||||||
|
required this.page,
|
||||||
|
this.search,
|
||||||
|
this.order,
|
||||||
|
this.type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
import 'package:didvan/models/overview_data.dart';
|
||||||
|
import 'package:didvan/models/tag.dart';
|
||||||
|
|
||||||
|
class StudioDetailsData {
|
||||||
|
final int id;
|
||||||
|
final int duration;
|
||||||
|
final String title;
|
||||||
|
final String description;
|
||||||
|
final String image;
|
||||||
|
final String media;
|
||||||
|
final String createdAt;
|
||||||
|
final int order;
|
||||||
|
bool marked;
|
||||||
|
int comments;
|
||||||
|
final List<Tag> tags;
|
||||||
|
final List<OverviewData> relatedContents = [];
|
||||||
|
|
||||||
|
StudioDetailsData({
|
||||||
|
required this.id,
|
||||||
|
required this.duration,
|
||||||
|
required this.title,
|
||||||
|
required this.description,
|
||||||
|
required this.image,
|
||||||
|
required this.media,
|
||||||
|
required this.createdAt,
|
||||||
|
required this.order,
|
||||||
|
required this.marked,
|
||||||
|
required this.comments,
|
||||||
|
required this.tags,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory StudioDetailsData.fromJson(Map<String, dynamic> json) {
|
||||||
|
return StudioDetailsData(
|
||||||
|
id: json['id'],
|
||||||
|
duration: json['duration'],
|
||||||
|
title: json['title'],
|
||||||
|
description: json['description'],
|
||||||
|
image: json['image'],
|
||||||
|
media: json['media'],
|
||||||
|
createdAt: json['createdAt'],
|
||||||
|
order: json['order'],
|
||||||
|
marked: json['marked'],
|
||||||
|
comments: json['comments'],
|
||||||
|
tags: List<Tag>.from(json['tags'].map((e) => Tag.fromJson(e))),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
'id': id,
|
||||||
|
'duration': duration,
|
||||||
|
'title': title,
|
||||||
|
'description': description,
|
||||||
|
'image': image,
|
||||||
|
'media': media,
|
||||||
|
'createdAt': createdAt,
|
||||||
|
'order': order,
|
||||||
|
'marked': marked,
|
||||||
|
'comments': comments,
|
||||||
|
'tags': tags.map((e) => e.toJson()).toList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,8 @@ class ActionSheetData {
|
||||||
final String? dismissTitle;
|
final String? dismissTitle;
|
||||||
final VoidCallback? onConfirmed;
|
final VoidCallback? onConfirmed;
|
||||||
final VoidCallback? onDismissed;
|
final VoidCallback? onDismissed;
|
||||||
final String title;
|
final String? title;
|
||||||
|
final bool hasPadding;
|
||||||
final IconData? titleIcon;
|
final IconData? titleIcon;
|
||||||
final Color? titleColor;
|
final Color? titleColor;
|
||||||
final bool hasDismissButton;
|
final bool hasDismissButton;
|
||||||
|
|
@ -16,10 +17,11 @@ class ActionSheetData {
|
||||||
|
|
||||||
const ActionSheetData({
|
const ActionSheetData({
|
||||||
required this.content,
|
required this.content,
|
||||||
required this.title,
|
this.title,
|
||||||
this.confrimTitle,
|
this.confrimTitle,
|
||||||
this.onConfirmed,
|
this.onConfirmed,
|
||||||
this.titleColor,
|
this.titleColor,
|
||||||
|
this.hasPadding = true,
|
||||||
this.hasDismissButton = true,
|
this.hasDismissButton = true,
|
||||||
this.hasConfirmButton = true,
|
this.hasConfirmButton = true,
|
||||||
this.titleIcon,
|
this.titleIcon,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,13 @@ class AppBarData {
|
||||||
final String? subtitle;
|
final String? subtitle;
|
||||||
final bool hasBack;
|
final bool hasBack;
|
||||||
final Widget? trailing;
|
final Widget? trailing;
|
||||||
|
final bool isSmall;
|
||||||
|
|
||||||
AppBarData({this.title, this.subtitle, this.hasBack = false, this.trailing});
|
AppBarData({
|
||||||
|
this.title,
|
||||||
|
this.subtitle,
|
||||||
|
this.hasBack = false,
|
||||||
|
this.trailing,
|
||||||
|
this.isSmall = false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ class ServerDataProvider {
|
||||||
await _getDirectTypes();
|
await _getDirectTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int labelToTypeId(String? label) => label == null
|
static int labelToTypeId(String label) => label.contains('پشتیبانی')
|
||||||
? 7
|
? 7
|
||||||
: directTypes.firstWhere((element) => element.value.contains(label)).key;
|
: directTypes.firstWhere((element) => element.value.contains(label)).key;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ class UserProvider extends CoreProvier {
|
||||||
|
|
||||||
static final List<MapEntry> _radarMarkQueue = [];
|
static final List<MapEntry> _radarMarkQueue = [];
|
||||||
static final List<MapEntry> _newsMarkQueue = [];
|
static final List<MapEntry> _newsMarkQueue = [];
|
||||||
|
static final List<MapEntry> _studioMarkQueue = [];
|
||||||
|
|
||||||
Future<String?> setAndGetToken({String? newToken}) async {
|
Future<String?> setAndGetToken({String? newToken}) async {
|
||||||
if (newToken == null) {
|
if (newToken == null) {
|
||||||
|
|
@ -24,13 +25,16 @@ class UserProvider extends CoreProvier {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getUserInfo() async {
|
Future<bool> getUserInfo() async {
|
||||||
isAuthenticated = true;
|
isAuthenticated = true;
|
||||||
final RequestService service = RequestService(RequestHelper.userInfo);
|
final RequestService service = RequestService(RequestHelper.userInfo);
|
||||||
await service.httpGet();
|
await service.httpGet();
|
||||||
if (service.isSuccess) {
|
if (service.isSuccess) {
|
||||||
user = User.fromJson(service.result['user']);
|
user = User.fromJson(service.result['user']);
|
||||||
return;
|
return true;
|
||||||
|
}
|
||||||
|
if (service.statusCode == 401) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
throw 'Getting user from API failed!';
|
throw 'Getting user from API failed!';
|
||||||
}
|
}
|
||||||
|
|
@ -138,6 +142,22 @@ class UserProvider extends CoreProvier {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> changeStudioMark(int id, bool value) async {
|
||||||
|
_studioMarkQueue.add(MapEntry(id, value));
|
||||||
|
Future.delayed(const Duration(milliseconds: 500), () async {
|
||||||
|
final MapEntry? lastChange =
|
||||||
|
_studioMarkQueue.lastWhereOrNull((item) => item.key == id);
|
||||||
|
if (lastChange == null) return;
|
||||||
|
final service = RequestService(RequestHelper.markStudio(id));
|
||||||
|
if (lastChange.value) {
|
||||||
|
await service.post();
|
||||||
|
} else {
|
||||||
|
await service.delete();
|
||||||
|
}
|
||||||
|
_studioMarkQueue.removeWhere((element) => element.key == id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static Future<void> changeNewsMark(int id, bool value) async {
|
static Future<void> changeNewsMark(int id, bool value) async {
|
||||||
_newsMarkQueue.add(MapEntry(id, value));
|
_newsMarkQueue.add(MapEntry(id, value));
|
||||||
Future.delayed(const Duration(milliseconds: 500), () async {
|
Future.delayed(const Duration(milliseconds: 500), () async {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ 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.dart';
|
||||||
import 'package:didvan/views/home/settings/general_settings/settings_state.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/settings/profile/profile.dart';
|
||||||
|
import 'package:didvan/views/home/studio/studio_details/studio_details.dart';
|
||||||
|
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||||
import 'package:didvan/views/home/studio/studio_state.dart';
|
import 'package:didvan/views/home/studio/studio_state.dart';
|
||||||
import 'package:didvan/views/splash/splash.dart';
|
import 'package:didvan/views/splash/splash.dart';
|
||||||
import 'package:didvan/routes/routes.dart';
|
import 'package:didvan/routes/routes.dart';
|
||||||
|
|
@ -62,6 +64,9 @@ class RouteGenerator {
|
||||||
ChangeNotifierProvider<StudioState>(
|
ChangeNotifierProvider<StudioState>(
|
||||||
create: (context) => StudioState(),
|
create: (context) => StudioState(),
|
||||||
),
|
),
|
||||||
|
ChangeNotifierProvider<StudioDetailsState>(
|
||||||
|
create: (context) => StudioDetailsState(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
child: const Home(),
|
child: const Home(),
|
||||||
),
|
),
|
||||||
|
|
@ -99,6 +104,15 @@ class RouteGenerator {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
case Routes.studioDetails:
|
||||||
|
return _createRoute(
|
||||||
|
ChangeNotifierProvider<StudioDetailsState>.value(
|
||||||
|
value: (settings.arguments as Map<String, dynamic>)['state'],
|
||||||
|
child: StudioDetails(
|
||||||
|
pageData: settings.arguments as Map<String, dynamic>,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
case Routes.directList:
|
case Routes.directList:
|
||||||
return _createRoute(
|
return _createRoute(
|
||||||
ChangeNotifierProvider<DirectListState>(
|
ChangeNotifierProvider<DirectListState>(
|
||||||
|
|
@ -170,7 +184,11 @@ class RouteGenerator {
|
||||||
final shortestSide = MediaQuery.of(context).size.shortestSide;
|
final shortestSide = MediaQuery.of(context).size.shortestSide;
|
||||||
final bool useMobileLayout = shortestSide < 600;
|
final bool useMobileLayout = shortestSide < 600;
|
||||||
if (kIsWeb && !useMobileLayout) {
|
if (kIsWeb && !useMobileLayout) {
|
||||||
return Center(child: AspectRatio(aspectRatio: 9 / 16, child: page));
|
return Container(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: AspectRatio(aspectRatio: 9 / 16, child: page),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return Container(
|
return Container(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ class Routes {
|
||||||
static const String generalSettings = '/general-settings';
|
static const String generalSettings = '/general-settings';
|
||||||
static const String radarDetails = '/radar-details';
|
static const String radarDetails = '/radar-details';
|
||||||
static const String newsDetails = '/news-details';
|
static const String newsDetails = '/news-details';
|
||||||
|
static const String studioDetails = '/studio-details';
|
||||||
static const String directList = '/direct-list';
|
static const String directList = '/direct-list';
|
||||||
static const String direct = '/direct';
|
static const String direct = '/direct';
|
||||||
static const String comments = '/comments';
|
static const String comments = '/comments';
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:didvan/models/requests/studio.dart';
|
||||||
|
import 'package:didvan/models/studio_details_data.dart';
|
||||||
import 'package:didvan/services/network/request.dart';
|
import 'package:didvan/services/network/request.dart';
|
||||||
import 'package:didvan/services/network/request_helper.dart';
|
import 'package:didvan/services/network/request_helper.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
@ -7,8 +9,8 @@ import 'package:just_audio/just_audio.dart';
|
||||||
class MediaService {
|
class MediaService {
|
||||||
static final AudioPlayer audioPlayer = AudioPlayer();
|
static final AudioPlayer audioPlayer = AudioPlayer();
|
||||||
static String? audioPlayerTag;
|
static String? audioPlayerTag;
|
||||||
static String? audioPlayerTitle;
|
static StudioDetailsData? currentPodcast;
|
||||||
static String? audioPlayerCover;
|
static StudioRequestArgs? podcastPlaylistArgs;
|
||||||
|
|
||||||
static void init() {
|
static void init() {
|
||||||
audioPlayer.positionStream.listen((event) {
|
audioPlayer.positionStream.listen((event) {
|
||||||
|
|
@ -21,6 +23,7 @@ class MediaService {
|
||||||
|
|
||||||
static Future<void> handleAudioPlayback({
|
static Future<void> handleAudioPlayback({
|
||||||
required dynamic audioSource,
|
required dynamic audioSource,
|
||||||
|
bool isVoiceMessage = true,
|
||||||
}) async {
|
}) async {
|
||||||
bool isNetworkAudio = audioSource.runtimeType == String;
|
bool isNetworkAudio = audioSource.runtimeType == String;
|
||||||
String tag;
|
String tag;
|
||||||
|
|
@ -40,9 +43,11 @@ class MediaService {
|
||||||
audioPlayerTag = tag;
|
audioPlayerTag = tag;
|
||||||
if (isNetworkAudio) {
|
if (isNetworkAudio) {
|
||||||
await audioPlayer.setUrl(
|
await audioPlayer.setUrl(
|
||||||
RequestHelper.baseUrl +
|
isVoiceMessage
|
||||||
audioSource +
|
? (RequestHelper.baseUrl +
|
||||||
'?accessToken=${RequestService.token}',
|
audioSource +
|
||||||
|
'?accessToken=${RequestService.token}')
|
||||||
|
: audioSource,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
|
|
@ -58,6 +63,8 @@ class MediaService {
|
||||||
|
|
||||||
static Future<void> resetAudioPlayer() async {
|
static Future<void> resetAudioPlayer() async {
|
||||||
audioPlayerTag = null;
|
audioPlayerTag = null;
|
||||||
|
currentPodcast = null;
|
||||||
|
podcastPlaylistArgs = null;
|
||||||
MediaService.audioPlayer.stop();
|
MediaService.audioPlayer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import 'package:http_parser/http_parser.dart' as parser;
|
||||||
|
|
||||||
class RequestService {
|
class RequestService {
|
||||||
static late String token;
|
static late String token;
|
||||||
|
int? statusCode;
|
||||||
|
|
||||||
Map<String, dynamic> get result => _body?['result'] ?? const {};
|
Map<String, dynamic> get result => _body?['result'] ?? const {};
|
||||||
Map<String, dynamic> get errors => _body?['errors'] ?? const {};
|
Map<String, dynamic> get errors => _body?['errors'] ?? const {};
|
||||||
|
|
@ -162,6 +163,7 @@ class RequestService {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleResponse(http.Response? response) {
|
void _handleResponse(http.Response? response) {
|
||||||
|
statusCode = response?.statusCode;
|
||||||
if (_handleError(response)) {
|
if (_handleError(response)) {
|
||||||
if (response!.body.isNotEmpty) {
|
if (response!.body.isNotEmpty) {
|
||||||
_body = json.decode(response.body);
|
_body = json.decode(response.body);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:didvan/models/requests/news.dart';
|
import 'package:didvan/models/requests/news.dart';
|
||||||
import 'package:didvan/models/requests/radar.dart';
|
import 'package:didvan/models/requests/radar.dart';
|
||||||
|
import 'package:didvan/models/requests/studio.dart';
|
||||||
|
|
||||||
class RequestHelper {
|
class RequestHelper {
|
||||||
static const String baseUrl = 'https://api.didvan.app';
|
static const String baseUrl = 'https://api.didvan.app';
|
||||||
|
|
@ -18,8 +19,19 @@ class RequestHelper {
|
||||||
static const String checkUsername = _baseUserUrl + '/CheckUsername';
|
static const String checkUsername = _baseUserUrl + '/CheckUsername';
|
||||||
static const String updateProfile = _baseUserUrl + '/profile/edit';
|
static const String updateProfile = _baseUserUrl + '/profile/edit';
|
||||||
static const String otp = _baseUserUrl + '/otp';
|
static const String otp = _baseUserUrl + '/otp';
|
||||||
static String bookmarks({String? type}) =>
|
static String bookmarks({
|
||||||
_baseUserUrl + '/marked/${type ?? ''}';
|
required int page,
|
||||||
|
String? search,
|
||||||
|
String? type,
|
||||||
|
String? studioType,
|
||||||
|
}) =>
|
||||||
|
_baseUserUrl +
|
||||||
|
'/marked/${type ?? ''}' +
|
||||||
|
_urlConcatGenerator([
|
||||||
|
MapEntry('page', page),
|
||||||
|
MapEntry('type', studioType),
|
||||||
|
MapEntry('search', search),
|
||||||
|
]);
|
||||||
|
|
||||||
static const String directTypes = baseUrl + '/direct/types';
|
static const String directTypes = baseUrl + '/direct/types';
|
||||||
static String direct(int id) => _baseDirectUrl + '/$id';
|
static String direct(int id) => _baseDirectUrl + '/$id';
|
||||||
|
|
@ -35,10 +47,11 @@ class RequestHelper {
|
||||||
baseUrl +
|
baseUrl +
|
||||||
'/tag' +
|
'/tag' +
|
||||||
_urlConcatGenerator([
|
_urlConcatGenerator([
|
||||||
MapEntry('limit', limit?.toString() ?? '3'),
|
MapEntry('page', page),
|
||||||
|
MapEntry('limit', limit ?? '3'),
|
||||||
MapEntry('type', type),
|
MapEntry('type', type),
|
||||||
MapEntry('id', itemId?.toString() ?? '1'),
|
MapEntry('id', itemId ?? '1'),
|
||||||
MapEntry('tags', _urlListConcatGenerator(ids))
|
MapEntry('tags', _urlListConcatGenerator(ids)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
static String markRadar(int id) => _baseRadarUrl + '/$id/mark';
|
static String markRadar(int id) => _baseRadarUrl + '/$id/mark';
|
||||||
|
|
@ -50,7 +63,7 @@ class RequestHelper {
|
||||||
_baseRadarUrl +
|
_baseRadarUrl +
|
||||||
'/$id' +
|
'/$id' +
|
||||||
_urlConcatGenerator([
|
_urlConcatGenerator([
|
||||||
MapEntry('page', args.page.toString()),
|
MapEntry('page', args.page),
|
||||||
MapEntry('start', args.startDate),
|
MapEntry('start', args.startDate),
|
||||||
MapEntry('end', args.endDate),
|
MapEntry('end', args.endDate),
|
||||||
MapEntry('search', args.search),
|
MapEntry('search', args.search),
|
||||||
|
|
@ -59,7 +72,7 @@ class RequestHelper {
|
||||||
static String radarOverviews({required RadarRequestArgs args}) =>
|
static String radarOverviews({required RadarRequestArgs args}) =>
|
||||||
_baseRadarUrl +
|
_baseRadarUrl +
|
||||||
_urlConcatGenerator([
|
_urlConcatGenerator([
|
||||||
MapEntry('page', args.page.toString()),
|
MapEntry('page', args.page),
|
||||||
MapEntry('start', args.startDate),
|
MapEntry('start', args.startDate),
|
||||||
MapEntry('end', args.endDate),
|
MapEntry('end', args.endDate),
|
||||||
MapEntry('search', args.search),
|
MapEntry('search', args.search),
|
||||||
|
|
@ -75,7 +88,7 @@ class RequestHelper {
|
||||||
_baseNewsUrl +
|
_baseNewsUrl +
|
||||||
'/$id' +
|
'/$id' +
|
||||||
_urlConcatGenerator([
|
_urlConcatGenerator([
|
||||||
MapEntry('page', args.page.toString()),
|
MapEntry('page', args.page),
|
||||||
MapEntry('start', args.startDate),
|
MapEntry('start', args.startDate),
|
||||||
MapEntry('end', args.endDate),
|
MapEntry('end', args.endDate),
|
||||||
MapEntry('search', args.search),
|
MapEntry('search', args.search),
|
||||||
|
|
@ -83,19 +96,45 @@ class RequestHelper {
|
||||||
static String newsOverviews({required NewsRequestArgs args}) =>
|
static String newsOverviews({required NewsRequestArgs args}) =>
|
||||||
_baseNewsUrl +
|
_baseNewsUrl +
|
||||||
_urlConcatGenerator([
|
_urlConcatGenerator([
|
||||||
MapEntry('page', args.page.toString()),
|
MapEntry('page', args.page),
|
||||||
MapEntry('start', args.startDate),
|
MapEntry('start', args.startDate),
|
||||||
MapEntry('end', args.endDate),
|
MapEntry('end', args.endDate),
|
||||||
MapEntry('search', args.search),
|
MapEntry('search', args.search),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
static String _urlConcatGenerator(List<MapEntry<String, String?>> additions) {
|
static String markStudio(int id) => _baseStudioUrl + '/$id/mark';
|
||||||
|
static String studioComments(int id) => _baseStudioUrl + '/$id/comments';
|
||||||
|
static String addStudioComment(int id) =>
|
||||||
|
_baseStudioUrl + '/$id/comments/add';
|
||||||
|
static String feedbackStudioComment(int studioId, int id) =>
|
||||||
|
_baseStudioUrl + '/$studioId/comments/$id/feedback';
|
||||||
|
static String studioDetails(int id, StudioRequestArgs args) =>
|
||||||
|
_baseStudioUrl +
|
||||||
|
'/$id' +
|
||||||
|
_urlConcatGenerator([
|
||||||
|
MapEntry('page', args.page),
|
||||||
|
MapEntry('type', args.type),
|
||||||
|
MapEntry('order', args.order),
|
||||||
|
MapEntry('search', args.search),
|
||||||
|
]);
|
||||||
|
static String studioOverviews({required StudioRequestArgs args}) =>
|
||||||
|
_baseStudioUrl +
|
||||||
|
_urlConcatGenerator([
|
||||||
|
MapEntry('page', args.page),
|
||||||
|
MapEntry('type', args.type),
|
||||||
|
MapEntry('order', args.order),
|
||||||
|
MapEntry('search', args.search),
|
||||||
|
]);
|
||||||
|
|
||||||
|
static String _urlConcatGenerator(List<MapEntry<String, dynamic>> additions) {
|
||||||
String result = '';
|
String result = '';
|
||||||
additions.removeWhere((element) => element.value == null);
|
additions.removeWhere(
|
||||||
|
(element) => element.value == null || element.value.toString().isEmpty,
|
||||||
|
);
|
||||||
if (additions.isNotEmpty) {
|
if (additions.isNotEmpty) {
|
||||||
result += '?';
|
result += '?';
|
||||||
for (var i = 0; i < additions.length; i++) {
|
for (var i = 0; i < additions.length; i++) {
|
||||||
result += (additions[i].key + '=' + additions[i].value!);
|
result += (additions[i].key + '=' + additions[i].value!.toString());
|
||||||
if (i != additions.length - 1) {
|
if (i != additions.length - 1) {
|
||||||
result += '&';
|
result += '&';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class ActionSheetUtils {
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => Container(
|
builder: (context) => Container(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: data.hasPadding ? const EdgeInsets.all(20) : EdgeInsets.zero,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
borderRadius: const BorderRadius.vertical(
|
borderRadius: const BorderRadius.vertical(
|
||||||
|
|
@ -91,6 +91,7 @@ class ActionSheetUtils {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
const SizedBox(height: 20),
|
||||||
Center(
|
Center(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 3,
|
height: 3,
|
||||||
|
|
@ -99,23 +100,24 @@ class ActionSheetUtils {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Row(
|
if (data.title != null)
|
||||||
children: [
|
Row(
|
||||||
if (data.titleIcon != null)
|
children: [
|
||||||
Icon(
|
if (data.titleIcon != null)
|
||||||
data.titleIcon,
|
Icon(
|
||||||
|
data.titleIcon,
|
||||||
|
color: data.titleColor ??
|
||||||
|
Theme.of(context).colorScheme.title,
|
||||||
|
),
|
||||||
|
if (data.titleIcon != null) const SizedBox(width: 8),
|
||||||
|
DidvanText(
|
||||||
|
data.title!,
|
||||||
|
style: Theme.of(context).textTheme.subtitle1,
|
||||||
color: data.titleColor ??
|
color: data.titleColor ??
|
||||||
Theme.of(context).colorScheme.title,
|
Theme.of(context).colorScheme.title,
|
||||||
),
|
)
|
||||||
if (data.titleIcon != null) const SizedBox(width: 8),
|
],
|
||||||
DidvanText(
|
),
|
||||||
data.title,
|
|
||||||
style: Theme.of(context).textTheme.subtitle1,
|
|
||||||
color:
|
|
||||||
data.titleColor ?? Theme.of(context).colorScheme.title,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 28),
|
const SizedBox(height: 28),
|
||||||
data.content,
|
data.content,
|
||||||
const SizedBox(height: 28),
|
const SizedBox(height: 28),
|
||||||
|
|
@ -169,30 +171,31 @@ class ActionSheetUtils {
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
if (data.title != null)
|
||||||
mainAxisSize: MainAxisSize.min,
|
Row(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
GestureDetector(
|
children: [
|
||||||
onTap: () => Navigator.of(context).pop(),
|
GestureDetector(
|
||||||
child: Icon(
|
onTap: () => Navigator.of(context).pop(),
|
||||||
data.titleIcon,
|
child: Icon(
|
||||||
size: 20,
|
data.titleIcon,
|
||||||
color: data.titleColor,
|
size: 20,
|
||||||
|
color: data.titleColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(
|
||||||
const SizedBox(
|
width: 8,
|
||||||
width: 8,
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: DidvanText(
|
|
||||||
data.title,
|
|
||||||
style: Theme.of(context).textTheme.headline3,
|
|
||||||
color: data.titleColor,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
),
|
||||||
),
|
Expanded(
|
||||||
],
|
child: DidvanText(
|
||||||
),
|
data.title!,
|
||||||
|
style: Theme.of(context).textTheme.headline3,
|
||||||
|
color: data.titleColor,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 12,
|
height: 12,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:didvan/models/view/action_sheet_data.dart';
|
||||||
import 'package:didvan/providers/server_data_provider.dart';
|
import 'package:didvan/providers/server_data_provider.dart';
|
||||||
import 'package:didvan/providers/user_provider.dart';
|
import 'package:didvan/providers/user_provider.dart';
|
||||||
import 'package:didvan/routes/routes.dart';
|
import 'package:didvan/routes/routes.dart';
|
||||||
|
import 'package:didvan/utils/action_sheet.dart';
|
||||||
import 'package:didvan/views/authentication/authentication_state.dart';
|
import 'package:didvan/views/authentication/authentication_state.dart';
|
||||||
import 'package:didvan/views/authentication/widgets/authentication_layout.dart';
|
import 'package:didvan/views/authentication/widgets/authentication_layout.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/button.dart';
|
import 'package:didvan/views/widgets/didvan/button.dart';
|
||||||
|
|
@ -76,6 +78,25 @@ class _PasswordInputState extends State<PasswordInput> {
|
||||||
log(token);
|
log(token);
|
||||||
await ServerDataProvider.getData();
|
await ServerDataProvider.getData();
|
||||||
Navigator.of(context).pushReplacementNamed(Routes.home);
|
Navigator.of(context).pushReplacementNamed(Routes.home);
|
||||||
|
_showResetPasswordDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showResetPasswordDialog() {
|
||||||
|
ActionSheetUtils.openDialog(
|
||||||
|
data: ActionSheetData(
|
||||||
|
content: const DidvanText(
|
||||||
|
'خوش آمدید!\nبرای امنیت بیشتر، رمز عبور خود را تغییر دهید.',
|
||||||
|
),
|
||||||
|
title: 'تغییر رمز عبور',
|
||||||
|
onConfirmed: () => Navigator.of(ActionSheetUtils.context).pushNamed(
|
||||||
|
Routes.authenticaion,
|
||||||
|
arguments: true,
|
||||||
|
),
|
||||||
|
confrimTitle: 'تغییر رمز عبور',
|
||||||
|
onDismissed: Navigator.of(ActionSheetUtils.context).pop,
|
||||||
|
dismissTitle: 'بعدا',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ class _UsernameInputState extends State<UsernameInput> {
|
||||||
if (value.length < 4) {
|
if (value.length < 4) {
|
||||||
return 'نام کاربری نمیتواند از 4 کاراکتر کمتر باشد';
|
return 'نام کاربری نمیتواند از 4 کاراکتر کمتر باشد';
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
state.username = value;
|
state.username = value;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:didvan/constants/assets.dart';
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/models/view/app_bar_data.dart';
|
import 'package:didvan/models/view/app_bar_data.dart';
|
||||||
import 'package:didvan/providers/server_data_provider.dart';
|
import 'package:didvan/providers/server_data_provider.dart';
|
||||||
|
|
@ -6,6 +7,7 @@ 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.dart';
|
||||||
import 'package:didvan/views/home/direct/widgets/message_box.dart';
|
import 'package:didvan/views/home/direct/widgets/message_box.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/scaffold.dart';
|
import 'package:didvan/views/widgets/didvan/scaffold.dart';
|
||||||
|
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
|
||||||
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
|
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
|
|
@ -66,6 +68,14 @@ class _DirectState extends State<Direct> {
|
||||||
sliver: SliverStateHandler<DirectState>(
|
sliver: SliverStateHandler<DirectState>(
|
||||||
itemPadding: const EdgeInsets.only(bottom: 12),
|
itemPadding: const EdgeInsets.only(bottom: 12),
|
||||||
state: state,
|
state: state,
|
||||||
|
enableEmptyState: state.messages.isEmpty,
|
||||||
|
emptyState: Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 160),
|
||||||
|
child: EmptyState(
|
||||||
|
asset: Assets.emptyChat,
|
||||||
|
title: 'اولین پیام را بنویسید...',
|
||||||
|
),
|
||||||
|
),
|
||||||
builder: (context, state, index) => Message(
|
builder: (context, state, index) => Message(
|
||||||
message: state.messages[index],
|
message: state.messages[index],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:didvan/services/media/media.dart';
|
import 'package:didvan/services/media/media.dart';
|
||||||
import 'package:didvan/views/home/widgets/audio_slider.dart';
|
import 'package:didvan/views/home/widgets/audio/audio_slider.dart';
|
||||||
import 'package:didvan/views/home/widgets/player_controller_button.dart';
|
import 'package:didvan/views/home/widgets/player_controller_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ class MessageBox extends StatelessWidget {
|
||||||
Consumer<DirectState>(
|
Consumer<DirectState>(
|
||||||
builder: (context, state, child) => state.replyRadar != null
|
builder: (context, state, child) => state.replyRadar != null
|
||||||
? _MessageBoxContainer(
|
? _MessageBoxContainer(
|
||||||
|
isMessage: false,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
@ -53,6 +54,7 @@ class MessageBox extends StatelessWidget {
|
||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
),
|
),
|
||||||
_MessageBoxContainer(
|
_MessageBoxContainer(
|
||||||
|
isMessage: true,
|
||||||
child: Consumer<DirectState>(
|
child: Consumer<DirectState>(
|
||||||
builder: (context, state, child) {
|
builder: (context, state, child) {
|
||||||
if (state.isRecording) {
|
if (state.isRecording) {
|
||||||
|
|
@ -71,12 +73,17 @@ class MessageBox extends StatelessWidget {
|
||||||
|
|
||||||
class _MessageBoxContainer extends StatelessWidget {
|
class _MessageBoxContainer extends StatelessWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
const _MessageBoxContainer({Key? key, required this.child}) : super(key: key);
|
final bool isMessage;
|
||||||
|
const _MessageBoxContainer({
|
||||||
|
Key? key,
|
||||||
|
required this.child,
|
||||||
|
required this.isMessage,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 68,
|
height: isMessage ? 68 : null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
top: BorderSide(
|
top: BorderSide(
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import 'package:didvan/models/tag.dart';
|
import 'package:didvan/models/tag.dart';
|
||||||
import 'package:didvan/models/view/app_bar_data.dart';
|
import 'package:didvan/models/view/app_bar_data.dart';
|
||||||
import 'package:didvan/views/home/hashtag/hashtag_state.dart';
|
import 'package:didvan/views/home/hashtag/hashtag_state.dart';
|
||||||
import 'package:didvan/views/home/widgets/news_overview.dart';
|
import 'package:didvan/views/home/widgets/overview/news.dart';
|
||||||
import 'package:didvan/views/home/widgets/radar_overview.dart';
|
import 'package:didvan/views/home/widgets/overview/radar.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/scaffold.dart';
|
import 'package:didvan/views/widgets/didvan/scaffold.dart';
|
||||||
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
|
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class Hashtag extends StatefulWidget {
|
class Hashtag extends StatefulWidget {
|
||||||
|
|
@ -37,10 +38,16 @@ class _HashtagState extends State<Hashtag> {
|
||||||
placeholder: RadarOverview.placeholder,
|
placeholder: RadarOverview.placeholder,
|
||||||
builder: (context, state, index) {
|
builder: (context, state, index) {
|
||||||
index++;
|
index++;
|
||||||
if (index % 15 == 0 && index / 15 >= state.page) {
|
if (index % 15 == 0 && state.lastPage != state.page) {
|
||||||
state.getTagItems(page: index ~/ 15 + 1);
|
state.getTagItems(page: state.page + 1);
|
||||||
}
|
}
|
||||||
index--;
|
index--;
|
||||||
|
if (index == state.items.length) {
|
||||||
|
return SpinKitThreeBounce(
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
size: 24,
|
||||||
|
);
|
||||||
|
}
|
||||||
final item = state.items[index];
|
final item = state.items[index];
|
||||||
final type = item.type;
|
final type = item.type;
|
||||||
if (type == 'radar') {
|
if (type == 'radar') {
|
||||||
|
|
@ -57,8 +64,9 @@ class _HashtagState extends State<Hashtag> {
|
||||||
}
|
}
|
||||||
return Container();
|
return Container();
|
||||||
},
|
},
|
||||||
childCount: state.items.length,
|
childCount:
|
||||||
onRetry: () => state.getTagItems(page: 1),
|
state.items.length + (state.page != state.lastPage ? 1 : 0),
|
||||||
|
onRetry: () => state.getTagItems(page: state.page),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ class HashtagState extends CoreProvier {
|
||||||
|
|
||||||
late final int id;
|
late final int id;
|
||||||
int page = 1;
|
int page = 1;
|
||||||
|
int lastPage = 1;
|
||||||
|
|
||||||
Future<void> getTagItems({required int page}) async {
|
Future<void> getTagItems({required int page}) async {
|
||||||
this.page = page;
|
this.page = page;
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import 'package:didvan/utils/action_sheet.dart';
|
||||||
import 'package:didvan/views/home/news/news_state.dart';
|
import 'package:didvan/views/home/news/news_state.dart';
|
||||||
import 'package:didvan/views/home/widgets/date_picker_button.dart';
|
import 'package:didvan/views/home/widgets/date_picker_button.dart';
|
||||||
import 'package:didvan/views/home/widgets/logo_app_bar.dart';
|
import 'package:didvan/views/home/widgets/logo_app_bar.dart';
|
||||||
import 'package:didvan/views/home/widgets/news_overview.dart';
|
import 'package:didvan/views/home/widgets/overview/news.dart';
|
||||||
import 'package:didvan/views/home/widgets/search_field.dart';
|
import 'package:didvan/views/home/widgets/search_field.dart';
|
||||||
import 'package:didvan/views/widgets/item_title.dart';
|
import 'package:didvan/views/widgets/item_title.dart';
|
||||||
import 'package:didvan/views/widgets/state_handlers/empty_result.dart';
|
import 'package:didvan/views/widgets/state_handlers/empty_result.dart';
|
||||||
|
|
@ -57,7 +57,7 @@ class _NewsState extends State<News> {
|
||||||
builder: (context, state, index) {
|
builder: (context, state, index) {
|
||||||
index += 2;
|
index += 2;
|
||||||
if (index % 15 == 0 && state.lastPage != state.page) {
|
if (index % 15 == 0 && state.lastPage != state.page) {
|
||||||
state.getNews(page: index ~/ 15 + 1);
|
state.getNews(page: state.page + 1);
|
||||||
}
|
}
|
||||||
index -= 2;
|
index -= 2;
|
||||||
if (index >= state.news.length) {
|
if (index >= state.news.length) {
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ class NewsDetailsState extends CoreProvier {
|
||||||
news.any((n) => newsItem != null && n != null && n.id == newsItem.id);
|
news.any((n) => newsItem != null && n != null && n.id == newsItem.id);
|
||||||
|
|
||||||
void onCommentsChanged(int count) {
|
void onCommentsChanged(int count) {
|
||||||
news.firstWhere((item) => item!.id == currentNews.id)!.comments = count;
|
news.firstWhere((item) => item?.id == currentNews.id)!.comments = count;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ import 'package:didvan/views/home/radar/widgets/categories_list.dart';
|
||||||
import 'package:didvan/views/home/widgets/date_picker_button.dart';
|
import 'package:didvan/views/home/widgets/date_picker_button.dart';
|
||||||
import 'package:didvan/views/home/widgets/logo_app_bar.dart';
|
import 'package:didvan/views/home/widgets/logo_app_bar.dart';
|
||||||
import 'package:didvan/utils/action_sheet.dart';
|
import 'package:didvan/utils/action_sheet.dart';
|
||||||
import 'package:didvan/views/home/widgets/radar_overview.dart';
|
import 'package:didvan/views/home/widgets/overview/radar.dart';
|
||||||
import 'package:didvan/views/home/widgets/search_field.dart';
|
import 'package:didvan/views/home/widgets/search_field.dart';
|
||||||
import 'package:didvan/views/widgets/animated_visibility.dart';
|
import 'package:didvan/views/widgets/animated_visibility.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/checkbox.dart';
|
import 'package:didvan/views/widgets/didvan/checkbox.dart';
|
||||||
|
|
@ -130,7 +130,7 @@ class _RadarState extends State<Radar> {
|
||||||
builder: (context, state, index) {
|
builder: (context, state, index) {
|
||||||
index += 2;
|
index += 2;
|
||||||
if (index % 15 == 0 && state.lastPage != state.page) {
|
if (index % 15 == 0 && state.lastPage != state.page) {
|
||||||
state.getRadars(page: index ~/ 15 + 1);
|
state.getRadars(page: state.page + 1);
|
||||||
}
|
}
|
||||||
index -= 2;
|
index -= 2;
|
||||||
if (index >= state.radars.length) {
|
if (index >= state.radars.length) {
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ class RadarDetailsState extends CoreProvier {
|
||||||
radars.any((r) => radar != null && r != null && r.id == radar.id);
|
radars.any((r) => radar != null && r != null && r.id == radar.id);
|
||||||
|
|
||||||
void onCommentsChanged(int count) {
|
void onCommentsChanged(int count) {
|
||||||
radars.firstWhere((radar) => radar!.id == currentRadar.id)!.comments =
|
radars.firstWhere((radar) => radar?.id == currentRadar.id)!.comments =
|
||||||
count;
|
count;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,10 @@ class CategoryItem extends StatelessWidget {
|
||||||
isVisible: !isColapsed,
|
isVisible: !isColapsed,
|
||||||
child: Container(
|
child: Container(
|
||||||
width: !_useWebMobileLayout(context)
|
width: !_useWebMobileLayout(context)
|
||||||
? _width(context) / 1.5
|
? _width(context) / 2
|
||||||
: ds.width / 5,
|
: ds.width / 5,
|
||||||
height: !_useWebMobileLayout(context)
|
height: !_useWebMobileLayout(context)
|
||||||
? _width(context) / 1.5
|
? _width(context) / 2
|
||||||
: ds.width / 5,
|
: ds.width / 5,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,27 @@ class BookmarksState extends CoreProvier {
|
||||||
final List<OverviewData> bookmarks = [];
|
final List<OverviewData> bookmarks = [];
|
||||||
String search = '';
|
String search = '';
|
||||||
String lastSearch = '';
|
String lastSearch = '';
|
||||||
|
int page = 1;
|
||||||
|
int lastPage = 1;
|
||||||
|
|
||||||
bool get searching => search != '';
|
bool get searching => search != '';
|
||||||
|
|
||||||
Future<void> getBookmarks() async {
|
Future<void> getBookmarks({required int page}) async {
|
||||||
if (search != '') {
|
if (search != '') {
|
||||||
lastSearch = search;
|
lastSearch = search;
|
||||||
}
|
}
|
||||||
|
if (page == 1) {
|
||||||
|
bookmarks.clear();
|
||||||
|
}
|
||||||
|
this.page = page;
|
||||||
appState = AppState.busy;
|
appState = AppState.busy;
|
||||||
final service = RequestService(RequestHelper.bookmarks());
|
final service =
|
||||||
|
RequestService(RequestHelper.bookmarks(page: page, search: search));
|
||||||
await service.httpGet();
|
await service.httpGet();
|
||||||
|
|
||||||
if (service.isSuccess) {
|
if (service.isSuccess) {
|
||||||
|
lastPage = service.result['lastPage'];
|
||||||
final marks = service.result['contents'];
|
final marks = service.result['contents'];
|
||||||
bookmarks.clear();
|
|
||||||
for (var i = 0; i < marks.length; i++) {
|
for (var i = 0; i < marks.length; i++) {
|
||||||
bookmarks.add(OverviewData.fromJson(marks[i]));
|
bookmarks.add(OverviewData.fromJson(marks[i]));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ 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/settings/bookmarks/bookmark_state.dart';
|
import 'package:didvan/views/home/settings/bookmarks/bookmark_state.dart';
|
||||||
import 'package:didvan/views/home/widgets/menu_item.dart';
|
import 'package:didvan/views/home/widgets/menu_item.dart';
|
||||||
import 'package:didvan/views/home/widgets/multitype_overview.dart';
|
import 'package:didvan/views/home/widgets/overview/multitype.dart';
|
||||||
import 'package:didvan/views/home/widgets/search_field.dart';
|
import 'package:didvan/views/home/widgets/search_field.dart';
|
||||||
import 'package:didvan/views/widgets/animated_visibility.dart';
|
import 'package:didvan/views/widgets/animated_visibility.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/card.dart';
|
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||||
|
|
@ -33,7 +33,7 @@ class _BookmarksState extends State<Bookmarks> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
Future.delayed(Duration.zero, () {
|
Future.delayed(Duration.zero, () {
|
||||||
context.read<BookmarksState>().getBookmarks();
|
context.read<BookmarksState>().getBookmarks(page: 1);
|
||||||
});
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
@ -74,14 +74,14 @@ class _BookmarksState extends State<Bookmarks> {
|
||||||
),
|
),
|
||||||
const DidvanDivider(),
|
const DidvanDivider(),
|
||||||
MenuItem(
|
MenuItem(
|
||||||
onTap: () => _onCategorySelected('videos'),
|
onTap: () => _onCategorySelected('video'),
|
||||||
title: 'ویدئوها',
|
title: 'ویدئوها',
|
||||||
icon: DidvanIcons.video_regular,
|
icon: DidvanIcons.video_regular,
|
||||||
iconSize: 24,
|
iconSize: 24,
|
||||||
),
|
),
|
||||||
const DidvanDivider(),
|
const DidvanDivider(),
|
||||||
MenuItem(
|
MenuItem(
|
||||||
onTap: () => _onCategorySelected('podcasts'),
|
onTap: () => _onCategorySelected('podcast'),
|
||||||
title: 'پادکستها',
|
title: 'پادکستها',
|
||||||
icon: DidvanIcons.podcast_regular,
|
icon: DidvanIcons.podcast_regular,
|
||||||
iconSize: 24,
|
iconSize: 24,
|
||||||
|
|
@ -103,26 +103,33 @@ class _BookmarksState extends State<Bookmarks> {
|
||||||
SliverStateHandler<BookmarksState>(
|
SliverStateHandler<BookmarksState>(
|
||||||
state: state,
|
state: state,
|
||||||
centerEmptyState: state.searching,
|
centerEmptyState: state.searching,
|
||||||
builder: (context, state, index) => MultitypeOverview(
|
builder: (context, state, index) {
|
||||||
item: state.bookmarks[index],
|
index++;
|
||||||
onMarkChanged: state.onMarkChanged,
|
if (index % 15 == 0 && state.lastPage != state.page) {
|
||||||
hasUnmarkConfirmation: true,
|
state.getBookmarks(page: state.page + 1);
|
||||||
),
|
}
|
||||||
|
index--;
|
||||||
|
return MultitypeOverview(
|
||||||
|
item: state.bookmarks[index],
|
||||||
|
onMarkChanged: state.onMarkChanged,
|
||||||
|
hasUnmarkConfirmation: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
placeholder: MultitypeOverview.placeholder,
|
placeholder: MultitypeOverview.placeholder,
|
||||||
itemPadding: const EdgeInsets.only(bottom: 8),
|
itemPadding: const EdgeInsets.only(bottom: 8),
|
||||||
emptyState: state.searching
|
emptyState: state.searching
|
||||||
? EmptyResult(onNewSearch: _focuseNode.requestFocus)
|
? EmptyResult(onNewSearch: _focuseNode.requestFocus)
|
||||||
: const EmptyList(),
|
: const EmptyList(),
|
||||||
enableEmptyState: state.bookmarks.isEmpty,
|
enableEmptyState: state.bookmarks.isEmpty,
|
||||||
childCount: state.bookmarks.length,
|
childCount:
|
||||||
onRetry: state.getBookmarks,
|
state.bookmarks.length + (state.page != state.lastPage ? 1 : 0),
|
||||||
|
onRetry: () => state.getBookmarks(page: state.page),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onCategorySelected(String type) {
|
void _onCategorySelected(String type) {
|
||||||
if (type != 'radar' && type != 'news') return;
|
|
||||||
FocusScope.of(context).unfocus();
|
FocusScope.of(context).unfocus();
|
||||||
Navigator.of(context).pushNamed(Routes.filteredBookmarks, arguments: type);
|
Navigator.of(context).pushNamed(Routes.filteredBookmarks, arguments: type);
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +142,7 @@ class _BookmarksState extends State<Bookmarks> {
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
_timer = Timer(const Duration(seconds: 1), () {
|
_timer = Timer(const Duration(seconds: 1), () {
|
||||||
state.search = value;
|
state.search = value;
|
||||||
state.getBookmarks();
|
state.getBookmarks(page: 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:didvan/models/view/app_bar_data.dart';
|
import 'package:didvan/models/view/app_bar_data.dart';
|
||||||
import 'package:didvan/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart';
|
import 'package:didvan/views/home/settings/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart';
|
||||||
import 'package:didvan/views/home/widgets/news_overview.dart';
|
import 'package:didvan/views/home/widgets/overview/news.dart';
|
||||||
import 'package:didvan/views/home/widgets/radar_overview.dart';
|
import 'package:didvan/views/home/widgets/overview/radar.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/scaffold.dart';
|
import 'package:didvan/views/widgets/didvan/scaffold.dart';
|
||||||
import 'package:didvan/views/widgets/state_handlers/empty_list.dart';
|
import 'package:didvan/views/widgets/state_handlers/empty_list.dart';
|
||||||
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
|
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
|
||||||
|
|
@ -20,7 +20,7 @@ class _FilteredBookmarksState extends State<FilteredBookmarks> {
|
||||||
void initState() {
|
void initState() {
|
||||||
Future.delayed(
|
Future.delayed(
|
||||||
Duration.zero,
|
Duration.zero,
|
||||||
context.read<FilteredBookmarksState>().getBookmarks,
|
() => context.read<FilteredBookmarksState>().getBookmarks(page: 1),
|
||||||
);
|
);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
@ -54,6 +54,11 @@ class _FilteredBookmarksState extends State<FilteredBookmarks> {
|
||||||
placeholder: RadarOverview.placeholder,
|
placeholder: RadarOverview.placeholder,
|
||||||
emptyState: const EmptyList(),
|
emptyState: const EmptyList(),
|
||||||
builder: (context, state, index) {
|
builder: (context, state, index) {
|
||||||
|
index++;
|
||||||
|
if (index % 15 == 0 && state.lastPage != state.page) {
|
||||||
|
state.getBookmarks(page: state.page + 1);
|
||||||
|
}
|
||||||
|
index--;
|
||||||
if (state.type == 'radar') {
|
if (state.type == 'radar') {
|
||||||
return RadarOverview(
|
return RadarOverview(
|
||||||
radar: state.bookmarks[index],
|
radar: state.bookmarks[index],
|
||||||
|
|
@ -69,7 +74,7 @@ class _FilteredBookmarksState extends State<FilteredBookmarks> {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
childCount: state.bookmarks.length,
|
childCount: state.bookmarks.length,
|
||||||
onRetry: () => state.getBookmarks(),
|
onRetry: () => state.getBookmarks(page: state.page),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -10,24 +10,42 @@ class FilteredBookmarksState extends CoreProvier {
|
||||||
String lastSearch = '';
|
String lastSearch = '';
|
||||||
final List<OverviewData> bookmarks = [];
|
final List<OverviewData> bookmarks = [];
|
||||||
final String type;
|
final String type;
|
||||||
|
int page = 1;
|
||||||
|
int lastPage = 1;
|
||||||
|
|
||||||
FilteredBookmarksState(this.type);
|
FilteredBookmarksState(this.type);
|
||||||
|
|
||||||
bool get searching => search != '';
|
bool get searching => search != '';
|
||||||
|
|
||||||
Future<void> getBookmarks() async {
|
Future<void> getBookmarks({required int page}) async {
|
||||||
if (search != '') {
|
if (search != '') {
|
||||||
lastSearch = search;
|
lastSearch = search;
|
||||||
}
|
}
|
||||||
|
if (page == 1) {
|
||||||
|
bookmarks.clear();
|
||||||
|
}
|
||||||
|
this.page = page;
|
||||||
appState = AppState.busy;
|
appState = AppState.busy;
|
||||||
|
String typeString = '';
|
||||||
|
if (type == 'video' || type == 'podcast') {
|
||||||
|
typeString = 'studios';
|
||||||
|
} else if (type == 'news') {
|
||||||
|
typeString = type;
|
||||||
|
} else {
|
||||||
|
typeString = type + 's';
|
||||||
|
}
|
||||||
final service = RequestService(
|
final service = RequestService(
|
||||||
RequestHelper.bookmarks(type: type == 'news' ? type : type + 's'),
|
RequestHelper.bookmarks(
|
||||||
|
type: typeString,
|
||||||
|
page: page,
|
||||||
|
studioType: type == 'podcast' || type == 'video' ? type : null,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
await service.httpGet();
|
await service.httpGet();
|
||||||
|
|
||||||
if (service.isSuccess) {
|
if (service.isSuccess) {
|
||||||
final marks = service.result[type != 'news' ? type + 's' : type];
|
lastPage = service.result['lastPage'];
|
||||||
bookmarks.clear();
|
final marks = service.result[typeString];
|
||||||
for (var i = 0; i < marks.length; i++) {
|
for (var i = 0; i < marks.length; i++) {
|
||||||
bookmarks.add(OverviewData.fromJson(marks[i]));
|
bookmarks.add(OverviewData.fromJson(marks[i]));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ class Settings extends StatelessWidget {
|
||||||
MenuItem(
|
MenuItem(
|
||||||
icon: DidvanIcons.didvan_solid,
|
icon: DidvanIcons.didvan_solid,
|
||||||
title: 'معرفی دیدوان',
|
title: 'معرفی دیدوان',
|
||||||
onTap: () => Navigator.of(context).pushNamed(Routes.aboutUs),
|
onTap: () => launch('https://didvan.app/'),
|
||||||
),
|
),
|
||||||
const DidvanDivider(),
|
const DidvanDivider(),
|
||||||
MenuItem(
|
MenuItem(
|
||||||
|
|
@ -101,14 +101,14 @@ class Settings extends StatelessWidget {
|
||||||
MenuItem(
|
MenuItem(
|
||||||
icon: DidvanIcons.alert_regular,
|
icon: DidvanIcons.alert_regular,
|
||||||
title: 'حریم خصوصی',
|
title: 'حریم خصوصی',
|
||||||
onTap: () => {},
|
onTap: () => launch('https://didvan.app/'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
DidvanText(
|
DidvanText(
|
||||||
'نسخه نرمافزار: آزمایشی',
|
'نسخه نرمافزار: 1.1.4',
|
||||||
style: Theme.of(context).textTheme.caption,
|
style: Theme.of(context).textTheme.caption,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class PodcastDetails extends StatelessWidget {
|
|
||||||
const PodcastDetails({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Material(
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).colorScheme.surface,
|
|
||||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(8)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:didvan/config/design_config.dart';
|
||||||
|
import 'package:didvan/models/view/app_bar_data.dart';
|
||||||
|
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/scaffold.dart';
|
||||||
|
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
|
||||||
|
class StudioDetails extends StatefulWidget {
|
||||||
|
final Map<String, dynamic> pageData;
|
||||||
|
|
||||||
|
const StudioDetails({Key? key, required this.pageData}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StudioDetails> createState() => _StudioDetailsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StudioDetailsState extends State<StudioDetails> {
|
||||||
|
bool _isFullScreen = false;
|
||||||
|
bool _isInit = true;
|
||||||
|
|
||||||
|
double _dwInPortrait = 0;
|
||||||
|
double _scaleInPortrait = 1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
final state = context.read<StudioDetailsState>();
|
||||||
|
|
||||||
|
Future.delayed(
|
||||||
|
Duration.zero,
|
||||||
|
() => state.getStudioDetails(widget.pageData['id']),
|
||||||
|
);
|
||||||
|
|
||||||
|
state.args = widget.pageData['args'];
|
||||||
|
if (Platform.isAndroid) WebView.platform = AndroidWebView();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _changeFullSceen(bool value) async {
|
||||||
|
if (value) {
|
||||||
|
await SystemChrome.setEnabledSystemUIMode(
|
||||||
|
SystemUiMode.manual,
|
||||||
|
overlays: [],
|
||||||
|
);
|
||||||
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
|
const SystemUiOverlayStyle(
|
||||||
|
systemNavigationBarColor: Colors.black,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await SystemChrome.setPreferredOrientations(
|
||||||
|
[DeviceOrientation.landscapeLeft],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await SystemChrome.setEnabledSystemUIMode(
|
||||||
|
SystemUiMode.manual,
|
||||||
|
overlays: [SystemUiOverlay.bottom, SystemUiOverlay.top],
|
||||||
|
);
|
||||||
|
await SystemChrome.setPreferredOrientations(
|
||||||
|
[DeviceOrientation.portraitUp],
|
||||||
|
);
|
||||||
|
DesignConfig.updateSystemUiOverlayStyle();
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_isFullScreen = value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final ds = MediaQuery.of(context).size;
|
||||||
|
if (_isInit) {
|
||||||
|
_dwInPortrait = MediaQuery.of(context).size.width;
|
||||||
|
_scaleInPortrait = _dwInPortrait / 576;
|
||||||
|
_isInit = false;
|
||||||
|
}
|
||||||
|
return Consumer<StudioDetailsState>(
|
||||||
|
builder: (context, state, child) => StateHandler<StudioDetailsState>(
|
||||||
|
state: state,
|
||||||
|
onRetry: () => state.getStudioDetails(state.currentStudio.id),
|
||||||
|
builder: (context, state) => state.studios.isEmpty
|
||||||
|
? const SizedBox()
|
||||||
|
: WillPopScope(
|
||||||
|
onWillPop: () async {
|
||||||
|
if (_isFullScreen) {
|
||||||
|
await _changeFullSceen(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
child: DidvanScaffold(
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
appBarData: _isFullScreen
|
||||||
|
? null
|
||||||
|
: AppBarData(
|
||||||
|
isSmall: true,
|
||||||
|
title: state.currentStudio.title,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: ds.width,
|
||||||
|
height: _isFullScreen ? ds.height : ds.width * 9 / 16,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
WebView(
|
||||||
|
allowsInlineMediaPlayback: true,
|
||||||
|
initialUrl: Uri.dataFromString(
|
||||||
|
'''
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width, initial-scale=$_scaleInPortrait"
|
||||||
|
/>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
||||||
|
.r1_iframe_embed {
|
||||||
|
height: ${MediaQuery.of(context).size.width / _scaleInPortrait}px !important;
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
@media(max-width:580px){
|
||||||
|
.r1_iframe_embed {
|
||||||
|
height: ${_dwInPortrait * 9 / 16 / _scaleInPortrait}px !important;
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
${state.currentStudio.media}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
''',
|
||||||
|
mimeType: 'text/html',
|
||||||
|
).toString(),
|
||||||
|
javascriptMode: JavascriptMode.unrestricted,
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
right: 42,
|
||||||
|
bottom: 8,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => _changeFullSceen(!_isFullScreen),
|
||||||
|
child: Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
width: 24,
|
||||||
|
height: 30,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:didvan/models/enums.dart';
|
||||||
|
import 'package:didvan/models/overview_data.dart';
|
||||||
|
import 'package:didvan/models/requests/studio.dart';
|
||||||
|
import 'package:didvan/models/studio_details_data.dart';
|
||||||
|
import 'package:didvan/providers/core_provider.dart';
|
||||||
|
import 'package:didvan/services/media/media.dart';
|
||||||
|
import 'package:didvan/services/network/request.dart';
|
||||||
|
import 'package:didvan/services/network/request_helper.dart';
|
||||||
|
|
||||||
|
class StudioDetailsState extends CoreProvier {
|
||||||
|
final List<StudioDetailsData?> studios = [];
|
||||||
|
late int initialIndex;
|
||||||
|
late StudioRequestArgs args;
|
||||||
|
int _selectedDetailsIndex = 0;
|
||||||
|
bool isFetchingNewItem = false;
|
||||||
|
final List<int> relatedQueue = [];
|
||||||
|
|
||||||
|
int _currentIndex = 0;
|
||||||
|
int get currentIndex => _currentIndex;
|
||||||
|
|
||||||
|
int get selectedDetailsIndex => _selectedDetailsIndex;
|
||||||
|
set selectedDetailsIndex(int value) {
|
||||||
|
_selectedDetailsIndex = value;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioDetailsData get currentStudio {
|
||||||
|
try {
|
||||||
|
return studios[_currentIndex]!;
|
||||||
|
} catch (e) {
|
||||||
|
return studios[_currentIndex + 1]!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getStudioDetails(int id,
|
||||||
|
{bool? isForward, StudioRequestArgs? args}) async {
|
||||||
|
if (args != null) {
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
if (isForward == null) {
|
||||||
|
appState = AppState.busy;
|
||||||
|
} else {
|
||||||
|
isFetchingNewItem = true;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
final service = RequestService(RequestHelper.studioDetails(id, this.args));
|
||||||
|
await service.httpGet();
|
||||||
|
if (service.isSuccess) {
|
||||||
|
final result = service.result;
|
||||||
|
final studio = StudioDetailsData.fromJson(result['studio']);
|
||||||
|
if (this.args.page == 0) {
|
||||||
|
studios.add(studio);
|
||||||
|
initialIndex = 0;
|
||||||
|
appState = AppState.idle;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.args.type == 'podcast') {
|
||||||
|
MediaService.currentPodcast = studio;
|
||||||
|
MediaService.podcastPlaylistArgs = args;
|
||||||
|
await MediaService.handleAudioPlayback(
|
||||||
|
audioSource: studio.media,
|
||||||
|
isVoiceMessage: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioDetailsData? prevStudio;
|
||||||
|
if (result['prevStudio'].isNotEmpty) {
|
||||||
|
prevStudio = StudioDetailsData.fromJson(result['prevStudio']);
|
||||||
|
}
|
||||||
|
|
||||||
|
StudioDetailsData? nextStudio;
|
||||||
|
if (result['nextStudio'].isNotEmpty) {
|
||||||
|
nextStudio = StudioDetailsData.fromJson(result['nextStudio']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isForward == null) {
|
||||||
|
studios
|
||||||
|
.addAll(List.generate(max(studio.order - 2, 0), (index) => null));
|
||||||
|
if (prevStudio != null) {
|
||||||
|
studios.add(prevStudio);
|
||||||
|
}
|
||||||
|
studios.add(studio);
|
||||||
|
if (nextStudio != null) {
|
||||||
|
studios.add(nextStudio);
|
||||||
|
}
|
||||||
|
_currentIndex = initialIndex = studio.order - 1;
|
||||||
|
} else if (isForward) {
|
||||||
|
if (!exists(nextStudio) && nextStudio != null) {
|
||||||
|
studios.add(nextStudio);
|
||||||
|
}
|
||||||
|
_currentIndex++;
|
||||||
|
} else if (!isForward) {
|
||||||
|
if (!exists(prevStudio) && prevStudio != null) {
|
||||||
|
studios[_currentIndex - 2] = prevStudio;
|
||||||
|
}
|
||||||
|
_currentIndex--;
|
||||||
|
}
|
||||||
|
isFetchingNewItem = false;
|
||||||
|
appState = AppState.idle;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//why? total page state shouldn't die!
|
||||||
|
if (isForward == null) {
|
||||||
|
appState = AppState.failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getRelatedContents() async {
|
||||||
|
if (currentStudio.relatedContents.isNotEmpty) return;
|
||||||
|
relatedQueue.add(currentStudio.id);
|
||||||
|
final service = RequestService(RequestHelper.tag(
|
||||||
|
ids: currentStudio.tags.map((tag) => tag.id).toList(),
|
||||||
|
itemId: currentStudio.id,
|
||||||
|
type: 'studio',
|
||||||
|
));
|
||||||
|
await service.httpGet();
|
||||||
|
if (service.isSuccess) {
|
||||||
|
final relateds = service.result['contents'];
|
||||||
|
for (var i = 0; i < relateds.length; i++) {
|
||||||
|
studios
|
||||||
|
.where((element) => element != null)
|
||||||
|
.firstWhere((element) => element!.id == currentStudio.id)!
|
||||||
|
.relatedContents
|
||||||
|
.add(OverviewData.fromJson(relateds[i]));
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool exists(StudioDetailsData? studio) =>
|
||||||
|
studios.any((r) => studio != null && r != null && r.id == studio.id);
|
||||||
|
|
||||||
|
void onCommentsChanged(int count) {
|
||||||
|
studios.firstWhere((studio) => studio?.id == currentStudio.id)!.comments =
|
||||||
|
count;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class StudioDetailsWidget extends StatelessWidget {
|
||||||
|
const StudioDetailsWidget({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Consumer<StudioDetailsState>(
|
||||||
|
builder: (context, state, child) => StateHandler<StudioDetailsState>(
|
||||||
|
onRetry: () {},
|
||||||
|
state: state,
|
||||||
|
builder: (context, state) => Container(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
children: [
|
||||||
|
_TabItem(
|
||||||
|
icon: DidvanIcons.description_solid,
|
||||||
|
title: 'توضیحات',
|
||||||
|
onTap: () => state.selectedDetailsIndex = 0,
|
||||||
|
isSelected: state.selectedDetailsIndex == 0,
|
||||||
|
),
|
||||||
|
_TabItem(
|
||||||
|
icon: DidvanIcons.chats_solid,
|
||||||
|
title: 'نظرات',
|
||||||
|
onTap: () => state.selectedDetailsIndex = 1,
|
||||||
|
isSelected: state.selectedDetailsIndex == 1,
|
||||||
|
),
|
||||||
|
_TabItem(
|
||||||
|
icon: DidvanIcons.puzzle_solid,
|
||||||
|
title: 'مطالب مرتبط',
|
||||||
|
onTap: () => state.selectedDetailsIndex = 2,
|
||||||
|
isSelected: state.selectedDetailsIndex == 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TabItem extends StatelessWidget {
|
||||||
|
final IconData icon;
|
||||||
|
final String title;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
final bool isSelected;
|
||||||
|
const _TabItem({
|
||||||
|
Key? key,
|
||||||
|
required this.icon,
|
||||||
|
required this.title,
|
||||||
|
required this.onTap,
|
||||||
|
required this.isSelected,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
Color? _color(context) =>
|
||||||
|
isSelected ? Theme.of(context).colorScheme.focusedBorder : null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
color: _color(context),
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: 64,
|
||||||
|
height: 1,
|
||||||
|
color: _color(context),
|
||||||
|
),
|
||||||
|
DidvanText(
|
||||||
|
title,
|
||||||
|
color: _color(context),
|
||||||
|
style: Theme.of(context).textTheme.caption,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,93 @@
|
||||||
|
import 'package:didvan/models/enums.dart';
|
||||||
|
import 'package:didvan/models/overview_data.dart';
|
||||||
|
import 'package:didvan/models/requests/studio.dart';
|
||||||
import 'package:didvan/providers/core_provider.dart';
|
import 'package:didvan/providers/core_provider.dart';
|
||||||
|
import 'package:didvan/providers/user_provider.dart';
|
||||||
|
import 'package:didvan/services/network/request.dart';
|
||||||
|
import 'package:didvan/services/network/request_helper.dart';
|
||||||
|
|
||||||
class StudioState extends CoreProvier {
|
class StudioState extends CoreProvier {
|
||||||
|
final List<OverviewData> studios = [];
|
||||||
|
|
||||||
|
String? search;
|
||||||
|
String? lastSearch;
|
||||||
|
int page = 1;
|
||||||
|
int lastPage = 1;
|
||||||
|
|
||||||
int selectedSortTypeIndex = 0;
|
int selectedSortTypeIndex = 0;
|
||||||
|
|
||||||
bool videosSelected = true;
|
bool _videosSelected = true;
|
||||||
|
|
||||||
|
bool get videosSelected => _videosSelected;
|
||||||
|
|
||||||
|
set videosSelected(bool value) {
|
||||||
|
if (_videosSelected == value) return;
|
||||||
|
_videosSelected = value;
|
||||||
|
studios.clear();
|
||||||
|
getStudioOverviews(page: page);
|
||||||
|
}
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
search = '';
|
||||||
|
lastSearch = '';
|
||||||
|
_videosSelected = true;
|
||||||
|
selectedSortTypeIndex = 0;
|
||||||
|
Future.delayed(Duration.zero, () {
|
||||||
|
getStudioOverviews(page: 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
String get order {
|
||||||
|
if (selectedSortTypeIndex == 0) return 'date';
|
||||||
|
if (selectedSortTypeIndex == 1) return 'view';
|
||||||
|
return 'comment';
|
||||||
|
}
|
||||||
|
|
||||||
|
String get type {
|
||||||
|
if (videosSelected) return 'video';
|
||||||
|
return 'podcast';
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getStudioOverviews({required int page}) async {
|
||||||
|
this.page = page;
|
||||||
|
if (page == 1) {
|
||||||
|
appState = AppState.busy;
|
||||||
|
}
|
||||||
|
final service = RequestService(
|
||||||
|
RequestHelper.studioOverviews(
|
||||||
|
args: StudioRequestArgs(
|
||||||
|
page: page,
|
||||||
|
type: type,
|
||||||
|
search: search,
|
||||||
|
order: order,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await service.httpGet();
|
||||||
|
if (service.isSuccess) {
|
||||||
|
if (page == 1) {
|
||||||
|
studios.clear();
|
||||||
|
}
|
||||||
|
lastPage = service.result['lastPage'];
|
||||||
|
final studioItems = service.result['studios'];
|
||||||
|
for (var i = 0; i < studioItems.length; i++) {
|
||||||
|
studios.add(OverviewData.fromJson(studioItems[i]));
|
||||||
|
}
|
||||||
|
appState = AppState.idle;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
appState = AppState.failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> changeMark(int id, bool value) async {
|
||||||
|
studios.firstWhere((element) => element.id == id).marked = value;
|
||||||
|
notifyListeners();
|
||||||
|
UserProvider.changeStudioMark(id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onCommentsChanged(int id, int count) {
|
||||||
|
studios.firstWhere((radar) => radar.id == id).comments = count;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,15 @@ class StudioTabBar extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final state = context.watch<StudioState>();
|
final state = context.watch<StudioState>();
|
||||||
return Container(
|
return AnimatedContainer(
|
||||||
|
duration: DesignConfig.lowAnimationDuration,
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: state.videosSelected
|
color: state.videosSelected
|
||||||
? Theme.of(context).colorScheme.secondary
|
? Theme.of(context).colorScheme.secondary
|
||||||
: Theme.of(context).primaryColor,
|
: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
borderRadius: DesignConfig.lowBorderRadius,
|
borderRadius: DesignConfig.lowBorderRadius,
|
||||||
),
|
),
|
||||||
|
|
@ -32,7 +33,7 @@ class StudioTabBar extends StatelessWidget {
|
||||||
icon: DidvanIcons.video_solid,
|
icon: DidvanIcons.video_solid,
|
||||||
selectedColor: Theme.of(context).colorScheme.secondary,
|
selectedColor: Theme.of(context).colorScheme.secondary,
|
||||||
title: 'ویدئو',
|
title: 'ویدئو',
|
||||||
onTap: () {},
|
onTap: () => state.videosSelected = true,
|
||||||
isSelected: state.videosSelected,
|
isSelected: state.videosSelected,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -46,7 +47,7 @@ class StudioTabBar extends StatelessWidget {
|
||||||
icon: DidvanIcons.podcast_solid,
|
icon: DidvanIcons.podcast_solid,
|
||||||
selectedColor: Theme.of(context).colorScheme.focusedBorder,
|
selectedColor: Theme.of(context).colorScheme.focusedBorder,
|
||||||
title: 'پادکست',
|
title: 'پادکست',
|
||||||
onTap: () {},
|
onTap: () => state.videosSelected = false,
|
||||||
isSelected: !state.videosSelected,
|
isSelected: !state.videosSelected,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,174 @@
|
||||||
|
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/studio_details_data.dart';
|
||||||
|
import 'package:didvan/services/media/media.dart';
|
||||||
|
import 'package:didvan/views/home/widgets/audio/audio_slider.dart';
|
||||||
|
import 'package:didvan/views/home/widgets/bookmark_button.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/icon_button.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:didvan/views/widgets/ink_wrapper.dart';
|
||||||
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AudioPlayerWidget extends StatelessWidget {
|
||||||
|
final StudioDetailsData podcast;
|
||||||
|
const AudioPlayerWidget({Key? key, required this.podcast}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.vertical(top: Radius.circular(8)),
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 20),
|
||||||
|
height: 3,
|
||||||
|
width: 50,
|
||||||
|
color: Theme.of(context).colorScheme.hint,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: SkeletonImage(
|
||||||
|
imageUrl: podcast.image,
|
||||||
|
aspectRatio: 1 / 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
DidvanText(
|
||||||
|
podcast.title,
|
||||||
|
style: Theme.of(context).textTheme.bodyText1,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: AudioSlider(
|
||||||
|
tag: podcast.media,
|
||||||
|
showTimer: true,
|
||||||
|
duration: podcast.duration,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
DidvanIconButton(
|
||||||
|
icon: DidvanIcons.sleep_timer_regular,
|
||||||
|
onPressed: () {},
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
DidvanIconButton(
|
||||||
|
size: 32,
|
||||||
|
icon: DidvanIcons.media_forward_solid,
|
||||||
|
onPressed: () {
|
||||||
|
MediaService.audioPlayer.seek(
|
||||||
|
Duration(
|
||||||
|
seconds:
|
||||||
|
MediaService.audioPlayer.position.inSeconds + 30,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const DidvanText('30', isEnglishFont: true),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
_PlayPouseAnimatedIcon(
|
||||||
|
audioSource: podcast.media,
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
DidvanIconButton(
|
||||||
|
size: 32,
|
||||||
|
icon: DidvanIcons.media_backward_solid,
|
||||||
|
onPressed: () {
|
||||||
|
MediaService.audioPlayer.seek(
|
||||||
|
Duration(
|
||||||
|
seconds:
|
||||||
|
MediaService.audioPlayer.position.inSeconds - 10,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const DidvanText('10', isEnglishFont: true),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
BookmarkButton(
|
||||||
|
gestureSize: 48,
|
||||||
|
value: podcast.marked,
|
||||||
|
onMarkChanged: (value) {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlayPouseAnimatedIcon extends StatefulWidget {
|
||||||
|
final String audioSource;
|
||||||
|
const _PlayPouseAnimatedIcon({Key? key, required this.audioSource})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_PlayPouseAnimatedIcon> createState() => __PlayPouseAnimatedIconState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class __PlayPouseAnimatedIconState extends State<_PlayPouseAnimatedIcon>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late final AnimationController _animationController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_animationController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: DesignConfig.lowAnimationDuration,
|
||||||
|
);
|
||||||
|
if (MediaService.audioPlayer.playing) {
|
||||||
|
_animationController.forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InkWrapper(
|
||||||
|
borderRadius: BorderRadius.circular(100),
|
||||||
|
onPressed: () {
|
||||||
|
MediaService.handleAudioPlayback(
|
||||||
|
audioSource: widget.audioSource,
|
||||||
|
isVoiceMessage: false,
|
||||||
|
);
|
||||||
|
if (MediaService.audioPlayer.playing) {
|
||||||
|
_animationController.forward();
|
||||||
|
} else {
|
||||||
|
_animationController.reverse();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.title,
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: AnimatedIcon(
|
||||||
|
size: 40,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
icon: AnimatedIcons.play_pause,
|
||||||
|
progress: _animationController,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_animationController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
|
||||||
|
import 'package:didvan/config/design_config.dart';
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/services/media/media.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AudioSlider extends StatelessWidget {
|
||||||
|
final String tag;
|
||||||
|
final bool showTimer;
|
||||||
|
final int? duration;
|
||||||
|
final bool disableThumb;
|
||||||
|
const AudioSlider({
|
||||||
|
Key? key,
|
||||||
|
required this.tag,
|
||||||
|
this.showTimer = false,
|
||||||
|
this.duration,
|
||||||
|
this.disableThumb = false,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
bool get _isPlaying => MediaService.audioPlayerTag == tag;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return IgnorePointer(
|
||||||
|
ignoring: MediaService.audioPlayerTag != tag,
|
||||||
|
child: Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: StreamBuilder<Duration>(
|
||||||
|
stream: _isPlaying ? MediaService.audioPlayer.positionStream : null,
|
||||||
|
builder: (context, snapshot) => ProgressBar(
|
||||||
|
thumbColor: Theme.of(context).colorScheme.title,
|
||||||
|
progressBarColor: DesignConfig.isDark
|
||||||
|
? Theme.of(context).colorScheme.title
|
||||||
|
: Theme.of(context).colorScheme.primary,
|
||||||
|
baseBarColor: Theme.of(context).colorScheme.border,
|
||||||
|
bufferedBarColor: Theme.of(context).colorScheme.splash,
|
||||||
|
total: MediaService.audioPlayer.duration ??
|
||||||
|
Duration(seconds: duration ?? 0),
|
||||||
|
progress: snapshot.data ?? Duration.zero,
|
||||||
|
buffered: _isPlaying
|
||||||
|
? MediaService.audioPlayer.bufferedPosition
|
||||||
|
: Duration.zero,
|
||||||
|
thumbRadius: disableThumb ? 0 : 6,
|
||||||
|
barHeight: 3,
|
||||||
|
timeLabelTextStyle: TextStyle(
|
||||||
|
fontSize: showTimer ? null : 0,
|
||||||
|
height: showTimer ? 3 : 0,
|
||||||
|
fontFamily: DesignConfig.fontFamily.replaceAll(
|
||||||
|
'-FA',
|
||||||
|
'',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onSeek: (value) => _onSeek(value.inMilliseconds),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSeek(int value) {
|
||||||
|
MediaService.audioPlayer.seek(Duration(milliseconds: value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
import 'package:audio_video_progress_bar/audio_video_progress_bar.dart';
|
|
||||||
import 'package:didvan/services/media/media.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class AudioSlider extends StatelessWidget {
|
|
||||||
final String tag;
|
|
||||||
const AudioSlider({Key? key, required this.tag}) : super(key: key);
|
|
||||||
|
|
||||||
bool get _isPlaying => MediaService.audioPlayerTag == tag;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return IgnorePointer(
|
|
||||||
ignoring: MediaService.audioPlayerTag != tag,
|
|
||||||
child: Directionality(
|
|
||||||
textDirection: TextDirection.ltr,
|
|
||||||
child: StreamBuilder<Duration>(
|
|
||||||
stream: _isPlaying ? MediaService.audioPlayer.positionStream : null,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
return ProgressBar(
|
|
||||||
total: MediaService.audioPlayer.duration ?? Duration.zero,
|
|
||||||
progress: snapshot.data ?? Duration.zero,
|
|
||||||
buffered:
|
|
||||||
_isPlaying ? MediaService.audioPlayer.bufferedPosition : null,
|
|
||||||
thumbRadius: 6,
|
|
||||||
barHeight: 3,
|
|
||||||
timeLabelTextStyle: const TextStyle(fontSize: 0),
|
|
||||||
onSeek: (value) => _onSeek(value.inMilliseconds),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onSeek(int value) {
|
|
||||||
MediaService.audioPlayer.seek(Duration(milliseconds: value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,10 +2,16 @@ import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/app_icons.dart';
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/services/media/media.dart';
|
import 'package:didvan/services/media/media.dart';
|
||||||
|
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||||
|
import 'package:didvan/views/home/studio/studio_details/widgets/studio_details.dart';
|
||||||
|
import 'package:didvan/views/home/widgets/audio/audio_player_widget.dart';
|
||||||
|
import 'package:didvan/views/home/widgets/audio/audio_slider.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:didvan/views/widgets/skeleton_image.dart';
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:expandable_bottom_sheet/expandable_bottom_sheet.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class DidvanBNB extends StatelessWidget {
|
class DidvanBNB extends StatelessWidget {
|
||||||
final int currentTabIndex;
|
final int currentTabIndex;
|
||||||
|
|
@ -15,6 +21,9 @@ class DidvanBNB extends StatelessWidget {
|
||||||
{Key? key, required this.currentTabIndex, required this.onTabChanged})
|
{Key? key, required this.currentTabIndex, required this.onTabChanged})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
|
bool get _enablePlayerController =>
|
||||||
|
MediaService.currentPodcast != null || MediaService.audioPlayer.playing;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return StreamBuilder<bool>(
|
return StreamBuilder<bool>(
|
||||||
|
|
@ -22,29 +31,89 @@ class DidvanBNB extends StatelessWidget {
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
return Stack(
|
return Stack(
|
||||||
children: [
|
children: [
|
||||||
AnimatedContainer(
|
GestureDetector(
|
||||||
duration: DesignConfig.lowAnimationDuration,
|
onTap: () => _showPlayerBottomSheet(context),
|
||||||
height: snapshot.data == true ? 120 : 72,
|
child: AnimatedContainer(
|
||||||
decoration: BoxDecoration(
|
padding: const EdgeInsets.only(top: 12),
|
||||||
color: DesignConfig.isDark
|
duration: DesignConfig.lowAnimationDuration,
|
||||||
? Theme.of(context).colorScheme.focused
|
height: _enablePlayerController ? 120 : 72,
|
||||||
: Theme.of(context).colorScheme.navigation,
|
decoration: BoxDecoration(
|
||||||
borderRadius: const BorderRadius.vertical(
|
color: DesignConfig.isDark
|
||||||
top: Radius.circular(16),
|
? Theme.of(context).colorScheme.focused
|
||||||
),
|
: Theme.of(context).colorScheme.navigation,
|
||||||
),
|
borderRadius: const BorderRadius.vertical(
|
||||||
child: Row(
|
top: Radius.circular(16),
|
||||||
children: [
|
|
||||||
const DidvanIconButton(
|
|
||||||
icon: DidvanIcons.close_regular,
|
|
||||||
gestureSize: 24,
|
|
||||||
onPressed: MediaService.resetAudioPlayer,
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
),
|
||||||
if (MediaService.audioPlayerCover != null)
|
child: !_enablePlayerController
|
||||||
SkeletonImage(imageUrl: MediaService.audioPlayerCover!),
|
? const SizedBox()
|
||||||
const SizedBox(width: 16),
|
: SizedBox(
|
||||||
],
|
height: 48,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
right: 12,
|
||||||
|
left: 16,
|
||||||
|
),
|
||||||
|
child: DidvanIconButton(
|
||||||
|
icon: DidvanIcons.close_regular,
|
||||||
|
color: DesignConfig.isDark
|
||||||
|
? null
|
||||||
|
: Theme.of(context).colorScheme.secondCTA,
|
||||||
|
gestureSize: 28,
|
||||||
|
onPressed: MediaService.resetAudioPlayer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SkeletonImage(
|
||||||
|
imageUrl: MediaService.currentPodcast!.image,
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
MediaService.currentPodcast!.title,
|
||||||
|
color: DesignConfig.isDark
|
||||||
|
? null
|
||||||
|
: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.secondCTA,
|
||||||
|
),
|
||||||
|
AudioSlider(
|
||||||
|
disableThumb: true,
|
||||||
|
tag: MediaService.audioPlayerTag!,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 12,
|
||||||
|
right: 16,
|
||||||
|
),
|
||||||
|
child: DidvanIconButton(
|
||||||
|
gestureSize: 28,
|
||||||
|
color: DesignConfig.isDark
|
||||||
|
? null
|
||||||
|
: Theme.of(context).colorScheme.secondCTA,
|
||||||
|
icon: snapshot.data!
|
||||||
|
? DidvanIcons.pause_solid
|
||||||
|
: DidvanIcons.play_solid,
|
||||||
|
onPressed: () {
|
||||||
|
MediaService.handleAudioPlayback(
|
||||||
|
audioSource: MediaService.audioPlayerTag,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
|
|
@ -105,6 +174,55 @@ class DidvanBNB extends StatelessWidget {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _showPlayerBottomSheet(BuildContext context) {
|
||||||
|
final sheetKey = GlobalKey<ExpandableBottomSheetState>();
|
||||||
|
bool isExpanded = false;
|
||||||
|
final detailsState = context.read<StudioDetailsState>();
|
||||||
|
showModalBottomSheet(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
context: context,
|
||||||
|
isScrollControlled: true,
|
||||||
|
builder: (context) => ChangeNotifierProvider<StudioDetailsState>.value(
|
||||||
|
value: detailsState,
|
||||||
|
child: ExpandableBottomSheet(
|
||||||
|
key: sheetKey,
|
||||||
|
background: const SizedBox(),
|
||||||
|
persistentHeader: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
AudioPlayerWidget(
|
||||||
|
podcast: MediaService.currentPodcast!,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
DidvanIconButton(
|
||||||
|
size: 32,
|
||||||
|
icon: DidvanIcons.angle_down_regular,
|
||||||
|
onPressed: () {
|
||||||
|
if (!isExpanded) {
|
||||||
|
sheetKey.currentState?.expand();
|
||||||
|
isExpanded = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isExpanded = false;
|
||||||
|
sheetKey.currentState?.contract();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
expandableContent: const StudioDetailsWidget(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NavBarItem extends StatelessWidget {
|
class _NavBarItem extends StatelessWidget {
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,14 @@ import 'package:flutter/material.dart';
|
||||||
class BookmarkButton extends StatefulWidget {
|
class BookmarkButton extends StatefulWidget {
|
||||||
final bool value;
|
final bool value;
|
||||||
final void Function(bool value) onMarkChanged;
|
final void Function(bool value) onMarkChanged;
|
||||||
final bool bigGestureSize;
|
|
||||||
final bool askForConfirmation;
|
final bool askForConfirmation;
|
||||||
|
final double gestureSize;
|
||||||
const BookmarkButton({
|
const BookmarkButton({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.value,
|
required this.value,
|
||||||
this.bigGestureSize = false,
|
|
||||||
required this.onMarkChanged,
|
required this.onMarkChanged,
|
||||||
this.askForConfirmation = false,
|
this.askForConfirmation = false,
|
||||||
|
required this.gestureSize,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -40,7 +40,7 @@ class _BookmarkButtonState extends State<BookmarkButton> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DidvanIconButton(
|
return DidvanIconButton(
|
||||||
gestureSize: widget.bigGestureSize ? 32 : 24,
|
gestureSize: widget.gestureSize,
|
||||||
icon: _value ? DidvanIcons.bookmark_solid : DidvanIcons.bookmark_regular,
|
icon: _value ? DidvanIcons.bookmark_solid : DidvanIcons.bookmark_regular,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
bool confirm = false;
|
bool confirm = false;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:didvan/utils/date_time.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DurationWidget extends StatelessWidget {
|
||||||
|
final int duration;
|
||||||
|
const DurationWidget({Key? key, required this.duration}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
DidvanIcons.timer_regular,
|
||||||
|
size: 16,
|
||||||
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
DidvanText(
|
||||||
|
DateTimeUtils.normalizeTimeDuration(
|
||||||
|
Duration(seconds: duration),
|
||||||
|
),
|
||||||
|
isEnglishFont: true,
|
||||||
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Icon(
|
||||||
|
DidvanIcons.play_circle_regular,
|
||||||
|
size: 16,
|
||||||
|
color: Theme.of(context).colorScheme.focusedBorder,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -112,7 +112,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
bigGestureSize: true,
|
gestureSize: 32,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: 60,
|
width: 60,
|
||||||
|
|
@ -151,7 +151,7 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
bigGestureSize: true,
|
gestureSize: 32,
|
||||||
),
|
),
|
||||||
if (widget.isRadar)
|
if (widget.isRadar)
|
||||||
DidvanIconButton(
|
DidvanIconButton(
|
||||||
|
|
@ -233,7 +233,19 @@ class _FloatingNavigationBarState extends State<FloatingNavigationBar> {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
Navigator.of(context).pushNamed(
|
Navigator.of(context).pushNamed(
|
||||||
Routes.direct,
|
Routes.direct,
|
||||||
arguments: {},
|
arguments: {
|
||||||
|
'radarAttachment': RadarAttachment(
|
||||||
|
id: widget.item.id,
|
||||||
|
title: widget.item.title,
|
||||||
|
description: widget.item.contents.first.text,
|
||||||
|
timeToRead: widget.item.timeToRead,
|
||||||
|
image: widget.item.image,
|
||||||
|
forManagers: widget.item.forManagers,
|
||||||
|
categories: widget.item.categories,
|
||||||
|
createdAt: widget.item.createdAt,
|
||||||
|
),
|
||||||
|
'type': 'پشتیبانی'
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: DidvanIcons.description_regular,
|
icon: DidvanIcons.description_regular,
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ class NewsOverview extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
BookmarkButton(
|
BookmarkButton(
|
||||||
|
gestureSize: 24,
|
||||||
value: news.marked,
|
value: news.marked,
|
||||||
onMarkChanged: (value) => onMarkChanged(news.id, value),
|
onMarkChanged: (value) => onMarkChanged(news.id, value),
|
||||||
askForConfirmation: hasUnmarkConfirmation,
|
askForConfirmation: hasUnmarkConfirmation,
|
||||||
|
|
@ -1,87 +1,90 @@
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/models/overview_data.dart';
|
import 'package:didvan/models/overview_data.dart';
|
||||||
import 'package:didvan/models/requests/news.dart';
|
import 'package:didvan/models/requests/studio.dart';
|
||||||
import 'package:didvan/routes/routes.dart';
|
|
||||||
import 'package:didvan/utils/date_time.dart';
|
import 'package:didvan/utils/date_time.dart';
|
||||||
|
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||||
import 'package:didvan/views/home/widgets/bookmark_button.dart';
|
import 'package:didvan/views/home/widgets/bookmark_button.dart';
|
||||||
|
import 'package:didvan/views/home/widgets/duration_widget.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/card.dart';
|
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/divider.dart';
|
import 'package:didvan/views/widgets/didvan/divider.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:didvan/views/widgets/shimmer_placeholder.dart';
|
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
|
||||||
import 'package:didvan/views/widgets/skeleton_image.dart';
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class PodcastOverview extends StatelessWidget {
|
class PodcastOverview extends StatelessWidget {
|
||||||
final OverviewData news;
|
final OverviewData podcast;
|
||||||
final NewsRequestArgs? newsRequestArgs;
|
|
||||||
final void Function(int id, bool value) onMarkChanged;
|
final void Function(int id, bool value) onMarkChanged;
|
||||||
final bool hasUnmarkConfirmation;
|
final StudioRequestArgs? studioRequestArgs;
|
||||||
const PodcastOverview({
|
const PodcastOverview({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.news,
|
required this.podcast,
|
||||||
required this.onMarkChanged,
|
required this.onMarkChanged,
|
||||||
this.newsRequestArgs,
|
this.studioRequestArgs,
|
||||||
this.hasUnmarkConfirmation = false,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DidvanCard(
|
return DidvanCard(
|
||||||
onTap: () => Navigator.of(context).pushNamed(
|
onTap: () {
|
||||||
Routes.newsDetails,
|
context
|
||||||
arguments: {
|
.read<StudioDetailsState>()
|
||||||
'onMarkChanged': onMarkChanged,
|
.getStudioDetails(podcast.id, args: studioRequestArgs);
|
||||||
'id': news.id,
|
},
|
||||||
'args': newsRequestArgs,
|
|
||||||
'hasUnmarkConfirmation': hasUnmarkConfirmation,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
SkeletonImage(
|
SkeletonImage(
|
||||||
imageUrl: news.image,
|
imageUrl: podcast.image,
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SizedBox(
|
child: Column(
|
||||||
height: 64,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
child: DidvanText(
|
children: [
|
||||||
news.title,
|
DidvanText(
|
||||||
style: Theme.of(context).textTheme.bodyText1,
|
podcast.title,
|
||||||
),
|
style: Theme.of(context).textTheme.bodyText1,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
DidvanText(
|
||||||
|
DateTimeUtils.momentGenerator(podcast.createdAt),
|
||||||
|
style: Theme.of(context).textTheme.overline,
|
||||||
|
color: Theme.of(context).colorScheme.caption,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
DidvanText(
|
DidvanText(
|
||||||
news.description,
|
podcast.description,
|
||||||
maxLines: 3,
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const DidvanDivider(verticalPadding: 8),
|
const DidvanDivider(verticalPadding: 8),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
DurationWidget(duration: podcast.duration!),
|
||||||
children: [
|
const Spacer(),
|
||||||
DidvanText(
|
DidvanIconButton(
|
||||||
news.reference!,
|
gestureSize: 28,
|
||||||
style: Theme.of(context).textTheme.caption,
|
icon: DidvanIcons.download_regular,
|
||||||
),
|
onPressed: () {},
|
||||||
DidvanText(
|
|
||||||
' - ' + DateTimeUtils.momentGenerator(news.createdAt),
|
|
||||||
style: Theme.of(context).textTheme.caption,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
BookmarkButton(
|
BookmarkButton(
|
||||||
value: news.marked,
|
gestureSize: 24,
|
||||||
onMarkChanged: (value) => onMarkChanged(news.id, value),
|
value: podcast.marked,
|
||||||
askForConfirmation: hasUnmarkConfirmation,
|
onMarkChanged: (value) => onMarkChanged(podcast.id, value),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
@ -95,7 +98,7 @@ class PodcastOverview extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const ShimmerPlaceholder(height: 64, width: 64),
|
const ShimmerPlaceholder(height: 64, width: 64),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
|
|
@ -109,7 +112,7 @@ class PodcastOverview extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 16),
|
||||||
const ShimmerPlaceholder(
|
const ShimmerPlaceholder(
|
||||||
height: 16,
|
height: 16,
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
|
|
@ -117,19 +120,21 @@ class PodcastOverview extends StatelessWidget {
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
const ShimmerPlaceholder(
|
const ShimmerPlaceholder(
|
||||||
height: 16,
|
height: 16,
|
||||||
width: double.infinity,
|
width: 200,
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
const ShimmerPlaceholder(
|
|
||||||
height: 16,
|
|
||||||
width: 100,
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
const DidvanDivider(verticalPadding: 8),
|
const DidvanDivider(verticalPadding: 8),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
children: [
|
||||||
children: const [
|
ShimmerPlaceholder(
|
||||||
ShimmerPlaceholder(height: 12, width: 150),
|
height: 36,
|
||||||
ShimmerPlaceholder(height: 24, width: 24),
|
width: 92,
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const ShimmerPlaceholder(width: 24, height: 24),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
const ShimmerPlaceholder(width: 24, height: 24),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -97,11 +97,13 @@ class RadarOverview extends StatelessWidget {
|
||||||
DidvanText(
|
DidvanText(
|
||||||
radar.description,
|
radar.description,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const DidvanDivider(),
|
const DidvanDivider(),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
BookmarkButton(
|
BookmarkButton(
|
||||||
|
gestureSize: 24,
|
||||||
value: radar.marked,
|
value: radar.marked,
|
||||||
onMarkChanged: (value) => onMarkChanged(radar.id, value),
|
onMarkChanged: (value) => onMarkChanged(radar.id, value),
|
||||||
askForConfirmation: hasUnmarkConfirmation,
|
askForConfirmation: hasUnmarkConfirmation,
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
|
import 'package:didvan/models/overview_data.dart';
|
||||||
|
import 'package:didvan/models/requests/studio.dart';
|
||||||
|
import 'package:didvan/routes/routes.dart';
|
||||||
|
import 'package:didvan/utils/date_time.dart';
|
||||||
|
import 'package:didvan/views/home/studio/studio_details/studio_details_state.dart';
|
||||||
|
import 'package:didvan/views/home/widgets/bookmark_button.dart';
|
||||||
|
import 'package:didvan/views/home/widgets/duration_widget.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/text.dart';
|
||||||
|
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
|
||||||
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class VideoOverview extends StatelessWidget {
|
||||||
|
final OverviewData video;
|
||||||
|
final void Function(int id, int count) onCommentsChanged;
|
||||||
|
final void Function(int id, bool value) onMarkChanged;
|
||||||
|
final bool hasUnmarkConfirmation;
|
||||||
|
final StudioRequestArgs? studioRequestArgs;
|
||||||
|
const VideoOverview({
|
||||||
|
Key? key,
|
||||||
|
required this.video,
|
||||||
|
required this.onCommentsChanged,
|
||||||
|
required this.onMarkChanged,
|
||||||
|
required this.hasUnmarkConfirmation,
|
||||||
|
this.studioRequestArgs,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return DidvanCard(
|
||||||
|
onTap: () => Navigator.of(context).pushNamed(
|
||||||
|
Routes.studioDetails,
|
||||||
|
arguments: {
|
||||||
|
'onMarkChanged': onMarkChanged,
|
||||||
|
'onCommentsChanged': onCommentsChanged,
|
||||||
|
'id': video.id,
|
||||||
|
'args': studioRequestArgs,
|
||||||
|
'hasUnmarkConfirmation': hasUnmarkConfirmation,
|
||||||
|
'isVideo': true,
|
||||||
|
'state': context.read<StudioDetailsState>(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
SkeletonImage(
|
||||||
|
imageUrl: video.image,
|
||||||
|
height: 108,
|
||||||
|
width: 108,
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: Center(
|
||||||
|
child: Container(
|
||||||
|
height: 28,
|
||||||
|
width: 28,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.secondary
|
||||||
|
.withOpacity(0.7),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
DidvanIcons.play_solid,
|
||||||
|
color: Theme.of(context).colorScheme.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
video.title,
|
||||||
|
style: Theme.of(context).textTheme.bodyText1,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
DidvanIcons.calendar_day_regular,
|
||||||
|
size: 16,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
DidvanText(
|
||||||
|
DateTimeUtils.momentGenerator(video.createdAt),
|
||||||
|
style: Theme.of(context).textTheme.overline,
|
||||||
|
color: Theme.of(context).colorScheme.caption,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const DidvanDivider(verticalPadding: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
DurationWidget(duration: video.duration!),
|
||||||
|
const Spacer(),
|
||||||
|
DidvanIconButton(
|
||||||
|
gestureSize: 28,
|
||||||
|
icon: DidvanIcons.download_regular,
|
||||||
|
onPressed: () {},
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
BookmarkButton(
|
||||||
|
gestureSize: 24,
|
||||||
|
value: video.marked,
|
||||||
|
onMarkChanged: (value) => onMarkChanged(video.id, value),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Widget get placeHolder => DidvanCard(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const ShimmerPlaceholder(height: 108, width: 108),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const ShimmerPlaceholder(height: 20),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
const ShimmerPlaceholder(width: 100, height: 16),
|
||||||
|
const DidvanDivider(verticalPadding: 10),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
ShimmerPlaceholder(
|
||||||
|
height: 36,
|
||||||
|
width: 92,
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
const ShimmerPlaceholder(width: 24, height: 24),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
const ShimmerPlaceholder(width: 24, height: 24),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -110,7 +110,15 @@ class _SplashState extends State<Splash> {
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
log(token);
|
log(token);
|
||||||
RequestService.token = token;
|
RequestService.token = token;
|
||||||
await userProvider.getUserInfo();
|
final result = await userProvider.getUserInfo();
|
||||||
|
if (!result) {
|
||||||
|
StorageService.delete(key: 'token');
|
||||||
|
Navigator.of(context).pushNamedAndRemoveUntil(
|
||||||
|
Routes.splash,
|
||||||
|
(_) => false,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await ServerDataProvider.getData();
|
await ServerDataProvider.getData();
|
||||||
}
|
}
|
||||||
Navigator.of(context).pushReplacementNamed(
|
Navigator.of(context).pushReplacementNamed(
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,10 @@ class DidvanAppBar extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: kToolbarHeight + MediaQuery.of(context).padding.top,
|
height: appBarData.isSmall ? 56 : 72,
|
||||||
width: MediaQuery.of(context).size.width,
|
width: MediaQuery.of(context).size.width,
|
||||||
padding: const EdgeInsets.only(right: 4, left: 20),
|
padding: const EdgeInsets.only(right: 4, left: 20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: backgroundColor,
|
|
||||||
border: hasBorder
|
border: hasBorder
|
||||||
? Border(
|
? Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
|
|
@ -53,6 +52,7 @@ class DidvanAppBar extends StatelessWidget {
|
||||||
appBarData.title!,
|
appBarData.title!,
|
||||||
style: Theme.of(context).textTheme.headline3,
|
style: Theme.of(context).textTheme.headline3,
|
||||||
color: Theme.of(context).colorScheme.title,
|
color: Theme.of(context).colorScheme.title,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
if (appBarData.subtitle != null)
|
if (appBarData.subtitle != null)
|
||||||
DidvanText(
|
DidvanText(
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/app_icons.dart';
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/utils/date_time.dart';
|
import 'package:didvan/utils/date_time.dart';
|
||||||
import 'package:didvan/views/home/widgets/multitype_overview.dart';
|
import 'package:didvan/views/home/widgets/overview/multitype.dart';
|
||||||
import 'package:didvan/views/home/widgets/tag_item.dart';
|
import 'package:didvan/views/home/widgets/tag_item.dart';
|
||||||
import 'package:didvan/views/widgets/animated_visibility.dart';
|
import 'package:didvan/views/widgets/animated_visibility.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/card.dart';
|
import 'package:didvan/views/widgets/didvan/card.dart';
|
||||||
|
|
@ -14,6 +14,7 @@ import 'package:didvan/views/widgets/item_title.dart';
|
||||||
import 'package:didvan/views/widgets/skeleton_image.dart';
|
import 'package:didvan/views/widgets/skeleton_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_html/flutter_html.dart';
|
import 'package:flutter_html/flutter_html.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
class DidvanPageView extends StatefulWidget {
|
class DidvanPageView extends StatefulWidget {
|
||||||
final List items;
|
final List items;
|
||||||
|
|
@ -183,9 +184,11 @@ class _DidvanPageViewState extends State<DidvanPageView> {
|
||||||
if (content.text != null) {
|
if (content.text != null) {
|
||||||
return Html(
|
return Html(
|
||||||
data: content.text,
|
data: content.text,
|
||||||
|
onAnchorTap: (href, context, map, element) => launch(href!),
|
||||||
style: {
|
style: {
|
||||||
'*': Style(
|
'*': Style(
|
||||||
direction: TextDirection.rtl,
|
direction: TextDirection.rtl,
|
||||||
|
textAlign: TextAlign.right,
|
||||||
lineHeight: LineHeight.percent(135),
|
lineHeight: LineHeight.percent(135),
|
||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import 'package:flutter/material.dart';
|
||||||
class DidvanScaffold extends StatefulWidget {
|
class DidvanScaffold extends StatefulWidget {
|
||||||
final List<Widget>? slivers;
|
final List<Widget>? slivers;
|
||||||
final List<Widget>? children;
|
final List<Widget>? children;
|
||||||
final AppBarData appBarData;
|
final AppBarData? appBarData;
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
final Color? backgroundColor;
|
final Color? backgroundColor;
|
||||||
final bool reverse;
|
final bool reverse;
|
||||||
|
|
@ -40,18 +40,15 @@ class _DidvanScaffoldState extends State<DidvanScaffold> {
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
reverse: widget.reverse,
|
reverse: widget.reverse,
|
||||||
slivers: [
|
slivers: [
|
||||||
if (!widget.reverse)
|
if (!widget.reverse && widget.appBarData != null)
|
||||||
SliverAppBar(
|
SliverAppBar(
|
||||||
toolbarHeight: kToolbarHeight,
|
toolbarHeight: (widget.appBarData!.isSmall ? 56 : 72) -
|
||||||
|
statusBarHeight,
|
||||||
backgroundColor: widget.backgroundColor ??
|
backgroundColor: widget.backgroundColor ??
|
||||||
Theme.of(context).colorScheme.background,
|
Theme.of(context).colorScheme.background,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
flexibleSpace: DidvanAppBar(appBarData: widget.appBarData),
|
flexibleSpace: DidvanAppBar(appBarData: widget.appBarData!),
|
||||||
),
|
|
||||||
if (!widget.reverse)
|
|
||||||
const SliverToBoxAdapter(
|
|
||||||
child: SizedBox(height: 16),
|
|
||||||
),
|
),
|
||||||
if (widget.children != null)
|
if (widget.children != null)
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
|
|
@ -79,9 +76,9 @@ class _DidvanScaffoldState extends State<DidvanScaffold> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (widget.reverse)
|
if (widget.reverse && widget.appBarData != null)
|
||||||
_AppBar(
|
_AppBar(
|
||||||
appBarData: widget.appBarData,
|
appBarData: widget.appBarData!,
|
||||||
scrollController: _scrollController,
|
scrollController: _scrollController,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@ class DidvanText extends StatelessWidget {
|
||||||
fontWeight: fontWeight,
|
fontWeight: fontWeight,
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
)).copyWith(
|
)).copyWith(
|
||||||
fontFamily: isEnglishFont ? DesignConfig.fontFamily : null,
|
fontFamily: isEnglishFont
|
||||||
|
? DesignConfig.fontFamily.replaceAll('-FA', '')
|
||||||
|
: null,
|
||||||
height: 1.7,
|
height: 1.7,
|
||||||
),
|
),
|
||||||
overflow: overflow,
|
overflow: overflow,
|
||||||
|
|
|
||||||
37
pubspec.lock
37
pubspec.lock
|
|
@ -127,6 +127,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4+1"
|
version: "1.0.4+1"
|
||||||
|
expandable_bottom_sheet:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: expandable_bottom_sheet
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1+1"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -566,7 +573,7 @@ packages:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.1.2"
|
||||||
process:
|
process:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
@ -782,6 +789,34 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.1"
|
||||||
|
webview_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: webview_flutter
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.1"
|
||||||
|
webview_flutter_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: webview_flutter_android
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.8.3"
|
||||||
|
webview_flutter_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: webview_flutter_platform_interface
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.8.1"
|
||||||
|
webview_flutter_wkwebview:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: webview_flutter_wkwebview
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.7.1"
|
||||||
win32:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -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: 1.1.0+1
|
version: 1.2.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.12.0 <3.0.0"
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
|
|
@ -62,6 +62,8 @@ dependencies:
|
||||||
image_cropper: ^1.5.0
|
image_cropper: ^1.5.0
|
||||||
firebase_messaging: ^11.2.8
|
firebase_messaging: ^11.2.8
|
||||||
firebase_core: ^1.13.1
|
firebase_core: ^1.13.1
|
||||||
|
webview_flutter: ^3.0.1
|
||||||
|
expandable_bottom_sheet: ^1.1.1+1
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue