From 77ef45345db664f9dbc02e8bcc293f1dc6bcd656 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 31 Oct 2024 08:56:21 +0200 Subject: [PATCH] added notifications for mobile --- android/app/capacitor.build.gradle | 2 + android/app/src/main/AndroidManifest.xml | 4 +- .../app/BackgroundFetchHeadlessTask.java | 31 + android/app/src/main/res/drawable/qort.png | Bin 0 -> 1907 bytes android/build.gradle | 4 + android/capacitor.settings.gradle | 6 + capacitor.config.ts | 8 +- package-lock.json | 18 + package.json | 2 + src/App.tsx | 8 +- src/background.ts | 683 +++++++++--------- 11 files changed, 428 insertions(+), 338 deletions(-) create mode 100644 android/app/src/main/java/com/example/app/BackgroundFetchHeadlessTask.java create mode 100644 android/app/src/main/res/drawable/qort.png diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index 3afe430..d4f22b6 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -11,7 +11,9 @@ apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { implementation project(':capacitor-browser') implementation project(':capacitor-filesystem') + implementation project(':capacitor-local-notifications') implementation project(':evva-capacitor-secure-storage-plugin') + implementation project(':transistorsoft-capacitor-background-fetch') implementation "androidx.webkit:webkit:1.4.0" } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 9da5091..2b2013c 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,7 +8,8 @@ android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" android:theme="@style/AppTheme" - android:requestLegacyExternalStorage="true"> + android:requestLegacyExternalStorage="true" + android:usesCleartextTraffic="true"> + diff --git a/android/app/src/main/java/com/example/app/BackgroundFetchHeadlessTask.java b/android/app/src/main/java/com/example/app/BackgroundFetchHeadlessTask.java new file mode 100644 index 0000000..fac2dc1 --- /dev/null +++ b/android/app/src/main/java/com/example/app/BackgroundFetchHeadlessTask.java @@ -0,0 +1,31 @@ +package com.example.app; + + +import android.content.Context; +import android.util.Log; + +import com.transistorsoft.tsbackgroundfetch.BackgroundFetch; +import com.transistorsoft.tsbackgroundfetch.BGTask; + +public class BackgroundFetchHeadlessTask{ + public void onFetch(Context context, BGTask task) { + // Get a reference to the BackgroundFetch Android API. + BackgroundFetch backgroundFetch = BackgroundFetch.getInstance(context); + // Get the taskId. + String taskId = task.getTaskId(); + // Log a message to adb logcat. + Log.d("MyHeadlessTask", "BackgroundFetchHeadlessTask onFetch -- CUSTOM IMPLEMENTATION: " + taskId); + + boolean isTimeout = task.getTimedOut(); + // Is this a timeout? + if (isTimeout) { + backgroundFetch.finish(taskId); + return; + } + // Do your work here... + // + // + // Signal finish just like the Javascript API. + backgroundFetch.finish(taskId); + } +} \ No newline at end of file diff --git a/android/app/src/main/res/drawable/qort.png b/android/app/src/main/res/drawable/qort.png new file mode 100644 index 0000000000000000000000000000000000000000..39d090f724b9c2708a771b0208dd41266d433cdf GIT binary patch literal 1907 zcmV-(2aNcMP)|H@`8$}eJETAGPXj)W25R?Ukkb0?o>#feEH&j>ZjYC{+oG`zj+g}jp#Eota zy;VvgZoPDK>n+ZKNC>sqA|RlG+EhhU2*P~ZnP9uS_Uz1?nT@qiS~*_N&c6Nf=Dj!H zyxmOGX0pw+NykuJdOOhP~gi*V948deV=bpF*|A2Bu zI{ahPVdS|i#QPO|PN@24C7cI;P^qL!*`p{~IUUweslF22PsfAH#fj&uJpYw!Hb zdVl;~N=sD&R0N=|i)4NA#0#ulKf|tHI6Jl7je8GSXZLqDc>0g4NkB~{P^k|2#08kG zbBpQFo&_cZZF--6m>%OmmIVgp7AkVHRoi|t;s9M-dPVE_or6DF#4R>Ccw!sab#A3? zZ9v8BVbi9c1dyvlR<^M}NP)HNU$0E#S#tpuezRvQ2m#1;{dIQgL_pf_@aezYxBiv_ zYcWWLdH!_T)xI(cT(yZ>ozo@%j$)f&(Db`7r2E~`&X1hFky9N`2bK>040lE-MtYo2rX}?KvM&y&x zg*U7WZC(Ew-TO7oUy0<}B=esIXwNj~L>dH(Smsf@jMC>>K|)}u7X02Om~Zw&l8 zPh$Y2PCC;O$F9?*LRtncS52jbHhgG-DEl4<1HQ|I*Tk{t1AU=SPtx!PDE#CV?gtyU zdrBz_BCtvnMqlU?efQll`eT4Du(fZ%Dm~Keg-185Oc1h&ND$6Au1FNtVYK+rH39WB z05u{)iU4~0%Wsv3b{^?mluU+YKlqK~pz=)9NI zI8E?_3IkfH0IdM60IdM6qyn@8be;hnRxpss5P)`lYXM5nguleG=%WPE1)u}pT5R2a ze6&nlWPJ#2kn*;M&Ul*!a@J;+=i!an?l zHjcSY6-k_0Xc7Jr<%d`wy2gQnkHm(*vCl$jBAE|OD@PVO``UNK6iwJAbb2gHJOJdF8__-FE>AamnB!jv=CRt4U}+rKR4V*6Z^!62C^8|uvjZS zwgim*|A30S8oVX=pV!Z%{uKqfMWuaotXIZh&?#zHxYVN)?F>Q%MQJPU=isLm>jVe`li7eZ0gtBO&O6BUv5Z#4b_PsMTa6fs#(uR5iKFD_blZy;kcA}tFYOoj)L5TK2SiV3_hsM%L4(yxH$dR32Ts%rh8_dFh`U2Hs zdSv%uf+F&FUHMdx($@h~%R)yiwR#{cC7>g~f*@F(6EJ5!ip@Hp0#w}E93Nx9IutV? z<8?I>)>bGIQWGkU1UiRQ?+vM53{ch=17rXTVkKhLt1GMkTfJPx0 txTCL73&mFC6>0 { + if (permission.display === 'granted') { + console.log("Notifications enabled"); + } +}); + export function cleanUrl(url) { return url?.replace(/^(https?:\/\/)?(www\.)?/, ""); @@ -384,35 +394,21 @@ const handleNotificationDirect = async (directs) => { (newestLatestTimestamp && newestLatestTimestamp?.timestamp > oldestLatestTimestamp?.timestamp) ) { - // const notificationId = - // "chat_notification_" + - // Date.now() + - // "_type=direct" + - // `_from=${newestLatestTimestamp.address}`; - // chrome.notifications.create(notificationId, { - // type: "basic", - // iconUrl: "qort.png", // Add an appropriate icon for chat notifications - // title: `New Direct message! ${ - // newestLatestTimestamp?.name && `from ${newestLatestTimestamp.name}` - // }`, - // message: "You have received a new direct message", - // priority: 2, // Use the maximum priority to ensure it's noticeable - // }); - // if (!isMobile) { - // setTimeout(() => { - // chrome.notifications.clear(notificationId); - // }, 7000); - // } + - // chrome.runtime.sendMessage( - // { - // action: "notification", - // payload: { - // }, - // } - // ) - // audio.play(); - playNotificationSound(); + LocalNotifications.schedule({ + notifications: [ + { + title: `New Direct message! ${ + newestLatestTimestamp?.name && `from ${newestLatestTimestamp.name}` + }`, + body: "You have received a new direct message", + id: notificationId, + schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now + } + ] + }); + } } catch (error) { if (!isFocused) { @@ -431,32 +427,21 @@ const handleNotificationDirect = async (directs) => { }); const notificationId = "chat_notification_" + Date.now(); - // chrome.notifications.create(notificationId, { - // type: "basic", - // iconUrl: "qort.png", // Add an appropriate icon for chat notifications - // title: `New Direct message!`, - // message: "You have received a new direct message", - // priority: 2, // Use the maximum priority to ensure it's noticeable - // }); - if (!isMobile) { - setTimeout(() => { - chrome.notifications.clear(notificationId); - }, 7000); - } - playNotificationSound(); - // audio.play(); - // } + + LocalNotifications.schedule({ + notifications: [ + { + title: `New Direct message!`, + body: "You have received a new direct message", + id: notificationId, + schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now + } + ] + }); } } finally { setChatHeadsDirect(dataDirects); - // chrome.runtime.sendMessage( - // { - // action: "setChatHeads", - // payload: { - // data, - // }, - // } - // ); + } }; async function getThreadActivity(): Promise { @@ -625,28 +610,21 @@ const handleNotification = async (groups) => { "_type=group" + `_from=${newestLatestTimestamp.groupId}`; - // chrome.notifications.create(notificationId, { - // type: "basic", - // iconUrl: "qort.png", // Add an appropriate icon for chat notifications - // title: "New Group Message!", - // message: `You have received a new message from ${newestLatestTimestamp?.groupName}`, - // priority: 2, // Use the maximum priority to ensure it's noticeable - - // }); + LocalNotifications.schedule({ + notifications: [ + { + title: "New Group Message!", + body: `You have received a new message from ${newestLatestTimestamp?.groupName}`, + id: notificationId, + schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now + } + ] + }); if (!isMobile) { setTimeout(() => { chrome.notifications.clear(notificationId); }, 7000); } - // chrome.runtime.sendMessage( - // { - // action: "notification", - // payload: { - // }, - // } - // ) - // audio.play(); - playNotificationSound(); lastGroupNotification = Date.now(); } } @@ -667,277 +645,31 @@ const handleNotification = async (groups) => { }); const notificationId = "chat_notification_" + Date.now(); - // chrome.notifications.create(notificationId, { - // type: "basic", - // iconUrl: "qort.png", // Add an appropriate icon for chat notifications - // title: "New Group Message!", - // message: "You have received a new message from one of your groups", - // priority: 2, // Use the maximum priority to ensure it's noticeable - - // }); - if (!isMobile) { - setTimeout(() => { - chrome.notifications.clear(notificationId); - }, 7000); - } - playNotificationSound(); - // audio.play(); + + LocalNotifications.schedule({ + notifications: [ + { + title: "New Group Message!", + body: "You have received a new message from one of your groups", + id: notificationId, + schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now + } + ] + }); + + lastGroupNotification = Date.now(); - // } + } } finally { if (!data || data?.length === 0) return; setChatHeads(dataWithUpdates); - // chrome.runtime.sendMessage( - // { - // action: "setChatHeads", - // payload: { - // data, - // }, - // } - // ); + } }; -export const checkThreads = async (bringBack) => { - try { - let myName = ""; - const userData = await getUserInfo(); - if (userData?.name) { - myName = userData.name; - } - let newAnnouncements = []; - let dataToBringBack = []; - const threadActivity = await getThreadActivity(); - if (!threadActivity) return null; - const selectedThreads = [ - ...threadActivity.createdThreads.slice(0, 2), - ...threadActivity.mostVisitedThreads.slice(0, 2), - ...threadActivity.recentThreads.slice(0, 2), - ]; - if (selectedThreads?.length === 0) return null; - const tempData = {}; - for (const thread of selectedThreads) { - try { - const identifier = `thmsg-${thread?.threadId}`; - const name = thread?.qortalName; - const endpoint = await getArbitraryEndpoint(); - const url = await createEndpoint( - `${endpoint}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=1&includemetadata=false&offset=${0}&reverse=true&prefix=true` - ); - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - const responseData = await response.json(); - - const latestMessage = responseData.filter( - (pub) => pub?.name !== myName - )[0]; - // const latestMessage = responseData[0] - - if (!latestMessage) { - continue; - } - - if ( - checkDifference(latestMessage.created) && - latestMessage.created > thread?.lastVisited && - (!thread?.lastNotified || thread?.lastNotified < thread?.created) - ) { - tempData[thread.threadId] = latestMessage.created; - newAnnouncements.push(thread); - } - if (latestMessage.created > thread?.lastVisited) { - dataToBringBack.push(thread); - } - } catch (error) { - conosle.log({ error }); - } - } - - if (bringBack) { - return dataToBringBack; - } - - const updateThreadWithLastNotified = { - ...threadActivity, - createdThreads: (threadActivity?.createdThreads || [])?.map((item) => { - if (tempData[item.threadId]) { - return { - ...item, - lastNotified: tempData[item.threadId], - }; - } else { - return item; - } - }), - mostVisitedThreads: (threadActivity?.mostVisitedThreads || [])?.map( - (item) => { - if (tempData[item.threadId]) { - return { - ...item, - lastNotified: tempData[item.threadId], - }; - } else { - return item; - } - } - ), - recentThreads: (threadActivity?.recentThreads || [])?.map((item) => { - if (tempData[item.threadId]) { - return { - ...item, - lastNotified: tempData[item.threadId], - }; - } else { - return item; - } - }), - }; - - const wallet = await getSaveWallet(); - const address = wallet.address0; - storeData(`threadactivity-${address}`, updateThreadWithLastNotified).catch(() => null); - if (newAnnouncements.length > 0) { - const notificationId = - "chat_notification_" + - Date.now() + - "_type=thread-post" + - `_data=${JSON.stringify(newAnnouncements[0])}`; - let isDisableNotifications = - (await getUserSettings({ key: "disable-push-notifications" })) || false; - if (!isDisableNotifications) { - // chrome.notifications.create(notificationId, { - // type: "basic", - // iconUrl: "qort.png", // Add an appropriate icon for chat notifications - // title: `New thread post!`, - // message: `New post in ${newAnnouncements[0]?.thread?.threadData?.title}`, - // priority: 2, // Use the maximum priority to ensure it's noticeable - - // }); - // if (!isMobile) { - // setTimeout(() => { - // chrome.notifications.clear(notificationId); - // }, 7000); - // } - playNotificationSound(); - } - } - const savedtimestampAfter = await getTimestampGroupAnnouncement(); - window.postMessage({ - action: "SET_GROUP_ANNOUNCEMENTS", - payload: savedtimestampAfter, - }, "*"); - } catch (error) { - } finally { - } -}; -export const checkNewMessages = async () => { - try { - let mutedGroups = (await getUserSettings({ key: "mutedGroups" })) || []; - if (!isArray(mutedGroups)) mutedGroups = []; - let myName = ""; - const userData = await getUserInfo(); - if (userData?.name) { - myName = userData.name; - } - - let newAnnouncements = []; - const activeData = (await getStoredData("active-groups-directs")) || { - groups: [], - directs: [], - }; - const groups = activeData?.groups; - if (!groups || groups?.length === 0) return; - const savedtimestamp = await getTimestampGroupAnnouncement(); - - await Promise.all( - groups.map(async (group) => { - try { - const identifier = `grp-${group.groupId}-anc-`; - const endpoint = await getArbitraryEndpoint(); - const url = await createEndpoint( - `${endpoint}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=1&includemetadata=false&offset=0&reverse=true&prefix=true` - ); - const response = await requestQueueAnnouncements.enqueue(() => { - return fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - }); - const responseData = await response.json(); - - const latestMessage = responseData.filter( - (pub) => pub?.name !== myName - )[0]; - if (!latestMessage) { - return; // continue to the next group - } - - if ( - checkDifference(latestMessage.created) && - (!savedtimestamp[group.groupId] || - latestMessage.created > - savedtimestamp?.[group.groupId]?.notification) - ) { - newAnnouncements.push(group); - await addTimestampGroupAnnouncement({ - groupId: group.groupId, - timestamp: Date.now(), - }); - // save new timestamp - } - } catch (error) { - console.error(error); // Handle error if needed - } - }) - ); - let isDisableNotifications = - (await getUserSettings({ key: "disable-push-notifications" })) || false; - - if ( - newAnnouncements.length > 0 && - !mutedGroups.includes(newAnnouncements[0]?.groupId) && - !isDisableNotifications - ) { - const notificationId = - "chat_notification_" + - Date.now() + - "_type=group-announcement" + - `_from=${newAnnouncements[0]?.groupId}`; - - // chrome.notifications.create(notificationId, { - // type: "basic", - // iconUrl: "qort.png", // Add an appropriate icon for chat notifications - // title: `New group announcement!`, - // message: `You have received a new announcement from ${newAnnouncements[0]?.groupName}`, - // priority: 2, // Use the maximum priority to ensure it's noticeable - - // }); - // if (!isMobile) { - // setTimeout(() => { - // chrome.notifications.clear(notificationId); - // }, 7000); - // } - playNotificationSound(); - } - const savedtimestampAfter = await getTimestampGroupAnnouncement(); - window.postMessage({ - action: "SET_GROUP_ANNOUNCEMENTS", - payload: savedtimestampAfter, - }, "*"); - - } catch (error) { - } finally { - } -}; const listenForNewGroupAnnouncements = async () => { try { @@ -3146,10 +2878,299 @@ const checkGroupList = async () => { }; +export const checkNewMessages = async () => { + try { + let mutedGroups = await getUserSettings({key: 'mutedGroups'}) || [] + if(!isArray(mutedGroups)) mutedGroups = [] + let myName = ""; + const userData = await getUserInfo(); + if (userData?.name) { + myName = userData.name; + } -// Reconnect when service worker wakes up + let newAnnouncements = []; + const activeData = (await getStoredData("active-groups-directs")) || { + groups: [], + directs: [], + }; + const groups = activeData?.groups; + if (!groups || groups?.length === 0) return; + const savedtimestamp = await getTimestampGroupAnnouncement(); + + await Promise.all( + groups.map(async (group) => { + try { + const identifier = `grp-${group.groupId}-anc-`; + const endpoint = await getArbitraryEndpoint(); + const url = await createEndpoint( + `${endpoint}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=1&includemetadata=false&offset=0&reverse=true&prefix=true` + ); + const response = await requestQueueAnnouncements.enqueue(() => { + return fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + }); + const responseData = await response.json(); + + const latestMessage = responseData.filter( + (pub) => pub?.name !== myName + )[0]; + if (!latestMessage) { + return; // continue to the next group + } + + if ( + checkDifference(latestMessage.created) && + (!savedtimestamp[group.groupId] || + latestMessage.created > + savedtimestamp?.[group.groupId]?.notification) + ) { + newAnnouncements.push(group); + await addTimestampGroupAnnouncement({ + groupId: group.groupId, + timestamp: Date.now(), + }); + // save new timestamp + } + } catch (error) { + console.error(error); // Handle error if needed + } + }) + ); + let isDisableNotifications = await getUserSettings({key: 'disable-push-notifications'}) || false + + if (newAnnouncements.length > 0 && !mutedGroups.includes(newAnnouncements[0]?.groupId) && !isDisableNotifications) { + const notificationId = + "chat_notification_" + + Date.now() + + "_type=group-announcement" + + `_from=${newAnnouncements[0]?.groupId}`; + + + LocalNotifications.schedule({ + notifications: [ + { + title: "New group announcement!", + body: `You have received a new announcement from ${newAnnouncements[0]?.groupName}`, + id: notificationId, + schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now + } + ] + }); + } + const savedtimestampAfter = await getTimestampGroupAnnouncement(); + window.postMessage({ + action: "SET_GROUP_ANNOUNCEMENTS", + payload: savedtimestampAfter, + }, "*"); + } catch (error) { + } finally { + } +}; + +const checkActiveChatsForNotifications = async () => { + try { + + checkGroupList(); + + + + } catch (error) {} +}; + +export const checkThreads = async (bringBack) => { + try { + let myName = ""; + const userData = await getUserInfo(); + if (userData?.name) { + myName = userData.name; + } + let newAnnouncements = []; + let dataToBringBack = []; + const threadActivity = await getThreadActivity(); + if (!threadActivity) return null; + + const selectedThreads = [ + ...threadActivity.createdThreads.slice(0, 2), + ...threadActivity.mostVisitedThreads.slice(0, 2), + ...threadActivity.recentThreads.slice(0, 2), + ]; + + if (selectedThreads?.length === 0) return null; + const tempData = {}; + for (const thread of selectedThreads) { + try { + const identifier = `thmsg-${thread?.threadId}`; + const name = thread?.qortalName; + const endpoint = await getArbitraryEndpoint(); + const url = await createEndpoint( + `${endpoint}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=1&includemetadata=false&offset=${0}&reverse=true&prefix=true` + ); + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + const responseData = await response.json(); + + const latestMessage = responseData.filter( + (pub) => pub?.name !== myName + )[0]; + // const latestMessage = responseData[0] + + if (!latestMessage) { + continue; + } + + if ( + checkDifference(latestMessage.created) && + latestMessage.created > thread?.lastVisited && + (!thread?.lastNotified || thread?.lastNotified < thread?.created) + ) { + tempData[thread.threadId] = latestMessage.created; + newAnnouncements.push(thread); + } + if (latestMessage.created > thread?.lastVisited) { + dataToBringBack.push(thread); + } + } catch (error) { + conosle.log({ error }); + } + } + + if (bringBack) { + return dataToBringBack; + } + + const updateThreadWithLastNotified = { + ...threadActivity, + createdThreads: (threadActivity?.createdThreads || [])?.map((item) => { + if (tempData[item.threadId]) { + return { + ...item, + lastNotified: tempData[item.threadId], + }; + } else { + return item; + } + }), + mostVisitedThreads: (threadActivity?.mostVisitedThreads || [])?.map( + (item) => { + if (tempData[item.threadId]) { + return { + ...item, + lastNotified: tempData[item.threadId], + }; + } else { + return item; + } + } + ), + recentThreads: (threadActivity?.recentThreads || [])?.map((item) => { + if (tempData[item.threadId]) { + return { + ...item, + lastNotified: tempData[item.threadId], + }; + } else { + return item; + } + }), + }; + + const wallet = await getSaveWallet(); + const address = wallet.address0; + const dataString = JSON.stringify(updateThreadWithLastNotified); + chrome.storage.local.set({ [`threadactivity-${address}`]: dataString }); + + if (newAnnouncements.length > 0) { + const notificationId = + "chat_notification_" + + Date.now() + + "_type=thread-post" + + `_data=${JSON.stringify(newAnnouncements[0])}`; + let isDisableNotifications = await getUserSettings({key: 'disable-push-notifications'}) || false + if(!isDisableNotifications){ + + LocalNotifications.schedule({ + notifications: [ + { + title: `New thread post!`, + body: `New post in ${newAnnouncements[0]?.thread?.threadData?.title}`, + id: notificationId, + schedule: { at: new Date(Date.now() + 1000) }, // 1 second from now + } + ] + }); + } + + } + const savedtimestampAfter = await getTimestampGroupAnnouncement(); + window.postMessage({ + action: "SET_GROUP_ANNOUNCEMENTS", + payload: savedtimestampAfter, + }, "*"); + } catch (error) { + } finally { + } +}; + +// Configure Background Fetch +BackgroundFetch.configure({ + minimumFetchInterval: 15, // Minimum 15-minute interval + enableHeadless: true, // Enable headless mode for Android +}, async (taskId) => { + // This is where your background task logic goes + const wallet = await getSaveWallet(); + const address = wallet.address0; + console.log('alarm', address) + if (!address) return; + checkActiveChatsForNotifications(); + checkNewMessages(); + checkThreads(); + + await new Promise((res)=> { + setTimeout(() => { + res() + }, 55000); + }) + // Always finish the task when complete + BackgroundFetch.finish(taskId); +}, (taskId) => { + // Optional timeout callback + BackgroundFetch.finish(taskId); +}); +LocalNotifications.addListener('localNotificationActionPerformed', async (notification) => { + const notificationId = notification.notification.id; + // Check the type of notification by parsing notificationId + const isDirect = notificationId.includes('_type=direct_'); + const isGroup = notificationId.includes('_type=group_'); + const isGroupAnnouncement = notificationId.includes('_type=group-announcement_'); + const isNewThreadPost = notificationId.includes('_type=thread-post_'); + + + // Handle specific notification types + if (isDirect) { + const fromValue = notificationId.split('_from=')[1]; + handleDirectNotification(fromValue); + } else if (isGroup) { + const fromValue = notificationId.split('_from=')[1]; + handleGroupNotification(fromValue); + } else if (isGroupAnnouncement) { + const fromValue = notificationId.split('_from=')[1]; + handleAnnouncementNotification(fromValue); + } else if (isNewThreadPost) { + const dataValue = notificationId.split('_data=')[1]; + const dataParsed = JSON.parse(dataValue); + handleThreadPostNotification(dataParsed); + } +});