feat(chess): notifications for challenge accepted and opponent moves
Add Android push notifications for chess events: - LiveChessGameAcceptEvent (kind 30065): notifies when opponent accepts challenge - LiveChessMoveEvent (kind 30066): notifies when opponent makes a move - New Chess notification channel alongside existing DM and Zap channels - Chess kinds added to NotificationFeedFilter for in-app notification feed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+71
@@ -29,6 +29,7 @@ import com.vitorpamplona.amethyst.R
|
|||||||
import com.vitorpamplona.amethyst.model.Account
|
import com.vitorpamplona.amethyst.model.Account
|
||||||
import com.vitorpamplona.amethyst.model.LocalCache
|
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.sendDMNotification
|
import com.vitorpamplona.amethyst.service.notifications.NotificationUtils.sendDMNotification
|
||||||
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
|
||||||
@@ -47,6 +48,8 @@ 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
|
||||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||||
|
import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent
|
||||||
|
import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent
|
||||||
import com.vitorpamplona.quartz.utils.Log
|
import com.vitorpamplona.quartz.utils.Log
|
||||||
import com.vitorpamplona.quartz.utils.TimeUtils
|
import com.vitorpamplona.quartz.utils.TimeUtils
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
@@ -108,6 +111,8 @@ class EventNotificationConsumer(
|
|||||||
is LnZapEvent -> notify(innerEvent, account)
|
is LnZapEvent -> notify(innerEvent, account)
|
||||||
is ChatMessageEvent -> notify(innerEvent, account)
|
is ChatMessageEvent -> notify(innerEvent, account)
|
||||||
is ChatMessageEncryptedFileHeaderEvent -> notify(innerEvent, account)
|
is ChatMessageEncryptedFileHeaderEvent -> notify(innerEvent, account)
|
||||||
|
is LiveChessGameAcceptEvent -> notify(innerEvent, account)
|
||||||
|
is LiveChessMoveEvent -> notify(innerEvent, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -481,6 +486,72 @@ class EventNotificationConsumer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun notify(
|
||||||
|
event: LiveChessGameAcceptEvent,
|
||||||
|
account: Account,
|
||||||
|
) {
|
||||||
|
Log.d(TAG, "New Chess Challenge Accept to Notify")
|
||||||
|
if (
|
||||||
|
event.createdAt > TimeUtils.fifteenMinutesAgo() &&
|
||||||
|
event.pubKey != account.signer.pubKey
|
||||||
|
) {
|
||||||
|
val author = LocalCache.getOrCreateUser(event.pubKey)
|
||||||
|
val user = author.toBestDisplayName()
|
||||||
|
val userPicture = author.profilePicture()
|
||||||
|
val title = stringRes(applicationContext, R.string.app_notification_chess_channel_name)
|
||||||
|
val content = stringRes(applicationContext, R.string.app_notification_chess_challenge_accepted, user)
|
||||||
|
val noteUri =
|
||||||
|
"notifications$ACCOUNT_QUERY_PARAM" +
|
||||||
|
account.signer.pubKey
|
||||||
|
.hexToByteArray()
|
||||||
|
.toNpub()
|
||||||
|
|
||||||
|
notificationManager()
|
||||||
|
.sendChessNotification(
|
||||||
|
event.id,
|
||||||
|
content,
|
||||||
|
title,
|
||||||
|
event.createdAt,
|
||||||
|
userPicture,
|
||||||
|
noteUri,
|
||||||
|
applicationContext,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun notify(
|
||||||
|
event: LiveChessMoveEvent,
|
||||||
|
account: Account,
|
||||||
|
) {
|
||||||
|
Log.d(TAG, "New Chess Move to Notify")
|
||||||
|
if (
|
||||||
|
event.createdAt > TimeUtils.fifteenMinutesAgo() &&
|
||||||
|
event.pubKey != account.signer.pubKey
|
||||||
|
) {
|
||||||
|
val author = LocalCache.getOrCreateUser(event.pubKey)
|
||||||
|
val user = author.toBestDisplayName()
|
||||||
|
val userPicture = author.profilePicture()
|
||||||
|
val title = stringRes(applicationContext, R.string.app_notification_chess_channel_name)
|
||||||
|
val content = stringRes(applicationContext, R.string.app_notification_chess_your_turn, user)
|
||||||
|
val noteUri =
|
||||||
|
"notifications$ACCOUNT_QUERY_PARAM" +
|
||||||
|
account.signer.pubKey
|
||||||
|
.hexToByteArray()
|
||||||
|
.toNpub()
|
||||||
|
|
||||||
|
notificationManager()
|
||||||
|
.sendChessNotification(
|
||||||
|
event.id,
|
||||||
|
content,
|
||||||
|
title,
|
||||||
|
event.createdAt,
|
||||||
|
userPicture,
|
||||||
|
noteUri,
|
||||||
|
applicationContext,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun notificationManager(): NotificationManager =
|
fun notificationManager(): NotificationManager =
|
||||||
ContextCompat.getSystemService(applicationContext, NotificationManager::class.java)
|
ContextCompat.getSystemService(applicationContext, NotificationManager::class.java)
|
||||||
as NotificationManager
|
as NotificationManager
|
||||||
|
|||||||
+52
@@ -45,8 +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 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"
|
||||||
|
private const val CHESS_GROUP_KEY = "com.vitorpamplona.amethyst.CHESS_NOTIFICATION"
|
||||||
const val REPLY_ACTION = "com.vitorpamplona.amethyst.REPLY_ACTION"
|
const val REPLY_ACTION = "com.vitorpamplona.amethyst.REPLY_ACTION"
|
||||||
const val MARK_READ_ACTION = "com.vitorpamplona.amethyst.MARK_READ_ACTION"
|
const val MARK_READ_ACTION = "com.vitorpamplona.amethyst.MARK_READ_ACTION"
|
||||||
const val KEY_REPLY_TEXT = "key_reply_text"
|
const val KEY_REPLY_TEXT = "key_reply_text"
|
||||||
@@ -56,6 +58,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 CHESS_SUMMARY_ID = 0x30000
|
||||||
|
|
||||||
fun getOrCreateDMChannel(applicationContext: Context): NotificationChannel {
|
fun getOrCreateDMChannel(applicationContext: Context): NotificationChannel {
|
||||||
if (dmChannel != null) return dmChannel!!
|
if (dmChannel != null) return dmChannel!!
|
||||||
@@ -99,6 +102,55 @@ object NotificationUtils {
|
|||||||
return zapChannel!!
|
return zapChannel!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getOrCreateChessChannel(applicationContext: Context): NotificationChannel {
|
||||||
|
if (chessChannel != null) return chessChannel!!
|
||||||
|
|
||||||
|
chessChannel =
|
||||||
|
NotificationChannel(
|
||||||
|
stringRes(applicationContext, R.string.app_notification_chess_channel_id),
|
||||||
|
stringRes(applicationContext, R.string.app_notification_chess_channel_name),
|
||||||
|
NotificationManager.IMPORTANCE_DEFAULT,
|
||||||
|
).apply {
|
||||||
|
description =
|
||||||
|
stringRes(applicationContext, R.string.app_notification_chess_channel_description)
|
||||||
|
}
|
||||||
|
|
||||||
|
val notificationManager: NotificationManager =
|
||||||
|
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
|
notificationManager.createNotificationChannel(chessChannel!!)
|
||||||
|
|
||||||
|
return chessChannel!!
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun NotificationManager.sendChessNotification(
|
||||||
|
id: String,
|
||||||
|
messageBody: String,
|
||||||
|
messageTitle: String,
|
||||||
|
time: Long,
|
||||||
|
pictureUrl: String?,
|
||||||
|
uri: String,
|
||||||
|
applicationContext: Context,
|
||||||
|
) {
|
||||||
|
getOrCreateChessChannel(applicationContext)
|
||||||
|
val channelId = stringRes(applicationContext, R.string.app_notification_chess_channel_id)
|
||||||
|
|
||||||
|
sendNotification(
|
||||||
|
id = id,
|
||||||
|
messageBody = messageBody,
|
||||||
|
messageTitle = messageTitle,
|
||||||
|
time = time,
|
||||||
|
pictureUrl = pictureUrl,
|
||||||
|
uri = uri,
|
||||||
|
channelId = channelId,
|
||||||
|
notificationGroupKey = CHESS_GROUP_KEY,
|
||||||
|
category = NotificationCompat.CATEGORY_SOCIAL,
|
||||||
|
summaryId = CHESS_SUMMARY_ID,
|
||||||
|
summaryText = stringRes(applicationContext, R.string.app_notification_chess_summary),
|
||||||
|
applicationContext = applicationContext,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun NotificationManager.sendZapNotification(
|
suspend fun NotificationManager.sendZapNotification(
|
||||||
id: String,
|
id: String,
|
||||||
messageBody: String,
|
messageBody: String,
|
||||||
|
|||||||
+4
@@ -58,6 +58,8 @@ import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEven
|
|||||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
||||||
import com.vitorpamplona.quartz.nip58Badges.BadgeAwardEvent
|
import com.vitorpamplona.quartz.nip58Badges.BadgeAwardEvent
|
||||||
|
import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent
|
||||||
|
import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent
|
||||||
import com.vitorpamplona.quartz.nip68Picture.PictureEvent
|
import com.vitorpamplona.quartz.nip68Picture.PictureEvent
|
||||||
import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent
|
import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent
|
||||||
import com.vitorpamplona.quartz.nip71Video.VideoNormalEvent
|
import com.vitorpamplona.quartz.nip71Video.VideoNormalEvent
|
||||||
@@ -83,6 +85,8 @@ class NotificationFeedFilter(
|
|||||||
CalendarRSVPEvent.KIND,
|
CalendarRSVPEvent.KIND,
|
||||||
ClassifiedsEvent.KIND,
|
ClassifiedsEvent.KIND,
|
||||||
LiveActivitiesEvent.KIND,
|
LiveActivitiesEvent.KIND,
|
||||||
|
LiveChessGameAcceptEvent.KIND,
|
||||||
|
LiveChessMoveEvent.KIND,
|
||||||
LongTextNoteEvent.KIND,
|
LongTextNoteEvent.KIND,
|
||||||
NipTextEvent.KIND,
|
NipTextEvent.KIND,
|
||||||
VideoVerticalEvent.KIND,
|
VideoVerticalEvent.KIND,
|
||||||
|
|||||||
@@ -738,6 +738,13 @@
|
|||||||
<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_chess_channel_id" translatable="false">ChessID</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_challenge_accepted">%1$s accepted your chess challenge</string>
|
||||||
|
<string name="app_notification_chess_your_turn">%1$s moved — your turn</string>
|
||||||
|
<string name="app_notification_chess_summary">Chess updates</string>
|
||||||
|
|
||||||
<string name="reply_notify">Notify: </string>
|
<string name="reply_notify">Notify: </string>
|
||||||
|
|
||||||
<string name="channel_list_join_conversation">Join Conversation</string>
|
<string name="channel_list_join_conversation">Join Conversation</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user