Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ public String getStringValue(String key, String defaultValue) {
return metaData.getString(META_PREFIX + key, defaultValue);
}

public int getIntValue(String key, int defaultValue) {
Bundle metaData = getMetaData();
if (metaData == null) return defaultValue;
return metaData.getInt(META_PREFIX + key, defaultValue);
}

public WritableMap getAll() {
Bundle metaData = getMetaData();
WritableMap map = Arguments.createMap();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ public boolean getBooleanValue(String key, boolean defaultValue) {
return getPreferences().getBoolean(key, defaultValue);
}

public void setIntValue(String key, int value) {
getPreferences().edit().putInt(key,value).apply();
}

public int getIntValue(String key, int defaultValue) {
return getPreferences().getInt(key,defaultValue);
}

public void setLongValue(String key, long value) {
getPreferences().edit().putLong(key, value).apply();
}
Expand Down
6 changes: 6 additions & 0 deletions packages/app/firebase-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@
"description": "On iOS, indicating how to present a notification in a foreground app.",
"type": "array"
},
"rn_firebase_messaging_max_stored_notifications": {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we prefix with the specific name traditionally - it is reasonable to argue that since SharedPreferences is a global namespace we should be more specific but we're using messaging_ so far for messaging items, and consistency does have value

Suggested change
"rn_firebase_messaging_max_stored_notifications": {
"messaging_max_stored_notifications": {

"description": "Controls how many notifications are retained on-device for background storage. Priority order is SharedPreferences -> firebase.json -> AndroidManifest. Values outside the 20-100 range are clamped to prevent excessive deletions or OOM.",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not believe in restricting developers capabilities without reason. Someone may have reason to set it to zero for zero data retention for some reason. Someone may have reason to set it to 1000 (small messages!) for their particular use case.

Our job is to pick a sensible default then allow people to do what they want, and just expose the risks.

100 was already selected as a reasonable default for most cases (and as mentioned has caused no problems for 5 years...) so we have done that. The rest is up to developers.

Suggested change
"description": "Controls how many notifications are retained on-device for background storage. Priority order is SharedPreferences -> firebase.json -> AndroidManifest. Values outside the 20-100 range are clamped to prevent excessive deletions or OOM.",
"description": "Controls how many notifications are retained on-device for background storage. Configuration priority order is SharedPreferences -> firebase.json -> AndroidManifest. Default 100. Very small values may cause unwanted excessive deletions, large values or large message contents run the risk of OutOfMemoryErrors.",

"type": "number",
"minimum": 20,
"maximum": 100
Comment on lines +124 to +126
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"type": "number",
"minimum": 20,
"maximum": 100
"type": "number"

},
"perf_auto_collection_enabled": {
"description": "Disable or enable auto collection of performance monitoring data collection.\n This is useful for opt-in-first data flows, for example when dealing with GDPR compliance.\nThis can be overridden in JavaScript.",
"type": "boolean"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,66 @@
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.google.firebase.messaging.RemoteMessage;
import io.invertase.firebase.common.UniversalFirebasePreferences;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;

import io.invertase.firebase.common.ReactNativeFirebaseJSON;
import io.invertase.firebase.common.ReactNativeFirebaseMeta;
import io.invertase.firebase.common.ReactNativeFirebasePreferences;
import io.invertase.firebase.common.UniversalFirebasePreferences;

public class ReactNativeFirebaseMessagingStoreImpl implements ReactNativeFirebaseMessagingStore {

private static final String KEY_MAX_STORED_NOTIFICATIONS = "rn_firebase_messaging_max_stored_notifications";
private static final String S_KEY_ALL_NOTIFICATION_IDS = "all_notification_ids";
private static final int MAX_SIZE_NOTIFICATIONS = 100;
private final String DELIMITER = ",";
private static final int DEFAULT_MAX_SIZE_NOTIFICATIONS = 100;
private static final int MIN_SIZE_NOTIFICATIONS = 20;
Comment on lines -21 to +30
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in the edit for the key description - please do not restrict developers via "good intentions" as those are frequently just our idea of what a developer needs but sometimes restrict valid use cases.

Just maintain the 100 original default, take the value in from configuration (if set) and log the final setting and source it came from for diagnostics just in case. No clamping

private static final int maxNotificationSize = resolveMaxNotificationSize();

private static int resolveMaxNotificationSize() {
int maxSize = DEFAULT_MAX_SIZE_NOTIFICATIONS;
ReactNativeFirebaseJSON json = ReactNativeFirebaseJSON.getSharedInstance();
ReactNativeFirebaseMeta meta = ReactNativeFirebaseMeta.getSharedInstance();
ReactNativeFirebasePreferences prefs = ReactNativeFirebasePreferences.getSharedInstance();

try {
// Priority: SharedPreferences -> firebase.json -> AndroidManifest
// Note: ReactNativeFirebaseMeta doesn't have getIntValue, so we check meta.contains
// and read from AndroidManifest directly if needed
if (prefs.contains(KEY_MAX_STORED_NOTIFICATIONS)) {
maxSize = prefs.getIntValue(KEY_MAX_STORED_NOTIFICATIONS, DEFAULT_MAX_SIZE_NOTIFICATIONS);
} else if (json.contains(KEY_MAX_STORED_NOTIFICATIONS)) {
maxSize = json.getIntValue(KEY_MAX_STORED_NOTIFICATIONS, DEFAULT_MAX_SIZE_NOTIFICATIONS);
} else if (meta.contains(KEY_MAX_STORED_NOTIFICATIONS)) {
maxSize = meta.getIntValue(KEY_MAX_STORED_NOTIFICATIONS, DEFAULT_MAX_SIZE_NOTIFICATIONS);
}

// Clamp to allowed range to avoid OOM (upper) or mass deletions (lower)
return Math.max(MIN_SIZE_NOTIFICATIONS, Math.min(maxSize, DEFAULT_MAX_SIZE_NOTIFICATIONS));
} catch (Exception e) {
// Ignore and use default
return DEFAULT_MAX_SIZE_NOTIFICATIONS;
}
}

@Override
public void storeFirebaseMessage(RemoteMessage remoteMessage) {
try {
String remoteMessageString =
reactToJSON(remoteMessageToWritableMap(remoteMessage)).toString();
reactToJSON(remoteMessageToWritableMap(remoteMessage)).toString();
// Log.d("storeFirebaseMessage", remoteMessageString);
UniversalFirebasePreferences preferences = UniversalFirebasePreferences.getSharedInstance();

// remove old notifications message before store to free space as needed
String notificationIds = preferences.getStringValue(S_KEY_ALL_NOTIFICATION_IDS, "");
List<String> allNotificationList = convertToArray(notificationIds);
while (allNotificationList.size() > MAX_SIZE_NOTIFICATIONS - 1) {
while (allNotificationList.size() > maxNotificationSize - 1) {
clearFirebaseMessage(allNotificationList.get(0));
allNotificationList.remove(0);
}
Expand Down Expand Up @@ -61,7 +96,7 @@ public RemoteMessage getFirebaseMessage(String remoteMessageId) {
@Override
public WritableMap getFirebaseMessageMap(String remoteMessageId) {
String remoteMessageString =
UniversalFirebasePreferences.getSharedInstance().getStringValue(remoteMessageId, null);
UniversalFirebasePreferences.getSharedInstance().getStringValue(remoteMessageId, null);
if (remoteMessageString != null) {
// Log.d("getFirebaseMessage", remoteMessageString);
try {
Expand Down
1 change: 1 addition & 0 deletions tests/firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"messaging_android_notification_color": "@color/hotpink",
"messaging_ios_auto_register_for_remote_messages": false,
"messaging_android_notification_delegation_enabled": true,
"rn_firebase_messaging_max_stored_notifications": 50,

"analytics_auto_collection_enabled": true,
"analytics_collection_deactivated": false,
Expand Down