Merge pull request #2046 from vitorpamplona/claude/add-reaction-notifications-NzyLO
Add push notifications for post reactions
This commit is contained in:
+78
@@ -31,6 +31,7 @@ import com.vitorpamplona.amethyst.model.LocalCache
|
|||||||
import com.vitorpamplona.amethyst.model.Note
|
import com.vitorpamplona.amethyst.model.Note
|
||||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendChessNotification
|
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendChessNotification
|
||||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendDMNotification
|
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendDMNotification
|
||||||
|
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendReactionNotification
|
||||||
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendZapNotification
|
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendZapNotification
|
||||||
import com.vitorpamplona.amethyst.ui.note.showAmount
|
import com.vitorpamplona.amethyst.ui.note.showAmount
|
||||||
import com.vitorpamplona.amethyst.ui.stringRes
|
import com.vitorpamplona.amethyst.ui.stringRes
|
||||||
@@ -44,6 +45,7 @@ import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEven
|
|||||||
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
||||||
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
import com.vitorpamplona.quartz.nip19Bech32.toNpub
|
||||||
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
|
import com.vitorpamplona.quartz.nip21UriScheme.toNostrUri
|
||||||
|
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||||
@@ -123,6 +125,10 @@ class EventNotificationConsumer(
|
|||||||
is ChatMessageEncryptedFileHeaderEvent -> {
|
is ChatMessageEncryptedFileHeaderEvent -> {
|
||||||
notify(innerEvent, account)
|
notify(innerEvent, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ReactionEvent -> {
|
||||||
|
notify(innerEvent, account)
|
||||||
|
}
|
||||||
|
|
||||||
is LiveChessGameAcceptEvent -> {
|
is LiveChessGameAcceptEvent -> {
|
||||||
notifyChessEvent(innerEvent, account, R.string.app_notification_chess_challenge_accepted)
|
notifyChessEvent(innerEvent, account, R.string.app_notification_chess_challenge_accepted)
|
||||||
@@ -504,6 +510,78 @@ class EventNotificationConsumer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun notify(
|
||||||
|
event: ReactionEvent,
|
||||||
|
account: Account,
|
||||||
|
) {
|
||||||
|
Log.d(TAG, "New Reaction to Notify")
|
||||||
|
|
||||||
|
// old event being re-broadcast
|
||||||
|
if (event.createdAt < TimeUtils.fifteenMinutesAgo()) return
|
||||||
|
|
||||||
|
// don't notify for own reactions
|
||||||
|
if (event.pubKey == account.signer.pubKey) return
|
||||||
|
|
||||||
|
// only notify if the reaction is for the current user
|
||||||
|
if (!event.isTaggedUser(account.signer.pubKey)) return
|
||||||
|
|
||||||
|
val reactedPostId = event.originalPost().firstOrNull() ?: return
|
||||||
|
val reactedNote = LocalCache.checkGetOrCreateNote(reactedPostId)
|
||||||
|
|
||||||
|
val author = LocalCache.getOrCreateUser(event.pubKey)
|
||||||
|
val user = author.toBestDisplayName()
|
||||||
|
val userPicture = author.profilePicture()
|
||||||
|
|
||||||
|
val reactionContent = event.content
|
||||||
|
val reactionSymbol =
|
||||||
|
when {
|
||||||
|
reactionContent == ReactionEvent.LIKE || reactionContent.isBlank() -> "\uD83E\uDD19"
|
||||||
|
reactionContent == ReactionEvent.DISLIKE -> "\uD83D\uDC4E"
|
||||||
|
else -> reactionContent
|
||||||
|
}
|
||||||
|
|
||||||
|
val title = "$reactionSymbol $user"
|
||||||
|
|
||||||
|
val reactedContent =
|
||||||
|
reactedNote
|
||||||
|
?.event
|
||||||
|
?.content
|
||||||
|
?.split("\n")
|
||||||
|
?.firstOrNull() ?: ""
|
||||||
|
|
||||||
|
val content =
|
||||||
|
if (reactedContent.isNotBlank()) {
|
||||||
|
stringRes(
|
||||||
|
applicationContext,
|
||||||
|
R.string.app_notification_reactions_channel_message_for,
|
||||||
|
reactedContent,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
stringRes(
|
||||||
|
applicationContext,
|
||||||
|
R.string.app_notification_reactions_channel_message,
|
||||||
|
user,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val noteUri =
|
||||||
|
"notifications$ACCOUNT_QUERY_PARAM" +
|
||||||
|
account.signer.pubKey
|
||||||
|
.hexToByteArray()
|
||||||
|
.toNpub()
|
||||||
|
|
||||||
|
notificationManager()
|
||||||
|
.sendReactionNotification(
|
||||||
|
event.id,
|
||||||
|
content,
|
||||||
|
title,
|
||||||
|
event.createdAt,
|
||||||
|
userPicture,
|
||||||
|
noteUri,
|
||||||
|
applicationContext,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun notifyChessEvent(
|
private suspend fun notifyChessEvent(
|
||||||
event: BaseChessEvent,
|
event: BaseChessEvent,
|
||||||
account: Account,
|
account: Account,
|
||||||
|
|||||||
+54
@@ -45,6 +45,10 @@ import kotlinx.coroutines.withContext
|
|||||||
object NotificationUtils {
|
object NotificationUtils {
|
||||||
private var dmChannel: NotificationChannel? = null
|
private var dmChannel: NotificationChannel? = null
|
||||||
private var zapChannel: NotificationChannel? = null
|
private var zapChannel: NotificationChannel? = null
|
||||||
|
private var reactionChannel: NotificationChannel? = null
|
||||||
|
private const val DM_GROUP_KEY = "com.vitorpamplona.amethyst.DM_NOTIFICATION"
|
||||||
|
private const val ZAP_GROUP_KEY = "com.vitorpamplona.amethyst.ZAP_NOTIFICATION"
|
||||||
|
private const val REACTION_GROUP_KEY = "com.vitorpamplona.amethyst.REACTION_NOTIFICATION"
|
||||||
private var chessChannel: NotificationChannel? = null
|
private var chessChannel: NotificationChannel? = null
|
||||||
private const val DM_GROUP_KEY = "com.vitorpamplona.amethyst.DM_NOTIFICATION"
|
private const val DM_GROUP_KEY = "com.vitorpamplona.amethyst.DM_NOTIFICATION"
|
||||||
private const val ZAP_GROUP_KEY = "com.vitorpamplona.amethyst.ZAP_NOTIFICATION"
|
private const val ZAP_GROUP_KEY = "com.vitorpamplona.amethyst.ZAP_NOTIFICATION"
|
||||||
@@ -58,6 +62,7 @@ object NotificationUtils {
|
|||||||
|
|
||||||
private const val DM_SUMMARY_ID = 0x10000
|
private const val DM_SUMMARY_ID = 0x10000
|
||||||
private const val ZAP_SUMMARY_ID = 0x20000
|
private const val ZAP_SUMMARY_ID = 0x20000
|
||||||
|
private const val REACTION_SUMMARY_ID = 0x40000
|
||||||
private const val CHESS_SUMMARY_ID = 0x30000
|
private const val CHESS_SUMMARY_ID = 0x30000
|
||||||
|
|
||||||
fun getOrCreateDMChannel(applicationContext: Context): NotificationChannel {
|
fun getOrCreateDMChannel(applicationContext: Context): NotificationChannel {
|
||||||
@@ -102,6 +107,27 @@ object NotificationUtils {
|
|||||||
return zapChannel!!
|
return zapChannel!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getOrCreateReactionChannel(applicationContext: Context): NotificationChannel {
|
||||||
|
if (reactionChannel != null) return reactionChannel!!
|
||||||
|
|
||||||
|
reactionChannel =
|
||||||
|
NotificationChannel(
|
||||||
|
stringRes(applicationContext, R.string.app_notification_reactions_channel_id),
|
||||||
|
stringRes(applicationContext, R.string.app_notification_reactions_channel_name),
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT,
|
||||||
|
).apply {
|
||||||
|
description =
|
||||||
|
stringRes(applicationContext, R.string.app_notification_reactions_channel_description)
|
||||||
|
}
|
||||||
|
|
||||||
|
val notificationManager: NotificationManager =
|
||||||
|
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
|
notificationManager.createNotificationChannel(reactionChannel!!)
|
||||||
|
|
||||||
|
return reactionChannel!!
|
||||||
|
}
|
||||||
|
|
||||||
fun getOrCreateChessChannel(applicationContext: Context): NotificationChannel {
|
fun getOrCreateChessChannel(applicationContext: Context): NotificationChannel {
|
||||||
if (chessChannel != null) return chessChannel!!
|
if (chessChannel != null) return chessChannel!!
|
||||||
|
|
||||||
@@ -122,7 +148,35 @@ object NotificationUtils {
|
|||||||
|
|
||||||
return chessChannel!!
|
return chessChannel!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun NotificationManager.sendChessNotification(
|
||||||
|
id: String,
|
||||||
|
messageBody: String,
|
||||||
|
messageTitle: String,
|
||||||
|
time: Long,
|
||||||
|
pictureUrl: String?,
|
||||||
|
uri: String,
|
||||||
|
applicationContext: Context,
|
||||||
|
) {
|
||||||
|
getOrCreateReactionChannel(applicationContext)
|
||||||
|
val channelId = stringRes(applicationContext, R.string.app_notification_reactions_channel_id)
|
||||||
|
|
||||||
|
sendNotification(
|
||||||
|
id = id,
|
||||||
|
messageBody = messageBody,
|
||||||
|
messageTitle = messageTitle,
|
||||||
|
time = time,
|
||||||
|
pictureUrl = pictureUrl,
|
||||||
|
uri = uri,
|
||||||
|
channelId = channelId,
|
||||||
|
notificationGroupKey = REACTION_GROUP_KEY,
|
||||||
|
category = NotificationCompat.CATEGORY_SOCIAL,
|
||||||
|
summaryId = REACTION_SUMMARY_ID,
|
||||||
|
summaryText = stringRes(applicationContext, R.string.app_notification_reactions_summary),
|
||||||
|
applicationContext = applicationContext,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun NotificationManager.sendChessNotification(
|
suspend fun NotificationManager.sendChessNotification(
|
||||||
id: String,
|
id: String,
|
||||||
messageBody: String,
|
messageBody: String,
|
||||||
|
|||||||
@@ -751,6 +751,12 @@
|
|||||||
<string name="app_notification_dms_summary">New messages</string>
|
<string name="app_notification_dms_summary">New messages</string>
|
||||||
<string name="app_notification_zaps_summary">New zaps</string>
|
<string name="app_notification_zaps_summary">New zaps</string>
|
||||||
|
|
||||||
|
<string name="app_notification_reactions_channel_id" translatable="false">ReactionsID</string>
|
||||||
|
<string name="app_notification_reactions_channel_name">Reactions</string>
|
||||||
|
<string name="app_notification_reactions_channel_description">Notifies you when somebody reacts to your post</string>
|
||||||
|
<string name="app_notification_reactions_channel_message">%1$s reacted to your post</string>
|
||||||
|
<string name="app_notification_reactions_channel_message_for">for %1$s</string>
|
||||||
|
<string name="app_notification_reactions_summary">New reactions</string>
|
||||||
<string name="app_notification_chess_channel_id" translatable="false">ChessID</string>
|
<string name="app_notification_chess_channel_id" translatable="false">ChessID</string>
|
||||||
<string name="app_notification_chess_channel_name">Chess</string>
|
<string name="app_notification_chess_channel_name">Chess</string>
|
||||||
<string name="app_notification_chess_channel_description">Notifies you about chess game events</string>
|
<string name="app_notification_chess_channel_description">Notifies you about chess game events</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user