Skip to content

Commit 1370cbd

Browse files
committed
chore: bump version to 1.9.1+5, update notification handling with new data structure and error state
1 parent 9ec37c2 commit 1370cbd

5 files changed

Lines changed: 203 additions & 50 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 1.9.1+5
4+
5+
### Updated
6+
7+
- update notification handling with new data structure and error state
8+
39
## 1.9.1+4
410

511
- Update authentication with equatable mixin

example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ packages:
10071007
path: ".."
10081008
relative: true
10091009
source: path
1010-
version: "1.9.1+3"
1010+
version: "1.9.1+4"
10111011
supa_carbon_icons:
10121012
dependency: transitive
10131013
description:

lib/blocs/push_notification/push_notification_bloc.dart

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import "package:equatable/equatable.dart";
66
import "package:firebase_messaging/firebase_messaging.dart";
77
import "package:flutter/foundation.dart";
88
import "package:supa_architecture/core/device_notification_token.dart";
9+
import "package:supa_architecture/models/user_notification.dart";
910
import "package:supa_architecture/repositories/utils_notification_repository.dart";
1011
import "package:supa_architecture/supa_architecture_platform_interface.dart";
1112

@@ -17,6 +18,7 @@ part "push_notification_state.dart";
1718
class PushNotificationBloc
1819
extends Bloc<PushNotificationEvent, PushNotificationState> {
1920
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
21+
2022
final UtilsNotificationRepository _notificationRepository =
2123
UtilsNotificationRepository();
2224

@@ -27,6 +29,27 @@ class PushNotificationBloc
2729
String? _deviceToken;
2830

2931
PushNotificationBloc() : super(const PushNotificationInitial()) {
32+
_onEvents();
33+
}
34+
35+
PushNotificationBloc.fromInitialMessage(
36+
RemoteMessage? initialMessage,
37+
) : super(
38+
initialMessage != null
39+
? PushNotificationOpened.fromFields(
40+
title: initialMessage.notification?.title ?? "",
41+
body: initialMessage.notification?.body ?? "",
42+
payload: PushNotificationPayload.fromJson(
43+
initialMessage.data,
44+
),
45+
linkMobile: initialMessage.data["linkMobile"],
46+
)
47+
: const PushNotificationInitial(),
48+
) {
49+
_onEvents();
50+
}
51+
52+
void _onEvents() {
3053
on<DidReceivedNotificationEvent>(_onDidNotificationReceived);
3154
on<DidUserOpenedNotificationEvent>(_onDidUserOpenedNotification);
3255
on<DidResetNotificationEvent>(_onDidResetNotification);
@@ -90,6 +113,7 @@ class PushNotificationBloc
90113

91114
/// Set handler for notifications opened from the background.
92115
void _setNotificationOpenAppHandler() {
116+
if (kIsWeb) return;
93117
_notificationOpenSubscription ??=
94118
FirebaseMessaging.onMessageOpenedApp.listen(_handleOpenedNotification);
95119
}
@@ -149,6 +173,7 @@ class PushNotificationBloc
149173
if (!await hasNotificationPermission() || _deviceToken == null) return;
150174

151175
final deviceInfo = SupaArchitecturePlatform.instance.deviceInfo;
176+
152177
final deviceToken = DeviceNotificationToken(
153178
osVersion: deviceInfo.systemVersion,
154179
deviceId: deviceInfo.deviceUuid,
@@ -159,6 +184,7 @@ class PushNotificationBloc
159184
);
160185

161186
try {
187+
_deviceToken = null;
162188
await _notificationRepository.deleteToken(deviceToken);
163189
} catch (error) {
164190
debugPrint("Failed to unregister device token: ${error.toString()}");
@@ -169,7 +195,7 @@ class PushNotificationBloc
169195
DidReceivedNotificationEvent event,
170196
Emitter<PushNotificationState> emit,
171197
) {
172-
emit(PushNotificationReceived(
198+
emit(PushNotificationReceived.fromFields(
173199
title: event.title,
174200
body: event.body,
175201
payload: event.payload,
@@ -181,7 +207,7 @@ class PushNotificationBloc
181207
DidUserOpenedNotificationEvent event,
182208
Emitter<PushNotificationState> emit,
183209
) {
184-
emit(PushNotificationOpened(
210+
emit(PushNotificationOpened.fromFields(
185211
title: event.title,
186212
body: event.body,
187213
payload: event.payload,
Lines changed: 167 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,96 @@
11
part of "push_notification_bloc.dart";
22

3+
/// Base notification data that is shared between different notification states
4+
class NotificationData {
5+
/// Notification title
6+
final String title;
7+
8+
/// Notification body
9+
final String body;
10+
11+
/// Notification payload
12+
final PushNotificationPayload payload;
13+
14+
/// Mobile link for the notification
15+
final String? linkMobile;
16+
17+
/// Creates a notification data instance
18+
const NotificationData({
19+
required this.title,
20+
required this.body,
21+
required this.payload,
22+
this.linkMobile,
23+
});
24+
25+
/// Converts the notification data to a JSON map
26+
Map<String, dynamic> toJson() {
27+
return {
28+
"title": title,
29+
"body": body,
30+
"payload": payload.toJson(),
31+
"linkMobile": linkMobile,
32+
};
33+
}
34+
35+
/// Creates a notification data instance from JSON
36+
factory NotificationData.fromJson(Map<String, dynamic> json) {
37+
return NotificationData(
38+
title: json["title"] ?? "",
39+
body: json["body"] ?? "",
40+
payload: PushNotificationPayload.fromJson(json["payload"] ?? {}),
41+
linkMobile: json["linkMobile"],
42+
);
43+
}
44+
45+
/// Creates a copy of this notification data with the given fields replaced
46+
NotificationData copyWith({
47+
String? title,
48+
String? body,
49+
PushNotificationPayload? payload,
50+
String? linkMobile,
51+
}) {
52+
return NotificationData(
53+
title: title ?? this.title,
54+
body: body ?? this.body,
55+
payload: payload ?? this.payload,
56+
linkMobile: linkMobile ?? this.linkMobile,
57+
);
58+
}
59+
60+
@override
61+
bool operator ==(Object other) {
62+
if (identical(this, other)) return true;
63+
return other is NotificationData &&
64+
other.title == title &&
65+
other.body == body &&
66+
other.payload == payload &&
67+
other.linkMobile == linkMobile;
68+
}
69+
70+
UserNotification toUserNotification() {
71+
return UserNotification()
72+
..title.value = title
73+
..titleWeb.value = title
74+
..titleMobile.value = title
75+
..content.value = body
76+
..contentWeb.value = body
77+
..contentMobile.value = body
78+
..link.value = linkMobile
79+
..linkWeb.value = linkMobile
80+
..linkMobile.value = linkMobile;
81+
}
82+
83+
@override
84+
int get hashCode {
85+
return Object.hash(title, body, payload, linkMobile);
86+
}
87+
}
88+
389
/// Push notification state
490
sealed class PushNotificationState {
591
const PushNotificationState();
692

7-
/// Converts the PushNotificationOpened state to a JSON map.
93+
/// Converts the state to a JSON map.
894
///
995
/// **Returns:**
1096
/// - A Map\<String, dynamic\> representing the state in JSON format.
@@ -18,82 +104,117 @@ final class PushNotificationInitial extends PushNotificationState {
18104

19105
@override
20106
Map<String, dynamic> toJson() {
21-
return {};
107+
return {"type": "initial"};
22108
}
23109
}
24110

25111
/// Push notification received state
26112
final class PushNotificationReceived extends PushNotificationState {
27-
/// Push notification received state
28-
final String title;
113+
/// Notification data
114+
final NotificationData data;
29115

30116
/// Push notification received state
31-
final String body;
32-
33-
/// Push notification received state
34-
final PushNotificationPayload payload;
35-
36-
/// Push notification received state
37-
final String? linkMobile;
38-
39-
/// Push notification received state
40-
PushNotificationReceived({
41-
required this.title,
42-
required this.body,
43-
required this.payload,
44-
this.linkMobile,
117+
const PushNotificationReceived({
118+
required this.data,
45119
});
46120

47-
/// Push notification opened state
48-
DidUserOpenedNotificationEvent opened() {
121+
/// Creates a received state from individual notification fields
122+
factory PushNotificationReceived.fromFields({
123+
required String title,
124+
required String body,
125+
required PushNotificationPayload payload,
126+
String? linkMobile,
127+
}) {
128+
return PushNotificationReceived(
129+
data: NotificationData(
130+
title: title,
131+
body: body,
132+
payload: payload,
133+
linkMobile: linkMobile,
134+
),
135+
);
136+
}
137+
138+
/// Converts to opened notification event
139+
DidUserOpenedNotificationEvent toOpenedEvent() {
49140
return DidUserOpenedNotificationEvent(
50-
title: title,
51-
body: body,
52-
payload: payload,
53-
linkMobile: linkMobile,
141+
title: data.title,
142+
body: data.body,
143+
payload: data.payload,
144+
linkMobile: data.linkMobile,
54145
);
55146
}
56147

57148
@override
58149
Map<String, dynamic> toJson() {
59150
return {
60-
"title": title,
61-
"body": body,
62-
"payload": payload.toJson(),
63-
"linkMobile": linkMobile,
151+
"type": "received",
152+
"data": data.toJson(),
64153
};
65154
}
66155
}
67156

68157
/// Push notification opened state
69158
final class PushNotificationOpened extends PushNotificationState {
70-
/// Push notification received state
71-
final String title;
159+
/// Notification data
160+
final NotificationData data;
72161

73-
/// Push notification received state
74-
final String body;
162+
/// Push notification opened state
163+
const PushNotificationOpened({
164+
required this.data,
165+
});
75166

76-
/// Push notification received state
77-
final PushNotificationPayload payload;
167+
/// Creates an opened state from individual notification fields
168+
factory PushNotificationOpened.fromFields({
169+
required String title,
170+
required String body,
171+
required PushNotificationPayload payload,
172+
String? linkMobile,
173+
}) {
174+
return PushNotificationOpened(
175+
data: NotificationData(
176+
title: title,
177+
body: body,
178+
payload: payload,
179+
linkMobile: linkMobile,
180+
),
181+
);
182+
}
78183

79-
/// Push notification received state
80-
final String? linkMobile;
184+
@override
185+
Map<String, dynamic> toJson() {
186+
return {
187+
"type": "opened",
188+
"data": data.toJson(),
189+
};
190+
}
191+
}
81192

82-
/// Push notification opened state
83-
const PushNotificationOpened({
84-
required this.title,
85-
required this.body,
86-
required this.payload,
87-
this.linkMobile,
193+
/// Push notification error state
194+
final class PushNotificationError extends PushNotificationState {
195+
/// Error message
196+
final String message;
197+
198+
/// Error code (optional)
199+
final String? code;
200+
201+
/// Original notification data if available
202+
final NotificationData? originalData;
203+
204+
/// Push notification error state
205+
const PushNotificationError({
206+
required this.message,
207+
this.code,
208+
this.originalData,
88209
});
89210

90211
@override
91212
Map<String, dynamic> toJson() {
92213
return {
93-
"title": title,
94-
"body": body,
95-
"payload": payload.toJson(),
96-
"linkMobile": linkMobile,
214+
"type": "error",
215+
"message": message,
216+
"code": code,
217+
"originalData": originalData?.toJson(),
97218
};
98219
}
99220
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: supa_architecture
22
description: Architecture library for Supa Flutter applications
3-
version: 1.9.1+4
3+
version: 1.9.1+5
44
homepage: https://github.supa.vn
55

66
environment:

0 commit comments

Comments
 (0)