aya playback, quick links changed with api & improvements.

development
Sikander Saleem 5 years ago
parent 25867bdf9a
commit 4165e17124

@ -0,0 +1,15 @@
<svg xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 66.9 68.71" xmlns="http://www.w3.org/2000/svg">
<defs>
<clipPath id="clip-path" transform="translate(4.85 2.92)">
<rect style="fill: none" height="60.82" id="SVGID" rx="6" width="60.82"/>
</clipPath>
</defs>
<g data-name="Layer 2" id="Layer_2">
<g data-name="Layer 1" id="Layer_1-2">
<g style="clip-path: url(#clip-path)">
<rect style="fill: #e4e5e6" height="66.9" width="66.9"/>
<path style="fill: #b3b3b3" d="M56.25,51.18c-3-1.44-8.33-4-9.87-4.76s-5.76-2.9-5.76-2.9-.21-3.5-1.44-3.71l.21-2.37a17.94,17.94,0,0,0,1.44-5.36s1.44,1.24,2-1.54,1.23-7.93-1-6.7a31.2,31.2,0,0,0,.05-6.59c-.41-2.47-2.52-10.09-12-10.09s-11.58,7.62-12,10.09a31.2,31.2,0,0,0,.05,6.59c-2.26-1.23-1.54,3.92-1,6.7s1.95,1.54,1.95,1.54a17.94,17.94,0,0,0,1.44,5.36l.21,2.37C19.21,40,19,43.52,19,43.52s-4.21,2.18-5.76,2.9-6.88,3.32-9.87,4.76S.1,61.73.1,61.73l29.71,4.06,29.71-4.06S59.24,52.63,56.25,51.18Z" transform="translate(4.85 2.92)"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,18 @@
import 'package:tangheem/app_state/app_state.dart';
import 'package:tangheem/classes/consts.dart';
import 'package:tangheem/models/general_response_model.dart';
import 'api_client.dart';
class AdminConfigurationApiClient {
static final AdminConfigurationApiClient _instance = AdminConfigurationApiClient._internal();
AdminConfigurationApiClient._internal();
factory AdminConfigurationApiClient() => _instance;
Future<GeneralResponseModel> addDiscussion(String discussionText, String ayaTangheemTypeId) async {
String url = "${ApiConsts.adminConfiguration}Discussion_Add";
var postParams = {"discussionText": discussionText, "ayaTangheemTypeId": ayaTangheemTypeId};
var _headers = {"Authorization": "Bearer ${AppState().token}"};
return await ApiClient().postJsonForObject((json) => GeneralResponseModel.fromJson(json), url, postParams, headers: _headers);
}
}

@ -4,6 +4,7 @@ import 'package:tangheem/models/aya_tangheem_property.dart';
import 'package:tangheem/models/aya_tangheem_type.dart';
import 'package:tangheem/models/aya_tangheem_type_mapped.dart';
import 'package:tangheem/models/country_model.dart';
import 'package:tangheem/models/discussion_model.dart';
import 'package:tangheem/models/general_response_model.dart';
import 'package:tangheem/models/quick_links_model.dart';
import 'package:tangheem/models/tangheem_type_model.dart';
@ -97,4 +98,12 @@ class TangheemUserApiClient {
var postParams = {};
return await ApiClient().postJsonForObject((json) => CountryModel.fromJson(json), url, postParams);
}
Future<DiscussionModel> getDiscussionByTangheemID(int pageNumber, String tangheemID) async {
String url = "${ApiConsts.tangheemUsers}Discussion_Get";
var postParams = {"itemsPerPage": 5, "currentPageNo": pageNumber, "ayaTangheemTypeId": tangheemID};
postParams["itemsPerPage"] = null;
postParams["currentPageNo"] = null;
return await ApiClient().postJsonForObject((json) => DiscussionModel.fromJson(json), url, postParams);
}
}

@ -0,0 +1,21 @@
import 'package:tangheem/models/surah_model.dart';
import 'package:tangheem/models/authentication_user_model.dart';
class AppState {
static final AppState _instance = AppState._internal();
AppState._internal();
factory AppState() => _instance;
SurahModel _surahModel;
SurahModel get getSurahModel => _surahModel;
void setSurahModel(SurahModel _surahModel) {
this._surahModel = _surahModel;
}
AuthenticationUserModel _authenticationUser;
bool get isUserLogin => _authenticationUser != null;
String get token => _authenticationUser?.result?.data?.token;
void setAuthenticationModel(AuthenticationUserModel _authenticationUser) {
this._authenticationUser = _authenticationUser;
}
}

@ -1,13 +0,0 @@
import 'package:tangheem/models/surah_model.dart';
class SurahAppState {
static final SurahAppState _instance = SurahAppState._internal();
SurahAppState._internal();
factory SurahAppState() => _instance;
SurahModel _surahModel;
SurahModel get getSurahModel => _surahModel;
void setSurahModel(SurahModel _surahModel) {
this._surahModel = _surahModel;
}
}

@ -3,5 +3,12 @@ class ApiConsts {
static String baseUrl = "https://api.cssynapses.com/tangheem/"; // Live server
static String authentication = baseUrl + "api/Authentication/";
static String tangheemUsers = baseUrl + "api/TangheemUsers/";
static String adminConfiguration = baseUrl + "api/AdminConfiguration/";
static String user = baseUrl + "api/User/";
}
class GlobalConsts {
static String isRememberMe = "remember_me";
static String email = "email";
static String password = "password";
}

