mirror of
https://github.com/Iconica-Development/flutter_notification_center.git
synced 2025-05-19 00:53:44 +02:00
Merge pull request #28 from Iconica-Development/5.0.0
refactor: new component structure
This commit is contained in:
commit
406589194f
51 changed files with 741 additions and 916 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -49,8 +49,10 @@ android/
|
||||||
web/
|
web/
|
||||||
linux/
|
linux/
|
||||||
macos/
|
macos/
|
||||||
|
windows/
|
||||||
|
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
.metadata
|
.metadata
|
||||||
flutter_notification_center.iml
|
flutter_notification_center.iml
|
||||||
dotenv
|
dotenv
|
||||||
|
firebase_options.dart
|
|
@ -1,3 +1,7 @@
|
||||||
|
## [5.0.0] - 24 September 2024
|
||||||
|
|
||||||
|
* Refactor package with the new component structure
|
||||||
|
|
||||||
## [4.0.0] - 14 August 2024
|
## [4.0.0] - 14 August 2024
|
||||||
|
|
||||||
* Fix overflow issue with long text in notification
|
* Fix overflow issue with long text in notification
|
||||||
|
|
|
@ -15,12 +15,13 @@ A Flutter package for creating notification center displaying a list of notifica
|
||||||
|
|
||||||
To use this package, add `flutter_notification_center` as a dependency in your pubspec.yaml file.
|
To use this package, add `flutter_notification_center` as a dependency in your pubspec.yaml file.
|
||||||
|
|
||||||
|
- Provide a `NotificationService` to the userstory to define and alter behaviour, this service accepts and `NotificationRepositoryInterface` as repository (data source)
|
||||||
|
|
||||||
- For custom notification styling provide the optional notificationWidgetBuilder with your own implementation.
|
- For custom notification styling provide the optional notificationWidgetBuilder with your own implementation.
|
||||||
|
|
||||||
The `NotificationConfig` has its own parameters, as specified below:
|
The `NotificationConfig` has its own parameters, as specified below:
|
||||||
| Parameter | Explanation |
|
| Parameter | Explanation |
|
||||||
|-----------|-------------|
|
|-----------|-------------|
|
||||||
| service | The notification service that will be used |
|
|
||||||
| seperateNotificationsWithDivider | If true notifications will be seperated with dividers within the notification center |
|
| seperateNotificationsWithDivider | If true notifications will be seperated with dividers within the notification center |
|
||||||
| translations | The translations that will be used |
|
| translations | The translations that will be used |
|
||||||
| notificationWidgetBuilder | The widget that defines the styles and logic for every notification |
|
| notificationWidgetBuilder | The widget that defines the styles and logic for every notification |
|
||||||
|
|
|
@ -22,31 +22,8 @@ migrate_working_dir/
|
||||||
#.vscode/
|
#.vscode/
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
**/doc/api/
|
**/doc/api/
|
||||||
**/ios/Flutter/.last_build_id
|
|
||||||
.dart_tool/
|
.dart_tool/
|
||||||
.flutter-plugins
|
build/
|
||||||
.flutter-plugins-dependencies
|
|
||||||
.pub-cache/
|
|
||||||
.pub/
|
|
||||||
/build/
|
|
||||||
.dart_tools/
|
|
||||||
|
|
||||||
# Symbolication related
|
|
||||||
app.*.symbols
|
|
||||||
|
|
||||||
# Obfuscation related
|
|
||||||
app.*.map.json
|
|
||||||
|
|
||||||
# Android Studio will place build artifacts here
|
|
||||||
/android/app/debug
|
|
||||||
/android/app/profile
|
|
||||||
/android/app/release
|
|
||||||
|
|
||||||
# iOS related
|
|
||||||
/ios/
|
|
||||||
|
|
||||||
lib/config/
|
|
||||||
pubspec.lock
|
|
||||||
dotenv
|
|
||||||
|
|
1
packages/firebase_notification_center_repository/CHANGELOG.md
Symbolic link
1
packages/firebase_notification_center_repository/CHANGELOG.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../CHANGELOG.md
|
1
packages/firebase_notification_center_repository/LICENSE
Symbolic link
1
packages/firebase_notification_center_repository/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE
|
1
packages/firebase_notification_center_repository/README.md
Symbolic link
1
packages/firebase_notification_center_repository/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../README.md
|
|
@ -0,0 +1 @@
|
||||||
|
export "src/firebase_notification_repository.dart";
|
|
@ -0,0 +1,151 @@
|
||||||
|
import "package:cloud_firestore/cloud_firestore.dart";
|
||||||
|
import "package:firebase_core/firebase_core.dart";
|
||||||
|
import "package:notification_center_repository_interface/notification_center_repository_interface.dart";
|
||||||
|
|
||||||
|
class FirebaseNotificationRepository
|
||||||
|
implements NotificationRepositoryInterface {
|
||||||
|
FirebaseNotificationRepository({
|
||||||
|
FirebaseApp? firebaseApp,
|
||||||
|
this.activeNotificationsCollection = "active_notifications",
|
||||||
|
this.plannedNotificationsCollection = "planned_notifications",
|
||||||
|
}) : firebaseApp = firebaseApp ?? Firebase.app();
|
||||||
|
|
||||||
|
final FirebaseApp firebaseApp;
|
||||||
|
final String activeNotificationsCollection;
|
||||||
|
final String plannedNotificationsCollection;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NotificationModel> addNotification(
|
||||||
|
String userId,
|
||||||
|
NotificationModel notification,
|
||||||
|
List<String> recipientIds,
|
||||||
|
) async {
|
||||||
|
var newNotification = notification;
|
||||||
|
|
||||||
|
for (var recipientId in recipientIds) {
|
||||||
|
DocumentReference notifications;
|
||||||
|
if (notification.scheduledFor != null &&
|
||||||
|
notification.scheduledFor!.isAfter(DateTime.now())) {
|
||||||
|
notifications = FirebaseFirestore.instanceFor(app: firebaseApp)
|
||||||
|
.collection(plannedNotificationsCollection)
|
||||||
|
.doc(recipientId)
|
||||||
|
.collection(plannedNotificationsCollection)
|
||||||
|
.doc(notification.id);
|
||||||
|
} else {
|
||||||
|
newNotification = notification.copyWith(
|
||||||
|
id: "${notification.id}-${DateTime.now().millisecondsSinceEpoch}",
|
||||||
|
);
|
||||||
|
|
||||||
|
notifications = FirebaseFirestore.instanceFor(app: firebaseApp)
|
||||||
|
.collection(activeNotificationsCollection)
|
||||||
|
.doc(recipientId)
|
||||||
|
.collection(activeNotificationsCollection)
|
||||||
|
.doc(newNotification.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentDateTime = DateTime.now();
|
||||||
|
newNotification.dateTimePushed = currentDateTime;
|
||||||
|
var notificationMap = newNotification.toMap();
|
||||||
|
await notifications.set(notificationMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newNotification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteNotification(
|
||||||
|
String userId,
|
||||||
|
String id,
|
||||||
|
bool planned,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
if (planned) {
|
||||||
|
await FirebaseFirestore.instanceFor(app: firebaseApp)
|
||||||
|
.collection(plannedNotificationsCollection)
|
||||||
|
.doc(userId)
|
||||||
|
.collection(plannedNotificationsCollection)
|
||||||
|
.doc(id)
|
||||||
|
.delete();
|
||||||
|
} else {
|
||||||
|
await FirebaseFirestore.instanceFor(app: firebaseApp)
|
||||||
|
.collection(activeNotificationsCollection)
|
||||||
|
.doc(userId)
|
||||||
|
.collection(activeNotificationsCollection)
|
||||||
|
.doc(id)
|
||||||
|
.delete();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception("Failed to delete notification: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<NotificationModel?> getNotification(String userId, String id) {
|
||||||
|
var notificationStream = FirebaseFirestore.instanceFor(app: firebaseApp)
|
||||||
|
.collection(activeNotificationsCollection)
|
||||||
|
.doc(userId)
|
||||||
|
.collection(activeNotificationsCollection)
|
||||||
|
.doc(id)
|
||||||
|
.snapshots()
|
||||||
|
.map((snapshot) {
|
||||||
|
if (snapshot.exists) {
|
||||||
|
if (snapshot.data() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NotificationModel.fromJson(snapshot.data()!);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return notificationStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<List<NotificationModel>> getNotifications(String userId) {
|
||||||
|
var notificationsStream = FirebaseFirestore.instanceFor(app: firebaseApp)
|
||||||
|
.collection(activeNotificationsCollection)
|
||||||
|
.doc(userId)
|
||||||
|
.collection(activeNotificationsCollection)
|
||||||
|
.snapshots()
|
||||||
|
.map(
|
||||||
|
(snapshot) => snapshot.docs
|
||||||
|
.map((doc) => NotificationModel.fromJson(doc.data()))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return notificationsStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NotificationModel> updateNotification(
|
||||||
|
String userId,
|
||||||
|
NotificationModel notification,
|
||||||
|
) async {
|
||||||
|
await FirebaseFirestore.instanceFor(app: firebaseApp)
|
||||||
|
.collection(activeNotificationsCollection)
|
||||||
|
.doc(userId)
|
||||||
|
.collection(activeNotificationsCollection)
|
||||||
|
.doc(notification.id)
|
||||||
|
.update(notification.toMap());
|
||||||
|
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<List<NotificationModel>> getPlannedNotifications(String userId) {
|
||||||
|
var notificationsStream = FirebaseFirestore.instanceFor(app: firebaseApp)
|
||||||
|
.collection(plannedNotificationsCollection)
|
||||||
|
.doc(userId)
|
||||||
|
.collection(plannedNotificationsCollection)
|
||||||
|
.snapshots()
|
||||||
|
.map(
|
||||||
|
(snapshot) => snapshot.docs
|
||||||
|
.map((doc) => NotificationModel.fromJson(doc.data()))
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return notificationsStream;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
name: firebase_notification_center_repository
|
||||||
|
description: "A new Flutter package project."
|
||||||
|
version: 5.0.0
|
||||||
|
homepage:
|
||||||
|
publish_to: 'none'
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ^3.5.3
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
# Firebase
|
||||||
|
cloud_firestore: ^5.4.2
|
||||||
|
firebase_core: ^3.5.0
|
||||||
|
|
||||||
|
notification_center_repository_interface:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_notification_center.git
|
||||||
|
path: packages/notification_center_repository_interface
|
||||||
|
ref: 5.0.0
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 7.0.0
|
||||||
|
|
||||||
|
flutter:
|
20
packages/flutter_notification_center/.gitignore
vendored
20
packages/flutter_notification_center/.gitignore
vendored
|
@ -22,22 +22,8 @@ migrate_working_dir/
|
||||||
#.vscode/
|
#.vscode/
|
||||||
|
|
||||||
# Flutter/Dart/Pub related
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
**/doc/api/
|
**/doc/api/
|
||||||
**/ios/Flutter/.last_build_id
|
|
||||||
.dart_tool/
|
.dart_tool/
|
||||||
.flutter-plugins
|
build/
|
||||||
.flutter-plugins-dependencies
|
|
||||||
.pub-cache/
|
|
||||||
.pub/
|
|
||||||
/build/
|
|
||||||
|
|
||||||
# Symbolication related
|
|
||||||
app.*.symbols
|
|
||||||
|
|
||||||
# Obfuscation related
|
|
||||||
app.*.map.json
|
|
||||||
|
|
||||||
# Android Studio will place build artifacts here
|
|
||||||
/android/app/debug
|
|
||||||
/android/app/profile
|
|
||||||
/android/app/release
|
|
||||||
|
|
1
packages/flutter_notification_center/CHANGELOG.md
Symbolic link
1
packages/flutter_notification_center/CHANGELOG.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../CHANGELOG.md
|
1
packages/flutter_notification_center/LICENSE
Symbolic link
1
packages/flutter_notification_center/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE
|
1
packages/flutter_notification_center/README.md
Symbolic link
1
packages/flutter_notification_center/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../README.md
|
|
@ -30,7 +30,6 @@ migrate_working_dir/
|
||||||
.pub-cache/
|
.pub-cache/
|
||||||
.pub/
|
.pub/
|
||||||
/build/
|
/build/
|
||||||
.dart_tools/
|
|
||||||
|
|
||||||
# Symbolication related
|
# Symbolication related
|
||||||
app.*.symbols
|
app.*.symbols
|
||||||
|
@ -42,11 +41,3 @@ app.*.map.json
|
||||||
/android/app/debug
|
/android/app/debug
|
||||||
/android/app/profile
|
/android/app/profile
|
||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
# iOS related
|
|
||||||
/ios/
|
|
||||||
|
|
||||||
lib/config/
|
|
||||||
pubspec.lock
|
|
||||||
dotenv
|
|
||||||
firebase_options.dart
|
|
||||||
|
|
|
@ -23,5 +23,6 @@ linter:
|
||||||
rules:
|
rules:
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
# Additional information about this file can be found at
|
||||||
# https://dart.dev/guides/language/analysis-options
|
# https://dart.dev/guides/language/analysis-options
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{"flutter":{"platforms":{"dart":{"lib/firebase_options.dart":{"projectId":"appshell-demo","configurations":{"web":"1:431820621472:web:f4b27eea24be24fd1babc5"}}}}}}
|
|
@ -1,167 +0,0 @@
|
||||||
import 'dart:async';
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_notification_center/flutter_notification_center.dart';
|
|
||||||
import 'package:flutter_notification_center_firebase/flutter_notification_center_firebase.dart';
|
|
||||||
|
|
||||||
class CustomNotificationWidget extends StatelessWidget {
|
|
||||||
final NotificationModel notification;
|
|
||||||
final FirebaseNotificationService notificationService;
|
|
||||||
final NotificationTranslations notificationTranslations;
|
|
||||||
final BuildContext context;
|
|
||||||
|
|
||||||
const CustomNotificationWidget({
|
|
||||||
required this.notification,
|
|
||||||
required this.notificationTranslations,
|
|
||||||
required this.notificationService,
|
|
||||||
required this.context,
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return notification.isPinned
|
|
||||||
//Pinned notification
|
|
||||||
? Dismissible(
|
|
||||||
key: Key('${notification.id}_pinned'),
|
|
||||||
onDismissed: (direction) async {
|
|
||||||
await unPinNotification(notificationService, notification,
|
|
||||||
notificationTranslations, context);
|
|
||||||
},
|
|
||||||
background: Container(
|
|
||||||
color: const Color.fromRGBO(59, 213, 111, 1),
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.only(left: 16.0),
|
|
||||||
child: Icon(
|
|
||||||
Icons.push_pin,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
secondaryBackground: Container(
|
|
||||||
color: const Color.fromRGBO(59, 213, 111, 1),
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.only(left: 16.0),
|
|
||||||
child: Icon(
|
|
||||||
Icons.push_pin,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () async =>
|
|
||||||
_navigateToNotificationDetail(context, notification),
|
|
||||||
child: ListTile(
|
|
||||||
title: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
notification.title,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
trailing: IconButton(
|
|
||||||
icon: const Icon(Icons.push_pin),
|
|
||||||
onPressed: () async =>
|
|
||||||
_navigateToNotificationDetail(context, notification),
|
|
||||||
padding: const EdgeInsets.only(left: 60.0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
//Dismissable notification
|
|
||||||
: Dismissible(
|
|
||||||
key: Key(notification.id),
|
|
||||||
onDismissed: (direction) async {
|
|
||||||
if (direction == DismissDirection.endToStart) {
|
|
||||||
await dismissNotification(notificationService, notification,
|
|
||||||
notificationTranslations, context);
|
|
||||||
} else if (direction == DismissDirection.startToEnd) {
|
|
||||||
await pinNotification(notificationService, notification,
|
|
||||||
notificationTranslations, context);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
background: Container(
|
|
||||||
color: const Color.fromRGBO(59, 213, 111, 1),
|
|
||||||
alignment: Alignment.centerLeft,
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.only(left: 16.0),
|
|
||||||
child: Icon(
|
|
||||||
Icons.push_pin,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
secondaryBackground: Container(
|
|
||||||
color: const Color.fromRGBO(255, 131, 131, 1),
|
|
||||||
alignment: Alignment.centerRight,
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.only(right: 16.0),
|
|
||||||
child: Icon(
|
|
||||||
Icons.delete,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () async => _navigateToNotificationDetail(
|
|
||||||
context,
|
|
||||||
notification,
|
|
||||||
),
|
|
||||||
child: ListTile(
|
|
||||||
leading: Icon(
|
|
||||||
notification.icon,
|
|
||||||
color: Colors.grey,
|
|
||||||
),
|
|
||||||
title: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
notification.title,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.black,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
fontSize: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
trailing: !notification.isRead
|
|
||||||
? Container(
|
|
||||||
margin: const EdgeInsets.only(right: 8.0),
|
|
||||||
width: 12.0,
|
|
||||||
height: 12.0,
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _navigateToNotificationDetail(
|
|
||||||
BuildContext context,
|
|
||||||
NotificationModel notification,
|
|
||||||
) async {
|
|
||||||
unawaited(markNotificationAsRead(notificationService, notification));
|
|
||||||
if (context.mounted) {
|
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => NotificationDetailPage(
|
|
||||||
translations: notificationTranslations,
|
|
||||||
notification: notification,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +1,10 @@
|
||||||
import 'package:example/custom_notification.dart';
|
// import 'package:example/firebase_options.dart';
|
||||||
// import 'package:firebase_auth/firebase_auth.dart';
|
// import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
import 'package:firebase_notification_center_repository/firebase_notification_center_repository.dart';
|
||||||
// import 'package:example/firebase_options.dart';
|
// import 'package:example/firebase_options.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
// import 'package:firebase_core/firebase_core.dart';
|
// import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:flutter_notification_center_firebase/flutter_notification_center_firebase.dart';
|
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
import 'package:flutter_notification_center/flutter_notification_center.dart';
|
import 'package:flutter_notification_center/flutter_notification_center.dart';
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ void main() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _configureApp() async {
|
Future<void> _configureApp() async {
|
||||||
|
// Generate a FirebaseOptions and uncomment the following lines to initialize Firebase.
|
||||||
// await Firebase.initializeApp(
|
// await Firebase.initializeApp(
|
||||||
// options: DefaultFirebaseOptions.currentPlatform,
|
// options: DefaultFirebaseOptions.currentPlatform,
|
||||||
// );
|
// );
|
||||||
|
@ -34,7 +35,8 @@ Future<void> _configureApp() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _signInUser() async {
|
Future<void> _signInUser() async {
|
||||||
/// Implement your own sign in logic here
|
// Sign in, you could use the line below or implement your own sign in method.
|
||||||
|
// await FirebaseAuth.instance.signInAnonymously();
|
||||||
}
|
}
|
||||||
|
|
||||||
class NotificationCenterDemo extends StatefulWidget {
|
class NotificationCenterDemo extends StatefulWidget {
|
||||||
|
@ -45,30 +47,39 @@ class NotificationCenterDemo extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
|
class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
|
||||||
late NotificationConfig config;
|
late NotificationService service;
|
||||||
late PopupHandler popupHandler;
|
// Provide a user ID here. For Firebase you can use the commented line below.
|
||||||
|
String userId = ""; //FirebaseAuth.instance.currentUser!.uid;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
var service = FirebaseNotificationService(
|
|
||||||
newNotificationCallback: (notification) {
|
service = NotificationService(
|
||||||
popupHandler.handleNotificationPopup(notification);
|
userId: userId,
|
||||||
},
|
repository: FirebaseNotificationRepository(),
|
||||||
);
|
);
|
||||||
config = NotificationConfig(
|
|
||||||
service: service,
|
// Uncomment the line below to send a test notification.
|
||||||
enableNotificationPopups: true,
|
// Provide a user ID in the list to send the notification to.
|
||||||
showAsSnackBar: true,
|
_sendTestNotification([userId]);
|
||||||
notificationWidgetBuilder: (notification, context) =>
|
}
|
||||||
CustomNotificationWidget(
|
|
||||||
notification: notification,
|
_sendTestNotification(List<String> recipientIds) async {
|
||||||
notificationService: service,
|
await service.pushNotification(
|
||||||
notificationTranslations: const NotificationTranslations.empty(),
|
NotificationModel(
|
||||||
context: context,
|
id: DateTime.now().millisecondsSinceEpoch.toString(),
|
||||||
|
title: 'Test Notification',
|
||||||
|
body: 'This is a test notification.',
|
||||||
|
// For a scheduled message provide a scheduledFor date.
|
||||||
|
// For a recurring message provide a scheduledFor date, set recurring to true and provide an occuringInterval.
|
||||||
|
//
|
||||||
|
// scheduledFor: DateTime.now().add(const Duration(seconds: 5)),
|
||||||
|
// recurring: true,
|
||||||
|
// occuringInterval: OcurringInterval.debug,
|
||||||
),
|
),
|
||||||
|
recipientIds,
|
||||||
);
|
);
|
||||||
popupHandler = PopupHandler(context: context, config: config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -79,7 +90,8 @@ class _NotificationCenterDemoState extends State<NotificationCenterDemo> {
|
||||||
centerTitle: true,
|
centerTitle: true,
|
||||||
actions: [
|
actions: [
|
||||||
NotificationBellWidgetStory(
|
NotificationBellWidgetStory(
|
||||||
config: config,
|
userId: userId,
|
||||||
|
service: service,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -2,38 +2,47 @@ name: example
|
||||||
description: "A new Flutter project."
|
description: "A new Flutter project."
|
||||||
# The following line prevents the package from being accidentally published to
|
# The following line prevents the package from being accidentally published to
|
||||||
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
# pub.dev using `flutter pub publish`. This is preferred for private packages.
|
||||||
publish_to: "none" # Remove this line if you wish to publish to pub.dev
|
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
|
|
||||||
version: 1.0.0
|
# The following defines the version and build number for your application.
|
||||||
|
# A version number is three numbers separated by dots, like 1.2.43
|
||||||
|
# followed by an optional build number separated by a +.
|
||||||
|
# Both the version and the builder number may be overridden in flutter
|
||||||
|
# build by specifying --build-name and --build-number, respectively.
|
||||||
|
# In Android, build-name is used as versionName while build-number used as versionCode.
|
||||||
|
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
|
||||||
|
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
|
||||||
|
# Read more about iOS versioning at
|
||||||
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
|
version: 1.0.0+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.3.2 <4.0.0"
|
sdk: ^3.5.3
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
intl: ^0.17.0
|
|
||||||
|
cupertino_icons: ^1.0.8
|
||||||
|
|
||||||
flutter_notification_center:
|
flutter_notification_center:
|
||||||
git:
|
path: ../
|
||||||
url: https://github.com/Iconica-Development/flutter_notification_center
|
|
||||||
path: packages/flutter_notification_center
|
|
||||||
ref: 3.0.0
|
|
||||||
|
|
||||||
flutter_notification_center_firebase:
|
firebase_notification_center_repository:
|
||||||
git:
|
path: ../../firebase_notification_center_repository
|
||||||
url: https://github.com/Iconica-Development/flutter_notification_center
|
|
||||||
path: packages/flutter_notification_center_firebase
|
intl: ^0.19.0
|
||||||
ref: 3.0.0
|
|
||||||
|
firebase_auth: any
|
||||||
|
firebase_core: any
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_iconica_analysis:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
|
||||||
ref: 7.0.0
|
|
||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
flutter_lints: ^4.0.0
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
1
packages/flutter_notification_center/firebase.json
Normal file
1
packages/flutter_notification_center/firebase.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"flutter":{"platforms":{"dart":{"lib/firebase_options.dart":{"projectId":"appshell-demo","configurations":{"web":"1:431820621472:web:f4b27eea24be24fd1babc5"}}}}}}
|
|
@ -1,17 +1,13 @@
|
||||||
// SPDX-FileCopyrightText: 2024 Iconica
|
export "package:notification_center_repository_interface/notification_center_repository_interface.dart";
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
export "package:flutter_animated_widgets/flutter_animated_widgets.dart";
|
|
||||||
|
|
||||||
export "src/models/notification.dart";
|
|
||||||
export "src/models/notification_config.dart";
|
|
||||||
export "src/models/notification_translation.dart";
|
|
||||||
export "src/notification_bell.dart";
|
|
||||||
export "src/notification_bell_story.dart";
|
export "src/notification_bell_story.dart";
|
||||||
export "src/notification_center.dart";
|
export "src/notification_center.dart";
|
||||||
export "src/notification_detail.dart";
|
|
||||||
export "src/notification_dialog.dart";
|
// Screens
|
||||||
export "src/notification_snackbar.dart";
|
export "src/screens/notification_bell.dart";
|
||||||
export "src/popup_handler.dart";
|
export "src/screens/notification_detail.dart";
|
||||||
export "src/services/notification_service.dart";
|
|
||||||
|
// Widgets
|
||||||
|
export "src/widgets/notification_dialog.dart";
|
||||||
|
export "src/widgets/notification_snackbar.dart";
|
||||||
|
export "src/widgets/popup_handler.dart";
|
||||||
|
|
|
@ -1,27 +1,64 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
import "package:flutter_animated_widgets/flutter_animated_widgets.dart";
|
||||||
|
import "package:flutter_notification_center/src/notification_center.dart";
|
||||||
|
import "package:flutter_notification_center/src/screens/notification_bell.dart";
|
||||||
|
import "package:notification_center_repository_interface/notification_center_repository_interface.dart";
|
||||||
|
|
||||||
/// A widget representing a notification bell.
|
/// A widget representing a notification bell.
|
||||||
class NotificationBellWidgetStory extends StatelessWidget {
|
class NotificationBellWidgetStory extends StatefulWidget {
|
||||||
/// Creates a new [NotificationBellWidgetStory] instance.
|
/// Creates a new [NotificationBellWidgetStory] instance.
|
||||||
///
|
///
|
||||||
/// The [config] parameter specifies the notification configuration.
|
/// The [config] parameter specifies the notification configuration.
|
||||||
const NotificationBellWidgetStory({
|
const NotificationBellWidgetStory({
|
||||||
required this.config,
|
required this.userId,
|
||||||
|
this.config,
|
||||||
|
this.service,
|
||||||
|
this.animatedIconStyle,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// The user ID.
|
||||||
|
final String userId;
|
||||||
|
|
||||||
/// The notification configuration.
|
/// The notification configuration.
|
||||||
final NotificationConfig config;
|
final NotificationConfig? config;
|
||||||
|
|
||||||
|
/// The notification service.
|
||||||
|
final NotificationService? service;
|
||||||
|
|
||||||
|
final AnimatedNotificationBellStyle? animatedIconStyle;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<NotificationBellWidgetStory> createState() =>
|
||||||
|
_NotificationBellWidgetStoryState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NotificationBellWidgetStoryState
|
||||||
|
extends State<NotificationBellWidgetStory> {
|
||||||
|
late NotificationConfig config;
|
||||||
|
late NotificationService service;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
config = widget.config ?? const NotificationConfig();
|
||||||
|
service = widget.service ??
|
||||||
|
NotificationService(
|
||||||
|
userId: widget.userId,
|
||||||
|
);
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => NotificationBell(
|
Widget build(BuildContext context) => NotificationBell(
|
||||||
config: config,
|
config: config,
|
||||||
|
service: service,
|
||||||
|
animatedIconStyle: widget.animatedIconStyle,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await Navigator.of(context).push(
|
await Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => NotificationCenter(
|
builder: (context) => NotificationCenter(
|
||||||
config: config,
|
config: config,
|
||||||
|
service: service,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
import "package:flutter_notification_center/src/screens/notification_detail.dart";
|
||||||
import "package:flutter_svg/svg.dart";
|
import "package:flutter_svg/svg.dart";
|
||||||
import "package:intl/intl.dart";
|
import "package:intl/intl.dart";
|
||||||
|
import "package:notification_center_repository_interface/notification_center_repository_interface.dart";
|
||||||
|
|
||||||
/// Widget for displaying the notification center.
|
/// Widget for displaying the notification center.
|
||||||
class NotificationCenter extends StatefulWidget {
|
class NotificationCenter extends StatefulWidget {
|
||||||
|
@ -10,39 +11,31 @@ class NotificationCenter extends StatefulWidget {
|
||||||
/// [config]: Configuration for the notification center.
|
/// [config]: Configuration for the notification center.
|
||||||
const NotificationCenter({
|
const NotificationCenter({
|
||||||
required this.config,
|
required this.config,
|
||||||
|
required this.service,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Configuration for the notification center.
|
/// Configuration for the notification center.
|
||||||
final NotificationConfig config;
|
final NotificationConfig config;
|
||||||
|
|
||||||
|
final NotificationService service;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
NotificationCenterState createState() => NotificationCenterState();
|
NotificationCenterState createState() => NotificationCenterState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State for the notification center.
|
/// State for the notification center.
|
||||||
class NotificationCenterState extends State<NotificationCenter> {
|
class NotificationCenterState extends State<NotificationCenter> {
|
||||||
late Future<List<NotificationModel>> _notificationsFuture;
|
late Stream<List<NotificationModel>> _notificationsStream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// ignore: discarded_futures
|
_notificationsStream = widget.service.getActiveNotifications();
|
||||||
_notificationsFuture = widget.config.service.getActiveNotifications();
|
|
||||||
widget.config.service.getActiveAmountStream().listen((amount) async {
|
|
||||||
_notificationsFuture = widget.config.service.getActiveNotifications();
|
|
||||||
});
|
|
||||||
widget.config.service.addListener(_listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
widget.service.getActiveAmountStream().listen((data) {
|
||||||
void dispose() {
|
|
||||||
widget.config.service.removeListener(_listener);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
void _listener() {
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -67,8 +60,8 @@ class NotificationCenterState extends State<NotificationCenter> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: FutureBuilder<List<NotificationModel>>(
|
body: StreamBuilder<List<NotificationModel>>(
|
||||||
future: _notificationsFuture,
|
stream: _notificationsStream,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
@ -101,7 +94,7 @@ class NotificationCenterState extends State<NotificationCenter> {
|
||||||
await _navigateToNotificationDetail(
|
await _navigateToNotificationDetail(
|
||||||
context,
|
context,
|
||||||
notification,
|
notification,
|
||||||
widget.config.service,
|
widget.service,
|
||||||
widget.config.translations,
|
widget.config.translations,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -111,7 +104,7 @@ class NotificationCenterState extends State<NotificationCenter> {
|
||||||
onDismissed: (direction) async {
|
onDismissed: (direction) async {
|
||||||
if (direction == DismissDirection.endToStart) {
|
if (direction == DismissDirection.endToStart) {
|
||||||
await unPinNotification(
|
await unPinNotification(
|
||||||
widget.config.service,
|
widget.service,
|
||||||
notification,
|
notification,
|
||||||
widget.config.translations,
|
widget.config.translations,
|
||||||
context,
|
context,
|
||||||
|
@ -119,7 +112,7 @@ class NotificationCenterState extends State<NotificationCenter> {
|
||||||
} else if (direction ==
|
} else if (direction ==
|
||||||
DismissDirection.startToEnd) {
|
DismissDirection.startToEnd) {
|
||||||
await unPinNotification(
|
await unPinNotification(
|
||||||
widget.config.service,
|
widget.service,
|
||||||
notification,
|
notification,
|
||||||
widget.config.translations,
|
widget.config.translations,
|
||||||
context,
|
context,
|
||||||
|
@ -183,7 +176,7 @@ class NotificationCenterState extends State<NotificationCenter> {
|
||||||
await _navigateToNotificationDetail(
|
await _navigateToNotificationDetail(
|
||||||
context,
|
context,
|
||||||
notification,
|
notification,
|
||||||
widget.config.service,
|
widget.service,
|
||||||
widget.config.translations,
|
widget.config.translations,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -193,7 +186,7 @@ class NotificationCenterState extends State<NotificationCenter> {
|
||||||
onDismissed: (direction) async {
|
onDismissed: (direction) async {
|
||||||
if (direction == DismissDirection.endToStart) {
|
if (direction == DismissDirection.endToStart) {
|
||||||
await dismissNotification(
|
await dismissNotification(
|
||||||
widget.config.service,
|
widget.service,
|
||||||
notification,
|
notification,
|
||||||
widget.config.translations,
|
widget.config.translations,
|
||||||
context,
|
context,
|
||||||
|
@ -201,7 +194,7 @@ class NotificationCenterState extends State<NotificationCenter> {
|
||||||
} else if (direction ==
|
} else if (direction ==
|
||||||
DismissDirection.startToEnd) {
|
DismissDirection.startToEnd) {
|
||||||
await pinNotification(
|
await pinNotification(
|
||||||
widget.config.service,
|
widget.service,
|
||||||
notification,
|
notification,
|
||||||
widget.config.translations,
|
widget.config.translations,
|
||||||
context,
|
context,
|
||||||
|
@ -270,8 +263,11 @@ Widget _notificationItem(
|
||||||
NotificationConfig config,
|
NotificationConfig config,
|
||||||
) {
|
) {
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
var dateTimePushed =
|
String? dateTimePushed;
|
||||||
DateFormat("dd/MM/yyyy 'at' HH:mm").format(notification.dateTimePushed!);
|
if (notification.dateTimePushed != null) {
|
||||||
|
dateTimePushed = DateFormat("dd/MM/yyyy 'at' HH:mm")
|
||||||
|
.format(notification.dateTimePushed!);
|
||||||
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8),
|
padding: const EdgeInsets.only(bottom: 8),
|
||||||
child: Container(
|
child: Container(
|
||||||
|
@ -332,7 +328,7 @@ Widget _notificationItem(
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
dateTimePushed,
|
dateTimePushed ?? "",
|
||||||
style: theme.textTheme.labelSmall,
|
style: theme.textTheme.labelSmall,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
import "package:flutter_animated_widgets/flutter_animated_widgets.dart";
|
||||||
|
import "package:notification_center_repository_interface/notification_center_repository_interface.dart";
|
||||||
|
|
||||||
/// A bell icon widget that displays the number of active notifications.
|
/// A bell icon widget that displays the number of active notifications.
|
||||||
///
|
///
|
||||||
|
@ -15,6 +16,8 @@ class NotificationBell extends StatefulWidget {
|
||||||
/// [onTap]: Callback function to be invoked when the bell icon is tapped.
|
/// [onTap]: Callback function to be invoked when the bell icon is tapped.
|
||||||
const NotificationBell({
|
const NotificationBell({
|
||||||
required this.config,
|
required this.config,
|
||||||
|
required this.service,
|
||||||
|
this.animatedIconStyle,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
@ -23,6 +26,12 @@ class NotificationBell extends StatefulWidget {
|
||||||
/// the notification service.
|
/// the notification service.
|
||||||
final NotificationConfig config;
|
final NotificationConfig config;
|
||||||
|
|
||||||
|
/// The notification service used to fetch active notifications.
|
||||||
|
final NotificationService service;
|
||||||
|
|
||||||
|
/// The style of the animated bell icon.
|
||||||
|
final AnimatedNotificationBellStyle? animatedIconStyle;
|
||||||
|
|
||||||
/// Callback function to be invoked when the bell icon is tapped.
|
/// Callback function to be invoked when the bell icon is tapped.
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
|
|
||||||
|
@ -37,7 +46,7 @@ class _NotificationBellState extends State<NotificationBell> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
widget.config.service.getActiveAmountStream().listen((amount) {
|
widget.service.getActiveAmountStream().listen((amount) {
|
||||||
setState(() {
|
setState(() {
|
||||||
notificationAmount = amount;
|
notificationAmount = amount;
|
||||||
});
|
});
|
||||||
|
@ -51,7 +60,8 @@ class _NotificationBellState extends State<NotificationBell> {
|
||||||
icon: AnimatedNotificationBell(
|
icon: AnimatedNotificationBell(
|
||||||
duration: const Duration(seconds: 1),
|
duration: const Duration(seconds: 1),
|
||||||
notificationCount: notificationAmount,
|
notificationCount: notificationAmount,
|
||||||
style: widget.config.bellStyle,
|
style:
|
||||||
|
widget.animatedIconStyle ?? const AnimatedNotificationBellStyle(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
|
||||||
import "package:intl/intl.dart";
|
import "package:intl/intl.dart";
|
||||||
|
import "package:notification_center_repository_interface/notification_center_repository_interface.dart";
|
||||||
|
|
||||||
/// A page displaying the details of a notification.
|
/// A page displaying the details of a notification.
|
||||||
class NotificationDetailPage extends StatelessWidget {
|
class NotificationDetailPage extends StatelessWidget {
|
|
@ -1,64 +0,0 @@
|
||||||
import "dart:async";
|
|
||||||
|
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:flutter_notification_center/src/models/notification.dart";
|
|
||||||
|
|
||||||
/// An abstract class representing a service for managing notifications.
|
|
||||||
abstract class NotificationService with ChangeNotifier {
|
|
||||||
/// Creates a new [NotificationService] instance.
|
|
||||||
///
|
|
||||||
/// The [listOfActiveNotifications] parameter specifies the
|
|
||||||
/// list of active notifications,
|
|
||||||
/// with a default value of an empty list.
|
|
||||||
///
|
|
||||||
/// The [listOfPlannedNotifications] parameter specifies the
|
|
||||||
/// list of planned notifications,
|
|
||||||
/// with a default value of an empty list.
|
|
||||||
NotificationService({
|
|
||||||
this.listOfActiveNotifications = const [],
|
|
||||||
this.listOfPlannedNotifications = const [],
|
|
||||||
});
|
|
||||||
|
|
||||||
/// A list of active notifications.
|
|
||||||
List<NotificationModel> listOfActiveNotifications;
|
|
||||||
|
|
||||||
/// A list of planned notifications.
|
|
||||||
List<NotificationModel> listOfPlannedNotifications;
|
|
||||||
|
|
||||||
/// Pushes a notification to the service.
|
|
||||||
Future pushNotification(
|
|
||||||
NotificationModel notification,
|
|
||||||
List<String> recipientIds, [
|
|
||||||
Function(NotificationModel model)? onNewNotification,
|
|
||||||
]);
|
|
||||||
|
|
||||||
/// Retrieves the list of active notifications.
|
|
||||||
Future<List<NotificationModel>> getActiveNotifications();
|
|
||||||
|
|
||||||
/// Creates a scheduled notification.
|
|
||||||
Future createScheduledNotification(NotificationModel notification);
|
|
||||||
|
|
||||||
/// Creates a recurring notification.
|
|
||||||
Future createRecurringNotification(NotificationModel notification);
|
|
||||||
|
|
||||||
/// Deletes a scheduled notification.
|
|
||||||
Future deletePlannedNotification(NotificationModel notification);
|
|
||||||
|
|
||||||
/// Dismisses an active notification.
|
|
||||||
Future dismissActiveNotification(NotificationModel notification);
|
|
||||||
|
|
||||||
/// Pin an active notification.
|
|
||||||
Future pinActiveNotification(NotificationModel notification);
|
|
||||||
|
|
||||||
/// Unpin an active notification.
|
|
||||||
Future unPinActiveNotification(NotificationModel notification);
|
|
||||||
|
|
||||||
/// Marks a notification as read.
|
|
||||||
Future markNotificationAsRead(NotificationModel notification);
|
|
||||||
|
|
||||||
/// Checks for scheduled notifications.
|
|
||||||
Future checkForScheduledNotifications();
|
|
||||||
|
|
||||||
/// Returns a stream of the number of active notifications.
|
|
||||||
Stream<int> getActiveAmountStream();
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
|
||||||
import "package:intl/intl.dart";
|
import "package:intl/intl.dart";
|
||||||
|
import "package:notification_center_repository_interface/notification_center_repository_interface.dart";
|
||||||
|
|
||||||
class NotificationDialog extends StatelessWidget {
|
class NotificationDialog extends StatelessWidget {
|
||||||
const NotificationDialog({
|
const NotificationDialog({
|
|
@ -1,6 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
|
||||||
import "package:intl/intl.dart";
|
import "package:intl/intl.dart";
|
||||||
|
import "package:notification_center_repository_interface/notification_center_repository_interface.dart";
|
||||||
|
|
||||||
class NotificationSnackbar extends SnackBar {
|
class NotificationSnackbar extends SnackBar {
|
||||||
NotificationSnackbar({
|
NotificationSnackbar({
|
|
@ -1,7 +1,8 @@
|
||||||
// Define a PopupHandler class to handle notification popups
|
// Define a PopupHandler class to handle notification popups
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_notification_center/src/widgets/notification_dialog.dart";
|
||||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
import "package:flutter_notification_center/src/widgets/notification_snackbar.dart";
|
||||||
|
import "package:notification_center_repository_interface/notification_center_repository_interface.dart";
|
||||||
|
|
||||||
class PopupHandler {
|
class PopupHandler {
|
||||||
PopupHandler({
|
PopupHandler({
|
|
@ -1,32 +1,37 @@
|
||||||
name: flutter_notification_center
|
name: flutter_notification_center
|
||||||
description: "A Flutter package for displaying notifications in a notification center."
|
description: "A new Flutter package project."
|
||||||
publish_to: "none"
|
version: 5.0.0
|
||||||
version: 4.0.0
|
homepage:
|
||||||
|
publish_to: 'none'
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=3.3.2 <4.0.0"
|
sdk: ^3.5.3
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
intl: any
|
|
||||||
|
intl: ^0.19.0
|
||||||
|
flutter_svg: ^2.0.10+1
|
||||||
|
|
||||||
flutter_animated_widgets:
|
flutter_animated_widgets:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_animated_widgets
|
url: https://github.com/Iconica-Development/flutter_animated_widgets
|
||||||
ref: 0.3.1
|
ref: 0.3.1
|
||||||
flutter_svg: ^2.0.10+1
|
|
||||||
|
notification_center_repository_interface:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_notification_center.git
|
||||||
|
path: packages/notification_center_repository_interface
|
||||||
|
ref: 5.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_iconica_analysis:
|
flutter_iconica_analysis:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
ref: 7.0.0
|
ref: 7.0.0
|
||||||
|
|
||||||
# The following section is specific to Flutter packages.
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
|
||||||
assets:
|
assets:
|
||||||
- assets/
|
- assets/
|
|
@ -1,12 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2022 Iconica
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
import "package:flutter_test/flutter_test.dart";
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
test("test", () {
|
|
||||||
expect(true, true);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2024 Iconica
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
export "src/services/firebase_notification_service.dart";
|
|
|
@ -1,420 +0,0 @@
|
||||||
import "dart:async";
|
|
||||||
|
|
||||||
import "package:cloud_firestore/cloud_firestore.dart";
|
|
||||||
import "package:firebase_auth/firebase_auth.dart";
|
|
||||||
import "package:firebase_core/firebase_core.dart";
|
|
||||||
import "package:flutter/material.dart";
|
|
||||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
|
||||||
|
|
||||||
class FirebaseNotificationService
|
|
||||||
with ChangeNotifier
|
|
||||||
implements NotificationService {
|
|
||||||
FirebaseNotificationService({
|
|
||||||
required this.newNotificationCallback,
|
|
||||||
this.firebaseApp,
|
|
||||||
this.activeNotificationsCollection = "active_notifications",
|
|
||||||
this.plannedNotificationsCollection = "planned_notifications",
|
|
||||||
this.listOfActiveNotifications = const [],
|
|
||||||
this.listOfPlannedNotifications = const [],
|
|
||||||
}) {
|
|
||||||
_firebaseApp = firebaseApp ?? Firebase.app();
|
|
||||||
unawaited(_startTimer());
|
|
||||||
}
|
|
||||||
final Function(NotificationModel) newNotificationCallback;
|
|
||||||
final FirebaseApp? firebaseApp;
|
|
||||||
final String activeNotificationsCollection;
|
|
||||||
final String plannedNotificationsCollection;
|
|
||||||
late FirebaseApp _firebaseApp;
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<NotificationModel> listOfActiveNotifications;
|
|
||||||
@override
|
|
||||||
List<NotificationModel> listOfPlannedNotifications;
|
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
late Timer _timer;
|
|
||||||
|
|
||||||
Future<void> _startTimer() async {
|
|
||||||
_timer = Timer.periodic(const Duration(seconds: 15), (timer) async {
|
|
||||||
await checkForScheduledNotifications();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> pushNotification(
|
|
||||||
NotificationModel notification,
|
|
||||||
List<String> recipientIds, [
|
|
||||||
Function(NotificationModel model)? onNewNotification,
|
|
||||||
]) async {
|
|
||||||
try {
|
|
||||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
debugPrint("User is not authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var recipientId in recipientIds) {
|
|
||||||
CollectionReference notifications =
|
|
||||||
FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(recipientId)
|
|
||||||
.collection(activeNotificationsCollection);
|
|
||||||
|
|
||||||
var currentDateTime = DateTime.now();
|
|
||||||
notification.dateTimePushed = currentDateTime;
|
|
||||||
var notificationMap = notification.toMap();
|
|
||||||
await notifications.doc(notification.id).set(notificationMap);
|
|
||||||
}
|
|
||||||
if (recipientIds.contains(userId)) {
|
|
||||||
listOfActiveNotifications = [
|
|
||||||
...listOfActiveNotifications,
|
|
||||||
notification,
|
|
||||||
];
|
|
||||||
|
|
||||||
//Show popup with notification conte
|
|
||||||
if (onNewNotification != null) {
|
|
||||||
onNewNotification(notification);
|
|
||||||
} else {
|
|
||||||
newNotificationCallback(notification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
} on Exception catch (e) {
|
|
||||||
debugPrint("Error creating document: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<List<NotificationModel>> getActiveNotifications() async {
|
|
||||||
try {
|
|
||||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
debugPrint("User is not authenticated");
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
CollectionReference activeNotificationsResult =
|
|
||||||
FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(userId)
|
|
||||||
.collection(activeNotificationsCollection);
|
|
||||||
|
|
||||||
var querySnapshot = await activeNotificationsResult.get();
|
|
||||||
|
|
||||||
var activeNotifications = querySnapshot.docs.map((doc) {
|
|
||||||
var data = doc.data()! as Map<String, dynamic>;
|
|
||||||
data["id"] = doc.id;
|
|
||||||
return NotificationModel.fromJson(data);
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
listOfActiveNotifications = List.from(activeNotifications);
|
|
||||||
|
|
||||||
listOfActiveNotifications.removeWhere((element) => element.isPinned);
|
|
||||||
activeNotifications
|
|
||||||
.sort((a, b) => b.dateTimePushed!.compareTo(a.dateTimePushed!));
|
|
||||||
|
|
||||||
listOfActiveNotifications
|
|
||||||
.sort((a, b) => b.dateTimePushed!.compareTo(a.dateTimePushed!));
|
|
||||||
|
|
||||||
listOfActiveNotifications.insertAll(
|
|
||||||
0,
|
|
||||||
activeNotifications.where((element) => element.isPinned),
|
|
||||||
);
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
return listOfActiveNotifications;
|
|
||||||
} on Exception catch (e) {
|
|
||||||
debugPrint("Error getting active notifications: $e");
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> createRecurringNotification(
|
|
||||||
NotificationModel notification,
|
|
||||||
) async {
|
|
||||||
if (notification.recurring) {
|
|
||||||
switch (notification.occuringInterval) {
|
|
||||||
case OcurringInterval.daily:
|
|
||||||
notification.scheduledFor =
|
|
||||||
DateTime.now().add(const Duration(days: 1));
|
|
||||||
break;
|
|
||||||
case OcurringInterval.weekly:
|
|
||||||
notification.scheduledFor =
|
|
||||||
DateTime.now().add(const Duration(days: 7));
|
|
||||||
break;
|
|
||||||
case OcurringInterval.monthly:
|
|
||||||
notification.scheduledFor =
|
|
||||||
DateTime.now().add(const Duration(days: 30));
|
|
||||||
break;
|
|
||||||
case OcurringInterval.debug:
|
|
||||||
notification.scheduledFor =
|
|
||||||
DateTime.now().add(const Duration(seconds: 10));
|
|
||||||
break;
|
|
||||||
case null:
|
|
||||||
}
|
|
||||||
await createScheduledNotification(notification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> createScheduledNotification(
|
|
||||||
NotificationModel notification,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
debugPrint("User is not authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CollectionReference plannedNotifications =
|
|
||||||
FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(plannedNotificationsCollection)
|
|
||||||
.doc(userId)
|
|
||||||
.collection(plannedNotificationsCollection);
|
|
||||||
|
|
||||||
var notificationMap = notification.toMap();
|
|
||||||
await plannedNotifications.doc(notification.id).set(notificationMap);
|
|
||||||
} on Exception catch (e) {
|
|
||||||
debugPrint("Error creating document: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> deletePlannedNotification(
|
|
||||||
NotificationModel notificationModel,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
debugPrint("User is not authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentReference documentReference =
|
|
||||||
FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(plannedNotificationsCollection)
|
|
||||||
.doc(userId)
|
|
||||||
.collection(plannedNotificationsCollection)
|
|
||||||
.doc(notificationModel.id);
|
|
||||||
await documentReference.delete();
|
|
||||||
|
|
||||||
QuerySnapshot querySnapshot =
|
|
||||||
await FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(plannedNotificationsCollection)
|
|
||||||
.doc(userId)
|
|
||||||
.collection(plannedNotificationsCollection)
|
|
||||||
.get();
|
|
||||||
|
|
||||||
if (querySnapshot.docs.isEmpty) {
|
|
||||||
debugPrint("The collection is now empty");
|
|
||||||
} else {
|
|
||||||
debugPrint(
|
|
||||||
"Deleted planned notification with title: ${notificationModel.title}",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} on Exception catch (e) {
|
|
||||||
debugPrint("Error deleting document: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> dismissActiveNotification(
|
|
||||||
NotificationModel notificationModel,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
debugPrint("User is not authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentReference documentReference =
|
|
||||||
FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(userId)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(notificationModel.id);
|
|
||||||
await documentReference.delete();
|
|
||||||
listOfActiveNotifications
|
|
||||||
.removeWhere((element) => element.id == notificationModel.id);
|
|
||||||
notifyListeners();
|
|
||||||
} on Exception catch (e) {
|
|
||||||
debugPrint("Error deleting document: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> pinActiveNotification(
|
|
||||||
NotificationModel notificationModel,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
debugPrint("User is not authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentReference documentReference =
|
|
||||||
FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(userId)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(notificationModel.id);
|
|
||||||
await documentReference.update({"isPinned": true});
|
|
||||||
notificationModel.isPinned = true;
|
|
||||||
|
|
||||||
listOfActiveNotifications
|
|
||||||
.removeAt(listOfActiveNotifications.indexOf(notificationModel));
|
|
||||||
listOfActiveNotifications.insert(0, notificationModel);
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
} on Exception catch (e) {
|
|
||||||
debugPrint("Error updating document: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> unPinActiveNotification(
|
|
||||||
NotificationModel notificationModel,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
debugPrint("User is not authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentReference documentReference =
|
|
||||||
FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(userId)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(notificationModel.id);
|
|
||||||
await documentReference.update({"isPinned": false});
|
|
||||||
notificationModel.isPinned = false;
|
|
||||||
|
|
||||||
listOfActiveNotifications
|
|
||||||
.removeAt(listOfActiveNotifications.indexOf(notificationModel));
|
|
||||||
|
|
||||||
listOfActiveNotifications.add(notificationModel);
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
} on Exception catch (e) {
|
|
||||||
debugPrint("Error updating document: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> markNotificationAsRead(
|
|
||||||
NotificationModel notificationModel,
|
|
||||||
) async {
|
|
||||||
try {
|
|
||||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
debugPrint("User is not authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentReference documentReference =
|
|
||||||
FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(userId)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(notificationModel.id);
|
|
||||||
await documentReference.update({"isRead": true});
|
|
||||||
notificationModel.isRead = true;
|
|
||||||
notifyListeners();
|
|
||||||
} on Exception catch (e) {
|
|
||||||
debugPrint("Error updating document: $e");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> checkForScheduledNotifications() async {
|
|
||||||
var currentTime = DateTime.now();
|
|
||||||
try {
|
|
||||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
debugPrint("User is not authenticated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CollectionReference plannedNotificationsResult =
|
|
||||||
FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(plannedNotificationsCollection)
|
|
||||||
.doc(userId)
|
|
||||||
.collection(plannedNotificationsCollection);
|
|
||||||
|
|
||||||
var querySnapshot = await plannedNotificationsResult.get();
|
|
||||||
|
|
||||||
if (querySnapshot.docs.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var plannedNotifications = querySnapshot.docs.map((doc) {
|
|
||||||
var data = doc.data()! as Map<String, dynamic>;
|
|
||||||
return NotificationModel.fromJson(data);
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
for (var notification in plannedNotifications) {
|
|
||||||
if (notification.scheduledFor!.isBefore(currentTime) ||
|
|
||||||
notification.scheduledFor!.isAtSameMomentAs(currentTime)) {
|
|
||||||
await pushNotification(
|
|
||||||
notification,
|
|
||||||
[userId],
|
|
||||||
newNotificationCallback,
|
|
||||||
);
|
|
||||||
|
|
||||||
await deletePlannedNotification(notification);
|
|
||||||
|
|
||||||
//Plan new recurring notification instance
|
|
||||||
if (notification.recurring) {
|
|
||||||
var newNotification = NotificationModel(
|
|
||||||
id: UniqueKey().toString(),
|
|
||||||
title: notification.title,
|
|
||||||
body: notification.body,
|
|
||||||
recurring: true,
|
|
||||||
occuringInterval: notification.occuringInterval,
|
|
||||||
scheduledFor: DateTime.now().add(const Duration(seconds: 10)),
|
|
||||||
);
|
|
||||||
await createScheduledNotification(newNotification);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} on Exception catch (e) {
|
|
||||||
debugPrint("Error getting planned notifications: $e");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<int> getActiveAmountStream() async* {
|
|
||||||
var userId = FirebaseAuth.instanceFor(app: _firebaseApp).currentUser?.uid;
|
|
||||||
|
|
||||||
if (userId == null) {
|
|
||||||
debugPrint("User is not authenticated");
|
|
||||||
yield 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var amount = FirebaseFirestore.instanceFor(app: _firebaseApp)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.doc(userId)
|
|
||||||
.collection(activeNotificationsCollection)
|
|
||||||
.where("isRead", isEqualTo: false)
|
|
||||||
.snapshots()
|
|
||||||
.map((e) => e.docs.length);
|
|
||||||
yield* amount;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
name: flutter_notification_center_firebase
|
|
||||||
description: "A new Flutter project."
|
|
||||||
publish_to: "none"
|
|
||||||
version: 4.0.0
|
|
||||||
|
|
||||||
environment:
|
|
||||||
sdk: ">=2.18.0 <3.0.0"
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
flutter:
|
|
||||||
sdk: flutter
|
|
||||||
intl: any
|
|
||||||
|
|
||||||
# Firebase
|
|
||||||
cloud_firestore: ^4.16.0
|
|
||||||
firebase_auth: ^4.2.6
|
|
||||||
firebase_core: ^2.5.0
|
|
||||||
|
|
||||||
cupertino_icons: ^1.0.2
|
|
||||||
|
|
||||||
flutter_notification_center:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_notification_center
|
|
||||||
ref: 4.0.0
|
|
||||||
path: packages/flutter_notification_center
|
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
flutter_test:
|
|
||||||
sdk: flutter
|
|
||||||
flutter_iconica_analysis:
|
|
||||||
git:
|
|
||||||
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
|
||||||
ref: 7.0.0
|
|
||||||
|
|
||||||
flutter:
|
|
||||||
uses-material-design: true
|
|
|
@ -1,30 +0,0 @@
|
||||||
// // This is a basic Flutter widget test.
|
|
||||||
// //
|
|
||||||
// // To perform an interaction with a widget in your test, use the WidgetTester
|
|
||||||
// // utility in the flutter_test package. For example, you can send tap and scroll
|
|
||||||
// // gestures. You can also use WidgetTester to find child widgets in the widget
|
|
||||||
// // tree, read text, and verify that the values of widget properties are correct.
|
|
||||||
|
|
||||||
// import 'package:flutter/material.dart';
|
|
||||||
// import 'package:flutter_test/flutter_test.dart';
|
|
||||||
|
|
||||||
// import 'package:example/main.dart';
|
|
||||||
|
|
||||||
// void main() {
|
|
||||||
// testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
|
||||||
// // Build our app and trigger a frame.
|
|
||||||
// await tester.pumpWidget(const MyApp());
|
|
||||||
|
|
||||||
// // Verify that our counter starts at 0.
|
|
||||||
// expect(find.text('0'), findsOneWidget);
|
|
||||||
// expect(find.text('1'), findsNothing);
|
|
||||||
|
|
||||||
// // Tap the '+' icon and trigger a frame.
|
|
||||||
// await tester.tap(find.byIcon(Icons.add));
|
|
||||||
// await tester.pump();
|
|
||||||
|
|
||||||
// // Verify that our counter has incremented.
|
|
||||||
// expect(find.text('0'), findsNothing);
|
|
||||||
// expect(find.text('1'), findsOneWidget);
|
|
||||||
// });
|
|
||||||
// }
|
|
29
packages/notification_center_repository_interface/.gitignore
vendored
Normal file
29
packages/notification_center_repository_interface/.gitignore
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
build/
|
1
packages/notification_center_repository_interface/CHANGELOG.md
Symbolic link
1
packages/notification_center_repository_interface/CHANGELOG.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../CHANGELOG.md
|
1
packages/notification_center_repository_interface/LICENSE
Symbolic link
1
packages/notification_center_repository_interface/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE
|
1
packages/notification_center_repository_interface/README.md
Symbolic link
1
packages/notification_center_repository_interface/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../README.md
|
|
@ -0,0 +1,9 @@
|
||||||
|
include: package:flutter_iconica_analysis/analysis_options.yaml
|
||||||
|
|
||||||
|
# Possible to overwrite the rules from the package
|
||||||
|
|
||||||
|
analyzer:
|
||||||
|
exclude:
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Interfaces
|
||||||
|
export "src/interfaces/notification_repository_interface.dart";
|
||||||
|
|
||||||
|
// Local
|
||||||
|
export "src/local/local_notification_repository.dart";
|
||||||
|
|
||||||
|
// Models
|
||||||
|
export "src/models/notification.dart";
|
||||||
|
export "src/models/notification_config.dart";
|
||||||
|
export "src/models/notification_translation.dart";
|
||||||
|
|
||||||
|
// Services
|
||||||
|
export "src/services/notification_service.dart";
|
|
@ -0,0 +1,27 @@
|
||||||
|
import "package:notification_center_repository_interface/notification_center_repository_interface.dart";
|
||||||
|
|
||||||
|
abstract class NotificationRepositoryInterface {
|
||||||
|
Future<NotificationModel> addNotification(
|
||||||
|
String userId,
|
||||||
|
NotificationModel notification,
|
||||||
|
List<String> recipientIds,
|
||||||
|
);
|
||||||
|
|
||||||
|
Stream<NotificationModel?> getNotification(String userId, String id);
|
||||||
|
|
||||||
|
Stream<List<NotificationModel>> getNotifications(String userId);
|
||||||
|
|
||||||
|
Stream<List<NotificationModel>> getPlannedNotifications(String userId);
|
||||||
|
|
||||||
|
Future<NotificationModel> updateNotification(
|
||||||
|
String userId,
|
||||||
|
NotificationModel notification,
|
||||||
|
);
|
||||||
|
|
||||||
|
Future<void> deleteNotification(
|
||||||
|
String userId,
|
||||||
|
String id,
|
||||||
|
// ignore: avoid_positional_boolean_parameters
|
||||||
|
bool planned,
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
import "dart:async";
|
||||||
|
|
||||||
|
import "package:notification_center_repository_interface/src/interfaces/notification_repository_interface.dart";
|
||||||
|
import "package:notification_center_repository_interface/src/models/notification.dart";
|
||||||
|
import "package:rxdart/rxdart.dart";
|
||||||
|
|
||||||
|
class LocalNotificationRepository implements NotificationRepositoryInterface {
|
||||||
|
final List<NotificationModel> _activeNotifications = [];
|
||||||
|
final List<NotificationModel> _plannedNotifications = [];
|
||||||
|
|
||||||
|
final StreamController<List<NotificationModel>> _notificationsController =
|
||||||
|
BehaviorSubject<List<NotificationModel>>();
|
||||||
|
|
||||||
|
final StreamController<List<NotificationModel>>
|
||||||
|
_plannedNotificationsController =
|
||||||
|
BehaviorSubject<List<NotificationModel>>();
|
||||||
|
|
||||||
|
final StreamController<NotificationModel> _notificationController =
|
||||||
|
BehaviorSubject<NotificationModel>();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NotificationModel> addNotification(
|
||||||
|
String userId,
|
||||||
|
NotificationModel notification,
|
||||||
|
List<String> recipientIds,
|
||||||
|
) async {
|
||||||
|
if (notification.scheduledFor != null &&
|
||||||
|
notification.scheduledFor!.isAfter(DateTime.now())) {
|
||||||
|
_plannedNotifications.add(notification);
|
||||||
|
} else {
|
||||||
|
_activeNotifications.add(notification);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNotifications(userId);
|
||||||
|
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> deleteNotification(
|
||||||
|
String userId,
|
||||||
|
String id,
|
||||||
|
bool planned,
|
||||||
|
) async {
|
||||||
|
if (planned) {
|
||||||
|
_plannedNotifications.removeWhere((element) => element.id == id);
|
||||||
|
} else {
|
||||||
|
_activeNotifications.removeWhere((element) => element.id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNotifications(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<NotificationModel?> getNotification(String userId, String id) {
|
||||||
|
var notification = _activeNotifications.firstWhere(
|
||||||
|
(element) => element.id == id,
|
||||||
|
);
|
||||||
|
|
||||||
|
_notificationController.add(notification);
|
||||||
|
|
||||||
|
return _notificationController.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<List<NotificationModel>> getNotifications(String userId) {
|
||||||
|
_notificationsController.add(_activeNotifications);
|
||||||
|
|
||||||
|
return _notificationsController.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<List<NotificationModel>> getPlannedNotifications(String userId) {
|
||||||
|
_plannedNotificationsController.add(_plannedNotifications);
|
||||||
|
|
||||||
|
return _plannedNotificationsController.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<NotificationModel> updateNotification(
|
||||||
|
String userId,
|
||||||
|
NotificationModel notification,
|
||||||
|
) async {
|
||||||
|
_activeNotifications
|
||||||
|
.removeWhere((element) => element.id == notification.id);
|
||||||
|
|
||||||
|
_activeNotifications.add(notification);
|
||||||
|
getNotifications(userId);
|
||||||
|
|
||||||
|
return notification;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
import "package:flutter/material.dart";
|
|
||||||
|
|
||||||
/// Enum representing the interval at which notifications occur.
|
/// Enum representing the interval at which notifications occur.
|
||||||
enum OcurringInterval {
|
enum OcurringInterval {
|
||||||
/// Notifications occur daily.
|
/// Notifications occur daily.
|
||||||
|
@ -39,7 +37,7 @@ class NotificationModel {
|
||||||
this.occuringInterval,
|
this.occuringInterval,
|
||||||
this.isPinned = false,
|
this.isPinned = false,
|
||||||
this.isRead = false,
|
this.isRead = false,
|
||||||
this.icon = Icons.notifications,
|
this.icon,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Method to create a NotificationModel object from JSON data
|
/// Method to create a NotificationModel object from JSON data
|
||||||
|
@ -59,9 +57,7 @@ class NotificationModel {
|
||||||
: null,
|
: null,
|
||||||
isPinned = json["isPinned"] ?? false,
|
isPinned = json["isPinned"] ?? false,
|
||||||
isRead = json["isRead"] ?? false,
|
isRead = json["isRead"] ?? false,
|
||||||
icon = json["icon"] != null
|
icon = json["icon"] ?? 0xe44f;
|
||||||
? IconData(json["icon"], fontFamily: Icons.notifications.fontFamily)
|
|
||||||
: Icons.notifications;
|
|
||||||
|
|
||||||
/// Unique identifier for the notification.
|
/// Unique identifier for the notification.
|
||||||
final String id;
|
final String id;
|
||||||
|
@ -91,14 +87,16 @@ class NotificationModel {
|
||||||
bool isRead;
|
bool isRead;
|
||||||
|
|
||||||
/// Icon to be displayed with the notification.
|
/// Icon to be displayed with the notification.
|
||||||
final IconData icon;
|
final int? icon;
|
||||||
|
|
||||||
/// Override toString() to provide custom string representation
|
/// Override toString() to provide custom string representation
|
||||||
@override
|
@override
|
||||||
String toString() => "NotificationModel{id: $id, title: $title, body: $body, "
|
String toString() => """
|
||||||
|
NotificationModel{id: $id, title: $title, body: $body, "
|
||||||
"dateTimePushed: $dateTimePushed, scheduledFor: $scheduledFor, "
|
"dateTimePushed: $dateTimePushed, scheduledFor: $scheduledFor, "
|
||||||
"recurring: $recurring, occuringInterval: $occuringInterval, "
|
"recurring: $recurring, occuringInterval: $occuringInterval, "
|
||||||
"isPinned: $isPinned, icon: $icon}";
|
"isPinned: $isPinned, icon: $icon
|
||||||
|
}""";
|
||||||
|
|
||||||
/// Convert the NotificationModel object to a Map.
|
/// Convert the NotificationModel object to a Map.
|
||||||
Map<String, dynamic> toMap() => {
|
Map<String, dynamic> toMap() => {
|
||||||
|
@ -111,7 +109,7 @@ class NotificationModel {
|
||||||
"occuringInterval": occuringInterval?.index,
|
"occuringInterval": occuringInterval?.index,
|
||||||
"isPinned": isPinned,
|
"isPinned": isPinned,
|
||||||
"isRead": isRead,
|
"isRead": isRead,
|
||||||
"icon": icon.codePoint,
|
"icon": icon,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create a copy of the NotificationModel with some fields replaced.
|
/// Create a copy of the NotificationModel with some fields replaced.
|
||||||
|
@ -125,7 +123,7 @@ class NotificationModel {
|
||||||
OcurringInterval? occuringInterval,
|
OcurringInterval? occuringInterval,
|
||||||
bool? isPinned,
|
bool? isPinned,
|
||||||
bool? isRead,
|
bool? isRead,
|
||||||
IconData? icon,
|
int? icon,
|
||||||
}) =>
|
}) =>
|
||||||
NotificationModel(
|
NotificationModel(
|
||||||
id: id ?? this.id,
|
id: id ?? this.id,
|
|
@ -1,6 +1,6 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:notification_center_repository_interface/src/models/notification.dart";
|
||||||
import "package:flutter_notification_center/flutter_notification_center.dart";
|
import "package:notification_center_repository_interface/src/models/notification_translation.dart";
|
||||||
|
|
||||||
/// Configuration class for notifications.
|
/// Configuration class for notifications.
|
||||||
class NotificationConfig {
|
class NotificationConfig {
|
||||||
|
@ -11,20 +11,15 @@ class NotificationConfig {
|
||||||
/// notification. The [translations] parameter is also optional and provides
|
/// notification. The [translations] parameter is also optional and provides
|
||||||
/// translations for notification messages.
|
/// translations for notification messages.
|
||||||
const NotificationConfig({
|
const NotificationConfig({
|
||||||
required this.service,
|
|
||||||
this.translations = const NotificationTranslations.empty(),
|
this.translations = const NotificationTranslations.empty(),
|
||||||
this.notificationWidgetBuilder,
|
this.notificationWidgetBuilder,
|
||||||
this.showAsSnackBar = true,
|
this.showAsSnackBar = true,
|
||||||
this.enableNotificationPopups = true,
|
this.enableNotificationPopups = true,
|
||||||
this.bellStyle = const AnimatedNotificationBellStyle(),
|
|
||||||
this.pinnedIconColor = Colors.black,
|
this.pinnedIconColor = Colors.black,
|
||||||
this.emptyNotificationsBuilder,
|
this.emptyNotificationsBuilder,
|
||||||
this.onNotificationTap,
|
this.onNotificationTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The notification service to use for delivering notifications.
|
|
||||||
final NotificationService service;
|
|
||||||
|
|
||||||
/// Translations for notification messages.
|
/// Translations for notification messages.
|
||||||
final NotificationTranslations translations;
|
final NotificationTranslations translations;
|
||||||
|
|
||||||
|
@ -39,9 +34,6 @@ class NotificationConfig {
|
||||||
/// Whether to show notification popups.
|
/// Whether to show notification popups.
|
||||||
final bool enableNotificationPopups;
|
final bool enableNotificationPopups;
|
||||||
|
|
||||||
/// The style of the notification bell.
|
|
||||||
final AnimatedNotificationBellStyle bellStyle;
|
|
||||||
|
|
||||||
/// The color of the trailing icon (if any) in the notification.
|
/// The color of the trailing icon (if any) in the notification.
|
||||||
final Color? pinnedIconColor;
|
final Color? pinnedIconColor;
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
import "dart:async";
|
||||||
|
|
||||||
|
import "package:notification_center_repository_interface/src/interfaces/notification_repository_interface.dart";
|
||||||
|
import "package:notification_center_repository_interface/src/local/local_notification_repository.dart";
|
||||||
|
import "package:notification_center_repository_interface/src/models/notification.dart";
|
||||||
|
|
||||||
|
class NotificationService {
|
||||||
|
NotificationService({
|
||||||
|
required this.userId,
|
||||||
|
this.pollingInterval = const Duration(seconds: 15),
|
||||||
|
NotificationRepositoryInterface? repository,
|
||||||
|
this.onNewNotification,
|
||||||
|
}) : repository = repository ?? LocalNotificationRepository() {
|
||||||
|
unawaited(_startTimer());
|
||||||
|
}
|
||||||
|
|
||||||
|
final NotificationRepositoryInterface repository;
|
||||||
|
final Function(NotificationModel)? onNewNotification;
|
||||||
|
final String userId;
|
||||||
|
final Duration pollingInterval;
|
||||||
|
|
||||||
|
Timer? timer;
|
||||||
|
|
||||||
|
Future<void> _startTimer() async {
|
||||||
|
timer = Timer.periodic(pollingInterval, (timer) async {
|
||||||
|
await checkForScheduledNotifications();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pushes a notification to the service.
|
||||||
|
Future<void> pushNotification(
|
||||||
|
NotificationModel notification,
|
||||||
|
List<String> recipientIds,
|
||||||
|
) {
|
||||||
|
var result = repository.addNotification(
|
||||||
|
userId,
|
||||||
|
notification,
|
||||||
|
recipientIds,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (recipientIds.contains(userId)) {
|
||||||
|
getActiveNotifications();
|
||||||
|
|
||||||
|
//Show popup with notification conte
|
||||||
|
if (onNewNotification != null) {
|
||||||
|
onNewNotification!.call(notification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the list of active notifications.
|
||||||
|
Stream<List<NotificationModel>> getActiveNotifications() =>
|
||||||
|
repository.getNotifications(userId);
|
||||||
|
|
||||||
|
/// Retrieves the list of planned notifications.
|
||||||
|
Stream<List<NotificationModel>> getPlannedNotifications() =>
|
||||||
|
repository.getPlannedNotifications(userId);
|
||||||
|
|
||||||
|
/// Creates a scheduled notification.
|
||||||
|
Future<void> createScheduledNotification(
|
||||||
|
NotificationModel notification,
|
||||||
|
List<String> recipientIds,
|
||||||
|
) async =>
|
||||||
|
pushNotification(
|
||||||
|
notification,
|
||||||
|
recipientIds,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Creates a recurring notification.
|
||||||
|
Future<void> createRecurringNotification(
|
||||||
|
NotificationModel notification,
|
||||||
|
List<String> recipientIds,
|
||||||
|
) async {
|
||||||
|
if (notification.recurring) {
|
||||||
|
switch (notification.occuringInterval) {
|
||||||
|
case OcurringInterval.daily:
|
||||||
|
notification.scheduledFor =
|
||||||
|
DateTime.now().add(const Duration(days: 1));
|
||||||
|
|
||||||
|
case OcurringInterval.weekly:
|
||||||
|
notification.scheduledFor =
|
||||||
|
DateTime.now().add(const Duration(days: 7));
|
||||||
|
|
||||||
|
case OcurringInterval.monthly:
|
||||||
|
notification.scheduledFor =
|
||||||
|
DateTime.now().add(const Duration(days: 30));
|
||||||
|
|
||||||
|
case OcurringInterval.debug:
|
||||||
|
notification.scheduledFor =
|
||||||
|
DateTime.now().add(const Duration(seconds: 10));
|
||||||
|
|
||||||
|
case null:
|
||||||
|
}
|
||||||
|
await createScheduledNotification(notification, recipientIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes a scheduled notification.
|
||||||
|
Future<void> deletePlannedNotification(NotificationModel notification) =>
|
||||||
|
repository.deleteNotification(userId, notification.id, true);
|
||||||
|
|
||||||
|
/// Dismisses an active notification.
|
||||||
|
Future<void> dismissActiveNotification(NotificationModel notification) =>
|
||||||
|
repository.deleteNotification(userId, notification.id, false);
|
||||||
|
|
||||||
|
/// Pin an active notification.
|
||||||
|
Future<void> pinActiveNotification(NotificationModel notification) =>
|
||||||
|
repository.updateNotification(
|
||||||
|
userId,
|
||||||
|
notification.copyWith(isPinned: true),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Unpin an active notification.
|
||||||
|
Future<void> unPinActiveNotification(NotificationModel notification) =>
|
||||||
|
repository.updateNotification(
|
||||||
|
userId,
|
||||||
|
notification.copyWith(isPinned: false),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Marks a notification as read.
|
||||||
|
Future<void> markNotificationAsRead(NotificationModel notification) =>
|
||||||
|
repository.updateNotification(
|
||||||
|
userId,
|
||||||
|
notification.copyWith(isRead: true),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Checks for scheduled notifications.
|
||||||
|
Future<void> checkForScheduledNotifications() async {
|
||||||
|
var notifications = await repository.getPlannedNotifications(userId).first;
|
||||||
|
|
||||||
|
for (var notification in notifications) {
|
||||||
|
if (notification.scheduledFor != null &&
|
||||||
|
notification.scheduledFor!.isBefore(DateTime.now()) ||
|
||||||
|
notification.scheduledFor!.isAtSameMomentAs(DateTime.now())) {
|
||||||
|
await pushNotification(
|
||||||
|
notification,
|
||||||
|
[userId],
|
||||||
|
);
|
||||||
|
|
||||||
|
await deletePlannedNotification(notification);
|
||||||
|
if (notification.recurring) {
|
||||||
|
await createRecurringNotification(
|
||||||
|
notification,
|
||||||
|
[userId],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a stream of the number of active notifications.
|
||||||
|
Stream<int> getActiveAmountStream() => repository
|
||||||
|
.getNotifications(userId)
|
||||||
|
.map((notifications) => notifications.length);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
name: notification_center_repository_interface
|
||||||
|
description: "A new Flutter package project."
|
||||||
|
version: 5.0.0
|
||||||
|
homepage:
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: ^3.5.3
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
|
rxdart: any
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_iconica_analysis:
|
||||||
|
git:
|
||||||
|
url: https://github.com/Iconica-Development/flutter_iconica_analysis
|
||||||
|
ref: 7.0.0
|
Loading…
Reference in a new issue