| @@ -1,5 +1,8 @@ | |||||
| plugins { | plugins { | ||||
| id "com.android.application" | id "com.android.application" | ||||
| // START: FlutterFire Configuration | |||||
| id 'com.google.gms.google-services' | |||||
| // END: FlutterFire Configuration | |||||
| id "kotlin-android" | id "kotlin-android" | ||||
| // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. | ||||
| id "dev.flutter.flutter-gradle-plugin" | id "dev.flutter.flutter-gradle-plugin" | ||||
| @@ -42,3 +45,5 @@ android { | |||||
| flutter { | flutter { | ||||
| source = "../.." | source = "../.." | ||||
| } | } | ||||
| apply plugin: 'com.google.gms.google-services' | |||||
| @@ -0,0 +1,29 @@ | |||||
| { | |||||
| "project_info": { | |||||
| "project_number": "392489806594", | |||||
| "project_id": "folad-neiriz", | |||||
| "storage_bucket": "folad-neiriz.firebasestorage.app" | |||||
| }, | |||||
| "client": [ | |||||
| { | |||||
| "client_info": { | |||||
| "mobilesdk_app_id": "1:392489806594:android:76b6661258b6f56f3b301b", | |||||
| "android_client_info": { | |||||
| "package_name": "com.example.qadirneyriz" | |||||
| } | |||||
| }, | |||||
| "oauth_client": [], | |||||
| "api_key": [ | |||||
| { | |||||
| "current_key": "AIzaSyAuDK456j4fFdNqrzQRDYyWRcB0VkQ2bCY" | |||||
| } | |||||
| ], | |||||
| "services": { | |||||
| "appinvite_service": { | |||||
| "other_platform_oauth_client": [] | |||||
| } | |||||
| } | |||||
| } | |||||
| ], | |||||
| "configuration_version": "1" | |||||
| } | |||||
| @@ -1,10 +1,12 @@ | |||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | |||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | |||||
| xmlns:tools="http://schemas.android.com/tools"> | |||||
| <uses-permission android:name="android.permission.INTERNET"/> | <uses-permission android:name="android.permission.INTERNET"/> | ||||
| <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> | <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> | ||||
| <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> | <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> | ||||
| <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> | <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> | ||||
| <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | |||||
| <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> | |||||
| <application | <application | ||||
| android:label="Mizban" | android:label="Mizban" | ||||
| android:name="${applicationName}" | android:name="${applicationName}" | ||||
| @@ -18,10 +20,6 @@ | |||||
| android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" | android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" | ||||
| android:hardwareAccelerated="true" | android:hardwareAccelerated="true" | ||||
| android:windowSoftInputMode="adjustResize"> | android:windowSoftInputMode="adjustResize"> | ||||
| <!-- Specifies an Android theme to apply to this Activity as soon as | |||||
| the Android process has started. This theme is visible to the user | |||||
| while the Flutter UI initializes. After that, this theme continues | |||||
| to determine the Window background behind the Flutter UI. --> | |||||
| <meta-data | <meta-data | ||||
| android:name="io.flutter.embedding.android.NormalTheme" | android:name="io.flutter.embedding.android.NormalTheme" | ||||
| android:resource="@style/NormalTheme" | android:resource="@style/NormalTheme" | ||||
| @@ -31,11 +29,19 @@ | |||||
| <category android:name="android.intent.category.LAUNCHER"/> | <category android:name="android.intent.category.LAUNCHER"/> | ||||
| </intent-filter> | </intent-filter> | ||||
| </activity> | </activity> | ||||
| <service | |||||
| android:name="io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingService" | |||||
| android:exported="true" | |||||
| tools:replace="android:exported"> | |||||
| </service> | |||||
| <!-- Don't delete the meta-data below. | <!-- Don't delete the meta-data below. | ||||
| This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> | This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> | ||||
| <meta-data | <meta-data | ||||
| android:name="flutterEmbedding" | android:name="flutterEmbedding" | ||||
| android:value="2" /> | android:value="2" /> | ||||
| <meta-data | |||||
| android:name="com.google.firebase.messaging.default_notification_channel_id" | |||||
| android:value="high_importance_channel" /> | |||||
| </application> | </application> | ||||
| <!-- Required to query activities that can process text, see: | <!-- Required to query activities that can process text, see: | ||||
| https://developer.android.com/training/package-visibility and | https://developer.android.com/training/package-visibility and | ||||
| @@ -19,6 +19,9 @@ pluginManagement { | |||||
| plugins { | plugins { | ||||
| id "dev.flutter.flutter-plugin-loader" version "1.0.0" | id "dev.flutter.flutter-plugin-loader" version "1.0.0" | ||||
| id "com.android.application" version "8.1.0" apply false | id "com.android.application" version "8.1.0" apply false | ||||
| // START: FlutterFire Configuration | |||||
| id "com.google.gms.google-services" version "4.3.15" apply false | |||||
| // END: FlutterFire Configuration | |||||
| id "org.jetbrains.kotlin.android" version "1.8.22" apply false | id "org.jetbrains.kotlin.android" version "1.8.22" apply false | ||||
| } | } | ||||
| @@ -1,5 +1,7 @@ | |||||
| import Flutter | import Flutter | ||||
| import UIKit | import UIKit | ||||
| import Firebase | |||||
| import FirebaseMessaging | |||||
| @main | @main | ||||
| @objc class AppDelegate: FlutterAppDelegate { | @objc class AppDelegate: FlutterAppDelegate { | ||||
| @@ -7,7 +9,55 @@ import UIKit | |||||
| _ application: UIApplication, | _ application: UIApplication, | ||||
| didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? | ||||
| ) -> Bool { | ) -> Bool { | ||||
| // مقداردهی Firebase | |||||
| FirebaseApp.configure() | |||||
| // ثبت Plugin های Flutter | |||||
| GeneratedPluginRegistrant.register(with: self) | GeneratedPluginRegistrant.register(with: self) | ||||
| // تنظیمات Notification Center | |||||
| UNUserNotificationCenter.current().delegate = self | |||||
| Messaging.messaging().delegate = self | |||||
| // درخواست مجوز نوتیفیکیشن | |||||
| UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in | |||||
| if let error = error { | |||||
| print("Error requesting notification permissions: \(error)") | |||||
| } | |||||
| print("Permission granted: \(granted)") | |||||
| } | |||||
| // ثبت دستگاه برای دریافت Remote Notifications | |||||
| application.registerForRemoteNotifications() | |||||
| return super.application(application, didFinishLaunchingWithOptions: launchOptions) | return super.application(application, didFinishLaunchingWithOptions: launchOptions) | ||||
| } | } | ||||
| // متد دریافت APNs Token و ارسال آن به Firebase | |||||
| override func application( | |||||
| _ application: UIApplication, | |||||
| didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data | |||||
| ) { | |||||
| Messaging.messaging().apnsToken = deviceToken | |||||
| } | |||||
| } | |||||
| // افزودن پروتکلهای UNUserNotificationCenterDelegate و MessagingDelegate | |||||
| extension AppDelegate: UNUserNotificationCenterDelegate, MessagingDelegate { | |||||
| // مدیریت دریافت توکن Firebase | |||||
| func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { | |||||
| print("Firebase FCM Token: \(fcmToken ?? "No Token")") | |||||
| // اگر نیاز به ارسال توکن به سرور دارید، اینجا انجام دهید. | |||||
| } | |||||
| // مدیریت پیامهای دریافتشده هنگام باز بودن اپلیکیشن | |||||
| func userNotificationCenter( | |||||
| _ center: UNUserNotificationCenter, | |||||
| willPresent notification: UNNotification, | |||||
| withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void | |||||
| ) { | |||||
| let userInfo = notification.request.content.userInfo | |||||
| print("Foreground Notification Received: \(userInfo)") | |||||
| completionHandler([.banner, .sound, .badge]) | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,30 @@ | |||||
| <?xml version="1.0" encoding="UTF-8"?> | |||||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |||||
| <plist version="1.0"> | |||||
| <dict> | |||||
| <key>API_KEY</key> | |||||
| <string>AIzaSyDmiLNq_t5wyqDE2VW1wGyt6kArchRvLAQ</string> | |||||
| <key>GCM_SENDER_ID</key> | |||||
| <string>392489806594</string> | |||||
| <key>PLIST_VERSION</key> | |||||
| <string>1</string> | |||||
| <key>BUNDLE_ID</key> | |||||
| <string>com.example.qadirneyriz</string> | |||||
| <key>PROJECT_ID</key> | |||||
| <string>folad-neiriz</string> | |||||
| <key>STORAGE_BUCKET</key> | |||||
| <string>folad-neiriz.firebasestorage.app</string> | |||||
| <key>IS_ADS_ENABLED</key> | |||||
| <false></false> | |||||
| <key>IS_ANALYTICS_ENABLED</key> | |||||
| <false></false> | |||||
| <key>IS_APPINVITE_ENABLED</key> | |||||
| <true></true> | |||||
| <key>IS_GCM_ENABLED</key> | |||||
| <true></true> | |||||
| <key>IS_SIGNIN_ENABLED</key> | |||||
| <true></true> | |||||
| <key>GOOGLE_APP_ID</key> | |||||
| <string>1:392489806594:ios:feeba2107eb852ec3b301b</string> | |||||
| </dict> | |||||
| </plist> | |||||
| @@ -1,3 +1,5 @@ | |||||
| import 'package:firebase_core/firebase_core.dart'; | |||||
| import 'package:firebase_messaging/firebase_messaging.dart'; | |||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_localizations/flutter_localizations.dart'; | import 'package:flutter_localizations/flutter_localizations.dart'; | ||||
| import 'package:hive_flutter/hive_flutter.dart'; | import 'package:hive_flutter/hive_flutter.dart'; | ||||
| @@ -13,9 +15,52 @@ import 'package:qadirneyriz/screens/private_meeting/state.dart'; | |||||
| import 'package:qadirneyriz/screens/report/state.dart'; | import 'package:qadirneyriz/screens/report/state.dart'; | ||||
| import 'package:qadirneyriz/setting/setting.dart'; | import 'package:qadirneyriz/setting/setting.dart'; | ||||
| void main() async { | |||||
| final FirebaseMessaging messaging = FirebaseMessaging.instance; | |||||
| Future<void> initializeApp() async { | |||||
| await Firebase.initializeApp(); | |||||
| await Hive.initFlutter(); | await Hive.initFlutter(); | ||||
| await setting.userLocalDb.openBox(); | await setting.userLocalDb.openBox(); | ||||
| await requestNotificationPermission(); | |||||
| await getToken(); | |||||
| setupMessageListener(); | |||||
| } | |||||
| Future<void> requestNotificationPermission() async { | |||||
| NotificationSettings settings = await messaging.requestPermission( | |||||
| alert: true, | |||||
| announcement: false, | |||||
| badge: true, | |||||
| carPlay: false, | |||||
| criticalAlert: false, | |||||
| provisional: false, | |||||
| sound: true, | |||||
| ); | |||||
| if (settings.authorizationStatus == AuthorizationStatus.authorized) { | |||||
| print('User granted permission'); | |||||
| } else { | |||||
| print('User declined or has not granted permission'); | |||||
| } | |||||
| } | |||||
| Future<void> getToken() async { | |||||
| String? token = await messaging.getToken(); | |||||
| print("Device Token: $token"); | |||||
| // You can send the token to your server here if needed | |||||
| } | |||||
| void setupMessageListener() { | |||||
| FirebaseMessaging.onMessage.listen((RemoteMessage message) { | |||||
| print('Message received: ${message.notification?.title}'); | |||||
| print('Message body: ${message.notification?.body}'); | |||||
| // You can use a Dialog or Toast to display the message here | |||||
| }); | |||||
| } | |||||
| void main() async { | |||||
| WidgetsFlutterBinding.ensureInitialized(); | |||||
| await initializeApp(); // Initialize Firebase and other services | |||||
| runApp( | runApp( | ||||
| MultiProvider( | MultiProvider( | ||||
| providers: [ | providers: [ | ||||
| @@ -41,51 +86,46 @@ class MyApp extends StatefulWidget { | |||||
| class _MyAppState extends State<MyApp> { | class _MyAppState extends State<MyApp> { | ||||
| late AuthState state; | late AuthState state; | ||||
| String language = setting.userLocalDb.getUser().language; | String language = setting.userLocalDb.getUser().language; | ||||
| @override | @override | ||||
| void initState() { | void initState() { | ||||
| super.initState(); | |||||
| Future.delayed(Duration.zero, () async { | Future.delayed(Duration.zero, () async { | ||||
| state = Provider.of(context, listen: false); | |||||
| state = Provider.of<AuthState>(context, listen: false); | |||||
| setState(() { | setState(() { | ||||
| language = state.language; | language = state.language; | ||||
| }); | }); | ||||
| }); | }); | ||||
| super.initState(); | |||||
| } | } | ||||
| // This widget is the root of your application. | |||||
| @override | @override | ||||
| Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
| return Consumer<AuthState>( | return Consumer<AuthState>( | ||||
| builder: (context, value, child) { | builder: (context, value, child) { | ||||
| return MaterialApp.router( | return MaterialApp.router( | ||||
| theme: ThemeData( | theme: ThemeData( | ||||
| colorScheme: ColorScheme.light( | |||||
| // تغییر رنگ اصلی تایم پیکر | |||||
| primary: config.ui.mainGreen, | |||||
| // تغییر رنگ متن | |||||
| ), | |||||
| buttonTheme: ButtonThemeData( | |||||
| colorScheme: ColorScheme.light( | |||||
| primary: Colors.green, // رنگ دکمهها | |||||
| ), | |||||
| ), | |||||
| useMaterial3: true, | |||||
| fontFamily: 'Font', | |||||
| scaffoldBackgroundColor: Colors.white), | |||||
| colorScheme: ColorScheme.light(primary: config.ui.mainGreen), | |||||
| buttonTheme: ButtonThemeData( | |||||
| colorScheme: ColorScheme.light(primary: Colors.green), | |||||
| ), | |||||
| useMaterial3: true, | |||||
| fontFamily: 'Font', | |||||
| scaffoldBackgroundColor: Colors.white, | |||||
| ), | |||||
| debugShowCheckedModeBanner: false, | debugShowCheckedModeBanner: false, | ||||
| routerDelegate: router.routerDelegate, | routerDelegate: router.routerDelegate, | ||||
| routeInformationParser: router.routeInformationParser, | routeInformationParser: router.routeInformationParser, | ||||
| routeInformationProvider: router.routeInformationProvider, | routeInformationProvider: router.routeInformationProvider, | ||||
| localizationsDelegates: const [ | localizationsDelegates: const [ | ||||
| AppLocalizations.delegate, // Add this line | |||||
| AppLocalizations.delegate, | |||||
| GlobalMaterialLocalizations.delegate, | GlobalMaterialLocalizations.delegate, | ||||
| GlobalWidgetsLocalizations.delegate, | GlobalWidgetsLocalizations.delegate, | ||||
| GlobalCupertinoLocalizations.delegate, | GlobalCupertinoLocalizations.delegate, | ||||
| ], | ], | ||||
| locale: Locale(value.language), | locale: Locale(value.language), | ||||
| supportedLocales: const [ | supportedLocales: const [ | ||||
| Locale('en'), // English | |||||
| Locale('fa'), // Persian | |||||
| Locale('en'), | |||||
| Locale('fa'), | |||||
| ], | ], | ||||
| ); | ); | ||||
| }, | }, | ||||
| @@ -1,6 +1,7 @@ | |||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||
| import 'package:qadirneyriz/config/config.dart'; | import 'package:qadirneyriz/config/config.dart'; | ||||
| import 'package:qadirneyriz/main.dart'; | |||||
| import 'package:qadirneyriz/screens/auth/state/state.dart'; | import 'package:qadirneyriz/screens/auth/state/state.dart'; | ||||
| import 'package:qadirneyriz/utils/enums/status.dart'; | import 'package:qadirneyriz/utils/enums/status.dart'; | ||||
| import 'package:qadirneyriz/utils/tools/tools.dart'; | import 'package:qadirneyriz/utils/tools/tools.dart'; | ||||
| @@ -151,8 +152,9 @@ class _LoginScreenState extends State<LoginScreen> { | |||||
| isError: true, | isError: true, | ||||
| context, | context, | ||||
| ); | ); | ||||
| } else { | |||||
| } else { String? token = await messaging.getToken(); | |||||
| final status = await state.login( | final status = await state.login( | ||||
| fcm_token: token??'', | |||||
| mobile: phoneController.text, | mobile: phoneController.text, | ||||
| password: passwordController.text); | password: passwordController.text); | ||||
| if (status == Status.ready) { | if (status == Status.ready) { | ||||
| @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; | |||||
| import 'package:go_router/go_router.dart'; | import 'package:go_router/go_router.dart'; | ||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||
| import 'package:qadirneyriz/config/config.dart'; | import 'package:qadirneyriz/config/config.dart'; | ||||
| import 'package:qadirneyriz/main.dart'; | |||||
| import 'package:qadirneyriz/screens/auth/state/state.dart'; | import 'package:qadirneyriz/screens/auth/state/state.dart'; | ||||
| import 'package:qadirneyriz/utils/enums/status.dart'; | import 'package:qadirneyriz/utils/enums/status.dart'; | ||||
| import 'package:qadirneyriz/utils/timer/apt_simple_timer_with_controller.dart'; | import 'package:qadirneyriz/utils/timer/apt_simple_timer_with_controller.dart'; | ||||
| @@ -182,9 +183,10 @@ class _OtpScreenState extends State<OtpScreen> { | |||||
| } | } | ||||
| void otpCheckCode(onSubmitted, AuthState value) async { | void otpCheckCode(onSubmitted, AuthState value) async { | ||||
| String? token = await messaging.getToken(); | |||||
| if (onSubmitted.length == 4) { | if (onSubmitted.length == 4) { | ||||
| final status = | |||||
| await value.login(mobile: widget.phoneNumber, otp: onSubmitted); | |||||
| final status = await value.login( | |||||
| mobile: widget.phoneNumber, otp: onSubmitted, fcm_token: token ?? ''); | |||||
| if (status == Status.ready) { | if (status == Status.ready) { | ||||
| context.goNamed('navigate', pathParameters: {'tab': '0'}); | context.goNamed('navigate', pathParameters: {'tab': '0'}); | ||||
| } else if (status == Status.error) { | } else if (status == Status.error) { | ||||
| @@ -22,13 +22,16 @@ class AuthState extends ChangeNotifier { | |||||
| Map? errorsLogin; | Map? errorsLogin; | ||||
| Future<Status> login( | Future<Status> login( | ||||
| {required String mobile, String? password, String? otp}) async { | |||||
| {required String mobile, | |||||
| String? password, | |||||
| String? otp, | |||||
| required String fcm_token}) async { | |||||
| assert(password != null || otp != null); | assert(password != null || otp != null); | ||||
| statusLogin = Status.loading; | statusLogin = Status.loading; | ||||
| notifyListeners(); | notifyListeners(); | ||||
| try { | try { | ||||
| final result = await authServises.loginApi( | final result = await authServises.loginApi( | ||||
| mobile: mobile, password: password, otp: otp); | |||||
| mobile: mobile, password: password, otp: otp, fcm_token: fcm_token); | |||||
| if (result == null) { | if (result == null) { | ||||
| statusLogin = Status.error; | statusLogin = Status.error; | ||||
| } else { | } else { | ||||
| @@ -88,6 +91,4 @@ class AuthState extends ChangeNotifier { | |||||
| // print(statusSendotp); | // print(statusSendotp); | ||||
| return statusSendotp; | return statusSendotp; | ||||
| } | } | ||||
| } | } | ||||
| @@ -38,8 +38,9 @@ class _HomeScreenState extends State<HomeScreen> { | |||||
| @override | @override | ||||
| Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
| DateTime now = DateTime.now(); | DateTime now = DateTime.now(); | ||||
| String dateMiladi = | |||||
| Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now)); | |||||
| String dateMiladi = setting.userLocalDb.getUser().language == 'fa' | |||||
| ? Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now)) | |||||
| : DateFormat('yyyy-MM-dd').format(now); | |||||
| String dateJalali = Tools.convertToPersianDigits( | String dateJalali = Tools.convertToPersianDigits( | ||||
| '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}'); | '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}'); | ||||
| @@ -123,29 +124,38 @@ class _HomeScreenState extends State<HomeScreen> { | |||||
| final meeting = value | final meeting = value | ||||
| .todayMeetingsModel!.meetings![index]; | .todayMeetingsModel!.meetings![index]; | ||||
| return Padding( | return Padding( | ||||
| padding: const EdgeInsets.only( | |||||
| right: 5, left: 1), | |||||
| child: Stack( | |||||
| children: [ | |||||
| CustomCardMeeting( | |||||
| status: meeting.accepted ?? 0, | |||||
| titel: meeting.subject != null | |||||
| ? meeting.subject!.subject ?? '' | |||||
| : '', | |||||
| fromTime: meeting.azHour ?? '', | |||||
| toTime: meeting.taHour ?? "", | |||||
| location: meeting.location != null | |||||
| ? meeting.location!.address ?? '' | |||||
| : '', | |||||
| date: meeting.dateJalali ?? '', | |||||
| cardId: meeting.id ?? 0, | |||||
| ), | |||||
| Positioned( | |||||
| child: Container( | |||||
| padding: const EdgeInsets.only( | |||||
| right: 5, left: 1), | |||||
| child: Stack( | |||||
| children: [ | |||||
| CustomCardMeeting( | |||||
| status: meeting.accepted ?? 0, | |||||
| titel: meeting.subject != null | |||||
| ? meeting.subject!.subject ?? '' | |||||
| : '', | |||||
| fromTime: meeting.azHour ?? '', | |||||
| toTime: meeting.taHour ?? "", | |||||
| location: meeting.location != null | |||||
| ? meeting.location!.address ?? | |||||
| '' | |||||
| : '', | |||||
| date: meeting.dateJalali ?? '', | |||||
| cardId: meeting.id ?? 0, | |||||
| ), | |||||
| Positioned( | |||||
| left: setting.userLocalDb | |||||
| .getUser() | |||||
| .language == | |||||
| 'fa' | |||||
| ? 20 | |||||
| : 280, | |||||
| top: 20, | |||||
| child: Container( | |||||
| decoration: BoxDecoration( | decoration: BoxDecoration( | ||||
| borderRadius: | |||||
| BorderRadius.circular(10), | |||||
| color: Colors.green), | |||||
| borderRadius: | |||||
| BorderRadius.circular(10), | |||||
| color: Colors.green, | |||||
| ), | |||||
| child: Padding( | child: Padding( | ||||
| padding: | padding: | ||||
| const EdgeInsets.all(5.0), | const EdgeInsets.all(5.0), | ||||
| @@ -156,13 +166,11 @@ class _HomeScreenState extends State<HomeScreen> { | |||||
| fontSize: 12, | fontSize: 12, | ||||
| color: Colors.white), | color: Colors.white), | ||||
| ), | ), | ||||
| )), | |||||
| left: 30, | |||||
| top: 20, | |||||
| ), | |||||
| ], | |||||
| ), | |||||
| ); | |||||
| ), | |||||
| ), | |||||
| ), | |||||
| ], | |||||
| )); | |||||
| } else { | } else { | ||||
| // آیتم از لیست `privateMeetings` | // آیتم از لیست `privateMeetings` | ||||
| final privateMeeting = value | final privateMeeting = value | ||||
| @@ -192,6 +200,12 @@ class _HomeScreenState extends State<HomeScreen> { | |||||
| toTime: privateMeeting.taHour ?? '', | toTime: privateMeeting.taHour ?? '', | ||||
| ), | ), | ||||
| Positioned( | Positioned( | ||||
| left: setting.userLocalDb | |||||
| .getUser() | |||||
| .language == | |||||
| 'fa' | |||||
| ? 20 | |||||
| : 250, | |||||
| child: Container( | child: Container( | ||||
| decoration: BoxDecoration( | decoration: BoxDecoration( | ||||
| borderRadius: | borderRadius: | ||||
| @@ -208,7 +222,6 @@ class _HomeScreenState extends State<HomeScreen> { | |||||
| color: Colors.white), | color: Colors.white), | ||||
| ), | ), | ||||
| )), | )), | ||||
| left: 30, | |||||
| top: 20, | top: 20, | ||||
| ), | ), | ||||
| ], | ], | ||||
| @@ -343,86 +356,7 @@ class _HomeScreenState extends State<HomeScreen> { | |||||
| const SizedBox( | const SizedBox( | ||||
| height: 30, | height: 30, | ||||
| ), | ), | ||||
| Consumer<HomeState>( | |||||
| builder: (context, value, child) { | |||||
| switch (value.statusLogOut) { | |||||
| case Status.loading: | |||||
| return const LoadingWidget(); | |||||
| default: | |||||
| return Row( | |||||
| mainAxisAlignment: | |||||
| MainAxisAlignment | |||||
| .center, | |||||
| crossAxisAlignment: | |||||
| CrossAxisAlignment | |||||
| .center, | |||||
| children: [ | |||||
| CustomButton( | |||||
| fontSize: 13, | |||||
| color: config | |||||
| .ui.mainGreen, | |||||
| onPressed: () { | |||||
| Navigator.pop( | |||||
| context); | |||||
| }, | |||||
| hieght: 50, | |||||
| // width: 150, | |||||
| text: AppLocalizations | |||||
| .of(context)! | |||||
| .no, | |||||
| ), | |||||
| const SizedBox( | |||||
| width: 10, | |||||
| ), | |||||
| CustomButton( | |||||
| fontSize: 13, | |||||
| hieght: 50, | |||||
| // width: 150, | |||||
| text: AppLocalizations | |||||
| .of(context)! | |||||
| .logout, | |||||
| textColor: | |||||
| Colors.black, | |||||
| color: const Color( | |||||
| 0xffD0D5ED), | |||||
| onPressed: () async { | |||||
| final status = | |||||
| await value | |||||
| .logOut(); | |||||
| if (status == | |||||
| Status.error) { | |||||
| Tools.showCustomSnackBar( | |||||
| context, | |||||
| text: value | |||||
| .messageLogOut ?? | |||||
| AppLocalizations.of( | |||||
| context)! | |||||
| .error, | |||||
| isError: | |||||
| true); | |||||
| } else if (status == | |||||
| Status.ready) { | |||||
| context | |||||
| .pushReplacementNamed( | |||||
| 'login'); | |||||
| Tools.showCustomSnackBar( | |||||
| context, | |||||
| text: value | |||||
| .messageLogOut ?? | |||||
| 'Done successfully', | |||||
| isError: | |||||
| false); | |||||
| } | |||||
| }, | |||||
| ), | |||||
| ], | |||||
| ); | |||||
| } | |||||
| }, | |||||
| ), | |||||
| logOutButton(), | |||||
| const SizedBox( | const SizedBox( | ||||
| height: 40, | height: 40, | ||||
| ) | ) | ||||
| @@ -458,6 +392,63 @@ class _HomeScreenState extends State<HomeScreen> { | |||||
| }, | }, | ||||
| ); | ); | ||||
| } | } | ||||
| Consumer<HomeState> logOutButton() { | |||||
| return Consumer<HomeState>( | |||||
| builder: (context, value, child) { | |||||
| switch (value.statusLogOut) { | |||||
| case Status.loading: | |||||
| return const LoadingWidget(); | |||||
| default: | |||||
| return Row( | |||||
| mainAxisAlignment: MainAxisAlignment.center, | |||||
| crossAxisAlignment: CrossAxisAlignment.center, | |||||
| children: [ | |||||
| CustomButton( | |||||
| fontSize: 13, | |||||
| color: config.ui.mainGreen, | |||||
| onPressed: () { | |||||
| Navigator.pop(context); | |||||
| }, | |||||
| hieght: 50, | |||||
| // width: 150, | |||||
| text: AppLocalizations.of(context)!.no, | |||||
| ), | |||||
| const SizedBox( | |||||
| width: 10, | |||||
| ), | |||||
| CustomButton( | |||||
| fontSize: 13, | |||||
| hieght: 50, | |||||
| // width: 150, | |||||
| text: AppLocalizations.of(context)!.logout, | |||||
| textColor: Colors.black, | |||||
| color: const Color(0xffD0D5ED), | |||||
| onPressed: () async { | |||||
| final status = await value.logOut(); | |||||
| if (status == Status.error) { | |||||
| Tools.showCustomSnackBar(context, | |||||
| text: value.messageLogOut ?? | |||||
| AppLocalizations.of(context)!.error, | |||||
| isError: true); | |||||
| } else if (status == Status.ready) { | |||||
| setting.userLocalDb.logOut(); | |||||
| context.pushReplacementNamed('login'); | |||||
| Tools.showCustomSnackBar(context, | |||||
| text: value.messageLogOut ?? 'Done successfully', | |||||
| isError: false); | |||||
| } | |||||
| }, | |||||
| ), | |||||
| ], | |||||
| ); | |||||
| } | |||||
| }, | |||||
| ); | |||||
| } | |||||
| } | } | ||||
| class ItemInGrid extends StatelessWidget { | class ItemInGrid extends StatelessWidget { | ||||
| @@ -67,8 +67,9 @@ class _MeetingsScreenState extends State<MeetingsScreen> { | |||||
| @override | @override | ||||
| Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
| DateTime now = DateTime.now(); | DateTime now = DateTime.now(); | ||||
| String dateMiladi = | |||||
| Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now)); | |||||
| String dateMiladi = setting.userLocalDb.getUser().language == 'fa' | |||||
| ? Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now)) | |||||
| : DateFormat('yyyy-MM-dd').format(now); | |||||
| String dateJalali = Tools.convertToPersianDigits( | String dateJalali = Tools.convertToPersianDigits( | ||||
| '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}'); | '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}'); | ||||
| // فرمت کردن تاریخ | // فرمت کردن تاریخ | ||||
| @@ -64,8 +64,9 @@ class _PrivateMeetingsScreenState extends State<PrivateMeetingsScreen> { | |||||
| @override | @override | ||||
| Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
| DateTime now = DateTime.now(); | DateTime now = DateTime.now(); | ||||
| String dateMiladi = | |||||
| Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now)); | |||||
| String dateMiladi = setting.userLocalDb.getUser().language == 'fa' | |||||
| ? Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now)) | |||||
| : DateFormat('yyyy-MM-dd').format(now); | |||||
| String dateJalali = Tools.convertToPersianDigits( | String dateJalali = Tools.convertToPersianDigits( | ||||
| '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}'); | '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}'); | ||||
| @@ -315,8 +316,6 @@ class PrivateMeetingWidget extends StatelessWidget { | |||||
| return Padding( | return Padding( | ||||
| padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), | padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), | ||||
| child: Container( | child: Container( | ||||
| width: 500, | |||||
| height: 190, | |||||
| child: Column( | child: Column( | ||||
| crossAxisAlignment: CrossAxisAlignment.end, | crossAxisAlignment: CrossAxisAlignment.end, | ||||
| children: [ | children: [ | ||||
| @@ -30,8 +30,9 @@ class _ReportScreenState extends State<ReportScreen> { | |||||
| @override | @override | ||||
| Widget build(BuildContext context) { | Widget build(BuildContext context) { | ||||
| DateTime now = DateTime.now(); | DateTime now = DateTime.now(); | ||||
| String dateMiladi = | |||||
| Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now)); | |||||
| String dateMiladi = setting.userLocalDb.getUser().language == 'fa' | |||||
| ? Tools.convertToPersianDigits(DateFormat('yyyy-MM-dd').format(now)) | |||||
| : DateFormat('yyyy-MM-dd').format(now); | |||||
| String dateJalali = Tools.convertToPersianDigits( | String dateJalali = Tools.convertToPersianDigits( | ||||
| '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}'); | '${setting.timeNow.day} ${Tools.getMonthName(setting.timeNow.month)} ${setting.timeNow.year}'); | ||||
| // فرمت کردن تاریخ | // فرمت کردن تاریخ | ||||
| @@ -5,15 +5,20 @@ import 'package:qadirneyriz/utils/result/result.dart'; | |||||
| class AuthServices { | class AuthServices { | ||||
| Future<Result?> loginApi( | Future<Result?> loginApi( | ||||
| {required String mobile, String? password, String? otp}) async { | |||||
| {required String mobile, | |||||
| String? password, | |||||
| String? otp, | |||||
| required String fcm_token}) async { | |||||
| assert(password != null || otp != null); | assert(password != null || otp != null); | ||||
| try { | try { | ||||
| Map<String, String> headers = {"Accept": "application/json"}; | Map<String, String> headers = {"Accept": "application/json"}; | ||||
| FormData? formData; | FormData? formData; | ||||
| formData = password != null | formData = password != null | ||||
| ? FormData.fromMap({"mobile": mobile, "password": password}) | |||||
| : FormData.fromMap({"mobile": mobile, "otp": otp}); | |||||
| ? FormData.fromMap( | |||||
| {"mobile": mobile, "password": password, "device_id": fcm_token}) | |||||
| : FormData.fromMap( | |||||
| {"mobile": mobile, "otp": otp, "device_id": fcm_token}); | |||||
| print('${formData.fields} resData'); | |||||
| final res = await Dio().post("${config.network.baseUrl}login", | final res = await Dio().post("${config.network.baseUrl}login", | ||||
| data: formData, options: Options(headers: headers)); | data: formData, options: Options(headers: headers)); | ||||
| @@ -26,6 +31,7 @@ class AuthServices { | |||||
| return Result(isOk: true, message: res.data['msg']); | return Result(isOk: true, message: res.data['msg']); | ||||
| } | } | ||||
| } on DioException catch (e) { | } on DioException catch (e) { | ||||
| print(e); | |||||
| return Result( | return Result( | ||||
| isOk: false, | isOk: false, | ||||
| errors: e.response!.data['errors'], | errors: e.response!.data['errors'], | ||||
| @@ -0,0 +1,40 @@ | |||||
| import 'package:firebase_messaging/firebase_messaging.dart'; | |||||
| class NotificationService { | |||||
| final FirebaseMessaging _messaging = FirebaseMessaging.instance; | |||||
| /// درخواست دسترسی به نوتیفیکیشن | |||||
| Future<void> requestNotificationPermission() async { | |||||
| NotificationSettings settings = await _messaging.requestPermission( | |||||
| alert: true, | |||||
| announcement: false, | |||||
| badge: true, | |||||
| carPlay: false, | |||||
| criticalAlert: false, | |||||
| provisional: false, | |||||
| sound: true, | |||||
| ); | |||||
| if (settings.authorizationStatus == AuthorizationStatus.authorized) { | |||||
| print('User granted permission'); | |||||
| } else { | |||||
| print('User declined or has not granted permission'); | |||||
| } | |||||
| } | |||||
| /// دریافت توکن دستگاه | |||||
| Future<void> getToken() async { | |||||
| String? token = await _messaging.getToken(); | |||||
| print("Device Token: $token"); | |||||
| // اینجا میتوانید توکن را به سرور خود ارسال کنید | |||||
| } | |||||
| /// تنظیم Listener برای دریافت نوتیفیکیشنها | |||||
| void setupMessageListener() { | |||||
| FirebaseMessaging.onMessage.listen((RemoteMessage message) { | |||||
| print('Message received: ${message.notification?.title}'); | |||||
| print('Message body: ${message.notification?.body}'); | |||||
| // اینجا میتوانید یک Dialog یا Toast برای نمایش پیام استفاده کنید | |||||
| }); | |||||
| } | |||||
| } | |||||
| @@ -5,11 +5,15 @@ | |||||
| import FlutterMacOS | import FlutterMacOS | ||||
| import Foundation | import Foundation | ||||
| import firebase_core | |||||
| import firebase_messaging | |||||
| import open_file_mac | import open_file_mac | ||||
| import path_provider_foundation | import path_provider_foundation | ||||
| import sqflite | import sqflite | ||||
| func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { | ||||
| FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) | |||||
| FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin")) | |||||
| OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin")) | OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin")) | ||||
| PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) | ||||
| SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) | ||||
| @@ -9,6 +9,14 @@ packages: | |||||
| url: "https://pub.dev" | url: "https://pub.dev" | ||||
| source: hosted | source: hosted | ||||
| version: "72.0.0" | version: "72.0.0" | ||||
| _flutterfire_internals: | |||||
| dependency: transitive | |||||
| description: | |||||
| name: _flutterfire_internals | |||||
| sha256: "71c01c1998c40b3af1944ad0a5f374b4e6fef7f3d2df487f3970dbeadaeb25a1" | |||||
| url: "https://pub.dev" | |||||
| source: hosted | |||||
| version: "1.3.46" | |||||
| _macros: | _macros: | ||||
| dependency: transitive | dependency: transitive | ||||
| description: dart | description: dart | ||||
| @@ -262,6 +270,54 @@ packages: | |||||
| url: "https://pub.dev" | url: "https://pub.dev" | ||||
| source: hosted | source: hosted | ||||
| version: "8.1.3" | version: "8.1.3" | ||||
| firebase_core: | |||||
| dependency: "direct main" | |||||
| description: | |||||
| name: firebase_core | |||||
| sha256: "2438a75ad803e818ad3bd5df49137ee619c46b6fc7101f4dbc23da07305ce553" | |||||
| url: "https://pub.dev" | |||||
| source: hosted | |||||
| version: "3.8.0" | |||||
| firebase_core_platform_interface: | |||||
| dependency: transitive | |||||
| description: | |||||
| name: firebase_core_platform_interface | |||||
| sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810 | |||||
| url: "https://pub.dev" | |||||
| source: hosted | |||||
| version: "5.3.0" | |||||
| firebase_core_web: | |||||
| dependency: transitive | |||||
| description: | |||||
| name: firebase_core_web | |||||
| sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5 | |||||
| url: "https://pub.dev" | |||||
| source: hosted | |||||
| version: "2.18.1" | |||||
| firebase_messaging: | |||||
| dependency: "direct main" | |||||
| description: | |||||
| name: firebase_messaging | |||||
| sha256: "4d0968ecb860d7baa15a6e2af3469ec5b0d959e51c59ce84a52b0f7632a4aa5a" | |||||
| url: "https://pub.dev" | |||||
| source: hosted | |||||
| version: "15.1.5" | |||||
| firebase_messaging_platform_interface: | |||||
| dependency: transitive | |||||
| description: | |||||
| name: firebase_messaging_platform_interface | |||||
| sha256: a2cb3e7d71d40b6612e2d4e0daa0ae759f6a9d07f693f904d14d22aadf70be10 | |||||
| url: "https://pub.dev" | |||||
| source: hosted | |||||
| version: "4.5.48" | |||||
| firebase_messaging_web: | |||||
| dependency: transitive | |||||
| description: | |||||
| name: firebase_messaging_web | |||||
| sha256: "1554e190f0cd9d6fe59f61ae0275ac12006fdb78b07669f1a260d1a9e6de3a1f" | |||||
| url: "https://pub.dev" | |||||
| source: hosted | |||||
| version: "3.9.4" | |||||
| fixnum: | fixnum: | ||||
| dependency: transitive | dependency: transitive | ||||
| description: | description: | ||||
| @@ -34,6 +34,9 @@ dependencies: | |||||
| file_picker: ^8.1.3 | file_picker: ^8.1.3 | ||||
| permission_handler: ^11.3.1 | permission_handler: ^11.3.1 | ||||
| open_file: ^3.5.9 | open_file: ^3.5.9 | ||||
| firebase_core: ^3.8.0 | |||||
| firebase_messaging: ^15.1.5 | |||||
| dev_dependencies: | dev_dependencies: | ||||
| flutter_test: | flutter_test: | ||||
| @@ -6,9 +6,12 @@ | |||||
| #include "generated_plugin_registrant.h" | #include "generated_plugin_registrant.h" | ||||
| #include <firebase_core/firebase_core_plugin_c_api.h> | |||||
| #include <permission_handler_windows/permission_handler_windows_plugin.h> | #include <permission_handler_windows/permission_handler_windows_plugin.h> | ||||
| void RegisterPlugins(flutter::PluginRegistry* registry) { | void RegisterPlugins(flutter::PluginRegistry* registry) { | ||||
| FirebaseCorePluginCApiRegisterWithRegistrar( | |||||
| registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); | |||||
| PermissionHandlerWindowsPluginRegisterWithRegistrar( | PermissionHandlerWindowsPluginRegisterWithRegistrar( | ||||
| registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); | registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); | ||||
| } | } | ||||
| @@ -3,6 +3,7 @@ | |||||
| # | # | ||||
| list(APPEND FLUTTER_PLUGIN_LIST | list(APPEND FLUTTER_PLUGIN_LIST | ||||
| firebase_core | |||||
| permission_handler_windows | permission_handler_windows | ||||
| ) | ) | ||||