@ -1,5 +0,0 @@
extension EmailValidator on String {
bool isValidEmail() {
return RegExp(r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$').hasMatch(this);
}
}

@ -0,0 +1,14 @@
import 'package:intl/intl.dart';
extension EmailValidator on String {
bool isValidEmail() {
return RegExp(r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$').hasMatch(this);
}
String toFormattedDate() {
DateFormat inputFormat = DateFormat('yyyy-mm-ddThh:mm:ss');
DateTime inputDate = inputFormat.parse(this);
DateFormat outputFormat = DateFormat('dd/MM/yyyy hh:mm:a');
return outputFormat.format(inputDate);
}
}

@ -42,6 +42,7 @@ class AyatTangheemTypeMappedData {
int ayatNumberInSurah;
String highlightText;
String userId;
String ayahTextBase;
List<TangheemProperty> property;
List<VoiceNote> voiceNote;
@ -57,6 +58,7 @@ class AyatTangheemTypeMappedData {
this.ayatNumberInSurah,
this.highlightText,
this.userId,
this.ayahTextBase,
this.property,
this.voiceNote});
@ -72,6 +74,7 @@ class AyatTangheemTypeMappedData {
ayatNumberInSurah = json['ayatNumberInSurah'];
highlightText = json['highlightText'];
userId = json['userId'];
ayahTextBase = json['ayahTextBase'];
if (json['property'] != null) {
property = new List<TangheemProperty>();
json['property'].forEach((v) {
@ -99,6 +102,7 @@ class AyatTangheemTypeMappedData {
data['ayatNumberInSurah'] = this.ayatNumberInSurah;
data['highlightText'] = this.highlightText;
data['userId'] = this.userId;
data['ayahTextBase'] = this.ayahTextBase;
if (this.property != null) {
data['property'] = this.property.map((v) => v.toJson()).toList();
}
@ -151,23 +155,28 @@ class VoiceNote {
String voiceNoteId;
String voiceNotePath;
String exposeFilePath;
String userId;
String filePath;
String voiceNoteAuthorId;
String userName;
String fileName;
String fileType;
String ayaTangheemTypeId;
String profilePicture;
VoiceNote({this.voiceNoteId, this.voiceNotePath, this.exposeFilePath, this.userId, this.userName, this.fileName, this.fileType, this.ayaTangheemTypeId});
VoiceNote(
{this.voiceNoteId, this.voiceNotePath, this.exposeFilePath, this.filePath, this.voiceNoteAuthorId, this.userName, this.fileName, this.fileType, this.ayaTangheemTypeId, this.profilePicture});
VoiceNote.fromJson(Map<String, dynamic> json) {
voiceNoteId = json['voiceNoteId'];
voiceNotePath = json['voiceNotePath'];
exposeFilePath = json['exposeFilePath'];
userId = json['userId'];
filePath = json['filePath'];
voiceNoteAuthorId = json['voiceNoteAuthorId'];
userName = json['userName'];
fileName = json['fileName'];
fileType = json['fileType'];
ayaTangheemTypeId = json['ayaTangheemTypeId'];
profilePicture = json['profilePicture'];
}
Map<String, dynamic> toJson() {
@ -175,11 +184,13 @@ class VoiceNote {
data['voiceNoteId'] = this.voiceNoteId;
data['voiceNotePath'] = this.voiceNotePath;
data['exposeFilePath'] = this.exposeFilePath;
data['userId'] = this.userId;
data['filePath'] = this.filePath;
data['voiceNoteAuthorId'] = this.voiceNoteAuthorId;
data['userName'] = this.userName;
data['fileName'] = this.fileName;
data['fileType'] = this.fileType;
data['ayaTangheemTypeId'] = this.ayaTangheemTypeId;
data['profilePicture'] = this.profilePicture;
return data;
}
}

@ -0,0 +1,97 @@
class DiscussionModel {
int totalItemsCount;
int statusCode;
String message;
List<DiscussionModelData> data;
DiscussionModel(
{this.totalItemsCount, this.statusCode, this.message, this.data});
DiscussionModel.fromJson(Map<String, dynamic> json) {
totalItemsCount = json['totalItemsCount'];
statusCode = json['statusCode'];
message = json['message'];
if (json['data'] != null) {
data = new List<DiscussionModelData>();
json['data'].forEach((v) {
data.add(new DiscussionModelData.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['totalItemsCount'] = this.totalItemsCount;
data['statusCode'] = this.statusCode;
data['message'] = this.message;
if (this.data != null) {
data['data'] = this.data.map((v) => v.toJson()).toList();
}
return data;
}
}
class DiscussionModelData {
String discussionId;
String discussionText;
String ayaTangheemTypeId;
int statusId;
String status;
bool isResponse;
String responseId;
String userName;
String ayaText;
String highlightText;
String adminResponse;
String adminDiscussionId;
String date;
DiscussionModelData(
{this.discussionId,
this.discussionText,
this.ayaTangheemTypeId,
this.statusId,
this.status,
this.isResponse,
this.responseId,
this.userName,
this.ayaText,
this.highlightText,
this.adminResponse,
this.adminDiscussionId,
this.date});
DiscussionModelData.fromJson(Map<String, dynamic> json) {
discussionId = json['discussionId'];
discussionText = json['discussionText'];
ayaTangheemTypeId = json['ayaTangheemTypeId'];
statusId = json['statusId'];
status = json['status'];
isResponse = json['isResponse'];
responseId = json['responseId'];
userName = json['userName'];
ayaText = json['ayaText'];
highlightText = json['highlightText'];
adminResponse = json['adminResponse'];
adminDiscussionId = json['adminDiscussionId'];
date = json['date'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['discussionId'] = this.discussionId;
data['discussionText'] = this.discussionText;
data['ayaTangheemTypeId'] = this.ayaTangheemTypeId;
data['statusId'] = this.statusId;
data['status'] = this.status;
data['isResponse'] = this.isResponse;
data['responseId'] = this.responseId;
data['userName'] = this.userName;
data['ayaText'] = this.ayaText;
data['highlightText'] = this.highlightText;
data['adminResponse'] = this.adminResponse;
data['adminDiscussionId'] = this.adminDiscussionId;
data['date'] = this.date;
return data;
}
}

@ -1,9 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:tangheem/api/tangheem_user_api_client.dart';
import 'package:tangheem/app_state/app_state.dart';
import 'package:tangheem/classes/colors.dart';
import 'package:tangheem/classes/utils.dart';
import 'package:tangheem/models/quick_links_model.dart';
import 'package:tangheem/ui/screens/contact_us_screen.dart';
import 'package:tangheem/ui/screens/login_screen.dart';
import 'package:tangheem/ui/screens/quran_screen.dart';
import 'package:url_launcher/url_launcher.dart';
class CommonAppbar extends StatefulWidget {
final bool showDrawer;
@ -20,10 +25,21 @@ class CommonAppbar extends StatefulWidget {
class _CommonAppbarState extends State<CommonAppbar> {
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
List<QuickLinksData> quickLinks = [];
@override
void initState() {
super.initState();
getQuickLinks();
}
getQuickLinks() async {
if (widget.showDrawer) {
try {
quickLinks = (await TangheemUserApiClient().quickLinks())?.data ?? [];
quickLinks.sort((a, b) => a.orderNo.compareTo(b.orderNo));
} catch (ex) {}
setState(() {});
}
}
@override
@ -115,6 +131,10 @@ class _CommonAppbarState extends State<CommonAppbar> {
commonIconButton("assets/icons/reduce_size.svg", () {}),
commonIconButton("assets/icons/notification.svg", () {}),
commonIconButton("assets/icons/user_logged.svg", () {
if (AppState().isUserLogin) {
Utils.showToast("أنت بالفعل تسجيل الدخول");
return;
}
Navigator.pushNamed(context, LoginScreen.routeName);
}),
],
@ -229,12 +249,10 @@ class _CommonAppbarState extends State<CommonAppbar> {
padding: EdgeInsets.only(left: 32, right: 32),
child: Row(
children: [
commonIconButton("assets/icons/linkedin.svg", () {}, size: 35),
commonIconButton("assets/icons/pinterest.svg", () {}, size: 35),
commonIconButton("assets/icons/whatsapp.svg", () {}, size: 35),
commonIconButton("assets/icons/facebook.svg", () {}, size: 35),
commonIconButton("assets/icons/instgram.svg", () {}, size: 35),
commonIconButton("assets/icons/twitter.svg", () {}, size: 35),
for (QuickLinksData _quickLink in quickLinks)
commonIconButton(_quickLink.exposeFilePath, () {
_launchURL(_quickLink.imageUrl);
}, size: 35, isAsset: false),
],
),
),
@ -261,11 +279,13 @@ class _CommonAppbarState extends State<CommonAppbar> {
);
}
Widget commonIconButton(String icon, VoidCallback onPressed, {double size}) {
void _launchURL(String _url) async => await canLaunch(_url) ? await launch(_url) : throw 'Could not launch $_url';
Widget commonIconButton(String icon, VoidCallback onPressed, {double size, bool isAsset = true}) {
return Expanded(
child: IconButton(
padding: EdgeInsets.zero,
icon: SvgPicture.asset(icon, height: size ?? 25, width: size ?? 30),
icon: isAsset ? SvgPicture.asset(icon, height: size ?? 25, width: size ?? 30) : Image.network(icon, height: size ?? 25, width: size ?? 30),
onPressed: () {
Navigator.pop(context);
Future.delayed(Duration(milliseconds: 200), () => onPressed());

@ -0,0 +1,98 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:tangheem/api/user_api_client.dart';
import 'package:tangheem/classes/colors.dart';
import 'package:tangheem/classes/utils.dart';
import 'package:tangheem/widgets/common_textfield_widget.dart';
import 'package:tangheem/widgets/otp_widget.dart';
class DiscussionInputDialog extends StatefulWidget {
final Function(String) onCommentPress;
DiscussionInputDialog({Key key, this.onCommentPress}) : super(key: key);
@override
_DiscussionInputDialogState createState() {
return _DiscussionInputDialogState();
}
}
class _DiscussionInputDialogState extends State<DiscussionInputDialog> {
final TextEditingController _commentController = TextEditingController();
final FocusNode _focusNode = FocusNode();
bool hasError = false;
String errorMessage;
String otpMessage = "";
@override
void initState() {
super.initState();
_focusNode.requestFocus();
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Dialog(
insetPadding: EdgeInsets.symmetric(horizontal: 60.0, vertical: 24.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 0,
backgroundColor: Colors.transparent,
child: Directionality(
textDirection: TextDirection.rtl,
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: ColorConsts.primaryBlue,
borderRadius: BorderRadius.circular(16),
),
padding: EdgeInsets.symmetric(vertical: 16, horizontal: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"اضاف تعليق",
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white, fontSize: 16),
),
SizedBox(height: 12),
CommonTextFieldWidget(hint: "فحوى التعليق", controller: _commentController, maxLines: 5, focusNode: _focusNode),
SizedBox(height: 12),
SizedBox(
width: double.infinity,
height: 40,
child: TextButton(
onPressed: () {
if (_commentController.text.length < 1) {
Utils.showToast("يجب أن تكتب كلمات قليلة");
return;
}
_focusNode.unfocus();
widget.onCommentPress(_commentController.text);
},
style: TextButton.styleFrom(
primary: Colors.white,
padding: EdgeInsets.all(2),
backgroundColor: ColorConsts.secondaryPink,
textStyle: TextStyle(fontSize: 14, fontFamily: "DroidKufi"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6.0),
),
),
child: Text("أضف تعليقك"),
),
),
],
),
),
),
);
}
}

@ -3,7 +3,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:tangheem/api/tangheem_user_api_client.dart';
import 'package:tangheem/classes/colors.dart';
import 'package:tangheem/classes/utils.dart';
import 'package:tangheem/extensions/email_validator.dart';
import 'package:tangheem/extensions/string_extensions.dart';
import 'package:tangheem/widgets/common_textfield_widget.dart';
class ContactUsScreen extends StatefulWidget {

@ -7,7 +7,7 @@ import 'package:tangheem/models/general_response_model.dart';
import 'package:tangheem/ui/dialogs/change_password_dialog.dart';
import 'package:tangheem/ui/dialogs/otp_dialog.dart';
import 'package:tangheem/widgets/common_textfield_widget.dart';
import 'package:tangheem/extensions/email_validator.dart';
import 'package:tangheem/extensions/string_extensions.dart';
class ForgotPasswordScreen extends StatefulWidget {
static const String routeName = "/forgot_password";

@ -4,12 +4,11 @@ import 'package:flutter/painting.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:tangheem/api/tangheem_user_api_client.dart';
import 'package:tangheem/app_state/surah_app_state.dart';
import 'package:tangheem/app_state/app_state.dart';
import 'package:tangheem/classes/colors.dart';
import 'package:tangheem/classes/utils.dart';
import 'package:tangheem/models/surah_model.dart';
import 'package:tangheem/models/tangheem_type_model.dart';
import 'package:tangheem/ui/screens/quran_screen.dart';
import 'package:tangheem/ui/screens/tangheem_screen.dart';
import 'package:tangheem/widgets/common_dropdown_button.dart';
@ -48,12 +47,17 @@ class _HomeScreenState extends State<HomeScreen> {
Utils.showLoading(context);
try {
_surahModel = await TangheemUserApiClient().getSurahs();
SurahAppState().setSurahModel(_surahModel);
AppState().setSurahModel(_surahModel);
_tangheemType = await TangheemUserApiClient().getTangheemType();
_surahList = _surahModel.data.map((element) => element.nameAR).toList();
_tangheemList = _tangheemType?.data?.where((element) => element.isActive)?.toList()?.map((element) => element.tangheemTypeName)?.toList() ?? [];
setState(() {});
// QuickLinkProvider().setQuickLinksModel(await TangheemUserApiClient().quickLinks());
// AppState().setQuickLinksModel(await TangheemUserApiClient().quickLinks());
} catch (ex, tr) {
print(ex);
Utils.handleException(ex, null);
} finally {
Utils.hideLoading(context);
@ -157,7 +161,7 @@ class _HomeScreenState extends State<HomeScreen> {
),
),
SizedBox(height: 16),
Container(
Container(
height: 50,
padding: EdgeInsets.only(top: 4, bottom: 6),
child: TextField(

@ -1,13 +1,16 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tangheem/api/authentication_api_client.dart';
import 'package:tangheem/app_state/app_state.dart';
import 'package:tangheem/classes/colors.dart';
import 'package:tangheem/classes/consts.dart';
import 'package:tangheem/classes/utils.dart';
import 'package:tangheem/models/authentication_user_model.dart';
import 'package:tangheem/ui/screens/forgot_password_screen.dart';
import 'package:tangheem/ui/screens/registration_screen.dart';
import 'package:tangheem/widgets/common_textfield_widget.dart';
import 'package:tangheem/extensions/email_validator.dart';
import 'package:tangheem/extensions/string_extensions.dart';
class LoginScreen extends StatefulWidget {
static const String routeName = "/login";
@ -30,6 +33,19 @@ class _LoginScreenState extends State<LoginScreen> {
@override
void initState() {
super.initState();
checkPrefs();
}
checkPrefs() {
SharedPreferences.getInstance().then((value) {
if ((value.getBool(GlobalConsts.isRememberMe) ?? false) == true) {
_isRemember = true;
_emailController.text = value.getString(GlobalConsts.email);
_passwordController.text = value.getString(GlobalConsts.password);
} else {
_isRemember = false;
}
});
}
@override
@ -42,9 +58,19 @@ class _LoginScreenState extends State<LoginScreen> {
try {
_authenticationUser = await AuthenticationApiClient().authenticateUser(_email, _password);
Utils.showToast("تسجيل الدخول بنجاح");
AppState().setAuthenticationModel(_authenticationUser);
SharedPreferences prefs = await SharedPreferences.getInstance();
if (!_isRemember) {
_email = "";
_password = "";
}
await prefs.setBool(GlobalConsts.isRememberMe, _isRemember);
await prefs.setString(GlobalConsts.email, _email);
await prefs.setString(GlobalConsts.password, _password);
Utils.hideLoading(context);
Navigator.pop(context);
} catch (ex, tr) {
Utils.handleException(ex, null);
} finally {
Utils.hideLoading(context);
}
}

@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:tangheem/api/tangheem_user_api_client.dart';
import 'package:tangheem/app_state/surah_app_state.dart';
import 'package:tangheem/app_state/app_state.dart';
import 'package:tangheem/classes/colors.dart';
import 'package:tangheem/classes/utils.dart';
import 'package:tangheem/models/aya_model.dart';
@ -57,12 +57,12 @@ class _QuranScreenState extends State<QuranScreen> {
void getSurah() async {
try {
if (SurahAppState().getSurahModel == null) {
if (AppState().getSurahModel == null) {
Utils.showLoading(context);
_surahModel = await TangheemUserApiClient().getSurahs();
SurahAppState().setSurahModel(_surahModel);
AppState().setSurahModel(_surahModel);
} else {
_surahModel = SurahAppState().getSurahModel;
_surahModel = AppState().getSurahModel;
}
_surahList = _surahModel.data.map((element) => element.nameAR).toList();
_currentPage = 1;
@ -145,11 +145,15 @@ class _QuranScreenState extends State<QuranScreen> {
final ScrollController scrollController = ScrollController();
ScrollToId scrollToId;
String getBismillahWithSurahName(String _surahName, bool isShowBismillah, bool isFirstIsAya) {
String getBismillahWithSurahName(int _surahID, String _surahName, bool isShowBismillah, bool isFirstIsAya) {
String _bismillah = "\n بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيم";
if (_surahID == 9) {
_bismillah = "";
}
if (isFirstIsAya && isShowBismillah) {
return "" + _surahName + "\n بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ \n";
return "" + _surahName + "$_bismillah \n";
} else if (isShowBismillah) {
return "\n" + _surahName + "\n بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ \n";
return "\n" + _surahName + "$_bismillah \n";
}
return "" + _surahName + "\n";
}
@ -181,7 +185,9 @@ class _QuranScreenState extends State<QuranScreen> {
String _surahAya = "";
_ayaModel?.data?.forEach((element) {
var temp = element.numberInSurah == 1 ? getBismillahWithSurahName(element.surahNameAR, element.surahID != 1, _surahAya.length <= 1) + element.reverseAyatNumber() : element.reverseAyatNumber();
var temp = element.numberInSurah == 1
? getBismillahWithSurahName(element.surahID, element.surahNameAR, element.surahID != 1, _surahAya.length <= 1) + element.reverseAyatNumber()
: element.reverseAyatNumber();
_surahAya = _surahAya + temp;
});

@ -7,7 +7,7 @@ import 'package:tangheem/classes/utils.dart';
import 'package:tangheem/models/country_model.dart';
import 'package:tangheem/ui/bottom_sheets/country_selection_bottom_sheet.dart';
import 'package:tangheem/widgets/common_textfield_widget.dart';
import 'package:tangheem/extensions/email_validator.dart';
import 'package:tangheem/extensions/string_extensions.dart';
class RegistrationScreen extends StatefulWidget {
static const String routeName = "/registration";

@ -1,10 +1,18 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:tangheem/api/admin_configuration_api_client.dart';
import 'package:tangheem/api/tangheem_user_api_client.dart';
import 'package:tangheem/app_state/app_state.dart';
import 'package:tangheem/classes/colors.dart';
import 'package:tangheem/classes/utils.dart';
import 'package:tangheem/models/aya_tangheem_type_mapped.dart';
import 'package:tangheem/models/discussion_model.dart';
import 'package:tangheem/ui/dialogs/discussion_input_dialog.dart';
import 'package:tangheem/widgets/aya_player_widget.dart';
import 'package:tangheem/widgets/text_highlight_widget.dart';
import 'package:tangheem/extensions/string_extensions.dart';
import 'login_screen.dart';
class TangheemDetailScreen extends StatefulWidget {
static const String routeName = "/tangheem_detail";
@ -24,9 +32,10 @@ class _TangheemDetailScreenState extends State<TangheemDetailScreen> {
List<TangheemProperty> _tangheemInsideTableEmptyList = [];
List<String> _tangheemWords = [];
int _currentTangheemPage = -1;
int _discussionPage = -1;
AyatTangheemTypeMapped _ayatTangheemTypeMapped;
AyatTangheemTypeMappedData _ayatTangheemTypeMappedData;
DiscussionModel _discussionModel;
@override
void initState() {
@ -34,20 +43,34 @@ class _TangheemDetailScreenState extends State<TangheemDetailScreen> {
_tangheemInsideTableValueList = [];
_tangheemInsideTableEmptyList = [];
filterData();
getTangheemDiscussion();
}
// void getTangheemData() async {
// Utils.showLoading(context);
// try {
// _ayatTangheemTypeMapped = await TangheemUserApiClient().getAyaTangheemTypeMapped(widget.surah.surahID, widget.tangheemTypeName);
// if (_ayatTangheemTypeMapped.data.length > 0) _currentTangheemPage = 0;
// } catch (ex, tr) {
// Utils.handleException(ex, null);
// } finally {
// Utils.hideLoading(context);
// }
// filterData();
// }
void getTangheemDiscussion() async {
Utils.showLoading(context);
try {
_discussionModel = await TangheemUserApiClient().getDiscussionByTangheemID(_discussionPage, widget.ayatTangheemTypeMappedData.ayaTangheemTypeId);
Utils.hideLoading(context);
setState(() {});
} catch (ex, tr) {
Utils.handleException(ex, null);
Utils.hideLoading(context);
}
}
void sendComment(String discussionText) async {
Utils.showLoading(context);
try {
await AdminConfigurationApiClient().addDiscussion(discussionText, widget.ayatTangheemTypeMappedData.ayaTangheemTypeId);
Utils.showToast("تم إرسال التعليق ، سيكون مرئيًا بمجرد موافقة المسؤول عليه");
Utils.hideLoading(context);
Navigator.pop(context);
} catch (ex, tr) {
print(ex);
Utils.handleException(ex, null);
Utils.hideLoading(context);
}
}
void filterData() {
_ayatTangheemTypeMappedData = widget.ayatTangheemTypeMappedData;
@ -83,135 +106,110 @@ class _TangheemDetailScreenState extends State<TangheemDetailScreen> {
),
SizedBox(height: 8),
Expanded(
child: Container(
margin: EdgeInsets.only(top: 4, bottom: 4),
padding: EdgeInsets.only(top: 8, bottom: 8, right: 4, left: 4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: RepaintBoundary(
key: _globalKey,
child: Material(
color: Colors.white,
child: ListView(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.all(4),
children: [
TextHighLightWidget(
text: _ayatTangheemTypeMappedData.reverseAyatNumber() ?? "",
valueColor: ColorConsts.primaryBlue,
highlights: _tangheemWords,
style: TextStyle(
fontFamily: "UthmanicHafs",
fontSize: 18,
fontWeight: FontWeight.bold,
),
child: ListView(
physics: BouncingScrollPhysics(),
padding: EdgeInsets.only(bottom: 16),
children: [
Container(
margin: EdgeInsets.only(top: 4, bottom: 4),
padding: EdgeInsets.only(top: 8, bottom: 8, right: 4, left: 4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
child: RepaintBoundary(
key: _globalKey,
child: Material(
color: Colors.white,
child: ListView(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
padding: EdgeInsets.all(4),
children: [
TextHighLightWidget(
text: _ayatTangheemTypeMappedData.reverseAyatNumber() ?? "",
valueColor: ColorConsts.primaryBlue,
highlights: _tangheemWords,
style: TextStyle(
fontFamily: "UthmanicHafs",
fontSize: 18,
fontWeight: FontWeight.bold,
),
SizedBox(height: 16),
ListView.separated(
itemCount: _tangheemInsideTableValueList.length,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
separatorBuilder: (context, index) {
return Divider(
color: Colors.white,
height: 1,
thickness: 0,
);
},
itemBuilder: (context, index) {
return Row(
children: [
Expanded(
child: Container(
height: 40,
padding: EdgeInsets.only(left: 4, right: 8),
alignment: Alignment.centerRight,
child: Text(
_tangheemInsideTableValueList[index].propertyText,
style: TextStyle(fontWeight: FontWeight.bold, color: ColorConsts.secondaryOrange),
),
color: ColorConsts.secondaryWhite,
),
SizedBox(height: 16),
ListView.separated(
itemCount: _tangheemInsideTableValueList.length,
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
separatorBuilder: (context, index) {
return Divider(
color: Colors.white,
height: 1,
thickness: 0,
);
},
itemBuilder: (context, index) {
return Row(
children: [
Expanded(
child: Container(
height: 40,
padding: EdgeInsets.only(left: 4, right: 8),
alignment: Alignment.centerRight,
child: Text(
_tangheemInsideTableValueList[index].propertyText,
style: TextStyle(fontWeight: FontWeight.bold, color: ColorConsts.secondaryOrange),
),
color: ColorConsts.secondaryWhite,
),
SizedBox(width: 8),
Expanded(
child: Container(
height: 40,
padding: EdgeInsets.only(left: 4, right: 8),
alignment: Alignment.centerRight,
child: Text(
_tangheemInsideTableValueList[index].propertyValue,
style: TextStyle(color: ColorConsts.primaryBlack),
),
color: ColorConsts.secondaryWhite,
),
)
],
);
}),
if (_tangheemInsideTableTrueList.isNotEmpty)
Container(
color: ColorConsts.primaryBlue,
margin: EdgeInsets.only(top: 8, bottom: 8),
padding: EdgeInsets.all(8),
child: Column(
children: [
Text(
_ayatTangheemTypeMappedData.tangheemTypeName ?? "",
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
SizedBox(height: 8),
tangheemPropertyView(_tangheemInsideTableTrueList)
SizedBox(width: 8),
Expanded(
child: Container(
height: 40,
padding: EdgeInsets.only(left: 4, right: 8),
alignment: Alignment.centerRight,
child: Text(
_tangheemInsideTableValueList[index].propertyValue,
style: TextStyle(color: ColorConsts.primaryBlack),
),
color: ColorConsts.secondaryWhite,
),
)
],
),
);
}),
if (_tangheemInsideTableTrueList.isNotEmpty)
Container(
color: ColorConsts.primaryBlue,
margin: EdgeInsets.only(top: 8, bottom: 8),
padding: EdgeInsets.all(8),
child: Column(
children: [
Text(
_ayatTangheemTypeMappedData.tangheemTypeName ?? "",
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
SizedBox(height: 8),
tangheemPropertyView(_tangheemInsideTableTrueList)
],
),
tangheemPropertyView(_tangheemInsideTableEmptyList)
],
),
),
tangheemPropertyView(_tangheemInsideTableEmptyList)
],
),
),
),
),
// SizedBox(height: 4),
// Padding(
// padding: EdgeInsets.only(left: 4, right: 4),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// nextOptionButton(
// "assets/icons/prev_single.svg",
// "الآيات السابقة",
// (_currentTangheemPage == 0 || (_ayatTangheemTypeMapped.data.length < 1))
// ? null
// : () {
// _currentTangheemPage = _currentTangheemPage - 1;
// filterData();
// }),سكندر
// previousOptionButton(
// "assets/icons/next_single.svg",
// "الآيات التالية",
// (_currentTangheemPage == (_ayatTangheemTypeMapped.data.length - 1) || (_ayatTangheemTypeMapped.data.length < 1))
// ? null
// : () {
// _currentTangheemPage = _currentTangheemPage + 1;
// filterData();
// }),
// ],
// ),
// ),
],
),
),
SizedBox(height: 8),
discussionView(_discussionModel?.data ?? []),
],
),
),
AyaPlayerWidget(surahName: _ayatTangheemTypeMappedData.surahNameAr ?? "", globalKey: _globalKey, voiceNoteList: _ayatTangheemTypeMappedData.voiceNote ?? [])
AyaPlayerWidget(surahName: _ayatTangheemTypeMappedData.surahNameAr ?? "", globalKey: _globalKey, voiceNoteList: _ayatTangheemTypeMappedData?.voiceNote ?? [])
],
),
);
@ -297,4 +295,116 @@ class _TangheemDetailScreenState extends State<TangheemDetailScreen> {
),
);
}
Widget discussionView(List<DiscussionModelData> _discussionList) {
return Stack(
alignment: Alignment.bottomCenter,
children: [
Container(
margin: EdgeInsets.only(top: 4, bottom: 25),
padding: EdgeInsets.all(8),
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: ListView.separated(
padding: EdgeInsets.only(top: 4, bottom: 24),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: _discussionList.length,
separatorBuilder: (context, index) => SizedBox(height: 16),
itemBuilder: (context, index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
SvgPicture.asset(
"assets/icons/chat_user.svg",
width: 60,
height: 60,
),
SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"تعليق على الآية ${widget.ayatTangheemTypeMappedData.ayatNumberInSurah}",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: ColorConsts.primaryBlue, height: 1.5),
),
SizedBox(height: 4),
Text(
_discussionList[index].date.toFormattedDate(),
style: TextStyle(fontSize: 12, color: ColorConsts.textGrey, height: 1),
),
],
)
],
),
SizedBox(height: 4),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Text(
// "عنوان التعليق",
// style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: ColorConsts.primaryBlue, height: 1.5),
// ),
Text(
_discussionList[index].discussionText,
style: TextStyle(fontSize: 14, color: ColorConsts.textGrey, height: 1.4),
),
],
)
],
);
},
),
),
Positioned(
bottom: 0,
child: InkWell(
borderRadius: BorderRadius.circular(30),
onTap: () async {
if (!AppState().isUserLogin) {
await Navigator.pushNamed(context, LoginScreen.routeName);
if (!AppState().isUserLogin) {
Utils.showToast("يجب عليك تسجيل الدخول للتعليق");
return;
}
}
showDialog(
context: context,
barrierColor: ColorConsts.secondaryWhite.withOpacity(0.8),
builder: (BuildContext context) => DiscussionInputDialog(onCommentPress: (comment) {
sendComment(comment);
}),
);
},
child: Container(
height: 40,
padding: EdgeInsets.only(left: 24, right: 24),
alignment: Alignment.centerRight,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30),
color: ColorConsts.gradientPink,
gradient: LinearGradient(
stops: [0.0, 0.5],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [ColorConsts.gradientPink, ColorConsts.gradientOrange],
),
),
child: Text(
"أضافة تعليق",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: Colors.white, height: 1.5),
),
),
),
),
],
);
}
}

@ -1,3 +1,4 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math' as math;
import 'dart:typed_data';
@ -45,18 +46,19 @@ class _AyaPlayerWidgetState extends State<AyaPlayerWidget> {
setAudioSource() {
try {
final _playlist = ConcatenatingAudioSource(children: [
AudioSource.uri(
Uri.parse("https://server6.mp3quran.net/thubti/109.mp3"),
),
]);
var voiceList = [AudioSource.uri(Uri.parse("https://server6.mp3quran.net/thubti/109.mp3"))];
// final _playlist = ConcatenatingAudioSource(children: [
// AudioSource.uri(
// Uri.parse("https://server6.mp3quran.net/thubti/109.mp3"),
// ),
// ]);
List<AudioSource> voiceList = [];
// if ((widget.voiceNoteList?.length ?? 0) > 0) {
// voiceList = widget.voiceNoteList.map((e) => AudioSource.uri(Uri.parse(e.exposeFilePath))).toList();
// _currentVoiceNote = 0;
// }
// final _playlist = ConcatenatingAudioSource(children: voiceList);
if ((widget.voiceNoteList?.length ?? 0) > 0) {
voiceList = widget.voiceNoteList.map((e) => AudioSource.uri(Uri.parse(e.exposeFilePath))).toList();
// _currentVoiceNote = 0;
}
// voiceList = [AudioSource.uri(Uri.parse(widget.voiceNoteList.first.exposeFilePath))];
final _playlist = ConcatenatingAudioSource(children: voiceList);
_player.setAudioSource(_playlist, initialIndex: 0, initialPosition: Duration.zero).then((value) => () {});
} catch (e) {
_isAudioHaveError = true;
@ -66,7 +68,7 @@ class _AyaPlayerWidgetState extends State<AyaPlayerWidget> {
@override
void didUpdateWidget(covariant AyaPlayerWidget oldWidget) {
if (widget.voiceNoteList != oldWidget.voiceNoteList) {
// setAudioSource();
setAudioSource();
}
super.didUpdateWidget(oldWidget);
}
@ -90,27 +92,65 @@ class _AyaPlayerWidgetState extends State<AyaPlayerWidget> {
children: [
Row(
children: [
Transform.rotate(
angle: 180 * math.pi / 180,
child: SvgPicture.asset(
"assets/icons/drop_menu.svg",
width: 16,
),
),
Container(
width: 50.0,
margin: EdgeInsets.only(left: 8, right: 8),
height: 50.0,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage("https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSgswb98Ga7aJjIaNvqUWBsjMkdR18xzp3pyg&usqp=CAU"),
),
borderRadius: BorderRadius.all(
Radius.circular(30.0),
PopupMenuButton(
padding: EdgeInsets.fromLTRB(4, 4, 0, 4),
onSelected: (int index) {
_player.seek(Duration.zero, index: index);
},
child: Transform.rotate(
angle: 180 * math.pi / 180,
child: SvgPicture.asset(
"assets/icons/drop_menu.svg",
width: 16,
),
),
itemBuilder: (context) {
var _voiceList = widget.voiceNoteList;
return List.generate(_voiceList.length, (index) {
return PopupMenuItem(
value: index,
child: Text(
'${_voiceList[index].userName}',
style: TextStyle(color: ColorConsts.primaryBlack),
),
);
});
},
),
StreamBuilder<int>(
stream: _player.currentIndexStream,
builder: (context, snapshot) {
final state = snapshot.data;
return Container(
width: 50.0,
margin: EdgeInsets.only(left: 8, right: 8),
height: 50.0,
decoration: BoxDecoration(
image: widget.voiceNoteList.length < 1
? null
: DecorationImage(
fit: BoxFit.cover,
image: MemoryImage(base64Decode(widget.voiceNoteList?.elementAt(state)?.profilePicture)),
),
borderRadius: BorderRadius.all(
Radius.circular(30.0),
),
),
child: widget.voiceNoteList.length < 1
? ClipRRect(
borderRadius: BorderRadius.all(
Radius.circular(30.0),
),
child: SvgPicture.asset(
"assets/icons/chat_user.svg",
clipBehavior: Clip.antiAlias,
),
)
: null,
);
},
),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,

@ -11,8 +11,10 @@ class CommonTextFieldWidget extends StatelessWidget {
final Widget suffixWidget;
final Function onTap;
final int maxLines;
final FocusNode focusNode;
CommonTextFieldWidget({Key key, @required this.hint, @required this.controller, this.maxLines = 1, this.isPassword = false, this.prefixIcon, this.suffixWidget, this.onTap}) : super(key: key);
CommonTextFieldWidget({Key key, @required this.hint, @required this.controller, this.maxLines = 1, this.focusNode, this.isPassword = false, this.prefixIcon, this.suffixWidget, this.onTap})
: super(key: key);
@override
Widget build(BuildContext context) {
@ -28,6 +30,7 @@ class CommonTextFieldWidget extends StatelessWidget {
readOnly: onTap != null,
maxLines: maxLines,
onTap: onTap,
focusNode: focusNode,
scrollPhysics: BouncingScrollPhysics(),
decoration: InputDecoration(
contentPadding: EdgeInsets.fromLTRB(4, 4, 8, 4),
@ -42,13 +45,6 @@ class CommonTextFieldWidget extends StatelessWidget {
padding: EdgeInsets.only(left: 8, right: 0),
child: suffixWidget,
),
// !showSuffix
// ? null
// : Padding(
// padding: EdgeInsets.only(left: 8, right: 0),
// child: Icon(Icons.keyboard_arrow_down, size: 18, color: ColorConsts.secondaryOrange),
// ),
suffixIconConstraints: suffixWidget == null ? null : BoxConstraints(maxHeight: 30),
prefixIconConstraints: prefixIcon == null ? prefixIcon : BoxConstraints(maxHeight: 18),
prefixIcon: prefixIcon == null

@ -36,6 +36,9 @@ dependencies:
share: ^2.0.1
just_audio: ^0.7.2
volume_controller: ^1.0.1+1
intl: ^0.17.0
shared_preferences: ^2.0.5
url_launcher: ^6.0.3
dev_dependencies:
flutter_test:

Loading…
Cancel
Save