Page MenuHomePhorge

D15441.1765029619.diff
No OneTemporary

Size
34 KB
Referenced Files
None
Subscribers
None

D15441.1765029619.diff

diff --git a/lib/types/notif-types.js b/lib/types/notif-types.js
--- a/lib/types/notif-types.js
+++ b/lib/types/notif-types.js
@@ -60,6 +60,9 @@
+unreadCount?: number,
+threadID: string,
+encryptionFailed?: '1',
+ +badge?: string,
+ +badgeOnly?: '1',
+ +farcasterBadge?: '1',
};
export type PlainTextWebNotification = $ReadOnly<{
diff --git a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
--- a/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
+++ b/native/android/app/src/main/java/app/comm/android/notifications/CommNotificationsHandler.java
@@ -58,6 +58,7 @@
private static final String KEYSERVER_ID_KEY = "keyserverID";
private static final String SENDER_DEVICE_ID_KEY = "senderDeviceID";
private static final String MESSAGE_TYPE_KEY = "type";
+ private static final String FARCASTER_BADGE_KEY = "farcasterBadge";
private static final String CHANNEL_ID = "default";
private static final long[] VIBRATION_SPEC = {500, 500};
private static final Map<Integer, String> NOTIF_PRIORITY_VERBOSE =
@@ -68,6 +69,7 @@
private static final String MMKV_KEY_SEPARATOR = ".";
private static final String MMKV_KEYSERVER_PREFIX = "KEYSERVER";
private static final String MMKV_UNREAD_COUNT_SUFFIX = "UNREAD_COUNT";
+ private static final String MMKV_FARCASTER_KEY = "FARCASTER";
private Bitmap displayableNotificationLargeIcon;
private NotificationManager notificationManager;
private LocalBroadcastManager localBroadcastManager;
@@ -301,6 +303,22 @@
senderKeyserverUnreadCountKey, senderKeyserverUnreadCount);
}
+ if ("1".equals(message.getData().get(FARCASTER_BADGE_KEY)) &&
+ message.getData().get(BADGE_KEY) != null) {
+ String farcasterBadge = message.getData().get(BADGE_KEY);
+ try {
+ int farcasterBadgeCount = Integer.parseInt(farcasterBadge);
+ String farcasterUnreadCountKey = String.join(
+ MMKV_KEY_SEPARATOR,
+ MMKV_KEYSERVER_PREFIX,
+ MMKV_FARCASTER_KEY,
+ MMKV_UNREAD_COUNT_SUFFIX);
+ CommMMKV.setInt(farcasterUnreadCountKey, farcasterBadgeCount);
+ } catch (NumberFormatException e) {
+ Log.w("COMM", "Invalid Farcaster badge count", e);
+ }
+ }
+
if (message.getData().get(SENDER_DEVICE_ID_KEY) != null &&
message.getData().get(THREAD_ID_KEY) != null &&
message.getData().get(RESCIND_KEY) != null) {
diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h
--- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.h
+++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.h
@@ -116,15 +116,15 @@
virtual jsi::Value isNotificationsSessionInitializedWithDevices(
jsi::Runtime &rt,
jsi::Array deviceIDs) override;
- virtual jsi::Value updateKeyserverDataInNotifStorage(
+ virtual jsi::Value updateDataInNotifStorage(
jsi::Runtime &rt,
- jsi::Array keyserversData) override;
- virtual jsi::Value removeKeyserverDataFromNotifStorage(
+ jsi::Array data) override;
+ virtual jsi::Value removeDataFromNotifStorage(
jsi::Runtime &rt,
- jsi::Array keyserverIDsToDelete) override;
- virtual jsi::Value getKeyserverDataFromNotifStorage(
+ jsi::Array idsToDelete) override;
+ virtual jsi::Value getDataFromNotifStorage(
jsi::Runtime &rt,
- jsi::Array keyserverIDs) override;
+ jsi::Array ids) override;
virtual jsi::Value updateUnreadThickThreadsInNotifsStorage(
jsi::Runtime &rt,
jsi::Array unreadThickThreadIDs) override;
diff --git a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
--- a/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
+++ b/native/cpp/CommonCpp/NativeModules/CommCoreModule.cpp
@@ -1328,26 +1328,26 @@
});
}
-jsi::Value CommCoreModule::updateKeyserverDataInNotifStorage(
+jsi::Value CommCoreModule::updateDataInNotifStorage(
jsi::Runtime &rt,
- jsi::Array keyserversData) {
-
- std::vector<std::pair<std::string, int>> keyserversDataCpp;
- for (auto idx = 0; idx < keyserversData.size(rt); idx++) {
- auto data = keyserversData.getValueAtIndex(rt, idx).asObject(rt);
- std::string keyserverID = data.getProperty(rt, "id").asString(rt).utf8(rt);
- std::string keyserverUnreadCountKey =
- "KEYSERVER." + keyserverID + ".UNREAD_COUNT";
- int unreadCount = data.getProperty(rt, "unreadCount").asNumber();
- keyserversDataCpp.push_back({keyserverUnreadCountKey, unreadCount});
+ jsi::Array data) {
+
+ std::vector<std::pair<std::string, int>> dataVectorCpp;
+ for (auto idx = 0; idx < data.size(rt); idx++) {
+ auto dataItem = data.getValueAtIndex(rt, idx).asObject(rt);
+ std::string id = dataItem.getProperty(rt, "id").asString(rt).utf8(rt);
+ std::string storageKey = "KEYSERVER." + id + ".UNREAD_COUNT";
+
+ int unreadCount = dataItem.getProperty(rt, "unreadCount").asNumber();
+ dataVectorCpp.push_back({storageKey, unreadCount});
}
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
std::string error;
try {
- for (const auto &keyserverData : keyserversDataCpp) {
- CommMMKV::setInt(keyserverData.first, keyserverData.second);
+ for (const auto &dataItem : dataVectorCpp) {
+ CommMMKV::setInt(dataItem.first, dataItem.second);
}
} catch (const std::exception &e) {
error = e.what();
@@ -1363,23 +1363,22 @@
});
}
-jsi::Value CommCoreModule::removeKeyserverDataFromNotifStorage(
+jsi::Value CommCoreModule::removeDataFromNotifStorage(
jsi::Runtime &rt,
- jsi::Array keyserverIDsToDelete) {
- std::vector<std::string> keyserverIDsToDeleteCpp{};
- for (auto idx = 0; idx < keyserverIDsToDelete.size(rt); idx++) {
- std::string keyserverID =
- keyserverIDsToDelete.getValueAtIndex(rt, idx).asString(rt).utf8(rt);
- std::string keyserverUnreadCountKey =
- "KEYSERVER." + keyserverID + ".UNREAD_COUNT";
- keyserverIDsToDeleteCpp.push_back(keyserverUnreadCountKey);
+ jsi::Array idsToDelete) {
+ std::vector<std::string> keysToDeleteCpp{};
+ for (auto idx = 0; idx < idsToDelete.size(rt); idx++) {
+ std::string id =
+ idsToDelete.getValueAtIndex(rt, idx).asString(rt).utf8(rt);
+ std::string storageKey = "KEYSERVER." + id + ".UNREAD_COUNT";
+ keysToDeleteCpp.push_back(storageKey);
}
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
std::string error;
try {
- CommMMKV::removeKeys(keyserverIDsToDeleteCpp);
+ CommMMKV::removeKeys(keysToDeleteCpp);
} catch (const std::exception &e) {
error = e.what();
}
@@ -1394,65 +1393,64 @@
});
}
-jsi::Value CommCoreModule::getKeyserverDataFromNotifStorage(
+jsi::Value CommCoreModule::getDataFromNotifStorage(
jsi::Runtime &rt,
- jsi::Array keyserverIDs) {
- std::vector<std::string> keyserverIDsCpp{};
- for (auto idx = 0; idx < keyserverIDs.size(rt); idx++) {
- std::string keyserverID =
- keyserverIDs.getValueAtIndex(rt, idx).asString(rt).utf8(rt);
- keyserverIDsCpp.push_back(keyserverID);
+ jsi::Array ids) {
+ std::vector<std::string> idsCpp{};
+ for (auto idx = 0; idx < ids.size(rt); idx++) {
+ std::string id =
+ ids.getValueAtIndex(rt, idx).asString(rt).utf8(rt);
+ idsCpp.push_back(id);
}
return createPromiseAsJSIValue(
rt, [=](jsi::Runtime &innerRt, std::shared_ptr<Promise> promise) {
std::string error;
- std::vector<std::pair<std::string, int>> keyserversDataVector{};
+ std::vector<std::pair<std::string, int>> dataVector{};
try {
- for (const auto &keyserverID : keyserverIDsCpp) {
- std::string keyserverUnreadCountKey =
- "KEYSERVER." + keyserverID + ".UNREAD_COUNT";
+ for (const auto &id : idsCpp) {
+ std::string storageKey = "KEYSERVER." + id + ".UNREAD_COUNT";
std::optional<int> unreadCount =
- CommMMKV::getInt(keyserverUnreadCountKey, -1);
+ CommMMKV::getInt(storageKey, -1);
if (!unreadCount.has_value()) {
continue;
}
- keyserversDataVector.push_back({keyserverID, unreadCount.value()});
+ dataVector.push_back({id, unreadCount.value()});
}
} catch (const std::exception &e) {
error = e.what();
}
- auto keyserversDataVectorPtr =
+ auto dataVectorPtr =
std::make_shared<std::vector<std::pair<std::string, int>>>(
- std::move(keyserversDataVector));
+ std::move(dataVector));
this->jsInvoker_->invokeAsync(
- [&innerRt, keyserversDataVectorPtr, error, promise]() {
+ [&innerRt, dataVectorPtr, error, promise]() {
if (error.size()) {
promise->reject(error);
return;
}
- size_t numKeyserversData = keyserversDataVectorPtr->size();
- jsi::Array jsiKeyserversData =
- jsi::Array(innerRt, numKeyserversData);
+ size_t numData = dataVectorPtr->size();
+ jsi::Array jsiData =
+ jsi::Array(innerRt, numData);
size_t writeIdx = 0;
- for (const auto &keyserverData : *keyserversDataVectorPtr) {
- jsi::Object jsiKeyserverData = jsi::Object(innerRt);
- jsiKeyserverData.setProperty(
- innerRt, "id", keyserverData.first);
- jsiKeyserverData.setProperty(
- innerRt, "unreadCount", keyserverData.second);
- jsiKeyserversData.setValueAtIndex(
- innerRt, writeIdx++, jsiKeyserverData);
+ for (const auto &dataItem : *dataVectorPtr) {
+ jsi::Object jsiDataItem = jsi::Object(innerRt);
+ jsiDataItem.setProperty(
+ innerRt, "id", dataItem.first);
+ jsiDataItem.setProperty(
+ innerRt, "unreadCount", dataItem.second);
+ jsiData.setValueAtIndex(
+ innerRt, writeIdx++, jsiDataItem);
}
- promise->resolve(std::move(jsiKeyserversData));
+ promise->resolve(std::move(jsiData));
});
});
}
diff --git a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp
--- a/native/cpp/CommonCpp/_generated/commJSI-generated.cpp
+++ b/native/cpp/CommonCpp/_generated/commJSI-generated.cpp
@@ -66,14 +66,14 @@
static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_isNotificationsSessionInitializedWithDevices(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->isNotificationsSessionInitializedWithDevices(rt, args[0].asObject(rt).asArray(rt));
}
-static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateKeyserverDataInNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
- return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->updateKeyserverDataInNotifStorage(rt, args[0].asObject(rt).asArray(rt));
+static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateDataInNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
+ return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->updateDataInNotifStorage(rt, args[0].asObject(rt).asArray(rt));
}
-static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeKeyserverDataFromNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
- return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->removeKeyserverDataFromNotifStorage(rt, args[0].asObject(rt).asArray(rt));
+static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeDataFromNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
+ return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->removeDataFromNotifStorage(rt, args[0].asObject(rt).asArray(rt));
}
-static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getKeyserverDataFromNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
- return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getKeyserverDataFromNotifStorage(rt, args[0].asObject(rt).asArray(rt));
+static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getDataFromNotifStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
+ return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->getDataFromNotifStorage(rt, args[0].asObject(rt).asArray(rt));
}
static jsi::Value __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateUnreadThickThreadsInNotifsStorage(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
return static_cast<CommCoreModuleSchemaCxxSpecJSI *>(&turboModule)->updateUnreadThickThreadsInNotifsStorage(rt, args[0].asObject(rt).asArray(rt));
@@ -270,9 +270,9 @@
methodMap_["isNotificationsSessionInitialized"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_isNotificationsSessionInitialized};
methodMap_["isDeviceNotificationsSessionInitialized"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_isDeviceNotificationsSessionInitialized};
methodMap_["isNotificationsSessionInitializedWithDevices"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_isNotificationsSessionInitializedWithDevices};
- methodMap_["updateKeyserverDataInNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateKeyserverDataInNotifStorage};
- methodMap_["removeKeyserverDataFromNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeKeyserverDataFromNotifStorage};
- methodMap_["getKeyserverDataFromNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getKeyserverDataFromNotifStorage};
+ methodMap_["updateDataInNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateDataInNotifStorage};
+ methodMap_["removeDataFromNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_removeDataFromNotifStorage};
+ methodMap_["getDataFromNotifStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getDataFromNotifStorage};
methodMap_["updateUnreadThickThreadsInNotifsStorage"] = MethodMetadata {1, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_updateUnreadThickThreadsInNotifsStorage};
methodMap_["getUnreadThickThreadIDsFromNotifsStorage"] = MethodMetadata {0, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_getUnreadThickThreadIDsFromNotifsStorage};
methodMap_["initializeContentOutboundSession"] = MethodMetadata {5, __hostFunction_CommCoreModuleSchemaCxxSpecJSI_initializeContentOutboundSession};
diff --git a/native/cpp/CommonCpp/_generated/commJSI.h b/native/cpp/CommonCpp/_generated/commJSI.h
--- a/native/cpp/CommonCpp/_generated/commJSI.h
+++ b/native/cpp/CommonCpp/_generated/commJSI.h
@@ -37,9 +37,9 @@
virtual jsi::Value isNotificationsSessionInitialized(jsi::Runtime &rt) = 0;
virtual jsi::Value isDeviceNotificationsSessionInitialized(jsi::Runtime &rt, jsi::String deviceID) = 0;
virtual jsi::Value isNotificationsSessionInitializedWithDevices(jsi::Runtime &rt, jsi::Array deviceIDs) = 0;
- virtual jsi::Value updateKeyserverDataInNotifStorage(jsi::Runtime &rt, jsi::Array keyserversData) = 0;
- virtual jsi::Value removeKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDsToDelete) = 0;
- virtual jsi::Value getKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDs) = 0;
+ virtual jsi::Value updateDataInNotifStorage(jsi::Runtime &rt, jsi::Array data) = 0;
+ virtual jsi::Value removeDataFromNotifStorage(jsi::Runtime &rt, jsi::Array idsToDelete) = 0;
+ virtual jsi::Value getDataFromNotifStorage(jsi::Runtime &rt, jsi::Array ids) = 0;
virtual jsi::Value updateUnreadThickThreadsInNotifsStorage(jsi::Runtime &rt, jsi::Array unreadThickThreadIDs) = 0;
virtual jsi::Value getUnreadThickThreadIDsFromNotifsStorage(jsi::Runtime &rt) = 0;
virtual jsi::Value initializeContentOutboundSession(jsi::Runtime &rt, jsi::String identityKeys, jsi::String prekey, jsi::String prekeySignature, std::optional<jsi::String> oneTimeKey, jsi::String deviceID) = 0;
@@ -254,29 +254,29 @@
return bridging::callFromJs<jsi::Value>(
rt, &T::isNotificationsSessionInitializedWithDevices, jsInvoker_, instance_, std::move(deviceIDs));
}
- jsi::Value updateKeyserverDataInNotifStorage(jsi::Runtime &rt, jsi::Array keyserversData) override {
+ jsi::Value updateDataInNotifStorage(jsi::Runtime &rt, jsi::Array data) override {
static_assert(
- bridging::getParameterCount(&T::updateKeyserverDataInNotifStorage) == 2,
- "Expected updateKeyserverDataInNotifStorage(...) to have 2 parameters");
+ bridging::getParameterCount(&T::updateDataInNotifStorage) == 2,
+ "Expected updateDataInNotifStorage(...) to have 2 parameters");
return bridging::callFromJs<jsi::Value>(
- rt, &T::updateKeyserverDataInNotifStorage, jsInvoker_, instance_, std::move(keyserversData));
+ rt, &T::updateDataInNotifStorage, jsInvoker_, instance_, std::move(data));
}
- jsi::Value removeKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDsToDelete) override {
+ jsi::Value removeDataFromNotifStorage(jsi::Runtime &rt, jsi::Array idsToDelete) override {
static_assert(
- bridging::getParameterCount(&T::removeKeyserverDataFromNotifStorage) == 2,
- "Expected removeKeyserverDataFromNotifStorage(...) to have 2 parameters");
+ bridging::getParameterCount(&T::removeDataFromNotifStorage) == 2,
+ "Expected removeDataFromNotifStorage(...) to have 2 parameters");
return bridging::callFromJs<jsi::Value>(
- rt, &T::removeKeyserverDataFromNotifStorage, jsInvoker_, instance_, std::move(keyserverIDsToDelete));
+ rt, &T::removeDataFromNotifStorage, jsInvoker_, instance_, std::move(idsToDelete));
}
- jsi::Value getKeyserverDataFromNotifStorage(jsi::Runtime &rt, jsi::Array keyserverIDs) override {
+ jsi::Value getDataFromNotifStorage(jsi::Runtime &rt, jsi::Array ids) override {
static_assert(
- bridging::getParameterCount(&T::getKeyserverDataFromNotifStorage) == 2,
- "Expected getKeyserverDataFromNotifStorage(...) to have 2 parameters");
+ bridging::getParameterCount(&T::getDataFromNotifStorage) == 2,
+ "Expected getDataFromNotifStorage(...) to have 2 parameters");
return bridging::callFromJs<jsi::Value>(
- rt, &T::getKeyserverDataFromNotifStorage, jsInvoker_, instance_, std::move(keyserverIDs));
+ rt, &T::getDataFromNotifStorage, jsInvoker_, instance_, std::move(ids));
}
jsi::Value updateUnreadThickThreadsInNotifsStorage(jsi::Runtime &rt, jsi::Array unreadThickThreadIDs) override {
static_assert(
diff --git a/native/database/sqlite-api.js b/native/database/sqlite-api.js
--- a/native/database/sqlite-api.js
+++ b/native/database/sqlite-api.js
@@ -59,7 +59,7 @@
const promises = [];
if (keyserversToRemoveFromNotifsStore.length > 0) {
promises.push(
- commCoreModule.removeKeyserverDataFromNotifStorage(
+ commCoreModule.removeDataFromNotifStorage(
keyserversToRemoveFromNotifsStore,
),
);
diff --git a/native/ios/NotificationService/NotificationService.mm b/native/ios/NotificationService/NotificationService.mm
--- a/native/ios/NotificationService/NotificationService.mm
+++ b/native/ios/NotificationService/NotificationService.mm
@@ -24,12 +24,14 @@
NSString *const encryptionKeyLabel = @"encryptionKey";
NSString *const needsSilentBadgeUpdateKey = @"needsSilentBadgeUpdate";
NSString *const notificationIdKey = @"notificationId";
+NSString *const farcasterBadgeKey = @"farcasterBadge";
// Those and future MMKV-related constants should match
// similar constants in CommNotificationsHandler.java
const std::string mmkvKeySeparator = ".";
const std::string mmkvKeyserverPrefix = "KEYSERVER";
const std::string mmkvUnreadCountSuffix = "UNREAD_COUNT";
+const std::string mmkvFarcasterKey = "FARCASTER";
// The context for this constant can be found here:
// https://linear.app/comm/issue/ENG-3074#comment-bd2f5e28
@@ -529,6 +531,16 @@
senderKeyserverUnreadCountKey, senderKeyserverUnreadCount);
}
+ if (content.userInfo[farcasterBadgeKey] &&
+ [content.userInfo[farcasterBadgeKey] isEqualToString:@"1"] &&
+ content.userInfo[@"badge"]) {
+ int farcasterBadgeCount = [content.userInfo[@"badge"] intValue];
+ std::string farcasterUnreadCountKey = joinStrings(
+ mmkvKeySeparator,
+ {mmkvKeyserverPrefix, mmkvFarcasterKey, mmkvUnreadCountSuffix});
+ comm::CommMMKV::setInt(farcasterUnreadCountKey, farcasterBadgeCount);
+ }
+
if (content.userInfo[senderDeviceIDKey] && content.userInfo[threadIDKey] &&
[self isRescind:content.userInfo]) {
comm::CommMMKV::removeElementFromStringSet(
@@ -541,7 +553,7 @@
std::string([content.userInfo[threadIDKey] UTF8String]));
}
- // calculate unread counts from keyservers
+ // calculate unread counts from keyservers and Farcaster
int totalUnreadCount = 0;
std::vector<std::string> allKeys = comm::CommMMKV::getAllKeys();
for (const auto &key : allKeys) {
diff --git a/native/push/push-handler.react.js b/native/push/push-handler.react.js
--- a/native/push/push-handler.react.js
+++ b/native/push/push-handler.react.js
@@ -362,9 +362,9 @@
let unreadThickThreadIDs: $ReadOnlyArray<string>;
try {
[queriedKeyserverData, unreadThickThreadIDs] = await Promise.all([
- commCoreModule.getKeyserverDataFromNotifStorage(notifsStorageQueries),
+ commCoreModule.getDataFromNotifStorage(notifsStorageQueries),
handleUnreadThickThreadIDsInNotifsStorage,
- commCoreModule.updateKeyserverDataInNotifStorage(notifStorageUpdates),
+ commCoreModule.updateDataInNotifStorage(notifStorageUpdates),
]);
} catch (e) {
if (__DEV__) {
@@ -386,6 +386,21 @@
}
totalUnreadCount += unreadThickThreadIDs.length;
+
+ let farcasterUnreadCount = 0;
+ try {
+ const farcasterData = await commCoreModule.getDataFromNotifStorage([
+ 'FARCASTER',
+ ]);
+ if (farcasterData.length > 0) {
+ farcasterUnreadCount = farcasterData[0].unreadCount;
+ }
+ } catch (e) {
+ console.error('Failed to get Farcaster unread count:', e);
+ }
+
+ totalUnreadCount += farcasterUnreadCount;
+
if (Platform.OS === 'ios') {
CommIOSNotifications.setBadgesCount(totalUnreadCount);
} else if (Platform.OS === 'android') {
@@ -398,9 +413,7 @@
this.props.thinThreadsUnreadCount,
);
try {
- await commCoreModule.removeKeyserverDataFromNotifStorage(
- keyserversDataToRemove,
- );
+ await commCoreModule.removeDataFromNotifStorage(keyserversDataToRemove);
} catch (e) {
if (__DEV__) {
Alert.alert(
diff --git a/native/schema/CommCoreModuleSchema.js b/native/schema/CommCoreModuleSchema.js
--- a/native/schema/CommCoreModuleSchema.js
+++ b/native/schema/CommCoreModuleSchema.js
@@ -79,14 +79,14 @@
+isNotificationsSessionInitializedWithDevices: (
deviceIDs: $ReadOnlyArray<string>,
) => Promise<{ +[deviceID: string]: boolean }>;
- +updateKeyserverDataInNotifStorage: (
- keyserversData: $ReadOnlyArray<{ +id: string, +unreadCount: number }>,
+ +updateDataInNotifStorage: (
+ data: $ReadOnlyArray<{ +id: string, +unreadCount: number }>,
) => Promise<void>;
- +removeKeyserverDataFromNotifStorage: (
- keyserverIDsToDelete: $ReadOnlyArray<string>,
+ +removeDataFromNotifStorage: (
+ idsToDelete: $ReadOnlyArray<string>,
) => Promise<void>;
- +getKeyserverDataFromNotifStorage: (
- keyserverIDs: $ReadOnlyArray<string>,
+ +getDataFromNotifStorage: (
+ ids: $ReadOnlyArray<string>,
) => Promise<$ReadOnlyArray<{ +id: string, +unreadCount: number }>>;
+updateUnreadThickThreadsInNotifsStorage: (
unreadThickThreadIDs: $ReadOnlyArray<string>,
diff --git a/services/tunnelbroker/src/notifs/generic_client.rs b/services/tunnelbroker/src/notifs/generic_client.rs
--- a/services/tunnelbroker/src/notifs/generic_client.rs
+++ b/services/tunnelbroker/src/notifs/generic_client.rs
@@ -66,9 +66,18 @@
#[derive(Clone, Debug, Serialize)]
pub struct GenericNotifPayload {
- pub title: String,
- pub body: String,
- pub thread_id: String,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub title: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub body: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub thread_id: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub badge: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub badge_only: Option<bool>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub farcaster_badge: Option<bool>,
}
enum APNsTopic {
@@ -135,36 +144,66 @@
apns_collapse_id: None,
};
- let payload = json!({
+ let mut payload_obj = json!({
"aps": {
- "alert": {
- "title": self.title,
- "body": self.body,
- },
- "thread-id": self.thread_id,
- "sound": "default",
"mutable-content": 1
},
});
+ // Only add alert if we have title or body
+ if self.title.is_some() || self.body.is_some() {
+ payload_obj["aps"]["alert"] = json!({
+ "title": self.title.unwrap_or_default(),
+ "body": self.body.unwrap_or_default(),
+ });
+ payload_obj["aps"]["sound"] = json!("default");
+ }
+
+ if let Some(thread_id) = self.thread_id {
+ payload_obj["aps"]["thread-id"] = json!(thread_id);
+ }
+ if let Some(badge) = self.badge {
+ payload_obj["badge"] = json!(badge);
+ }
+ if let Some(badge_only) = self.badge_only {
+ payload_obj["badgeOnly"] = json!(if badge_only { "1" } else { "0" });
+ }
+ if let Some(farcaster_badge) = self.farcaster_badge {
+ payload_obj["farcasterBadge"] =
+ json!(if farcaster_badge { "1" } else { "0" });
+ }
+
APNsNotif {
device_token: device_token.to_string(),
headers,
- payload: serde_json::to_string(&payload).unwrap(),
+ payload: serde_json::to_string(&payload_obj).unwrap(),
}
}
fn into_fcm(self, device_token: &str) -> FCMMessage {
use super::fcm::firebase_message::{AndroidConfig, AndroidMessagePriority};
- let data = json!({
+ let mut data = json!({
"id": uuid::Uuid::new_v4().to_string(),
- "title": self.title,
- "body": self.body,
- "threadID": self.thread_id,
- "badgeOnly": "0",
+ "badgeOnly": if self.badge_only.unwrap_or(false) { "1" } else { "0" },
});
+ if let Some(title) = self.title {
+ data["title"] = json!(title);
+ }
+ if let Some(body) = self.body {
+ data["body"] = json!(body);
+ }
+ if let Some(thread_id) = self.thread_id {
+ data["threadID"] = json!(thread_id);
+ }
+ if let Some(badge) = self.badge {
+ data["badge"] = json!(badge);
+ }
+ if let Some(farcaster_badge) = self.farcaster_badge {
+ data["farcasterBadge"] = json!(if farcaster_badge { "1" } else { "0" });
+ }
+
FCMMessage {
data,
token: device_token.to_string(),
@@ -177,13 +216,30 @@
fn into_web_push(self, device_token: &str) -> WebPushNotif {
use crate::notifs::web_push::WebPushNotif;
- let payload = json!({
+ let mut payload = json!({
"id": uuid::Uuid::new_v4().to_string(),
- "title": self.title,
- "body": self.body,
- "threadID": self.thread_id,
});
+ if let Some(title) = self.title {
+ payload["title"] = json!(title);
+ }
+ if let Some(body) = self.body {
+ payload["body"] = json!(body);
+ }
+ if let Some(thread_id) = self.thread_id {
+ payload["threadID"] = json!(thread_id);
+ }
+ if let Some(badge) = self.badge {
+ payload["badge"] = json!(badge);
+ }
+ if let Some(badge_only) = self.badge_only {
+ payload["badgeOnly"] = json!(if badge_only { "1" } else { "0" });
+ }
+ if let Some(farcaster_badge) = self.farcaster_badge {
+ payload["farcasterBadge"] =
+ json!(if farcaster_badge { "1" } else { "0" });
+ }
+
WebPushNotif {
device_token: device_token.to_string(),
payload: serde_json::to_string(&payload).unwrap(),
@@ -191,11 +247,27 @@
}
fn into_wns(self, device_token: &str) -> WNSNotif {
- let payload = json!({
- "title": self.title,
- "body": self.body,
- "threadID": self.thread_id,
- });
+ let mut payload = json!({});
+
+ if let Some(title) = self.title {
+ payload["title"] = json!(title);
+ }
+ if let Some(body) = self.body {
+ payload["body"] = json!(body);
+ }
+ if let Some(thread_id) = self.thread_id {
+ payload["threadID"] = json!(thread_id);
+ }
+ if let Some(badge) = self.badge {
+ payload["badge"] = json!(badge);
+ }
+ if let Some(badge_only) = self.badge_only {
+ payload["badgeOnly"] = json!(if badge_only { "1" } else { "0" });
+ }
+ if let Some(farcaster_badge) = self.farcaster_badge {
+ payload["farcasterBadge"] =
+ json!(if farcaster_badge { "1" } else { "0" });
+ }
WNSNotif {
device_token: device_token.to_string(),
diff --git a/services/tunnelbroker/src/token_distributor/notif_utils.rs b/services/tunnelbroker/src/token_distributor/notif_utils.rs
--- a/services/tunnelbroker/src/token_distributor/notif_utils.rs
+++ b/services/tunnelbroker/src/token_distributor/notif_utils.rs
@@ -58,9 +58,12 @@
};
Some(GenericNotifPayload {
- title: trim_text(title, 100),
- body: trim_text(&body, 300),
- thread_id: format!("FARCASTER#{}", conversation_id),
+ title: Some(trim_text(title, 100)),
+ body: Some(trim_text(&body, 300)),
+ thread_id: Some(format!("FARCASTER#{}", conversation_id)),
+ badge: None,
+ badge_only: None,
+ farcaster_badge: None,
})
}
diff --git a/services/tunnelbroker/src/token_distributor/token_connection.rs b/services/tunnelbroker/src/token_distributor/token_connection.rs
--- a/services/tunnelbroker/src/token_distributor/token_connection.rs
+++ b/services/tunnelbroker/src/token_distributor/token_connection.rs
@@ -6,7 +6,9 @@
use crate::database::DatabaseClient;
use crate::farcaster::FarcasterClient;
use crate::log::redact_sensitive_data;
-use crate::notifs::{GenericNotifClient, NotifRecipientDescriptor};
+use crate::notifs::{
+ GenericNotifClient, GenericNotifPayload, NotifRecipientDescriptor,
+};
use crate::token_distributor::config::TokenDistributorConfig;
use crate::token_distributor::error::TokenConnectionError;
use crate::token_distributor::notif_utils::prepare_notif_payload;
@@ -341,7 +343,7 @@
debug!("Processing refresh-self-direct-casts-inbox message");
}
FarcasterPayload::Unseen { .. } => {
- if let Err(e) = self.handle_unseen_message(&mut client).await {
+ if let Err(e) = self.handle_unseen_message(&farcaster_msg, &mut client).await {
info!(
metricType = "TokenDistributor_ConnectionFailure",
metricValue = 1,
@@ -667,8 +669,24 @@
async fn handle_unseen_message(
&self,
+ farcaster_msg: &FarcasterMessage,
client: &mut ChainedInterceptedServicesAuthClient,
) -> Result<(), TokenConnectionError> {
+ let inbox_count = if let Some(data_str) = &farcaster_msg.data {
+ match serde_json::from_str::<serde_json::Value>(data_str) {
+ Ok(data_json) => data_json
+ .get("inboxCount")
+ .and_then(|v| v.as_u64())
+ .map(|v| v as i32),
+ Err(e) => {
+ warn!("Failed to parse unseen message data: {}", e);
+ None
+ }
+ }
+ } else {
+ None
+ };
+
let conversations = self
.farcaster_client
.fetch_inbox(&self.token_info.user_id)
@@ -707,6 +725,38 @@
let recipient_devices = self.get_self_user_device_list(client).await?;
+ if let Some(count) = inbox_count {
+ for (device_id, platform_details) in &recipient_devices {
+ let Ok(platform) = platform_details.device_type().try_into() else {
+ continue;
+ };
+
+ let target = NotifRecipientDescriptor {
+ platform,
+ device_id: device_id.to_string(),
+ };
+
+ // Send badge-only notification using GenericNotifPayload
+ let badge_payload = GenericNotifPayload {
+ title: None,
+ body: None,
+ thread_id: None,
+ badge: Some(count.to_string()),
+ badge_only: Some(true),
+ farcaster_badge: Some(true),
+ };
+
+ if let Err(err) =
+ self.notif_client.send_notif(badge_payload, target).await
+ {
+ if !err.is_invalid_token() {
+ tracing::error!("Failed to send Farcaster badge notif: {:?}", err);
+ }
+ }
+ }
+ }
+
+ // Send inbox status messages to all devices
for (device_id, _) in &recipient_devices {
self
.message_sender
diff --git a/web/push-notif/badge-handler.react.js b/web/push-notif/badge-handler.react.js
--- a/web/push-notif/badge-handler.react.js
+++ b/web/push-notif/badge-handler.react.js
@@ -31,7 +31,7 @@
const unreadCountUpdates: {
[keyserverID: string]: number,
} = {};
- const unreadCountQueries: Array<string> = [];
+ const unreadCountQueries: Array<string> = ['FARCASTER'];
for (const keyserverID in thinThreadsUnreadCount) {
if (connection[keyserverID]?.status !== 'connected') {
@@ -76,6 +76,7 @@
}
totalUnreadCount += unreadThickThreadIDs.length;
+
document.title = getTitle(totalUnreadCount);
electron?.setBadge(totalUnreadCount === 0 ? null : totalUnreadCount);
})();
diff --git a/web/push-notif/service-worker.js b/web/push-notif/service-worker.js
--- a/web/push-notif/service-worker.js
+++ b/web/push-notif/service-worker.js
@@ -100,6 +100,17 @@
event.waitUntil(
(async () => {
+ if (data.farcasterBadge === '1' && data.badgeOnly === '1') {
+ const farcasterBadgeCount = parseInt(data.badge, 10) || 0;
+ // Use the same key format as queryNotifsUnreadCountStorage
+ // KEYSERVER:FARCASTER:unreadCount
+ await localforage.setItem(
+ 'keyserver:FARCASTER:unreadCount',
+ farcasterBadgeCount,
+ );
+ return;
+ }
+
let plainTextData: PlainTextWebNotification;
let decryptionResult: PlainTextWebNotification | WebNotifDecryptionError;

File Metadata

Mime Type
text/plain
Expires
Sat, Dec 6, 2:00 PM (19 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5838312
Default Alt Text
D15441.1765029619.diff (34 KB)

Event Timeline