Skip to content

Commit 5ca29e3

Browse files
committed
refactor: clean up push notifications code and fix redundant checks
1 parent d2a1771 commit 5ca29e3

File tree

10 files changed

+372
-295
lines changed

10 files changed

+372
-295
lines changed

public/firebase/firebase-messaging-sw.js

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ importScripts('https://www.gstatic.com/firebasejs/9.22.1/firebase-messaging-comp
44
importScripts('https://cdnjs.cloudflare.com/ajax/libs/tweetnacl/1.0.3/nacl.min.js')
55
importScripts('/js/ed2curve.min.js')
66

7+
const NOTIFICATION_TYPES = {
8+
NO_NOTIFICATIONS: 0,
9+
BACKGROUND_FETCH: 1,
10+
PUSH: 2
11+
}
12+
713
// Checking double notifications
814
const processedEvents = new Set()
915
setInterval(() => processedEvents.clear(), 2 * 60 * 1000) // 2 minutes
@@ -107,31 +113,58 @@ channel.onmessage = (event) => {
107113
}
108114
}
109115

110-
// Handling background messages
111-
messaging.onBackgroundMessage(async (payload) => {
112-
if (!payload.data?.txn) return
116+
function parseTransactionPayload(payload) {
117+
if (!payload.data?.txn) {
118+
return null
119+
}
113120

114-
let transaction, eventId
115121
try {
116-
transaction = JSON.parse(payload.data.txn)
117-
eventId = transaction.id
122+
const transaction = JSON.parse(payload.data.txn)
123+
return {
124+
transaction,
125+
eventId: transaction.id
126+
}
118127
} catch (error) {
119128
console.warn('Invalid transaction JSON:', error)
120-
return
129+
return null
121130
}
131+
}
122132

123-
if (!eventId || processedEvents.has(eventId)) return
133+
function isEventProcessed(eventId) {
134+
if (!eventId || processedEvents.has(eventId)) {
135+
return true
136+
}
124137
processedEvents.add(eventId)
138+
return false
139+
}
140+
141+
async function ensureSettingsInitialized() {
142+
if (notificationSettings.initialized) {
143+
return
144+
}
145+
146+
await new Promise((resolve) => setTimeout(resolve, 2000))
125147

126-
// Wait for settings if needed
127148
if (!notificationSettings.initialized) {
128-
await new Promise((resolve) => setTimeout(resolve, 2000))
129-
if (!notificationSettings.initialized) {
130-
notificationSettings.initialized = true
131-
}
149+
notificationSettings.initialized = true
132150
}
151+
}
152+
153+
function isPushEnabled() {
154+
return notificationSettings.type === NOTIFICATION_TYPES.PUSH
155+
}
156+
157+
messaging.onBackgroundMessage(async (payload) => {
158+
const parsed = parseTransactionPayload(payload)
159+
if (!parsed) return
160+
161+
const { transaction, eventId } = parsed
162+
163+
if (isEventProcessed(eventId)) return
164+
165+
await ensureSettingsInitialized()
133166

134-
if (notificationSettings.type !== 2 || (await isAppVisible())) {
167+
if (!isPushEnabled() || (await isAppVisible())) {
135168
return
136169
}
137170

Lines changed: 21 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,45 @@
1-
import { ref, computed } from 'vue'
1+
import { computed } from 'vue'
22
import { Capacitor } from '@capacitor/core'
33

4-
export interface AndroidNotificationSettings {
5-
type: number
6-
privateKey?: string
7-
}
8-
9-
/**
10-
* Android-specific composable for managing push notifications
11-
* Handles only data passing to AndroidPushService
12-
*/
134
export function useAndroidPushNotifications() {
14-
const privateKey = ref<string | null>(null)
15-
const isSupported = Capacitor.getPlatform() === 'android'
16-
17-
const isActive = computed(() => privateKey.value !== null)
5+
const isSupported = computed(() => Capacitor.getPlatform() === 'android')
186

197
/**
20-
* Gets reference to pushService
8+
* Gets reference to pushService (always AndroidPushService on Android)
219
*/
2210
const getPushService = async () => {
23-
try {
24-
const { pushService } = await import('@/lib/notifications/pushServiceFactory')
25-
return pushService
26-
} catch (error) {
27-
console.error('[Android Push] Failed to import pushService:', error)
28-
return null
29-
}
30-
}
31-
32-
/**
33-
* Stores private key and passes it to AndroidPushService
34-
*/
35-
const sendPrivateKey = async (key: string): Promise<boolean> => {
36-
if (!isSupported || !key) {
37-
return false
38-
}
11+
if (!isSupported.value) return null
3912

40-
try {
41-
// Store locally
42-
privateKey.value = key
43-
44-
// Pass to push service
45-
const pushService = await getPushService()
46-
if (pushService) {
47-
pushService.setPrivateKey(key)
48-
return true
49-
}
50-
51-
return false
52-
} catch (error) {
53-
console.error('[Android Push] Failed to send private key:', error)
54-
return false
55-
}
13+
const { pushService } = await import('@/lib/notifications/pushServiceFactory')
14+
return pushService
5615
}
5716

5817
/**
59-
* Clears private key
18+
* Sets private key - proxy to pushService.setPrivateKey()
6019
*/
61-
const clearPrivateKey = async (): Promise<boolean> => {
62-
if (!isSupported) {
63-
return false
64-
}
65-
66-
try {
67-
// Clear locally
68-
privateKey.value = null
20+
const setPrivateKey = async (key: string): Promise<boolean> => {
21+
if (!isSupported.value) return false
6922

70-
// Clear in push service
71-
const pushService = await getPushService()
72-
if (pushService) {
73-
pushService.setPrivateKey('')
74-
return true
75-
}
76-
77-
return false
78-
} catch (error) {
79-
console.error('[Android Push] Failed to clear private key:', error)
80-
return false
23+
const service = await getPushService()
24+
if (service) {
25+
service.setPrivateKey(key)
26+
return true
8127
}
28+
29+
return false
8230
}
8331

8432
/**
85-
* Syncs notification settings (mainly stores private key if provided)
33+
* Clears private key - proxy to pushService.setPrivateKey('')
8634
*/
87-
const syncNotificationSettings = async (
88-
settings: AndroidNotificationSettings
89-
): Promise<boolean> => {
90-
if (!isSupported) {
91-
return false
92-
}
93-
94-
try {
95-
// If private key provided - store it
96-
if (settings.privateKey) {
97-
await sendPrivateKey(settings.privateKey)
98-
}
99-
100-
return true
101-
} catch (error) {
102-
console.error('[Android Push] Failed to sync settings:', error)
103-
return false
104-
}
35+
const clearPrivateKey = async (): Promise<boolean> => {
36+
return setPrivateKey('')
10537
}
10638

10739
return {
108-
isActive,
109-
sendPrivateKey,
40+
isSupported,
41+
setPrivateKey,
11042
clearPrivateKey,
111-
syncNotificationSettings
43+
getPushService
11244
}
11345
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { useStore } from 'vuex'
2+
import { Capacitor } from '@capacitor/core'
3+
import { useWebPushNotifications } from './useWebPushNotifications'
4+
import { useAndroidPushNotifications } from './useAndroidPushNotifications'
5+
6+
/**
7+
* Composable for managing private key across platforms
8+
* Handles sending/clearing private keys to both Service Worker and Push Services
9+
*/
10+
export function usePrivateKeyManager() {
11+
const store = useStore()
12+
const platform = Capacitor.getPlatform()
13+
14+
// Platform-specific composables
15+
const webPush = platform === 'web' ? useWebPushNotifications() : null
16+
const androidPush = platform === 'android' ? useAndroidPushNotifications() : null
17+
18+
/**
19+
* Gets private key from store
20+
*/
21+
const getPrivateKey = async (): Promise<string | null> => {
22+
try {
23+
return await store.dispatch('getPrivateKeyForPush')
24+
} catch (error) {
25+
console.error('Error getting private key:', error)
26+
return null
27+
}
28+
}
29+
30+
/**
31+
* Sends private key to appropriate platform handlers
32+
*/
33+
const sendPrivateKey = async (): Promise<boolean> => {
34+
const privateKey = await getPrivateKey()
35+
if (!privateKey) return false
36+
37+
if (webPush) {
38+
return webPush.setPrivateKey(privateKey)
39+
}
40+
if (androidPush) {
41+
return await androidPush.setPrivateKey(privateKey)
42+
}
43+
44+
return false
45+
}
46+
47+
/**
48+
* Clears private key from appropriate platform handlers
49+
*/
50+
const clearPrivateKey = async (): Promise<boolean> => {
51+
if (webPush) {
52+
return webPush.clearPrivateKey()
53+
}
54+
if (androidPush) {
55+
return await androidPush.clearPrivateKey()
56+
}
57+
58+
return false
59+
}
60+
61+
/**
62+
* Syncs notification settings (web only)
63+
*/
64+
const syncNotificationSettings = (type: number) => {
65+
if (webPush) {
66+
webPush.syncNotificationSettings({ type })
67+
}
68+
// Android settings are managed through pushService directly
69+
}
70+
71+
return {
72+
getPrivateKey,
73+
sendPrivateKey,
74+
clearPrivateKey,
75+
syncNotificationSettings
76+
}
77+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { onMounted, onBeforeUnmount } from 'vue'
2+
import { useRouter } from 'vue-router'
3+
4+
/**
5+
* Composable for handling push notification events and navigation
6+
*/
7+
export function usePushEventHandlers() {
8+
const router = useRouter()
9+
10+
/**
11+
* Handles opening chat from notification click
12+
*/
13+
const handleOpenChat = (event: Event) => {
14+
const detail = (event as CustomEvent).detail
15+
if (detail?.partnerId) {
16+
router.push({
17+
name: 'Chat',
18+
params: { partnerId: detail.partnerId }
19+
})
20+
}
21+
}
22+
23+
/**
24+
* Handles messages from Service Worker
25+
*/
26+
const handleServiceWorkerMessage = (event: MessageEvent) => {
27+
if (!event.data) return
28+
29+
const { action, partnerId } = event.data
30+
31+
if (action === 'OPEN_CHAT' && partnerId) {
32+
const currentRoute = router.currentRoute.value
33+
34+
if (currentRoute.name !== 'Chat' || currentRoute.params.partnerId !== partnerId) {
35+
router.push({
36+
name: 'Chat',
37+
params: { partnerId }
38+
})
39+
}
40+
41+
window.focus()
42+
}
43+
}
44+
45+
/**
46+
* Sets up event listeners
47+
*/
48+
const setupEventListeners = () => {
49+
window.addEventListener('openChat', handleOpenChat)
50+
navigator.serviceWorker?.addEventListener('message', handleServiceWorkerMessage)
51+
}
52+
53+
/**
54+
* Removes event listeners
55+
*/
56+
const removeEventListeners = () => {
57+
window.removeEventListener('openChat', handleOpenChat)
58+
navigator.serviceWorker?.removeEventListener('message', handleServiceWorkerMessage)
59+
}
60+
61+
onMounted(setupEventListeners)
62+
onBeforeUnmount(removeEventListeners)
63+
64+
return {
65+
handleOpenChat,
66+
handleServiceWorkerMessage,
67+
setupEventListeners,
68+
removeEventListeners
69+
}
70+
}

0 commit comments

Comments
 (0)