Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 14a975ccfa | |||
| 897e6d85be | |||
| 1e17849803 | |||
| f5d231f2f0 | |||
| 8e44de0168 | |||
| 62aa42f073 | |||
| 849b5ab139 | |||
| 516c9e2143 | |||
| cec1c3e5a2 | |||
| ffe15a981a | |||
| 82e53d6a64 | |||
| 6823d10327 | |||
| 1948495857 | |||
| d976ab31c6 | |||
| f3523200f5 | |||
| ac0ba3ade7 | |||
| 035fad8bc6 | |||
| f9c5c42290 | |||
| d6a2ee4e9e | |||
| 83056bbf2b | |||
| 37ff335a12 | |||
| bf92de36eb | |||
| fb12def9f9 | |||
| f24a4fed2a | |||
| 124e8f1d55 | |||
| 1a8a154c12 | |||
| 86b2bca927 | |||
| 3ee7b50847 | |||
| e97bf27e6f | |||
| c1ac691953 | |||
| 0e951e6141 | |||
| be738ca9f6 | |||
| a67a17a904 | |||
| f195fad22c | |||
| 458005445f | |||
| 7dad1c280b |
@@ -4,6 +4,30 @@ import UIKit
|
||||
let APP_GROUP = "group.app.bsky"
|
||||
typealias ContentHandler = (UNNotificationContent) -> Void
|
||||
|
||||
enum NotificationType: String, CaseIterable {
|
||||
case like
|
||||
case repost
|
||||
case follow
|
||||
case reply
|
||||
case quote
|
||||
case chatMessage = "chat-message"
|
||||
case markReadGeneric = "mark-read-generic"
|
||||
case markReadMessages = "mark-read-messages"
|
||||
case starterPackJoined = "starterpack-joined"
|
||||
}
|
||||
|
||||
enum BadgeType: String, CaseIterable {
|
||||
case generic
|
||||
case messages
|
||||
}
|
||||
|
||||
enum BadgeOperation {
|
||||
case increment
|
||||
case decrement
|
||||
}
|
||||
|
||||
let INCREMENTED_FOR_KEY = "incremented-for-convos"
|
||||
|
||||
// This extension allows us to do some processing of the received notification
|
||||
// data before displaying the notification to the user. In our use case, there
|
||||
// are a few particular things that we want to do:
|
||||
@@ -30,27 +54,32 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
private var bestAttempt: UNMutableNotificationContent?
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
self.contentHandler = contentHandler
|
||||
|
||||
guard let bestAttempt = NSEUtil.createCopy(request.content),
|
||||
let reason = request.content.userInfo["reason"] as? String
|
||||
let reasonString = request.content.userInfo["reason"] as? String,
|
||||
let reason = NotificationType(rawValue: reasonString)
|
||||
else {
|
||||
contentHandler(request.content)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
self.contentHandler = contentHandler
|
||||
self.bestAttempt = bestAttempt
|
||||
if reason == "chat-message" {
|
||||
mutateWithChatMessage(bestAttempt)
|
||||
} else {
|
||||
mutateWithBadge(bestAttempt)
|
||||
|
||||
NSEUtil.shared.prefsQueue.sync {
|
||||
switch reason {
|
||||
case .like, .repost, .follow, .reply, .quote, .starterPackJoined:
|
||||
NSEUtil.mutateWithBadge(bestAttempt, badgeType: .generic, operation: .increment)
|
||||
case .chatMessage:
|
||||
NSEUtil.mutateWithChatMessage(bestAttempt)
|
||||
NSEUtil.mutateWithBadge(bestAttempt, badgeType: .messages, operation: .increment)
|
||||
case .markReadGeneric:
|
||||
NSEUtil.mutateWithBadge(bestAttempt, badgeType: .generic, operation: .decrement)
|
||||
case .markReadMessages:
|
||||
NSEUtil.mutateWithBadge(bestAttempt, badgeType: .messages, operation: .decrement)
|
||||
}
|
||||
contentHandler(bestAttempt)
|
||||
}
|
||||
|
||||
// Any image downloading (or other network tasks) should be handled at the end
|
||||
// of this block. Otherwise, if there is a timeout and serviceExtensionTimeWillExpire
|
||||
// gets called, we might not have all the needed mutations completed in time.
|
||||
|
||||
contentHandler(bestAttempt)
|
||||
}
|
||||
|
||||
override func serviceExtensionTimeWillExpire() {
|
||||
@@ -60,38 +89,14 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
contentHandler(bestAttempt)
|
||||
}
|
||||
|
||||
// MARK: Mutations
|
||||
|
||||
func mutateWithBadge(_ content: UNMutableNotificationContent) {
|
||||
NSEUtil.shared.prefsQueue.sync {
|
||||
var count = NSEUtil.shared.prefs?.integer(forKey: "badgeCount") ?? 0
|
||||
count += 1
|
||||
|
||||
// Set the new badge number for the notification, then store that value for using later
|
||||
content.badge = NSNumber(value: count)
|
||||
NSEUtil.shared.prefs?.setValue(count, forKey: "badgeCount")
|
||||
}
|
||||
}
|
||||
|
||||
func mutateWithChatMessage(_ content: UNMutableNotificationContent) {
|
||||
if NSEUtil.shared.prefs?.bool(forKey: "playSoundChat") == true {
|
||||
mutateWithDmSound(content)
|
||||
}
|
||||
}
|
||||
|
||||
func mutateWithDefaultSound(_ content: UNMutableNotificationContent) {
|
||||
content.sound = UNNotificationSound.default
|
||||
}
|
||||
|
||||
func mutateWithDmSound(_ content: UNMutableNotificationContent) {
|
||||
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "dm.aiff"))
|
||||
}
|
||||
}
|
||||
|
||||
// NSEUtil's purpose is to create a shared instance of `UserDefaults` across
|
||||
// `NotificationService` instances. It also includes a queue so that we can process
|
||||
// updates to `UserDefaults` in parallel.
|
||||
//
|
||||
// Any time that you increment or decrement counts for notifications, you should use
|
||||
// the prefsQueue so that things remain in sync.
|
||||
|
||||
private class NSEUtil {
|
||||
static let shared = NSEUtil()
|
||||
@@ -99,7 +104,91 @@ private class NSEUtil {
|
||||
var prefs = UserDefaults(suiteName: APP_GROUP)
|
||||
var prefsQueue = DispatchQueue(label: "NSEPrefsQueue")
|
||||
|
||||
// MARK: - Utils
|
||||
|
||||
static func createCopy(_ content: UNNotificationContent) -> UNMutableNotificationContent? {
|
||||
return content.mutableCopy() as? UNMutableNotificationContent
|
||||
}
|
||||
|
||||
static func getDecrementedBadgeCount(current: Int, decrementBy by: Int) -> Int {
|
||||
let new = current - by
|
||||
if new < 0 {
|
||||
return 0
|
||||
}
|
||||
return new
|
||||
}
|
||||
|
||||
// MARK: - Mutations
|
||||
|
||||
static func mutateWithBadge(_ content: UNMutableNotificationContent,
|
||||
badgeType type: BadgeType,
|
||||
operation: BadgeOperation) {
|
||||
var genericCount = Self.shared.prefs?.integer(forKey: BadgeType.generic.rawValue) ?? 0
|
||||
var messagesCount = Self.shared.prefs?.integer(forKey: BadgeType.messages.rawValue) ?? 0
|
||||
|
||||
if type == .generic {
|
||||
if operation == .decrement {
|
||||
genericCount = 0
|
||||
} else {
|
||||
genericCount += 1
|
||||
}
|
||||
Self.shared.prefs?.setValue(genericCount, forKey: BadgeType.generic.rawValue)
|
||||
// TEMPORARY - since we have not implemented message count clearing on the server, we'll clear
|
||||
// those here as well.
|
||||
Self.shared.prefs?.setValue(messagesCount, forKey: BadgeType.messages.rawValue)
|
||||
} else if type == .messages {
|
||||
// Not yet implemented, but here's the logic
|
||||
if operation == .decrement,
|
||||
Self.shouldDecrementForConvo(content) {
|
||||
messagesCount = Self.getDecrementedBadgeCount(current: messagesCount, decrementBy: 1)
|
||||
} else if operation == .increment,
|
||||
shouldIncrementForConvo(content) {
|
||||
messagesCount += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func mutateWithChatMessage(_ content: UNMutableNotificationContent) {
|
||||
if Self.shared.prefs?.bool(forKey: "playSoundChat") == true {
|
||||
Self.mutateWithDmSound(content)
|
||||
}
|
||||
}
|
||||
|
||||
static func mutateWithDefaultSound(_ content: UNMutableNotificationContent) {
|
||||
content.sound = UNNotificationSound.default
|
||||
}
|
||||
|
||||
static func mutateWithDmSound(_ content: UNMutableNotificationContent) {
|
||||
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "dm.aiff"))
|
||||
}
|
||||
|
||||
static func shouldIncrementForConvo(_ content: UNMutableNotificationContent) -> Bool {
|
||||
guard let convoId = content.userInfo["convoId"] as? String,
|
||||
var dict = Self.shared.prefs?.dictionary(forKey: INCREMENTED_FOR_KEY) as? [String: Bool] else {
|
||||
return false
|
||||
}
|
||||
|
||||
if dict["convoId"] == true {
|
||||
return false
|
||||
}
|
||||
|
||||
dict[convoId] = true
|
||||
Self.shared.prefs?.set(dict, forKey: INCREMENTED_FOR_KEY)
|
||||
return true
|
||||
}
|
||||
|
||||
static func shouldDecrementForConvo(_ content: UNMutableNotificationContent) -> Bool {
|
||||
guard let convoId = content.userInfo["convoId"] as? String,
|
||||
var dict = Self.shared.prefs?.dictionary(forKey: INCREMENTED_FOR_KEY) as? [String: Bool] else {
|
||||
return false
|
||||
}
|
||||
|
||||
if dict["convoId"] != true {
|
||||
return false
|
||||
}
|
||||
|
||||
dict.removeValue(forKey: convoId)
|
||||
Self.shared.prefs?.set(dict, forKey: INCREMENTED_FOR_KEY)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,88 +6,89 @@ group = 'expo.modules.backgroundnotificationhandler'
|
||||
version = '0.5.0'
|
||||
|
||||
buildscript {
|
||||
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
||||
if (expoModulesCorePlugin.exists()) {
|
||||
apply from: expoModulesCorePlugin
|
||||
applyKotlinExpoModulesCorePlugin()
|
||||
}
|
||||
|
||||
// Simple helper that allows the root project to override versions declared by this library.
|
||||
ext.safeExtGet = { prop, fallback ->
|
||||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||
}
|
||||
|
||||
// Ensures backward compatibility
|
||||
ext.getKotlinVersion = {
|
||||
if (ext.has("kotlinVersion")) {
|
||||
ext.kotlinVersion()
|
||||
} else {
|
||||
ext.safeExtGet("kotlinVersion", "1.8.10")
|
||||
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
||||
if (expoModulesCorePlugin.exists()) {
|
||||
apply from: expoModulesCorePlugin
|
||||
applyKotlinExpoModulesCorePlugin()
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
// Simple helper that allows the root project to override versions declared by this library.
|
||||
ext.safeExtGet = { prop, fallback ->
|
||||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
|
||||
}
|
||||
// Ensures backward compatibility
|
||||
ext.getKotlinVersion = {
|
||||
if (ext.has("kotlinVersion")) {
|
||||
ext.kotlinVersion()
|
||||
} else {
|
||||
ext.safeExtGet("kotlinVersion", "1.8.10")
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
publishing {
|
||||
publications {
|
||||
release(MavenPublication) {
|
||||
from components.release
|
||||
}
|
||||
publishing {
|
||||
publications {
|
||||
release(MavenPublication) {
|
||||
from components.release
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url = mavenLocal().url
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
url = mavenLocal().url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion safeExtGet("compileSdkVersion", 33)
|
||||
compileSdkVersion safeExtGet("compileSdkVersion", 33)
|
||||
|
||||
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
|
||||
if (agpVersion.tokenize('.')[0].toInteger() < 8) {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
|
||||
if (agpVersion.tokenize('.')[0].toInteger() < 8) {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.majorVersion
|
||||
}
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.majorVersion
|
||||
namespace "expo.modules.backgroundnotificationhandler"
|
||||
defaultConfig {
|
||||
minSdkVersion safeExtGet("minSdkVersion", 21)
|
||||
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
||||
versionCode 1
|
||||
versionName "0.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
namespace "expo.modules.backgroundnotificationhandler"
|
||||
defaultConfig {
|
||||
minSdkVersion safeExtGet("minSdkVersion", 21)
|
||||
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
||||
versionCode 1
|
||||
versionName "0.5.0"
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
publishing {
|
||||
singleVariant("release") {
|
||||
withSourcesJar()
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
publishing {
|
||||
singleVariant("release") {
|
||||
withSourcesJar()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':expo-modules-core')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
|
||||
implementation 'com.google.firebase:firebase-messaging-ktx:24.0.0'
|
||||
implementation project(':expo-bluesky-swiss-army')
|
||||
implementation project(':expo-modules-core')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
|
||||
implementation 'com.google.firebase:firebase-messaging-ktx:24.0.0'
|
||||
}
|
||||
|
||||
+20
-2
@@ -2,6 +2,20 @@ package expo.modules.backgroundnotificationhandler
|
||||
|
||||
import android.content.Context
|
||||
import com.google.firebase.messaging.RemoteMessage
|
||||
import expo.modules.blueskyswissarmy.sharedprefs.SharedPrefs
|
||||
|
||||
enum class NotificationType(
|
||||
val type: String,
|
||||
) {
|
||||
Like("like"),
|
||||
Repost("repost"),
|
||||
Follow("follow"),
|
||||
Reply("reply"),
|
||||
Quote("quote"),
|
||||
ChatMessage("chat-message"),
|
||||
MarkReadGeneric("mark-read-generic"),
|
||||
MarkReadMessages("mark-read-messages"),
|
||||
}
|
||||
|
||||
class BackgroundNotificationHandler(
|
||||
private val context: Context,
|
||||
@@ -13,15 +27,19 @@ class BackgroundNotificationHandler(
|
||||
return
|
||||
}
|
||||
|
||||
if (remoteMessage.data["reason"] == "chat-message") {
|
||||
val type = NotificationType.valueOf(remoteMessage.data["reason"] ?: return)
|
||||
|
||||
if (type == NotificationType.ChatMessage) {
|
||||
mutateWithChatMessage(remoteMessage)
|
||||
} else if (type == NotificationType.MarkReadGeneric || type == NotificationType.MarkReadMessages) {
|
||||
return
|
||||
}
|
||||
|
||||
notifInterface.showMessage(remoteMessage)
|
||||
}
|
||||
|
||||
private fun mutateWithChatMessage(remoteMessage: RemoteMessage) {
|
||||
if (NotificationPrefs(context).getBoolean("playSoundChat")) {
|
||||
if (SharedPrefs(context).getBoolean("playSoundChat") == true) {
|
||||
// If oreo or higher
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
remoteMessage.data["channelId"] = "chat-messages"
|
||||
|
||||
+51
-41
@@ -1,19 +1,50 @@
|
||||
package expo.modules.backgroundnotificationhandler
|
||||
|
||||
import android.content.Context
|
||||
import expo.modules.blueskyswissarmy.sharedprefs.SharedPrefs
|
||||
import expo.modules.kotlin.modules.Module
|
||||
import expo.modules.kotlin.modules.ModuleDefinition
|
||||
|
||||
val DEFAULTS =
|
||||
mapOf<String, Any>(
|
||||
"playSoundChat" to true,
|
||||
"playSoundFollow" to false,
|
||||
"playSoundLike" to false,
|
||||
"playSoundMention" to false,
|
||||
"playSoundQuote" to false,
|
||||
"playSoundReply" to false,
|
||||
"playSoundRepost" to false,
|
||||
"mutedThreads" to mapOf<String, List<String>>(),
|
||||
)
|
||||
|
||||
enum class BadgeType(val rawValue: String) {
|
||||
Generic("badgeCountGeneric"),
|
||||
Messages("badgeCountMessages"),
|
||||
}
|
||||
|
||||
const val INCREMENTED_FOR_KEY = "incremented-for-convos"
|
||||
|
||||
class ExpoBackgroundNotificationHandlerModule : Module() {
|
||||
companion object {
|
||||
var isForegrounded = false
|
||||
}
|
||||
|
||||
fun getContext(): Context {
|
||||
return appContext.reactContext ?: throw Error("Context is null")
|
||||
}
|
||||
|
||||
override fun definition() =
|
||||
ModuleDefinition {
|
||||
Name("ExpoBackgroundNotificationHandler")
|
||||
|
||||
OnCreate {
|
||||
NotificationPrefs(appContext.reactContext).initialize()
|
||||
val context = appContext.reactContext ?: throw Error("Context is null")
|
||||
DEFAULTS.forEach { (key, value) ->
|
||||
if (SharedPrefs(context).hasValue(key)) {
|
||||
return@forEach
|
||||
}
|
||||
SharedPrefs(context)._setAnyValue(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
OnActivityEntersForeground {
|
||||
@@ -24,52 +55,31 @@ class ExpoBackgroundNotificationHandlerModule : Module() {
|
||||
isForegrounded = false
|
||||
}
|
||||
|
||||
AsyncFunction("getAllPrefsAsync") {
|
||||
return@AsyncFunction NotificationPrefs(appContext.reactContext).getAllPrefs()
|
||||
AsyncFunction("getPrefsAsync") {
|
||||
val keys = DEFAULTS.keys
|
||||
return@AsyncFunction SharedPrefs(getContext()).getValues(keys)
|
||||
}
|
||||
|
||||
AsyncFunction("getBoolAsync") { forKey: String ->
|
||||
return@AsyncFunction NotificationPrefs(appContext.reactContext).getBoolean(forKey)
|
||||
AsyncFunction("resetGenericCountAsync") {
|
||||
SharedPrefs(getContext()).setValue(BadgeType.Generic.rawValue, 0f)
|
||||
}
|
||||
|
||||
AsyncFunction("getStringAsync") { forKey: String ->
|
||||
return@AsyncFunction NotificationPrefs(appContext.reactContext).getString(forKey)
|
||||
AsyncFunction("maybeIncrementMessagesCountAsync") { convoId: String ->
|
||||
val prefs = SharedPrefs(getContext())
|
||||
if (!prefs.setContains(INCREMENTED_FOR_KEY, convoId)) {
|
||||
val curr = prefs.getFloat(BadgeType.Messages.rawValue) ?: 0f
|
||||
prefs.setValue(BadgeType.Messages.rawValue, curr + 1)
|
||||
}
|
||||
}
|
||||
|
||||
AsyncFunction("getStringArrayAsync") { forKey: String ->
|
||||
return@AsyncFunction NotificationPrefs(appContext.reactContext).getStringArray(forKey)
|
||||
}
|
||||
|
||||
AsyncFunction("setBoolAsync") { forKey: String, value: Boolean ->
|
||||
NotificationPrefs(appContext.reactContext).setBoolean(forKey, value)
|
||||
}
|
||||
|
||||
AsyncFunction("setStringAsync") { forKey: String, value: String ->
|
||||
NotificationPrefs(appContext.reactContext).setString(forKey, value)
|
||||
}
|
||||
|
||||
AsyncFunction("setStringArrayAsync") { forKey: String, value: Array<String> ->
|
||||
NotificationPrefs(appContext.reactContext).setStringArray(forKey, value)
|
||||
}
|
||||
|
||||
AsyncFunction("addToStringArrayAsync") { forKey: String, string: String ->
|
||||
NotificationPrefs(appContext.reactContext).addToStringArray(forKey, string)
|
||||
}
|
||||
|
||||
AsyncFunction("removeFromStringArrayAsync") { forKey: String, string: String ->
|
||||
NotificationPrefs(appContext.reactContext).removeFromStringArray(forKey, string)
|
||||
}
|
||||
|
||||
AsyncFunction("addManyToStringArrayAsync") { forKey: String, strings: Array<String> ->
|
||||
NotificationPrefs(appContext.reactContext).addManyToStringArray(forKey, strings)
|
||||
}
|
||||
|
||||
AsyncFunction("removeManyFromStringArrayAsync") { forKey: String, strings: Array<String> ->
|
||||
NotificationPrefs(appContext.reactContext).removeManyFromStringArray(forKey, strings)
|
||||
}
|
||||
|
||||
AsyncFunction("setBadgeCountAsync") { _: Int ->
|
||||
// This does nothing on Android
|
||||
AsyncFunction("maybeDecrementMessagesCountAsync") { convoId: String ->
|
||||
val prefs = SharedPrefs(getContext())
|
||||
if (prefs.setContains(INCREMENTED_FOR_KEY, convoId)) {
|
||||
val curr = prefs.getFloat(BadgeType.Messages.rawValue) ?: 0f
|
||||
if (curr != 0f) {
|
||||
prefs.setValue(BadgeType.Messages.rawValue, curr - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
package expo.modules.backgroundnotificationhandler
|
||||
|
||||
import android.content.Context
|
||||
|
||||
val DEFAULTS =
|
||||
mapOf<String, Any>(
|
||||
"playSoundChat" to true,
|
||||
"playSoundFollow" to false,
|
||||
"playSoundLike" to false,
|
||||
"playSoundMention" to false,
|
||||
"playSoundQuote" to false,
|
||||
"playSoundReply" to false,
|
||||
"playSoundRepost" to false,
|
||||
"mutedThreads" to mapOf<String, List<String>>(),
|
||||
)
|
||||
|
||||
class NotificationPrefs(
|
||||
private val context: Context?,
|
||||
) {
|
||||
private val prefs =
|
||||
context?.getSharedPreferences("xyz.blueskyweb.app", Context.MODE_PRIVATE)
|
||||
?: throw Error("Context is null")
|
||||
|
||||
fun initialize() {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
DEFAULTS.forEach { (key, value) ->
|
||||
if (prefs.contains(key)) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
when (value) {
|
||||
is Boolean -> {
|
||||
putBoolean(key, value)
|
||||
}
|
||||
is String -> {
|
||||
putString(key, value)
|
||||
}
|
||||
is Array<*> -> {
|
||||
putStringSet(key, value.map { it.toString() }.toSet())
|
||||
}
|
||||
is Map<*, *> -> {
|
||||
putStringSet(key, value.map { it.toString() }.toSet())
|
||||
}
|
||||
}
|
||||
}
|
||||
}.apply()
|
||||
}
|
||||
|
||||
fun getAllPrefs(): MutableMap<String, *> = prefs.all
|
||||
|
||||
fun getBoolean(key: String): Boolean = prefs.getBoolean(key, false)
|
||||
|
||||
fun getString(key: String): String? = prefs.getString(key, null)
|
||||
|
||||
fun getStringArray(key: String): Array<String>? = prefs.getStringSet(key, null)?.toTypedArray()
|
||||
|
||||
fun setBoolean(
|
||||
key: String,
|
||||
value: Boolean,
|
||||
) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
putBoolean(key, value)
|
||||
}.apply()
|
||||
}
|
||||
|
||||
fun setString(
|
||||
key: String,
|
||||
value: String,
|
||||
) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
putString(key, value)
|
||||
}.apply()
|
||||
}
|
||||
|
||||
fun setStringArray(
|
||||
key: String,
|
||||
value: Array<String>,
|
||||
) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
putStringSet(key, value.toSet())
|
||||
}.apply()
|
||||
}
|
||||
|
||||
fun addToStringArray(
|
||||
key: String,
|
||||
string: String,
|
||||
) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
val set = prefs.getStringSet(key, null)?.toMutableSet() ?: mutableSetOf()
|
||||
set.add(string)
|
||||
putStringSet(key, set)
|
||||
}.apply()
|
||||
}
|
||||
|
||||
fun removeFromStringArray(
|
||||
key: String,
|
||||
string: String,
|
||||
) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
val set = prefs.getStringSet(key, null)?.toMutableSet() ?: mutableSetOf()
|
||||
set.remove(string)
|
||||
putStringSet(key, set)
|
||||
}.apply()
|
||||
}
|
||||
|
||||
fun addManyToStringArray(
|
||||
key: String,
|
||||
strings: Array<String>,
|
||||
) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
val set = prefs.getStringSet(key, null)?.toMutableSet() ?: mutableSetOf()
|
||||
set.addAll(strings.toSet())
|
||||
putStringSet(key, set)
|
||||
}.apply()
|
||||
}
|
||||
|
||||
fun removeManyFromStringArray(
|
||||
key: String,
|
||||
strings: Array<String>,
|
||||
) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
val set = prefs.getStringSet(key, null)?.toMutableSet() ?: mutableSetOf()
|
||||
set.removeAll(strings.toSet())
|
||||
putStringSet(key, set)
|
||||
}.apply()
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,4 @@
|
||||
import {BackgroundNotificationHandler} from './src/ExpoBackgroundNotificationHandlerModule'
|
||||
export default BackgroundNotificationHandler
|
||||
import {BackgroundNotificationPreferencesProvider} from './src/BackgroundNotificationHandlerProvider'
|
||||
import * as BackgroundNotifications from './src/index'
|
||||
|
||||
export {BackgroundNotificationPreferencesProvider, BackgroundNotifications}
|
||||
|
||||
+1
@@ -10,6 +10,7 @@ Pod::Spec.new do |s|
|
||||
s.static_framework = true
|
||||
|
||||
s.dependency 'ExpoModulesCore'
|
||||
s.dependency 'ExpoBlueskySwissArmy'
|
||||
|
||||
# Swift/Objective-C compatibility
|
||||
s.pod_target_xcconfig = {
|
||||
|
||||
+41
-77
@@ -1,4 +1,5 @@
|
||||
import ExpoModulesCore
|
||||
import ExpoBlueskySwissArmy
|
||||
|
||||
let APP_GROUP = "group.app.bsky"
|
||||
|
||||
@@ -10,10 +11,11 @@ let DEFAULTS: [String: Any] = [
|
||||
"playSoundQuote": false,
|
||||
"playSoundReply": false,
|
||||
"playSoundRepost": false,
|
||||
"mutedThreads": [:] as! [String: [String]],
|
||||
"badgeCount": 0
|
||||
]
|
||||
|
||||
let INCREMENTED_FOR_KEY = "incremented-for-convos"
|
||||
|
||||
/*
|
||||
* The purpose of this module is to store values that are needed by the notification service
|
||||
* extension. Since we would rather get and store values such as age or user mute state
|
||||
@@ -29,92 +31,54 @@ public class ExpoBackgroundNotificationHandlerModule: Module {
|
||||
|
||||
OnCreate {
|
||||
DEFAULTS.forEach { p in
|
||||
if userDefaults?.value(forKey: p.key) == nil {
|
||||
userDefaults?.setValue(p.value, forKey: p.key)
|
||||
if !SharedPrefs.shared.hasValue(p.key) {
|
||||
SharedPrefs.shared._setAnyValue(p.key, p.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AsyncFunction("getAllPrefsAsync") { () -> [String: Any]? in
|
||||
var keys: [String] = []
|
||||
DEFAULTS.forEach { p in
|
||||
keys.append(p.key)
|
||||
AsyncFunction("resetGenericCountAsync") {
|
||||
SharedPrefs.shared.setValue(BadgeType.generic.toKeyName(), 0)
|
||||
}
|
||||
|
||||
AsyncFunction("maybeIncrementMessagesCountAsync") { (convoId: String) in
|
||||
guard !SharedPrefs.shared.setContains(INCREMENTED_FOR_KEY, convoId) else {
|
||||
return false
|
||||
}
|
||||
return userDefaults?.dictionaryWithValues(forKeys: keys)
|
||||
|
||||
var count = SharedPrefs.shared.getNumber(BadgeType.messages.toKeyName()) ?? 0
|
||||
count += 1
|
||||
|
||||
SharedPrefs.shared.addToSet(INCREMENTED_FOR_KEY, convoId)
|
||||
SharedPrefs.shared.setValue(BadgeType.messages.toKeyName(), count)
|
||||
return true
|
||||
}
|
||||
|
||||
AsyncFunction("getBoolAsync") { (forKey: String) -> Bool in
|
||||
if let pref = userDefaults?.bool(forKey: forKey) {
|
||||
return pref
|
||||
AsyncFunction("maybeDecrementMessagesCountAsync") { (convoId: String) in
|
||||
guard SharedPrefs.shared.setContains(INCREMENTED_FOR_KEY, convoId) else {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
AsyncFunction("getStringAsync") { (forKey: String) -> String? in
|
||||
if let pref = userDefaults?.string(forKey: forKey) {
|
||||
return pref
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var count = SharedPrefs.shared.getNumber(BadgeType.messages.toKeyName()) ?? 0
|
||||
count -= 1
|
||||
|
||||
AsyncFunction("getStringArrayAsync") { (forKey: String) -> [String]? in
|
||||
if let pref = userDefaults?.stringArray(forKey: forKey) {
|
||||
return pref
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
AsyncFunction("setBoolAsync") { (forKey: String, value: Bool) in
|
||||
userDefaults?.setValue(value, forKey: forKey)
|
||||
}
|
||||
|
||||
AsyncFunction("setStringAsync") { (forKey: String, value: String) in
|
||||
userDefaults?.setValue(value, forKey: forKey)
|
||||
}
|
||||
|
||||
AsyncFunction("setStringArrayAsync") { (forKey: String, value: [String]) in
|
||||
userDefaults?.setValue(value, forKey: forKey)
|
||||
}
|
||||
|
||||
AsyncFunction("addToStringArrayAsync") { (forKey: String, string: String) in
|
||||
if var curr = userDefaults?.stringArray(forKey: forKey),
|
||||
!curr.contains(string) {
|
||||
curr.append(string)
|
||||
userDefaults?.setValue(curr, forKey: forKey)
|
||||
}
|
||||
}
|
||||
|
||||
AsyncFunction("removeFromStringArrayAsync") { (forKey: String, string: String) in
|
||||
if var curr = userDefaults?.stringArray(forKey: forKey) {
|
||||
curr.removeAll { s in
|
||||
return s == string
|
||||
}
|
||||
userDefaults?.setValue(curr, forKey: forKey)
|
||||
}
|
||||
}
|
||||
|
||||
AsyncFunction("addManyToStringArrayAsync") { (forKey: String, strings: [String]) in
|
||||
if var curr = userDefaults?.stringArray(forKey: forKey) {
|
||||
strings.forEach { s in
|
||||
if !curr.contains(s) {
|
||||
curr.append(s)
|
||||
}
|
||||
}
|
||||
userDefaults?.setValue(curr, forKey: forKey)
|
||||
}
|
||||
}
|
||||
|
||||
AsyncFunction("removeManyFromStringArrayAsync") { (forKey: String, strings: [String]) in
|
||||
if var curr = userDefaults?.stringArray(forKey: forKey) {
|
||||
strings.forEach { s in
|
||||
curr.removeAll(where: { $0 == s })
|
||||
}
|
||||
userDefaults?.setValue(curr, forKey: forKey)
|
||||
}
|
||||
}
|
||||
|
||||
AsyncFunction("setBadgeCountAsync") { (count: Int) in
|
||||
userDefaults?.setValue(count, forKey: "badgeCount")
|
||||
SharedPrefs.shared.removeFromSet(INCREMENTED_FOR_KEY, convoId)
|
||||
SharedPrefs.shared.setValue(BadgeType.messages.toKeyName(), count)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum BadgeType: String, Enumerable {
|
||||
case generic
|
||||
case messages
|
||||
|
||||
func toKeyName() -> String {
|
||||
switch self {
|
||||
case .generic:
|
||||
return "badgeCountGeneric"
|
||||
case .messages:
|
||||
return "badgeCountMessages"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-35
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
|
||||
import {BackgroundNotificationHandlerPreferences} from './ExpoBackgroundNotificationHandler.types'
|
||||
import {BackgroundNotificationHandler} from './ExpoBackgroundNotificationHandlerModule'
|
||||
import {SharedPrefs} from '../../expo-bluesky-swiss-army'
|
||||
import {BackgroundNotificationHandlerPreferences} from './types'
|
||||
|
||||
interface BackgroundNotificationPreferencesContext {
|
||||
preferences: BackgroundNotificationHandlerPreferences
|
||||
@@ -29,42 +29,25 @@ export function BackgroundNotificationPreferencesProvider({
|
||||
|
||||
React.useEffect(() => {
|
||||
;(async () => {
|
||||
const prefs = await BackgroundNotificationHandler.getAllPrefsAsync()
|
||||
setPreferences(prefs)
|
||||
const prefs: BackgroundNotificationHandlerPreferences = {
|
||||
playSoundChat: SharedPrefs.getBool('playSoundChat') ?? true,
|
||||
}
|
||||
if (prefs) {
|
||||
setPreferences(prefs)
|
||||
}
|
||||
})()
|
||||
}, [])
|
||||
|
||||
const value = React.useMemo(
|
||||
() => ({
|
||||
preferences,
|
||||
setPref: async <
|
||||
Key extends keyof BackgroundNotificationHandlerPreferences,
|
||||
>(
|
||||
k: Key,
|
||||
v: BackgroundNotificationHandlerPreferences[Key],
|
||||
) => {
|
||||
switch (typeof v) {
|
||||
case 'boolean': {
|
||||
await BackgroundNotificationHandler.setBoolAsync(k, v)
|
||||
break
|
||||
}
|
||||
case 'string': {
|
||||
await BackgroundNotificationHandler.setStringAsync(k, v)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Invalid type for value: ${typeof v}`)
|
||||
}
|
||||
}
|
||||
|
||||
setPreferences(prev => ({
|
||||
...prev,
|
||||
[k]: v,
|
||||
}))
|
||||
},
|
||||
}),
|
||||
[preferences],
|
||||
)
|
||||
const value = {
|
||||
preferences,
|
||||
setPref: <Key extends keyof BackgroundNotificationHandlerPreferences>(
|
||||
k: Key,
|
||||
v: BackgroundNotificationHandlerPreferences[Key],
|
||||
) => {
|
||||
SharedPrefs.setValue(k, v)
|
||||
setPreferences(prev => ({...prev, [k]: v}))
|
||||
},
|
||||
}
|
||||
|
||||
return <Context.Provider value={value}>{children}</Context.Provider>
|
||||
}
|
||||
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
export type ExpoBackgroundNotificationHandlerModule = {
|
||||
getAllPrefsAsync: () => Promise<BackgroundNotificationHandlerPreferences>
|
||||
getBoolAsync: (forKey: string) => Promise<boolean>
|
||||
getStringAsync: (forKey: string) => Promise<string>
|
||||
getStringArrayAsync: (forKey: string) => Promise<string[]>
|
||||
setBoolAsync: (
|
||||
forKey: keyof BackgroundNotificationHandlerPreferences,
|
||||
value: boolean,
|
||||
) => Promise<void>
|
||||
setStringAsync: (
|
||||
forKey: keyof BackgroundNotificationHandlerPreferences,
|
||||
value: string,
|
||||
) => Promise<void>
|
||||
setStringArrayAsync: (
|
||||
forKey: keyof BackgroundNotificationHandlerPreferences,
|
||||
value: string[],
|
||||
) => Promise<void>
|
||||
addToStringArrayAsync: (
|
||||
forKey: keyof BackgroundNotificationHandlerPreferences,
|
||||
value: string,
|
||||
) => Promise<void>
|
||||
removeFromStringArrayAsync: (
|
||||
forKey: keyof BackgroundNotificationHandlerPreferences,
|
||||
value: string,
|
||||
) => Promise<void>
|
||||
addManyToStringArrayAsync: (
|
||||
forKey: keyof BackgroundNotificationHandlerPreferences,
|
||||
value: string[],
|
||||
) => Promise<void>
|
||||
removeManyFromStringArrayAsync: (
|
||||
forKey: keyof BackgroundNotificationHandlerPreferences,
|
||||
value: string[],
|
||||
) => Promise<void>
|
||||
setBadgeCountAsync: (count: number) => Promise<void>
|
||||
}
|
||||
|
||||
// TODO there are more preferences in the native code, however they have not been added here yet.
|
||||
// Don't add them until the native logic also handles the notifications for those preference types.
|
||||
export type BackgroundNotificationHandlerPreferences = {
|
||||
playSoundChat: boolean
|
||||
}
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
import {requireNativeModule} from 'expo-modules-core'
|
||||
|
||||
import {ExpoBackgroundNotificationHandlerModule} from './ExpoBackgroundNotificationHandler.types'
|
||||
|
||||
export const BackgroundNotificationHandler =
|
||||
requireNativeModule<ExpoBackgroundNotificationHandlerModule>(
|
||||
'ExpoBackgroundNotificationHandler',
|
||||
)
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
import {
|
||||
BackgroundNotificationHandlerPreferences,
|
||||
ExpoBackgroundNotificationHandlerModule,
|
||||
} from './ExpoBackgroundNotificationHandler.types'
|
||||
|
||||
// Stub for web
|
||||
export const BackgroundNotificationHandler = {
|
||||
getAllPrefsAsync: async () => {
|
||||
return {} as BackgroundNotificationHandlerPreferences
|
||||
},
|
||||
getBoolAsync: async (_: string) => {
|
||||
return false
|
||||
},
|
||||
getStringAsync: async (_: string) => {
|
||||
return ''
|
||||
},
|
||||
getStringArrayAsync: async (_: string) => {
|
||||
return []
|
||||
},
|
||||
setBoolAsync: async (_: string, __: boolean) => {},
|
||||
setStringAsync: async (_: string, __: string) => {},
|
||||
setStringArrayAsync: async (_: string, __: string[]) => {},
|
||||
addToStringArrayAsync: async (_: string, __: string) => {},
|
||||
removeFromStringArrayAsync: async (_: string, __: string) => {},
|
||||
addManyToStringArrayAsync: async (_: string, __: string[]) => {},
|
||||
removeManyFromStringArrayAsync: async (_: string, __: string[]) => {},
|
||||
setBadgeCountAsync: async (_: number) => {},
|
||||
} as ExpoBackgroundNotificationHandlerModule
|
||||
@@ -0,0 +1,16 @@
|
||||
import {Platform} from 'react-native'
|
||||
|
||||
export class NotImplementedError extends Error {
|
||||
constructor(params = {}) {
|
||||
if (__DEV__) {
|
||||
const caller = new Error().stack?.split('\n')[2]
|
||||
super(
|
||||
`Not implemented on ${Platform.OS}. Given params: ${JSON.stringify(
|
||||
params,
|
||||
)} ${caller}`,
|
||||
)
|
||||
} else {
|
||||
super('Not implemented')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import {requireNativeModule} from 'expo'
|
||||
|
||||
const NativeModule = requireNativeModule('ExpoBackgroundNotificationHandler')
|
||||
|
||||
export async function resetGenericCountAsync(): Promise<void> {
|
||||
await NativeModule.resetGenericCountAsync()
|
||||
}
|
||||
|
||||
export async function maybeIncrementMessagesCountAsync(
|
||||
convoId: string,
|
||||
): Promise<boolean> {
|
||||
return await NativeModule.maybeIncrementMessagesCountAsync(convoId)
|
||||
}
|
||||
|
||||
export async function maybeDecrementMessagesCountAsync(
|
||||
convoId: string,
|
||||
): Promise<boolean> {
|
||||
return await NativeModule.maybeDecrementMessagesCountAsync(convoId)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import {NotImplementedError} from './NotImplemented'
|
||||
import {BackgroundNotificationHandlerPreferences} from './types'
|
||||
|
||||
export function resetGenericCountAsync(): Promise<void> {
|
||||
throw new NotImplementedError()
|
||||
}
|
||||
|
||||
export function maybeIncrementMessagesCountAsync(
|
||||
convoId: string,
|
||||
): Promise<boolean> {
|
||||
throw new NotImplementedError({convoId})
|
||||
}
|
||||
|
||||
export function maybeDecrementMessagesCountAsync(
|
||||
convoId: string,
|
||||
): Promise<boolean> {
|
||||
throw new NotImplementedError({convoId})
|
||||
}
|
||||
|
||||
export function getPrefsAsync(): Promise<BackgroundNotificationHandlerPreferences | null> {
|
||||
throw new NotImplementedError()
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type BackgroundNotificationHandlerPreferences = {
|
||||
playSoundChat: boolean
|
||||
}
|
||||
@@ -26,6 +26,8 @@ type NotificationReason =
|
||||
| 'quote'
|
||||
| 'chat-message'
|
||||
| 'starterpack-joined'
|
||||
| 'mark-read-generic'
|
||||
| 'mark-read-messages'
|
||||
|
||||
type NotificationPayload =
|
||||
| {
|
||||
@@ -195,6 +197,20 @@ export function useNotificationsHandler() {
|
||||
shouldPlaySound: false,
|
||||
shouldSetBadge: false,
|
||||
}
|
||||
} else if (
|
||||
payload.reason === 'mark-read-generic' ||
|
||||
payload.reason === 'mark-read-messages'
|
||||
) {
|
||||
logger.debug(
|
||||
`Notifications: ${payload.reason}`,
|
||||
{},
|
||||
logger.DebugContext.notifications,
|
||||
)
|
||||
return {
|
||||
shouldShowAlert: false,
|
||||
shouldPlaySound: false,
|
||||
shouldSetBadge: false,
|
||||
}
|
||||
}
|
||||
|
||||
// Any notification other than a chat message should invalidate the unread page
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import React from 'react'
|
||||
import * as Notifications from 'expo-notifications'
|
||||
import {getBadgeCountAsync, setBadgeCountAsync} from 'expo-notifications'
|
||||
import {setBadgeCountAsync} from 'expo-notifications'
|
||||
import {BskyAgent} from '@atproto/api'
|
||||
|
||||
import {logger} from '#/logger'
|
||||
import {SessionAccount, useAgent, useSession} from '#/state/session'
|
||||
import {logEvent} from 'lib/statsig/statsig'
|
||||
import {devicePlatform, isAndroid, isNative} from 'platform/detection'
|
||||
import BackgroundNotificationHandler from '../../../modules/expo-background-notification-handler'
|
||||
import {BackgroundNotifications} from '../../../modules/expo-background-notification-handler'
|
||||
|
||||
const SERVICE_DID = (serviceUrl?: string) =>
|
||||
serviceUrl?.includes('staging')
|
||||
@@ -134,20 +134,7 @@ export function useRequestNotificationsPermission() {
|
||||
}
|
||||
}
|
||||
|
||||
export async function decrementBadgeCount(by: number) {
|
||||
if (!isNative) return
|
||||
|
||||
let count = await getBadgeCountAsync()
|
||||
count -= by
|
||||
if (count < 0) {
|
||||
count = 0
|
||||
}
|
||||
|
||||
await BackgroundNotificationHandler.setBadgeCountAsync(count)
|
||||
await setBadgeCountAsync(count)
|
||||
}
|
||||
|
||||
export async function resetBadgeCount() {
|
||||
await BackgroundNotificationHandler.setBadgeCountAsync(0)
|
||||
export async function resetGenericBadgeCount() {
|
||||
await BackgroundNotifications.resetGenericCountAsync()
|
||||
await setBadgeCountAsync(0)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {useHaptics} from '#/lib/haptics'
|
||||
import {decrementBadgeCount} from '#/lib/notifications/notifications'
|
||||
import {logEvent} from '#/lib/statsig/statsig'
|
||||
import {sanitizeDisplayName} from '#/lib/strings/display-names'
|
||||
import {
|
||||
@@ -33,6 +32,7 @@ import {Link} from '#/components/Link'
|
||||
import {useMenuControl} from '#/components/Menu'
|
||||
import {PostAlerts} from '#/components/moderation/PostAlerts'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {BackgroundNotifications} from '../../../../modules/expo-background-notification-handler'
|
||||
|
||||
export let ChatListItem = ({
|
||||
convo,
|
||||
@@ -179,7 +179,7 @@ function ChatListItemReady({
|
||||
|
||||
const onPress = useCallback(
|
||||
(e: GestureResponderEvent) => {
|
||||
decrementBadgeCount(convo.unreadCount)
|
||||
BackgroundNotifications.maybeDecrementMessagesCountAsync(convo.id)
|
||||
if (isDeletedAccount) {
|
||||
e.preventDefault()
|
||||
menuControl.open()
|
||||
@@ -188,7 +188,7 @@ function ChatListItemReady({
|
||||
logEvent('chat:open', {logContext: 'ChatsList'})
|
||||
}
|
||||
},
|
||||
[convo.unreadCount, isDeletedAccount, menuControl],
|
||||
[convo.id, isDeletedAccount, menuControl],
|
||||
)
|
||||
|
||||
const onLongPress = useCallback(() => {
|
||||
|
||||
@@ -10,7 +10,7 @@ import EventEmitter from 'eventemitter3'
|
||||
import BroadcastChannel from '#/lib/broadcast'
|
||||
import {logger} from '#/logger'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {resetBadgeCount} from 'lib/notifications/notifications'
|
||||
import {resetGenericBadgeCount} from 'lib/notifications/notifications'
|
||||
import {useModerationOpts} from '../../preferences/moderation-opts'
|
||||
import {truncateAndInvalidate} from '../util'
|
||||
import {RQKEY as RQKEY_NOTIFS} from './feed'
|
||||
@@ -117,7 +117,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
// update & broadcast
|
||||
setNumUnread('')
|
||||
broadcast.postMessage({event: ''})
|
||||
resetBadgeCount()
|
||||
resetGenericBadgeCount()
|
||||
},
|
||||
|
||||
async checkUnread({
|
||||
|
||||
Reference in New Issue
Block a user