diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 0e7557a..47515db 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -4,20 +4,26 @@
+
+
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 02e5f58..aeaff6f 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
diff --git a/ios/Podfile b/ios/Podfile
index adfd349..2c0add7 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -38,6 +38,18 @@ post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
+ ################ Awesome Notifications pod modification 1 ###################
+ awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks')
+ require awesome_pod_file
+ update_awesome_pod_build_settings(installer)
+ ################ Awesome Notifications pod modification 1 ###################
+ end
+
+ ################ Awesome Notifications pod modification 2 ###################
+ awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks')
+ require awesome_pod_file
+ update_awesome_main_target_settings('Runner', File.dirname(File.realpath(__FILE__)), flutter_root)
+ ################ Awesome Notifications pod modification 2 ###################
installer.generated_projects.each do |project|
project.targets.each do |target|
target.build_configurations.each do |config|
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 42a0532..4a094b3 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -50,5 +50,9 @@
UIViewControllerBasedStatusBarAppearance
+ UNNotificationServiceExtension
+
+ $(PRODUCT_BUNDLE_IDENTIFIER).MyNotificationServiceExtension
+
diff --git a/lib/main.dart b/lib/main.dart
index 07afaae..8ccfa8d 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -7,8 +7,12 @@ import 'package:didvan/providers/theme.dart';
import 'package:didvan/providers/user.dart';
import 'package:didvan/routes/route_generator.dart';
import 'package:didvan/services/app_initalizer.dart';
+import 'package:didvan/services/notification/awsome/awsome_notification_handler.dart';
+import 'package:didvan/services/notification/fcm/firebase_notification_handler.dart';
import 'package:didvan/views/podcasts/podcasts_state.dart';
import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart';
+import 'package:didvan/services/notification/lc/local_notification_service.dart';
+import 'package:didvan/services/notification/lc/show_notification_handler.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
@@ -16,36 +20,45 @@ import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart';
+@pragma('vm:entry-point')
+Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
+ // If you're going to use other Firebase services in the background, such as Firestore,
+ // make sure you call `initializeApp` before using other Firebase services.
+ WidgetsFlutterBinding.ensureInitialized();
+ await Firebase.initializeApp(
+ options: kIsWeb
+ ? const FirebaseOptions(
+ apiKey: "AIzaSyA0HZjKpRuPOi1SC3f_EZTvlS3mcj9UVo0",
+ authDomain: "didvan-9b7da.firebaseapp.com",
+ projectId: "didvan-9b7da",
+ storageBucket: "didvan-9b7da.appspot.com",
+ messagingSenderId: "935017686266",
+ appId: "1:935017686266:web:a93f7a19bed23c51d2d543",
+ measurementId: "G-80B4H9E8Y0")
+ : const FirebaseOptions(
+ apiKey: 'AIzaSyBp-UHjWeM0H0UHtX5yguFKG-riMzvvCzw',
+ appId: '1:935017686266:android:f9cbc9aba8e3d65ed2d543',
+ messagingSenderId: '935017686266',
+ projectId: 'didvan-9b7da',
+ ),
+ );
+
+ // LocalNotificationService.initialize();
+ // LocalNotificationService.display(message);
+ // LocalNotificationService.showBigPictureNotification();
+ AwsomeNotificationHandler().main();
+ AwsomeNotificationHandler().show(message);
+
+ print("Handling a background message: ${message.messageId}");
+}
+
void main() async {
try {
WidgetsFlutterBinding.ensureInitialized();
-
- await Firebase.initializeApp(
- options: kIsWeb
- ? const FirebaseOptions(
- apiKey: "AIzaSyA0HZjKpRuPOi1SC3f_EZTvlS3mcj9UVo0",
- authDomain: "didvan-9b7da.firebaseapp.com",
- projectId: "didvan-9b7da",
- storageBucket: "didvan-9b7da.appspot.com",
- messagingSenderId: "935017686266",
- appId: "1:935017686266:web:a93f7a19bed23c51d2d543",
- measurementId: "G-80B4H9E8Y0")
- : const FirebaseOptions(
- apiKey: 'AIzaSyBp-UHjWeM0H0UHtX5yguFKG-riMzvvCzw',
- appId: '1:935017686266:android:f9cbc9aba8e3d65ed2d543',
- messagingSenderId: '935017686266',
- projectId: 'didvan-9b7da',
- ),
- );
- final initMsg = await FirebaseMessaging.instance.getInitialMessage();
- if (initMsg != null) {
- AppInitializer.clickAction = initMsg.data['click_action'].replaceAll(
- 'navigate-',
- '',
- );
- }
+ FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
+ FirebaseNotificationHandler().initial();
} catch (e) {
- log(e.toString());
+ print(e.toString());
}
runApp(const Didvan());
}
@@ -54,6 +67,7 @@ final GlobalKey navigatorKey = GlobalKey();
class Didvan extends StatelessWidget {
const Didvan({Key? key}) : super(key: key);
+
@override
Widget build(BuildContext context) {
return MultiProvider(
@@ -94,7 +108,8 @@ class Didvan extends StatelessWidget {
themeMode: themeProvider.themeMode,
onGenerateRoute: (settings) =>
RouteGenerator.generateRoute(settings),
- builder: BotToastInit(), //1. call BotToastInit
+ builder: BotToastInit(),
+ //1. call BotToastInit
navigatorObservers: [BotToastNavigatorObserver()],
initialRoute: '/',
localizationsDelegates: const [
diff --git a/lib/models/comment/comment.dart b/lib/models/comment/comment.dart
index 1fbf8d3..89c4d1e 100644
--- a/lib/models/comment/comment.dart
+++ b/lib/models/comment/comment.dart
@@ -6,9 +6,12 @@ class CommentData {
int id;
final String text;
final String createdAt;
+ String? type;
bool liked;
bool disliked;
+ bool private;
int status;
+ dynamic? mention;
final FeedbackData feedback;
final UserOverview user;
final List replies;
@@ -19,17 +22,21 @@ class CommentData {
required this.createdAt,
required this.liked,
required this.disliked,
+ required this.private,
required this.feedback,
required this.user,
required this.replies,
required this.status,
+ this.type,
+ required this.mention,
});
- factory CommentData.fromJson(Map json) => CommentData(
+ factory CommentData.fromJson(Map json, bool private) => CommentData(
id: json['id'],
text: json['text'],
createdAt: json['createdAt'],
liked: json['liked'],
+ private: private,
disliked: json['disliked'],
feedback: FeedbackData.fromJson(json['feedback']),
user: UserOverview.fromJson(json['user']),
@@ -39,14 +46,19 @@ class CommentData {
),
),
status: json['status'],
+ mention: json['mention'],
);
+
Map toJson() => {
'id': id,
'text': text,
'createdAt': createdAt,
'liked': liked,
'disliked': disliked,
+ 'private': private,
+ 'mention': mention,
+ 'type': type,
'feedback': feedback.toJson(),
'user': user.toJson(),
'replies': replies.map((e) => e.toJson()).toList(),
diff --git a/lib/models/comment/reply.dart b/lib/models/comment/reply.dart
index a5804f9..7358bf5 100644
--- a/lib/models/comment/reply.dart
+++ b/lib/models/comment/reply.dart
@@ -11,6 +11,7 @@ class Reply {
final FeedbackData feedback;
final UserOverview user;
final UserOverview toUser;
+ final String? mention;
Reply({
required this.id,
@@ -22,6 +23,7 @@ class Reply {
required this.user,
required this.toUser,
required this.status,
+ this.mention,
});
factory Reply.fromJson(Map json) => Reply(
@@ -34,6 +36,7 @@ class Reply {
user: UserOverview.fromJson(json['user']),
toUser: UserOverview.fromJson(json['toUser']),
status: json['status'],
+ mention: json['mention'],
);
Map toJson() => {
@@ -45,5 +48,6 @@ class Reply {
'feedback': feedback.toJson(),
'user': user.toJson(),
'toUser': toUser.toJson(),
+ 'mention': mention,
};
}
diff --git a/lib/models/notification_message.dart b/lib/models/notification_message.dart
new file mode 100644
index 0000000..901f1c1
--- /dev/null
+++ b/lib/models/notification_message.dart
@@ -0,0 +1,36 @@
+class NotificationMessage {
+ String? title;
+ String? body;
+ String? type;
+ String? clickAction;
+ String? url;
+ String? image;
+
+ NotificationMessage(
+ {this.title,
+ this.body,
+ this.type,
+ this.clickAction,
+ this.url,
+ this.image});
+
+ NotificationMessage.fromJson(Map json) {
+ title = json['title'];
+ body = json['body'];
+ type = json['type'];
+ clickAction = json['click_action'];
+ url = json['url'];
+ image = json['image'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['title'] = this.title;
+ data['body'] = this.body;
+ data['type'] = this.type;
+ data['click_action'] = this.clickAction;
+ data['url'] = this.url;
+ data['image'] = this.image;
+ return data;
+ }
+}
\ No newline at end of file
diff --git a/lib/models/users_mention.dart b/lib/models/users_mention.dart
new file mode 100644
index 0000000..0a75aa1
--- /dev/null
+++ b/lib/models/users_mention.dart
@@ -0,0 +1,24 @@
+class UsersMention {
+ int? id;
+ String? name;
+ String? type;
+ String? photo;
+
+ UsersMention({this.id, this.name, this.type, this.photo});
+
+ UsersMention.fromJson(Map json) {
+ id = json['id'];
+ name = json['name'];
+ type = json['type'];
+ photo = json['photo'];
+ }
+
+ Map toJson() {
+ final Map data = new Map();
+ data['id'] = this.id;
+ data['name'] = this.name;
+ data['type'] = this.type;
+ data['photo'] = this.photo;
+ return data;
+ }
+}
\ No newline at end of file
diff --git a/lib/services/network/request.dart b/lib/services/network/request.dart
index db54e3a..411ae74 100644
--- a/lib/services/network/request.dart
+++ b/lib/services/network/request.dart
@@ -11,6 +11,9 @@ class RequestService {
static late String token;
int? statusCode;
+ dynamic data(String s){
+ return _body?[s] ?? const {};
+ }
Map get result => _body?['result'] ?? const {};
Map get errors => _body?['errors'] ?? const {};
diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart
index 1693f9d..cb0b9ca 100644
--- a/lib/services/network/request_helper.dart
+++ b/lib/services/network/request_helper.dart
@@ -186,12 +186,13 @@ class RequestHelper {
static String mark(int id, String type) => '$baseUrl/$type/$id/mark';
static String tracking(int id, String type) => '$baseUrl/$type/$id/tracking';
- static String comments(int id, String type) => '$baseUrl/$type/$id/comments';
+ static String comments(int id, String type) => '$baseUrl/$type/$id/comments/v2';
+ static String usersMentions(String search) => '$baseUrl/comment/user?search=$search';
static String feedback(int id, int commentId, String type) =>
- '$baseUrl/$type/$id/comments/$commentId/feedback';
+ '$baseUrl/$type/$id/comments/$commentId/feedback/v2';
static String addComment(int id, String type) =>
'$baseUrl/$type/$id/comments/add';
- static String deleteComment(int id) => '$baseUrl/comment/$id';
+ static String deleteComment(int id) => '$baseUrl/comment/$id/v2';
static String reportComment(int id) => '$baseUrl/comment/$id/report';
static String _urlConcatGenerator(List> additions) {
diff --git a/lib/services/notification/awsome/awsome_notification_controller.dart b/lib/services/notification/awsome/awsome_notification_controller.dart
new file mode 100644
index 0000000..4781752
--- /dev/null
+++ b/lib/services/notification/awsome/awsome_notification_controller.dart
@@ -0,0 +1,41 @@
+import 'package:awesome_notifications/awesome_notifications.dart';
+
+class NotificationController {
+
+ /// Use this method to detect when a new notification or a schedule is created
+ @pragma("vm:entry-point")
+ static Future onNotificationCreatedMethod(ReceivedNotification receivedNotification) async {
+ // Your code goes here
+ print("onNotificationCreatedMethod--------------------------------------------------------------");
+
+ }
+
+ /// Use this method to detect every time that a new notification is displayed
+ @pragma("vm:entry-point")
+ static Future onNotificationDisplayedMethod(ReceivedNotification receivedNotification) async {
+ // Your code goes here
+ print("onNotificationDisplayedMethod--------------------------------------------------------------");
+
+ }
+
+ /// Use this method to detect if the user dismissed a notification
+ @pragma("vm:entry-point")
+ static Future onDismissActionReceivedMethod(ReceivedAction receivedAction) async {
+ // Your code goes here
+ print("onDismissActionReceivedMethod--------------------------------------------------------------");
+
+ }
+
+ /// Use this method to detect when the user taps on a notification or action button
+ @pragma("vm:entry-point")
+ static Future onActionReceivedMethod(ReceivedAction receivedAction) async {
+ // Your code goes here
+
+ // Navigate into pages, avoiding to open the notification details page over another details page already opened
+
+ // MyApp.navigatorKey.currentState?.pushNamedAndRemoveUntil('/notification-page',
+ // (route) => (route.settings.name != '/notification-page') || route.isFirst,
+ // arguments: receivedAction);
+ print("onActionReceivedMethod--------------------------------------------------------------");
+ }
+}
\ No newline at end of file
diff --git a/lib/services/notification/awsome/awsome_notification_handler.dart b/lib/services/notification/awsome/awsome_notification_handler.dart
new file mode 100644
index 0000000..e7091e4
--- /dev/null
+++ b/lib/services/notification/awsome/awsome_notification_handler.dart
@@ -0,0 +1,154 @@
+import 'dart:ui';
+
+import 'package:awesome_notifications/awesome_notifications.dart';
+import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/material.dart';
+import 'package:intl/intl.dart';
+
+import '../../../models/notification_message.dart';
+import 'awsome_notification_controller.dart';
+
+class AwsomeNotificationHandler {
+ main() async {
+ late ReceivedAction? initialAction;
+
+ AwesomeNotifications().initialize(
+ // set the icon to null if you want to use the default app icon
+ null,
+ [
+ NotificationChannel(
+ channelKey: 'alerts',
+ channelName: 'Alerts',
+ channelDescription: 'Notification tests as alerts',
+ playSound: true,
+ onlyAlertOnce: true,
+ groupAlertBehavior: GroupAlertBehavior.Children,
+ importance: NotificationImportance.High,
+ defaultPrivacy: NotificationPrivacy.Public,
+ defaultColor: const Color(0xFF007EA7),
+ criticalAlerts: true,
+ ledColor: Colors.white)
+ ],
+
+ // Channel groups are only visual and are not required
+ // channelGroups: [
+ // NotificationChannelGroup(
+ // channelGroupKey: 'basic_channel_group',
+ // channelGroupName: 'Basic group')
+ // ],
+ debug: true);
+
+ initialAction = await AwesomeNotifications()
+ .getInitialNotificationAction(removeFromActionEvents: false);
+
+ AwesomeNotifications().setListeners(
+ onActionReceivedMethod: NotificationController.onActionReceivedMethod,
+ onNotificationCreatedMethod:
+ NotificationController.onNotificationCreatedMethod,
+ onNotificationDisplayedMethod:
+ NotificationController.onNotificationDisplayedMethod,
+ onDismissActionReceivedMethod:
+ NotificationController.onDismissActionReceivedMethod);
+
+ AwesomeNotifications().isNotificationAllowed().then((isAllowed) {
+ //It would be more appropriate if you can show your own dialog
+ //to the user before requesting the notifications permissons.
+ if (!isAllowed) {
+ AwesomeNotifications().requestPermissionToSendNotifications(
+ permissions: [
+ NotificationPermission.Alert,
+ NotificationPermission.Sound,
+ NotificationPermission.Badge,
+ NotificationPermission.Vibration,
+ NotificationPermission.Light,
+ NotificationPermission.FullScreenIntent,
+ ],
+ );
+ }
+ });
+ }
+
+ show(RemoteMessage message) async {
+ NotificationMessage notificationMessage =
+ NotificationMessage.fromJson(message.data);
+
+ switch (notificationMessage.type) {
+ case "1":
+ await showNotificationTypeNews(notificationMessage);
+ break;
+
+ case "2":
+ await showNotificationTypeMessage(notificationMessage);
+
+ break;
+
+ case "3":
+ await showNotificationTypeEmoji(notificationMessage);
+
+ break;
+ }
+ try {} catch (ex) {}
+ }
+
+ showNotificationTypeNews(NotificationMessage message) async {
+ AwesomeNotifications().createNotification(
+ content: NotificationContent(
+ id: DateTime.now().millisecondsSinceEpoch ~/ 1000,
+ channelKey: 'alerts',
+ actionType: ActionType.Default,
+ title: "\u200f ${message.title} \u200f",
+ body: "${message.body.toString()}",
+ notificationLayout: NotificationLayout.BigText,
+ largeIcon: message.image.toString(),
+ color: const Color(0xFF007EA7)),
+ );
+ }
+
+ showNotificationTypeMessage(NotificationMessage message) async {
+ AwesomeNotifications().createNotification(
+ content: NotificationContent(
+ id: DateTime.now().millisecondsSinceEpoch ~/ 1000,
+ channelKey: 'alerts',
+ actionType: ActionType.Default,
+ title: message.title.toString(),
+ body: message.body.toString(),
+ largeIcon: message.image.toString(),
+ roundedLargeIcon: true,
+ notificationLayout: NotificationLayout.Inbox,
+ color: const Color(0xFF007EA7)),
+ actionButtons: [
+ NotificationActionButton(
+ key: 'REPLY',
+ label: 'جواب دادن',
+ requireInputText: true,
+ actionType: ActionType.SilentAction),
+ NotificationActionButton(
+ key: 'DISMISS',
+ label: 'خوانده شده',
+ actionType: ActionType.DismissAction,
+ isDangerousOption: true)
+ ]);
+ }
+
+ showNotificationTypeEmoji(NotificationMessage message) async {
+ AwesomeNotifications().createNotification(
+ content: NotificationContent(
+ id: DateTime.now().millisecondsSinceEpoch ~/ 1000,
+ channelKey: 'alerts',
+ title: 'Emojis are awesome too! ' +
+ Emojis.animals_lady_beetle +
+ Emojis.activites_balloon +
+ Emojis.emotion_red_heart,
+ body:
+ 'Simple body with a bunch of Emojis! ${Emojis.transport_police_car} ${Emojis.animals_dog} ${Emojis.flag_UnitedStates} ${Emojis.person_baby}',
+ largeIcon:
+ 'https://cdn.britannica.com/72/232772-050-4E3D86CC/mind-blown-emoji-head-exploding-emoticon.jpg',
+ notificationLayout: NotificationLayout.BigPicture,
+ payload: {
+ 'title': 'Notification Title',
+ 'body': 'Notification Body',
+ 'image': 'path/to/smallImage.png', // Path to small image
+ 'largeImage': 'path/to/largeImage.png', // Path to large image
+ }));
+ }
+}
diff --git a/lib/services/notification/fcm/firebase_notification_handler.dart b/lib/services/notification/fcm/firebase_notification_handler.dart
new file mode 100644
index 0000000..c1f905e
--- /dev/null
+++ b/lib/services/notification/fcm/firebase_notification_handler.dart
@@ -0,0 +1,161 @@
+import 'package:didvan/services/notification/awsome/awsome_notification_handler.dart';
+import 'package:firebase_core/firebase_core.dart';
+import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/foundation.dart';
+
+import '../lc/local_notification_service.dart';
+import '../../app_initalizer.dart';
+
+class FirebaseNotificationHandler {
+ Future initial() async {
+ try {
+ WidgetsFlutterBinding.ensureInitialized();
+
+ await Firebase.initializeApp(
+ options: kIsWeb
+ ? const FirebaseOptions(
+ apiKey: "AIzaSyA0HZjKpRuPOi1SC3f_EZTvlS3mcj9UVo0",
+ authDomain: "didvan-9b7da.firebaseapp.com",
+ projectId: "didvan-9b7da",
+ storageBucket: "didvan-9b7da.appspot.com",
+ messagingSenderId: "935017686266",
+ appId: "1:935017686266:web:a93f7a19bed23c51d2d543",
+ measurementId: "G-80B4H9E8Y0")
+ : const FirebaseOptions(
+ apiKey: 'AIzaSyBp-UHjWeM0H0UHtX5yguFKG-riMzvvCzw',
+ appId: '1:935017686266:android:f9cbc9aba8e3d65ed2d543',
+ messagingSenderId: '935017686266',
+ projectId: 'didvan-9b7da',
+ ),
+ );
+ final initMsg = await FirebaseMessaging.instance.getInitialMessage();
+ if (initMsg != null) {
+ AppInitializer.clickAction = initMsg.data['click_action'].replaceAll(
+ 'navigate-',
+ '',
+ );
+ }
+
+ // LocalNotificationService.initialize();
+
+ NotificationSettings settings =
+ await FirebaseMessaging.instance.requestPermission(
+ alert: true,
+ announcement: false,
+ badge: true,
+ carPlay: false,
+ criticalAlert: false,
+ provisional: false,
+ sound: true,
+ );
+
+ debugPrint(
+ 'User granted notifications permission: ${settings.authorizationStatus}');
+
+ // Handling background messages using the specified handler
+ // FirebaseMessaging.onBackgroundMessage(
+ // _firebaseMessagingBackgroundHandler);
+
+ // Listening for incoming messages while the app is in the foreground
+ FirebaseMessaging.onMessage.listen((RemoteMessage message) {
+ if (message.data != null) {
+/* if (message.notification!.title != null &&
+ message.notification!.body != null) {
+ final notificationData = message.data;
+ final screen = notificationData['screen'];
+
+ // Showing an alert dialog when a notification is received (Foreground state)
+ // showDialog(
+ // context: context,
+ // barrierDismissible: false,
+ // builder: (BuildContext context) {
+ // return WillPopScope(
+ // onWillPop: () async => false,
+ // child: AlertDialog(
+ // title: Text(message.notification!.title!),
+ // content: Text(message.notification!.body!),
+ // actions: [
+ // if (notificationData.containsKey('screen'))
+ // TextButton(
+ // onPressed: () {
+ // Navigator.pop(context);
+ // Navigator.of(context).pushNamed(screen);
+ // },
+ // child: const Text('Open Screen'),
+ // ),
+ // TextButton(
+ // onPressed: () => Navigator.of(context).pop(),
+ // child: const Text('Dismiss'),
+ // ),
+ // ],
+ // ),
+ // );
+ // },
+ // );
+ }*/
+ // LocalNotificationService.initialize();
+ // LocalNotificationService.showBigPictureNotification();
+ // LocalNotificationService.display(message);
+ AwsomeNotificationHandler().main();
+ AwsomeNotificationHandler().show(message);
+ }
+ });
+
+ // Handling the initial message received when the app is launched from dead (killed state)
+ // When the app is killed and a new notification arrives when user clicks on it
+ // It gets the data to which screen to open
+ FirebaseMessaging.instance.getInitialMessage().then((message) {
+ if (message != null) {
+ _handleNotificationClick(message);
+ }
+ });
+
+ // Handling a notification click event when the app is in the background
+ FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
+ _handleNotificationClick(message);
+ });
+ } catch (e) {
+ print(e.toString());
+ }
+ }
+
+ // Handling a notification click event by navigating to the specified screen
+ void _handleNotificationClick(RemoteMessage message) {
+ final notificationData = message.data;
+
+ if (notificationData.containsKey('screen')) {
+ final screen = notificationData['screen'];
+ // Navigator.of(context).pushNamed(screen);
+ }
+ }
+}
+
+// Handler for background messages
+// @pragma('vm:entry-point')
+// Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
+// await Firebase.initializeApp(
+// options: kIsWeb
+// ? const FirebaseOptions(
+// apiKey: "AIzaSyA0HZjKpRuPOi1SC3f_EZTvlS3mcj9UVo0",
+// authDomain: "didvan-9b7da.firebaseapp.com",
+// projectId: "didvan-9b7da",
+// storageBucket: "didvan-9b7da.appspot.com",
+// messagingSenderId: "935017686266",
+// appId: "1:935017686266:web:a93f7a19bed23c51d2d543",
+// measurementId: "G-80B4H9E8Y0")
+// : const FirebaseOptions(
+// apiKey: 'AIzaSyBp-UHjWeM0H0UHtX5yguFKG-riMzvvCzw',
+// appId: '1:935017686266:android:f9cbc9aba8e3d65ed2d543',
+// messagingSenderId: '935017686266',
+// projectId: 'didvan-9b7da',
+// ),
+// );
+//
+// LocalNotificationService.initialize();
+// LocalNotificationService.display(message);
+// // AwsomeNotificationHandler().main();
+// // AwsomeNotificationHandler().show(message);
+//
+// return Future.value();
+// }
diff --git a/lib/services/notification/lc/local_notification_service.dart b/lib/services/notification/lc/local_notification_service.dart
new file mode 100644
index 0000000..63d9b70
--- /dev/null
+++ b/lib/services/notification/lc/local_notification_service.dart
@@ -0,0 +1,133 @@
+import 'dart:io';
+
+import 'package:firebase_messaging/firebase_messaging.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_local_notifications/flutter_local_notifications.dart';
+import 'package:http/http.dart';
+import 'package:path_provider/path_provider.dart';
+
+import '../../../constants/assets.dart';
+import '../../../models/notification_message.dart';
+import '../../../views/widgets/notification/notification_dynamic_dialog.dart';
+
+class LocalNotificationService {
+// Instance of Flutternotification plugin
+ static final FlutterLocalNotificationsPlugin _notificationsPlugin =
+ FlutterLocalNotificationsPlugin();
+
+ static void initialize() {
+ // Initialization setting for android
+ const InitializationSettings initializationSettingsAndroid =
+ InitializationSettings(
+ android: AndroidInitializationSettings("@mipmap/ic_launcher"));
+ _notificationsPlugin.initialize(
+ initializationSettingsAndroid,
+ // to handle event when we receive notification
+ onDidReceiveNotificationResponse: (details) {
+ if (details.input != null) {}
+ },
+ );
+ }
+
+ static Future display(RemoteMessage message) async {
+ // To display the notification in device
+ try {
+ final id = DateTime.now().millisecondsSinceEpoch ~/ 1000;
+ final BigPictureStyleInformation bigPictureStyleInformation =
+ BigPictureStyleInformation(
+ FilePathAndroidBitmap(
+ "lib/assets/aaaa.jpg"),
+ contentTitle: NotificationMessage.fromJson(message.data).title,
+ htmlFormatContentTitle: true,
+ summaryText: NotificationMessage.fromJson(message.data).body,
+ htmlFormatSummaryText: true,
+ htmlFormatContent: true,
+ largeIcon: const FilePathAndroidBitmap(
+ "lib/assets/aaaa.jpg"));
+
+ NotificationDetails notificationDetails = NotificationDetails(
+ android: AndroidNotificationDetails("Channel Id", "Main Channel",
+ groupKey: "gfg",
+ color: Colors.white,
+ importance: Importance.max,
+ playSound: true,
+ priority: Priority.high),
+ );
+ if (message.notification == null) {
+ final NotificationMessage notificationModel =
+ NotificationMessage.fromJson(message.data);
+ await _notificationsPlugin.show(id, notificationModel.title,
+ notificationModel.body, notificationDetails,
+ payload: message.data['route']);
+ } else {
+ await _notificationsPlugin.show(id, message.notification?.title,
+ message.notification?.body, notificationDetails,
+ payload: message.data['route']);
+ }
+ } catch (e) {
+ debugPrint(e.toString());
+ }
+ }
+
+ void messageListener(BuildContext context) {
+ // Either you can pass buildcontext or you
+ // can take a context from navigator key
+ FirebaseMessaging.onMessage.listen((RemoteMessage message) {
+ print('Got a message in the foreground!');
+ print('Message data: ${message.data}');
+
+ if (message.data != null) {
+ print(
+ 'Message also contained a notification: so it will pop up ${message.notification!.body}');
+ showDialog(
+ context: context,
+ // context: navigatorKey!.currentContext!,
+ builder: ((BuildContext context) {
+ return NotificationDynamicDialog(
+ title: message.notification!.title,
+ body: message.notification!.body);
+ }));
+ }
+ });
+ }
+
+ static Future _downloadAndSaveFile(String url, String fileName) async {
+ final Directory directory = await getApplicationDocumentsDirectory();
+ final String filePath = '${directory.path}/$fileName';
+ final Response response = await get(Uri.parse(url));
+ final File file = File(filePath);
+ await file.writeAsBytes(response.bodyBytes);
+ return filePath;
+ }
+
+ static Future showBigPictureNotification() async {
+ final String largeIconPath = await _downloadAndSaveFile(
+ 'https://via.placeholder.com/48x48', 'largeIcon');
+ final String bigPicturePath = await _downloadAndSaveFile(
+ 'https://via.placeholder.com/400x800', 'bigPicture');
+ final BigPictureStyleInformation bigPictureStyleInformation =
+ BigPictureStyleInformation(FilePathAndroidBitmap(bigPicturePath),
+ largeIcon: FilePathAndroidBitmap(largeIconPath),
+ contentTitle: 'overridden big content title',
+ htmlFormatContentTitle: true,
+ summaryText: 'summary text',
+ htmlFormatSummaryText: true);
+ final AndroidNotificationDetails androidPlatformChannelSpecifics =
+ AndroidNotificationDetails(
+ 'big text channel id', 'big text channel name',
+ channelDescription: 'big text channel description',
+ largeIcon: FilePathAndroidBitmap(largeIconPath),
+ channelShowBadge: false,
+
+
+ styleInformation: bigPictureStyleInformation);
+ final NotificationDetails platformChannelSpecifics =
+ NotificationDetails(android: androidPlatformChannelSpecifics);
+ await _notificationsPlugin.show(
+ 0, 'big text title', 'silent body', platformChannelSpecifics);
+ }
+
+}
+
+
+
diff --git a/lib/services/notification/lc/show_notification_handler.dart b/lib/services/notification/lc/show_notification_handler.dart
new file mode 100644
index 0000000..c085647
--- /dev/null
+++ b/lib/services/notification/lc/show_notification_handler.dart
@@ -0,0 +1,40 @@
+import 'package:flutter_local_notifications/flutter_local_notifications.dart';
+
+void showNotificationAndroid(String title, String value) async {
+ const AndroidNotificationDetails androidNotificationDetails =
+ AndroidNotificationDetails('channel_id', 'Channel Name',
+ channelDescription: 'Channel Description',
+ importance: Importance.max,
+ priority: Priority.high,
+ ticker: 'ticker');
+
+ int notification_id = 1;
+ const NotificationDetails notificationDetails =
+ NotificationDetails(android: androidNotificationDetails);
+
+ await FlutterLocalNotificationsPlugin().show(
+ notification_id, title, value, notificationDetails,
+ payload: 'Not present');
+}
+
+void showNotificationIos(String title, String value) async {
+ DarwinNotificationDetails iOSPlatformChannelSpecifics = DarwinNotificationDetails(
+ presentAlert: true,
+ // Present an alert when the notification is displayed and the application is in the foreground (only from iOS 10 onwards)
+ presentBadge: true,
+ // Present the badge number when the notification is displayed and the application is in the foreground (only from iOS 10 onwards)
+ presentSound: true,
+ // Play a sound when the notification is displayed and the application is in the foreground (only from iOS 10 onwards)
+ subtitle: title,
+ //Secondary description (only from iOS 10 onwards)
+ threadIdentifier: value);
+
+ int notification_id = 1;
+
+ NotificationDetails platformChannelSpecifics =
+ NotificationDetails(iOS: iOSPlatformChannelSpecifics);
+
+ await FlutterLocalNotificationsPlugin().show(
+ notification_id, title, value, platformChannelSpecifics,
+ payload: 'Not present');
+}
diff --git a/lib/views/comments/comments.dart b/lib/views/comments/comments.dart
index 1488eff..e70175e 100644
--- a/lib/views/comments/comments.dart
+++ b/lib/views/comments/comments.dart
@@ -1,22 +1,33 @@
+import 'dart:ui';
+
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart';
+import 'package:didvan/models/users_mention.dart';
import 'package:didvan/models/view/app_bar_data.dart';
+import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/views/comments/comments_state.dart';
import 'package:didvan/views/comments/widgets/comment.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
+import 'package:didvan/views/widgets/didvan/checkbox.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/scaffold.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
+import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
+import '../widgets/skeleton_image.dart';
+import '../widgets/user_mention.dart';
+
class Comments extends StatefulWidget {
final Map pageData;
+
const Comments({
Key? key,
required this.pageData,
@@ -47,6 +58,8 @@ class _CommentsState extends State {
@override
Widget build(BuildContext context) {
+ final commentsState = context.watch();
+
final bottomViewInset = MediaQuery.of(context).viewInsets.bottom;
if (bottomViewInset == 0) {
if (_bottomPadding != 0) {
@@ -97,6 +110,48 @@ class _CommentsState extends State {
),
],
),
+ AnimatedVisibility(
+ duration: DesignConfig.lowAnimationDuration,
+ isVisible: commentsState.showUsersForMentionsLayout,
+ child: BackdropFilter(
+ filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
+ child: Container(
+ decoration: BoxDecoration(
+ color:
+ Theme.of(context).colorScheme.focused.withOpacity(0.5)),
+ child: DidvanScaffold(
+ appBarData: null,
+ padding: const EdgeInsets.only(
+ left: 16, right: 16, top: 16, bottom: 92),
+ backgroundColor: Colors.white.withOpacity(0.0),
+ slivers: [
+ Consumer(
+ builder: (context, state, child) =>
+ SliverStateHandler(
+ onRetry: state.getUsersMention,
+ state: state,
+ itemPadding: const EdgeInsets.symmetric(vertical: 8),
+ childCount: state.usersMention.length,
+ placeholder: const _UsersPlaceholder(),
+ centerEmptyState: _isPage,
+ enableEmptyState: state.usersMention.isEmpty,
+ emptyState: EmptyState(
+ asset: Assets.emptyBookmark,
+ title: 'لیست خالی است',
+ ),
+ builder: (context, state, index) {
+ return UserMention(
+ user: state.usersMention[index],
+ index: index,
+ );
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
Positioned(
left: 0,
right: 0,
@@ -111,6 +166,7 @@ class _CommentsState extends State {
class _MessageBox extends StatefulWidget {
final FocusNode focusNode;
+
const _MessageBox({Key? key, required this.focusNode}) : super(key: key);
@override
@@ -118,59 +174,190 @@ class _MessageBox extends StatefulWidget {
}
class _MessageBoxState extends State<_MessageBox> {
- final _controller = TextEditingController();
-
@override
Widget build(BuildContext context) {
final state = context.watch();
+
+ void _onCheckBoxChange(bool b) {
+ state.hideMentionedUser = b;
+ state.update();
+ }
+
return Column(
children: [
AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: state.showReplyBox,
- child: Container(
- padding: const EdgeInsets.all(8),
- decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.surface,
- border: Border(
- top: BorderSide(
- color: Theme.of(context).colorScheme.border,
+ child: Column(
+ children: [
+ Container(
+ padding: const EdgeInsets.all(8),
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.secondCTA,
+ borderRadius: const BorderRadius.only(
+ topRight: Radius.circular(16),
+ topLeft: Radius.circular(16)),
+ boxShadow: [
+ BoxShadow(
+ color: Theme.of(context)
+ .colorScheme
+ .title
+ .withOpacity(0.2),
+ offset: const Offset(
+ 5.0,
+ 5.0,
+ ),
+ blurRadius: 10.0,
+ spreadRadius: 2.0,
+ )
+ ]
+
+ // border: Border(
+ // top: BorderSide(
+ // color: Theme.of(context).colorScheme.border,
+ // ),
+ // ),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ if (state.replyingTo != null)
+ DidvanText(
+ 'پاسخ به ${state.replyingTo!.fullName}:',
+ color: Theme.of(context).colorScheme.primary,
+ style: Theme.of(context).textTheme.bodySmall,
+ ),
+ const Spacer(),
+ DidvanIconButton(
+ gestureSize: 24,
+ color: Theme.of(context).colorScheme.primary,
+ icon: DidvanIcons.close_regular,
+ onPressed: () {
+ state.commentId = null;
+ state.replyingTo = null;
+ state.showReplyBox = false;
+ state.update();
+ },
+ ),
+ ],
),
),
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- if (state.replyingTo != null)
- DidvanText(
- 'پاسخ به ${state.replyingTo!.fullName}:',
- color: Theme.of(context).colorScheme.caption,
- style: Theme.of(context).textTheme.bodySmall,
- ),
- const Spacer(),
- DidvanIconButton(
- gestureSize: 24,
- color: Theme.of(context).colorScheme.caption,
- icon: DidvanIcons.close_regular,
- onPressed: () {
- state.commentId = null;
- state.replyingTo = null;
- state.showReplyBox = false;
- state.update();
- },
+ Container(
+ color: Theme.of(context).colorScheme.secondCTA,
+ padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
+ child: Divider(
+ height: 2,
+ color: Theme.of(context).colorScheme.border,
),
- ],
- ),
+ )
+ ],
+ ),
+ ),
+ AnimatedVisibility(
+ duration: DesignConfig.lowAnimationDuration,
+ isVisible: state.usersMentioned.name != null,
+ child: Column(
+ children: [
+ Container(
+ padding: const EdgeInsets.all(8),
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.secondCTA,
+ borderRadius: !state.showReplyBox
+ ? BorderRadius.only(
+ topLeft: Radius.circular(16),
+ topRight: Radius.circular(16))
+ : null
+ // border: Border(
+ // top: BorderSide(
+ // color: Theme.of(context).colorScheme.border,
+ // ),
+ // ),
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Row(
+ mainAxisAlignment: MainAxisAlignment.start,
+ children: [
+ Container(
+ color: Theme.of(context).colorScheme.primary,
+ width: 2,
+ height: 40,
+ ),
+ SizedBox(
+ width: 8,
+ ),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ DidvanText(
+ "اشاره به",
+ color: Theme.of(context).colorScheme.inputText,
+ style: Theme.of(context).textTheme.labelSmall,
+ ),
+ DidvanText(
+ state.usersMentioned.name.toString(),
+ color: Theme.of(context).colorScheme.navigation,
+ style: Theme.of(context).textTheme.bodySmall,
+ ),
+ ],
+ )
+ ],
+ ),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.end,
+ children: [
+ DidvanIconButton(
+ gestureSize: 24,
+ color: Theme.of(context).colorScheme.primary,
+ icon: DidvanIcons.close_regular,
+ onPressed: () {
+ state.usersMentioned = UsersMention();
+ state.update();
+ },
+ ),
+ !state.showReplyBox
+ ? DidvanCheckbox(
+ title: "پنهان کردن فراخوانی",
+ value: state.hideMentionedUser,
+ color: Theme.of(context).colorScheme.primary,
+ onChanged: _onCheckBoxChange,
+ size: 12,
+ )
+ : SizedBox(),
+ ],
+ )
+ ],
+ ),
+ ),
+ ],
),
),
Container(
decoration: BoxDecoration(
- color: Theme.of(context).colorScheme.surface,
- border: Border(
- top: BorderSide(
- color: Theme.of(context).colorScheme.border,
- ),
- ),
+ boxShadow: state.showReplyBox && state.usersMentioned.name == null
+ ? null
+ : [
+ BoxShadow(
+ color:
+ Theme.of(context).colorScheme.title.withOpacity(0.2),
+ offset: const Offset(
+ 5.0,
+ 5.0,
+ ),
+ blurRadius: 10.0,
+ spreadRadius: 2.0,
+ )
+ ],
+ color: state.showReplyBox && state.usersMentioned.name == null
+ ? Theme.of(context).colorScheme.secondCTA
+ : Theme.of(context).colorScheme.surface,
+ // border: Border(
+ // top: BorderSide(
+ // color: Theme.of(context).colorScheme.border,
+ // ),
+ // ),
),
child: Row(
children: [
@@ -183,7 +370,7 @@ class _MessageBoxState extends State<_MessageBox> {
Expanded(
child: TextField(
focusNode: widget.focusNode,
- controller: _controller,
+ controller: state.commentTextFieldController,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.send,
style: Theme.of(context).textTheme.bodyMedium,
@@ -195,7 +382,7 @@ class _MessageBoxState extends State<_MessageBox> {
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Theme.of(context).colorScheme.disabledText),
),
- onChanged: (value) => state.text = value,
+ onChanged: (value) => _onChange(state, value),
),
),
],
@@ -206,9 +393,33 @@ class _MessageBoxState extends State<_MessageBox> {
}
void _onSend(CommentsState state) {
- if (state.text.replaceAll(' ', '').isNotEmpty) {
+ if (state.commentTextFieldController.text.replaceAll(' ', '').isNotEmpty) {
state.addComment();
- _controller.text = '';
+ state.commentTextFieldController.text = '';
+ }
+ }
+
+ void _onChange(CommentsState state, value) {
+ state.commentTextFieldController.text = value;
+ if (state.usersMentioned.name == null) {
+ if (state.commentTextFieldController.text.contains("@")) {
+ int index = state.commentTextFieldController.text.indexOf("@");
+ if (state.commentTextFieldController.text.length > index + 1) {
+ if (state.commentTextFieldController.text
+ .substring(index)
+ .contains(" ")) {
+ state.showUsersForMentionsLayout = false;
+ } else {
+ state.mentionedText =
+ state.commentTextFieldController.text.substring(index);
+ state.getUsersMention();
+ state.showUsersForMentionsLayout = true;
+ }
+ }
+ } else {
+ state.showUsersForMentionsLayout = false;
+ }
+ state.update();
}
}
}
@@ -276,3 +487,28 @@ class _CommentPlaceholder extends StatelessWidget {
);
}
}
+
+class _UsersPlaceholder extends StatelessWidget {
+ const _UsersPlaceholder({Key? key}) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return const Row(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ ShimmerPlaceholder(
+ height: 40,
+ width: 40,
+ borderRadius: DesignConfig.highBorderRadius,
+ ),
+ SizedBox(width: 16),
+ Expanded(
+ child: ShimmerPlaceholder(
+ borderRadius: DesignConfig.highBorderRadius,
+ height: 40,
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/lib/views/comments/comments_state.dart b/lib/views/comments/comments_state.dart
index 4a0594c..1344860 100644
--- a/lib/views/comments/comments_state.dart
+++ b/lib/views/comments/comments_state.dart
@@ -4,24 +4,32 @@ import 'package:didvan/models/comment/feedback.dart';
import 'package:didvan/models/comment/reply.dart';
import 'package:didvan/models/comment/user.dart';
import 'package:didvan/models/enums.dart';
+import 'package:didvan/models/users_mention.dart';
import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/providers/user.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/utils/action_sheet.dart';
+import 'package:didvan/views/widgets/user_mention.dart';
+import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
class CommentsState extends CoreProvier {
- String text = '';
+ TextEditingController commentTextFieldController = TextEditingController();
+ UsersMention usersMentioned = UsersMention();
+ String mentionedText = '';
int? commentId;
UserOverview? replyingTo;
bool showReplyBox = false;
+ bool showUsersForMentionsLayout = false;
+ bool hideMentionedUser = false;
late void Function(int count) onCommentsChanged;
int _count = 0;
late String type;
final List comments = [];
+ final List usersMention = [];
final Map> _feedbackQueue = {};
int itemId = 0;
@@ -33,9 +41,9 @@ class CommentsState extends CoreProvier {
await service.httpGet();
if (service.isSuccess) {
comments.clear();
- final messages = service.result['comments'];
+ final messages = service.result['comments']['public'];
for (var i = 0; i < messages.length; i++) {
- comments.add(CommentData.fromJson(messages[i]));
+ comments.add(CommentData.fromJson(messages[i], false));
_count++;
for (var j = 0; j < messages[i]['replies'].length; j++) {
_count++;
@@ -47,6 +55,23 @@ class CommentsState extends CoreProvier {
appState = AppState.failed;
}
+ Future getUsersMention() async {
+ final service = RequestService(
+ RequestHelper.usersMentions(mentionedText.replaceAll("@", "")),
+ );
+ await service.httpGet();
+ if (service.isSuccess) {
+ usersMention.clear();
+ final List users = service.data('users');
+ usersMention
+ .addAll(users.map((users) => UsersMention.fromJson(users)).toList());
+
+ appState = AppState.idle;
+ return;
+ }
+ appState = AppState.failed;
+ }
+
Future feedback({
required int id,
required bool like,
@@ -92,7 +117,7 @@ class CommentsState extends CoreProvier {
comments.firstWhere((comment) => comment.id == commentId).replies.add(
Reply(
id: 0,
- text: text,
+ text: commentTextFieldController.text,
createdAt: DateTime.now().toString(),
liked: false,
disliked: false,
@@ -104,26 +129,29 @@ class CommentsState extends CoreProvier {
photo: user.photo,
),
status: 2,
+ mention: usersMentioned.name,
),
);
} else {
comments.insert(
0,
CommentData(
- id: 0,
- text: text,
- createdAt: DateTime.now().toString(),
- liked: false,
- disliked: false,
- feedback: FeedbackData(like: 0, dislike: 0),
- user: UserOverview(
- id: user.id,
- fullName: user.fullName,
- photo: user.photo,
- ),
- replies: [],
- status: 2,
- ),
+ id: 0,
+ text: commentTextFieldController.text,
+ createdAt: DateTime.now().toString(),
+ liked: false,
+ disliked: false,
+ feedback: FeedbackData(like: 0, dislike: 0),
+ user: UserOverview(
+ id: user.id,
+ fullName: user.fullName,
+ photo: user.photo,
+ ),
+ replies: [],
+ status: 2,
+ private: hideMentionedUser,
+ mention: usersMentioned.name,
+ ),
);
}
@@ -138,8 +166,12 @@ class CommentsState extends CoreProvier {
body.addAll({'status': 2});
showReplyBox = false;
- update();
- body.addAll({'text': text});
+ // update();
+ body.addAll({
+ 'text': commentTextFieldController.text,
+ "mention": usersMentioned.id,
+ "private": hideMentionedUser
+ });
final service = RequestService(
RequestHelper.addComment(itemId, type),
body: body,
@@ -159,6 +191,9 @@ class CommentsState extends CoreProvier {
}
commentId = null;
replyingTo = null;
+ usersMentioned = UsersMention();
+ mentionedText = '';
+ update();
}
}
diff --git a/lib/views/comments/widgets/comment.dart b/lib/views/comments/widgets/comment.dart
index 124d8bc..f070cc1 100644
--- a/lib/views/comments/widgets/comment.dart
+++ b/lib/views/comments/widgets/comment.dart
@@ -20,6 +20,7 @@ import 'package:provider/provider.dart';
class Comment extends StatefulWidget {
final FocusNode focusNode;
final CommentData comment;
+
const Comment({
Key? key,
required this.focusNode,
@@ -109,6 +110,13 @@ class CommentState extends State {
),
],
),
+ comment.mention != null
+ ? DidvanText(
+ comment.mention.toString(),
+ color: Theme.of(context).colorScheme.primary,
+ style: Theme.of(context).textTheme.titleSmall,
+ )
+ : SizedBox(),
const SizedBox(height: 8),
if (isReply)
DidvanText(
@@ -254,6 +262,7 @@ class _FeedbackButtons extends StatefulWidget {
final bool dislikeValue;
final void Function(bool like, bool dislike, int likeCount, int dislikeCount)
onFeedback;
+
const _FeedbackButtons({
Key? key,
required this.onFeedback,
diff --git a/lib/views/widgets/didvan/checkbox.dart b/lib/views/widgets/didvan/checkbox.dart
index e0df308..9200422 100644
--- a/lib/views/widgets/didvan/checkbox.dart
+++ b/lib/views/widgets/didvan/checkbox.dart
@@ -1,15 +1,19 @@
+import 'package:didvan/config/theme_data.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
+import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class DidvanCheckbox extends StatefulWidget {
final String title;
final bool? value;
+ final double size;
+ final Color? color;
final void Function(bool value) onChanged;
const DidvanCheckbox({
Key? key,
required this.title,
required this.value,
- required this.onChanged,
+ required this.onChanged, this.size = 15, this.color,
}) : super(key: key);
@override
@@ -29,6 +33,9 @@ class _DidvanCheckboxState extends State {
@override
Widget build(BuildContext context) {
+ Color? _color = widget.color;
+ _color ??= Theme.of(context).colorScheme.text;
+
return GestureDetector(
onTap: () {
setState(() {
@@ -40,16 +47,22 @@ class _DidvanCheckboxState extends State {
color: Colors.transparent,
child: Row(
children: [
- Checkbox(
- value: _value,
- onChanged: (value) {
- setState(() {
- _value = value ?? false;
- });
- widget.onChanged(_value);
- },
- ),
- DidvanText(widget.title),
+ Transform.scale(
+ scale: widget.size ==15 ? 1 : 0.8,
+ child: Checkbox(
+ activeColor: widget.color,
+
+ visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
+ side: BorderSide(color: _color, width: 1.5),
+ value: _value,
+ onChanged: (value) {
+ setState(() {
+ _value = value ?? false;
+ });
+ widget.onChanged(_value);
+ },
+ ),
+ ), DidvanText(widget.title,fontSize: widget.size,color: _color, ),
],
),
),
diff --git a/lib/views/widgets/notification/notification_dynamic_dialog.dart b/lib/views/widgets/notification/notification_dynamic_dialog.dart
new file mode 100644
index 0000000..4256ae5
--- /dev/null
+++ b/lib/views/widgets/notification/notification_dynamic_dialog.dart
@@ -0,0 +1,31 @@
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+class NotificationDynamicDialog extends StatefulWidget {
+ final title;
+ final body;
+ const NotificationDynamicDialog({this.title, this.body});
+ @override
+ _NotificationDynamicDialogState createState() => _NotificationDynamicDialogState();
+}
+
+class _NotificationDynamicDialogState extends State {
+ @override
+ Widget build(BuildContext context) {
+ // You can change the UI as per
+ // your requirement or choice
+ return AlertDialog(
+ title: Text(widget.title),
+ actions: [
+ OutlinedButton.icon(
+ label: const Text('Close'),
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ icon: const Icon(Icons.close))
+ ],
+ content: Text(widget.body),
+ );
+ }
+}
+
diff --git a/lib/views/widgets/user_mention.dart b/lib/views/widgets/user_mention.dart
new file mode 100644
index 0000000..201ff39
--- /dev/null
+++ b/lib/views/widgets/user_mention.dart
@@ -0,0 +1,77 @@
+import 'package:didvan/config/theme_data.dart';
+import 'package:didvan/views/widgets/ink_wrapper.dart';
+import 'package:didvan/views/widgets/skeleton_image.dart';
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+import '../../config/design_config.dart';
+import '../../constants/app_icons.dart';
+import '../../models/users_mention.dart';
+import '../comments/comments_state.dart';
+import 'didvan/text.dart';
+
+class UserMention extends StatefulWidget {
+ final UsersMention user;
+ final int index;
+
+ const UserMention({
+ super.key,
+ required this.user,
+ required this.index,
+ });
+
+ @override
+ State createState() => _UserMentionState();
+}
+
+class _UserMentionState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final state = context.watch();
+
+ return InkWrapper(
+ onPressed: () {
+ state.usersMentioned = widget.user;
+ state.commentTextFieldController.text = state
+ .commentTextFieldController.text
+ .replaceAll(state.mentionedText.toString(), "")
+ .replaceAll("@", "");
+ state.showUsersForMentionsLayout = false;
+ state.update();
+ },
+ child: Column(
+ children: [
+ Row(
+ children: [
+ widget.user.photo == null
+ ? const Icon(DidvanIcons.avatar_light,size: 40,)
+ : SkeletonImage(
+ imageUrl: widget.user.photo.toString(),
+ height: 40,
+ width: 40,
+ borderRadius: BorderRadius.circular(360),
+ ),
+ const SizedBox(
+ width: 8,
+ ),
+ DidvanText(
+ widget.user.name.toString(),
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ ],
+ ),
+ const SizedBox(
+ height: 8,
+ ),
+ widget.index == state.usersMention.length - 1
+ ? const SizedBox()
+ : Divider(
+ height: 2,
+ color: Theme.of(context).colorScheme.splash,
+ )
+ ],
+ ),
+ );
+ }
+}
diff --git a/pubspec.lock b/pubspec.lock
index dbedf97..2654060 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -5,10 +5,10 @@ packages:
dependency: transitive
description:
name: _flutterfire_internals
- sha256: "4eec93681221723a686ad580c2e7d960e1017cf1a4e0a263c2573c2c6b0bf5cd"
+ sha256: "2350805d7afefb0efe7acd325cb19d3ae8ba4039b906eade3807ffb69938a01f"
url: "https://pub.dev"
source: hosted
- version: "1.3.25"
+ version: "1.3.33"
animated_custom_dropdown:
dependency: "direct main"
description:
@@ -29,10 +29,10 @@ packages:
dependency: transitive
description:
name: args
- sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
+ sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
url: "https://pub.dev"
source: hosted
- version: "2.4.2"
+ version: "2.5.0"
assets_audio_player:
dependency: "direct main"
description:
@@ -65,6 +65,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.2"
+ awesome_notifications:
+ dependency: "direct main"
+ description:
+ name: awesome_notifications
+ sha256: "65f730f9c0e73a346039ef746384bcff1773f9f03821b859705a7ab8db977b23"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.8.2"
+ awesome_notifications_core:
+ dependency: "direct main"
+ description:
+ name: awesome_notifications_core
+ sha256: "38c9f9d2618c0c2abe0cea267dc161aa8c4a0a239ca2263400900b9610f33e6f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.3"
better_player:
dependency: "direct main"
description:
@@ -110,10 +126,10 @@ packages:
dependency: transitive
description:
name: cached_network_image_web
- sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316"
+ sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7"
url: "https://pub.dev"
source: hosted
- version: "1.1.1"
+ version: "1.2.0"
carousel_slider:
dependency: "direct main"
description:
@@ -158,10 +174,10 @@ packages:
dependency: transitive
description:
name: cross_file
- sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e
+ sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
url: "https://pub.dev"
source: hosted
- version: "0.3.3+8"
+ version: "0.3.4+1"
crypto:
dependency: transitive
description:
@@ -182,10 +198,10 @@ packages:
dependency: "direct main"
description:
name: cupertino_icons
- sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d
+ sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev"
source: hosted
- version: "1.0.6"
+ version: "1.0.8"
day_night_time_picker:
dependency: "direct main"
description:
@@ -194,6 +210,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
+ dbus:
+ dependency: transitive
+ description:
+ name: dbus
+ sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.10"
equatable:
dependency: transitive
description:
@@ -222,10 +246,10 @@ packages:
dependency: transitive
description:
name: ffi
- sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
+ sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
url: "https://pub.dev"
source: hosted
- version: "2.1.0"
+ version: "2.1.2"
file:
dependency: transitive
description:
@@ -246,10 +270,10 @@ packages:
dependency: transitive
description:
name: file_selector_macos
- sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6
+ sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385
url: "https://pub.dev"
source: hosted
- version: "0.9.3+3"
+ version: "0.9.4"
file_selector_platform_interface:
dependency: transitive
description:
@@ -270,10 +294,10 @@ packages:
dependency: "direct main"
description:
name: firebase_core
- sha256: "53316975310c8af75a96e365f9fccb67d1c544ef0acdbf0d88bbe30eedd1c4f9"
+ sha256: "372d94ced114b9c40cb85e18c50ac94a7e998c8eec630c50d7aec047847d27bf"
url: "https://pub.dev"
source: hosted
- version: "2.27.0"
+ version: "2.31.0"
firebase_core_platform_interface:
dependency: transitive
description:
@@ -286,34 +310,34 @@ packages:
dependency: transitive
description:
name: firebase_core_web
- sha256: c8e1d59385eee98de63c92f961d2a7062c5d9a65e7f45bdc7f1b0b205aab2492
+ sha256: "43d9e951ac52b87ae9cc38ecdcca1e8fa7b52a1dd26a96085ba41ce5108db8e9"
url: "https://pub.dev"
source: hosted
- version: "2.11.5"
+ version: "2.17.0"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
- sha256: e41586e0fd04fe9a40424f8b0053d0832e6d04f49e020cdaf9919209a28497e9
+ sha256: e0882a7426821f7caccaabfc15a535155cd15b4daa73a5a7b3af701a552d73ab
url: "https://pub.dev"
source: hosted
- version: "14.7.19"
+ version: "14.9.2"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
- sha256: f7a9d74ff7fc588a924f6b2eaeaa148b0db521b13a9db55f6ad45864fa98c06e
+ sha256: "52e12cc50e1395ad7ea3552dcbe9958fb1994b5afcf58ee4c0db053932a6fce5"
url: "https://pub.dev"
source: hosted
- version: "4.5.27"
+ version: "4.5.35"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
- sha256: fc21e771166860c55b103701c5ac7cdb2eec28897b97c42e6e5703cbedf9e02e
+ sha256: "8812cc5929380b783f92290d934bf32e2fea06701583f47cdccd5f13f4f24522"
url: "https://pub.dev"
source: hosted
- version: "3.6.8"
+ version: "3.8.5"
fl_chart:
dependency: "direct main"
description:
@@ -351,6 +375,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.3"
+ flutter_local_notifications:
+ dependency: "direct main"
+ description:
+ name: flutter_local_notifications
+ sha256: "40e6fbd2da7dcc7ed78432c5cdab1559674b4af035fddbfb2f9a8f9c2112fcef"
+ url: "https://pub.dev"
+ source: hosted
+ version: "17.1.2"
+ flutter_local_notifications_linux:
+ dependency: transitive
+ description:
+ name: flutter_local_notifications_linux
+ sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.0+1"
+ flutter_local_notifications_platform_interface:
+ dependency: transitive
+ description:
+ name: flutter_local_notifications_platform_interface
+ sha256: "340abf67df238f7f0ef58f4a26d2a83e1ab74c77ab03cd2b2d5018ac64db30b7"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.1.0"
flutter_localizations:
dependency: "direct main"
description: flutter
@@ -360,10 +408,10 @@ packages:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
- sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
+ sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f"
url: "https://pub.dev"
source: hosted
- version: "2.0.17"
+ version: "2.0.19"
flutter_secure_storage:
dependency: "direct main"
description:
@@ -376,34 +424,34 @@ packages:
dependency: transitive
description:
name: flutter_secure_storage_linux
- sha256: "3d5032e314774ee0e1a7d0a9f5e2793486f0dff2dd9ef5a23f4e3fb2a0ae6a9e"
+ sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b"
url: "https://pub.dev"
source: hosted
- version: "1.2.0"
+ version: "1.2.1"
flutter_secure_storage_macos:
dependency: transitive
description:
name: flutter_secure_storage_macos
- sha256: bd33935b4b628abd0b86c8ca20655c5b36275c3a3f5194769a7b3f37c905369c
+ sha256: "8cfa53010a294ff095d7be8fa5bb15f2252c50018d69c5104851303f3ff92510"
url: "https://pub.dev"
source: hosted
- version: "3.0.1"
+ version: "3.1.0"
flutter_secure_storage_platform_interface:
dependency: transitive
description:
name: flutter_secure_storage_platform_interface
- sha256: "0d4d3a5dd4db28c96ae414d7ba3b8422fd735a8255642774803b2532c9a61d7e"
+ sha256: "301f67ee9b87f04aef227f57f13f126fa7b13543c8e7a93f25c5d2d534c28a4a"
url: "https://pub.dev"
source: hosted
- version: "1.0.2"
+ version: "1.1.1"
flutter_secure_storage_web:
dependency: transitive
description:
name: flutter_secure_storage_web
- sha256: "30f84f102df9dcdaa2241866a958c2ec976902ebdaa8883fbfe525f1f2f3cf20"
+ sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
url: "https://pub.dev"
source: hosted
- version: "1.1.2"
+ version: "1.2.1"
flutter_secure_storage_windows:
dependency: transitive
description:
@@ -424,10 +472,10 @@ packages:
dependency: "direct main"
description:
name: flutter_svg
- sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c
+ sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2"
url: "https://pub.dev"
source: hosted
- version: "2.0.9"
+ version: "2.0.10+1"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -482,10 +530,10 @@ packages:
dependency: "direct main"
description:
name: http
- sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
+ sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
url: "https://pub.dev"
source: hosted
- version: "0.13.6"
+ version: "1.2.1"
http_parser:
dependency: "direct main"
description:
@@ -506,34 +554,34 @@ packages:
dependency: "direct main"
description:
name: image_picker
- sha256: "26222b01a0c9a2c8fe02fc90b8208bd3325da5ed1f4a2acabf75939031ac0bdd"
+ sha256: "33974eca2e87e8b4e3727f1b94fa3abcb25afe80b6bc2c4d449a0e150aedf720"
url: "https://pub.dev"
source: hosted
- version: "1.0.7"
+ version: "1.1.1"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
- sha256: "42c098e7fb6334746be37cdc30369ade356ed4f14d48b7a0313f95a9159f4321"
+ sha256: "79455f6cff4cbef583b2b524bbf0d4ec424e5959f4d464e36ef5323715b98370"
url: "https://pub.dev"
source: hosted
- version: "0.8.9+5"
+ version: "0.8.12"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
- sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3
+ sha256: "5d6eb13048cd47b60dbf1a5495424dea226c5faf3950e20bf8120a58efb5b5f3"
url: "https://pub.dev"
source: hosted
- version: "3.0.2"
+ version: "3.0.4"
image_picker_ios:
dependency: transitive
description:
name: image_picker_ios
- sha256: "917a5cadd67d052554cfb258595e54217de53fac5b52939426e26319a02e6297"
+ sha256: cb0db0ec0d3e2cd49674f2e6053be25ccdb959832607c1cbd215dd6cf10fb0dd
url: "https://pub.dev"
source: hosted
- version: "0.8.9+2"
+ version: "0.8.11"
image_picker_linux:
dependency: transitive
description:
@@ -554,10 +602,10 @@ packages:
dependency: transitive
description:
name: image_picker_platform_interface
- sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b
+ sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80"
url: "https://pub.dev"
source: hosted
- version: "2.9.3"
+ version: "2.10.0"
image_picker_windows:
dependency: transitive
description:
@@ -690,26 +738,26 @@ packages:
dependency: "direct main"
description:
name: path_provider
- sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b
+ sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
url: "https://pub.dev"
source: hosted
- version: "2.1.2"
+ version: "2.1.3"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
- sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
+ sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
url: "https://pub.dev"
source: hosted
- version: "2.2.2"
+ version: "2.2.4"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
- sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f"
+ sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
url: "https://pub.dev"
source: hosted
- version: "2.3.2"
+ version: "2.4.0"
path_provider_linux:
dependency: transitive
description:
@@ -746,10 +794,10 @@ packages:
dependency: transitive
description:
name: permission_handler_android
- sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474"
+ sha256: "8bb852cd759488893805c3161d0b2b5db55db52f773dbb014420b304055ba2c5"
url: "https://pub.dev"
source: hosted
- version: "12.0.5"
+ version: "12.0.6"
permission_handler_apple:
dependency: transitive
description:
@@ -935,18 +983,18 @@ packages:
dependency: transitive
description:
name: sqflite
- sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6
+ sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d
url: "https://pub.dev"
source: hosted
- version: "2.3.2"
+ version: "2.3.3+1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
- sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5"
+ sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
url: "https://pub.dev"
source: hosted
- version: "2.5.3"
+ version: "2.5.4"
stack_trace:
dependency: transitive
description:
@@ -995,6 +1043,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.1"
+ timezone:
+ dependency: transitive
+ description:
+ name: timezone
+ sha256: a6ccda4a69a442098b602c44e61a1e2b4bf6f5516e875bbf0f427d5df14745d5
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.3"
toggle_switch:
dependency: "direct main"
description:
@@ -1031,26 +1087,26 @@ packages:
dependency: "direct main"
description:
name: url_launcher
- sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e"
+ sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
url: "https://pub.dev"
source: hosted
- version: "6.2.5"
+ version: "6.2.6"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
- sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745
+ sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
url: "https://pub.dev"
source: hosted
- version: "6.3.0"
+ version: "6.3.1"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
- sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
+ sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89"
url: "https://pub.dev"
source: hosted
- version: "6.2.5"
+ version: "6.3.0"
url_launcher_linux:
dependency: transitive
description:
@@ -1063,10 +1119,10 @@ packages:
dependency: transitive
description:
name: url_launcher_macos
- sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234
+ sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de"
url: "https://pub.dev"
source: hosted
- version: "3.1.0"
+ version: "3.2.0"
url_launcher_platform_interface:
dependency: transitive
description:
@@ -1079,10 +1135,10 @@ packages:
dependency: transitive
description:
name: url_launcher_web
- sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b
+ sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a"
url: "https://pub.dev"
source: hosted
- version: "2.2.3"
+ version: "2.3.1"
url_launcher_windows:
dependency: transitive
description:
@@ -1103,26 +1159,26 @@ packages:
dependency: transitive
description:
name: vector_graphics
- sha256: "4ac59808bbfca6da38c99f415ff2d3a5d7ca0a6b4809c71d9cf30fba5daf9752"
+ sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3"
url: "https://pub.dev"
source: hosted
- version: "1.1.10+1"
+ version: "1.1.11+1"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
- sha256: f3247e7ab0ec77dc759263e68394990edc608fb2b480b80db8aa86ed09279e33
+ sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da
url: "https://pub.dev"
source: hosted
- version: "1.1.10+1"
+ version: "1.1.11+1"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
- sha256: "18489bdd8850de3dd7ca8a34e0c446f719ec63e2bab2e7a8cc66a9028dd76c5a"
+ sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81"
url: "https://pub.dev"
source: hosted
- version: "1.1.10+1"
+ version: "1.1.11+1"
vector_math:
dependency: transitive
description:
@@ -1191,10 +1247,10 @@ packages:
dependency: transitive
description:
name: web
- sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05"
+ sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev"
source: hosted
- version: "0.4.2"
+ version: "0.5.1"
webview_flutter:
dependency: "direct main"
description:
@@ -1207,10 +1263,10 @@ packages:
dependency: transitive
description:
name: webview_flutter_android
- sha256: f038ee2fae73b509dde1bc9d2c5a50ca92054282de17631a9a3d515883740934
+ sha256: dad3313c9ead95517bb1cae5e1c9d20ba83729d5a59e5e83c0a2d66203f27f91
url: "https://pub.dev"
source: hosted
- version: "3.16.0"
+ version: "3.16.1"
webview_flutter_platform_interface:
dependency: transitive
description:
@@ -1252,5 +1308,5 @@ packages:
source: hosted
version: "6.5.0"
sdks:
- dart: ">=3.2.3 <4.0.0"
- flutter: ">=3.16.6"
+ dart: ">=3.3.0 <4.0.0"
+ flutter: ">=3.19.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 3fdf8a1..f11cb7f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -78,6 +78,10 @@ dependencies:
http: any
http_parser: any
cached_network_image_platform_interface: any
+ flutter_local_notifications: ^17.1.2
+ awesome_notifications_core: ^0.9.0
+ awesome_notifications: any
+
dev_dependencies:
flutter_test:
sdk: flutter