Bluesky app fork with some witchin' additions 💫

Android notification channels (#8539)

authored by samuel.fm and committed by

GitHub 4c75b568 9702b210

+102 -7
+15
modules/expo-background-notification-handler/android/src/main/java/expo/modules/backgroundnotificationhandler/BackgroundNotificationHandler.kt
··· 15 15 16 16 if (remoteMessage.data["reason"] == "chat-message") { 17 17 mutateWithChatMessage(remoteMessage) 18 + } else { 19 + mutateWithOtherReason(remoteMessage) 18 20 } 19 21 20 22 notifInterface.showMessage(remoteMessage) ··· 38 40 39 41 // TODO - Remove this once we have more backend capability 40 42 remoteMessage.data["badge"] = null 43 + } 44 + 45 + private fun mutateWithOtherReason(remoteMessage: RemoteMessage) { 46 + // If oreo or higher 47 + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { 48 + // If one of "like", "repost", "follow", "mention", "reply", "quote", "like-via-repost", "repost-via-repost" 49 + // assign to it's eponymous channel. otherwise do nothing, let expo handle it 50 + when (remoteMessage.data["reason"]) { 51 + "like", "repost", "follow", "mention", "reply", "quote", "like-via-repost", "repost-via-repost" -> { 52 + remoteMessage.data["channelId"] = remoteMessage.data["reason"] 53 + } 54 + } 55 + } 41 56 } 42 57 }
+87 -7
src/lib/hooks/useNotificationHandler.ts
··· 1 - import React from 'react' 1 + import {useEffect} from 'react' 2 2 import * as Notifications from 'expo-notifications' 3 + import {type AppBskyNotificationListNotifications} from '@atproto/api' 4 + import {msg} from '@lingui/macro' 5 + import {useLingui} from '@lingui/react' 3 6 import {CommonActions, useNavigation} from '@react-navigation/native' 4 7 import {useQueryClient} from '@tanstack/react-query' 5 8 ··· 25 28 | 'quote' 26 29 | 'chat-message' 27 30 | 'starterpack-joined' 31 + | 'like-via-repost' 32 + | 'repost-via-repost' 33 + | 'verified' 34 + | 'unverified' 28 35 29 36 /** 30 37 * Manually overridden type, but retains the possibility of ··· 66 73 const {currentConvoId} = useCurrentConvoId() 67 74 const {setShowLoggedOut} = useLoggedOutViewControls() 68 75 const closeAllActiveElements = useCloseAllActiveElements() 76 + const {_} = useLingui() 69 77 70 78 // On Android, we cannot control which sound is used for a notification on Android 71 79 // 28 or higher. Instead, we have to configure a notification channel ahead of time 72 80 // which has the sounds we want in the configuration for that channel. These two 73 81 // channels allow for the mute/unmute functionality we want for the background 74 82 // handler. 75 - React.useEffect(() => { 83 + useEffect(() => { 76 84 if (!isAndroid) return 85 + // assign both chat notifications to a group 86 + // NOTE: I don't think that it will retroactively move them into the group 87 + // if the channels already exist. no big deal imo -sfn 88 + const CHAT_GROUP = 'chat' 89 + Notifications.setNotificationChannelGroupAsync(CHAT_GROUP, { 90 + name: _(msg`Chat`), 91 + description: _( 92 + msg`You can choose whether chat notifications have sound in the chat settings within the app`, 93 + ), 94 + }) 77 95 Notifications.setNotificationChannelAsync('chat-messages', { 78 - name: 'Chat', 96 + name: _(msg`Chat messages - sound`), 97 + groupId: CHAT_GROUP, 79 98 importance: Notifications.AndroidImportance.MAX, 80 99 sound: 'dm.mp3', 81 100 showBadge: true, 82 101 vibrationPattern: [250], 83 102 lockscreenVisibility: Notifications.AndroidNotificationVisibility.PRIVATE, 84 103 }) 85 - 86 104 Notifications.setNotificationChannelAsync('chat-messages-muted', { 87 - name: 'Chat - Muted', 105 + name: _(msg`Chat messages - silent`), 106 + groupId: CHAT_GROUP, 88 107 importance: Notifications.AndroidImportance.MAX, 89 108 sound: null, 90 109 showBadge: true, 91 110 vibrationPattern: [250], 92 111 lockscreenVisibility: Notifications.AndroidNotificationVisibility.PRIVATE, 93 112 }) 94 - }, []) 95 113 96 - React.useEffect(() => { 114 + Notifications.setNotificationChannelAsync( 115 + 'like' satisfies AppBskyNotificationListNotifications.Notification['reason'], 116 + { 117 + name: _(msg`Likes`), 118 + importance: Notifications.AndroidImportance.HIGH, 119 + }, 120 + ) 121 + Notifications.setNotificationChannelAsync( 122 + 'repost' satisfies AppBskyNotificationListNotifications.Notification['reason'], 123 + { 124 + name: _(msg`Reposts`), 125 + importance: Notifications.AndroidImportance.HIGH, 126 + }, 127 + ) 128 + Notifications.setNotificationChannelAsync( 129 + 'reply' satisfies AppBskyNotificationListNotifications.Notification['reason'], 130 + { 131 + name: _(msg`Replies`), 132 + importance: Notifications.AndroidImportance.HIGH, 133 + }, 134 + ) 135 + Notifications.setNotificationChannelAsync( 136 + 'mention' satisfies AppBskyNotificationListNotifications.Notification['reason'], 137 + { 138 + name: _(msg`Mentions`), 139 + importance: Notifications.AndroidImportance.HIGH, 140 + }, 141 + ) 142 + Notifications.setNotificationChannelAsync( 143 + 'quote' satisfies AppBskyNotificationListNotifications.Notification['reason'], 144 + { 145 + name: _(msg`Quotes`), 146 + importance: Notifications.AndroidImportance.HIGH, 147 + }, 148 + ) 149 + Notifications.setNotificationChannelAsync( 150 + 'follow' satisfies AppBskyNotificationListNotifications.Notification['reason'], 151 + { 152 + name: _(msg`New followers`), 153 + importance: Notifications.AndroidImportance.HIGH, 154 + }, 155 + ) 156 + Notifications.setNotificationChannelAsync( 157 + 'like-via-repost' satisfies AppBskyNotificationListNotifications.Notification['reason'], 158 + { 159 + name: _(msg`Likes of your reposts`), 160 + importance: Notifications.AndroidImportance.HIGH, 161 + }, 162 + ) 163 + Notifications.setNotificationChannelAsync( 164 + 'repost-via-repost' satisfies AppBskyNotificationListNotifications.Notification['reason'], 165 + { 166 + name: _(msg`Reposts of your reposts`), 167 + importance: Notifications.AndroidImportance.HIGH, 168 + }, 169 + ) 170 + }, [_]) 171 + 172 + useEffect(() => { 97 173 const handleNotification = (payload?: NotificationPayload) => { 98 174 if (!payload) return 99 175 ··· 151 227 case 'quote': 152 228 case 'reply': 153 229 case 'starterpack-joined': 230 + case 'like-via-repost': 231 + case 'repost-via-repost': 232 + case 'verified': 233 + case 'unverified': 154 234 resetToTab('NotificationsTab') 155 235 break 156 236 // TODO implement these after we have an idea of how to handle each individual case