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 0000000..39d090f Binary files /dev/null and b/android/app/src/main/res/drawable/qort.png differ diff --git a/android/build.gradle b/android/build.gradle index 85a5dda..7e1c3f3 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -21,6 +21,10 @@ allprojects { repositories { google() mavenCentral() + maven { + // capacitor-background-fetch + url("${project(':transistorsoft-capacitor-background-fetch').projectDir}/libs") + } } } diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index 6835b8c..c72458d 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -8,5 +8,11 @@ project(':capacitor-browser').projectDir = new File('../node_modules/@capacitor/ include ':capacitor-filesystem' project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android') +include ':capacitor-local-notifications' +project(':capacitor-local-notifications').projectDir = new File('../node_modules/@capacitor/local-notifications/android') + include ':evva-capacitor-secure-storage-plugin' project(':evva-capacitor-secure-storage-plugin').projectDir = new File('../node_modules/@evva/capacitor-secure-storage-plugin/android') + +include ':transistorsoft-capacitor-background-fetch' +project(':transistorsoft-capacitor-background-fetch').projectDir = new File('../node_modules/@transistorsoft/capacitor-background-fetch/android') diff --git a/capacitor.config.ts b/capacitor.config.ts index c52b4e7..d64b8cd 100644 --- a/capacitor.config.ts +++ b/capacitor.config.ts @@ -3,7 +3,13 @@ import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.example.app', appName: 'Qortal ', - webDir: 'dist' + webDir: 'dist', + "plugins": { + "LocalNotifications": { + "smallIcon": "qort", + "iconColor": "#09b6e8" + } + } }; export default config; diff --git a/package-lock.json b/package-lock.json index 720f615..5258f9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@capacitor/cli": "^6.1.2", "@capacitor/core": "^6.1.2", "@capacitor/filesystem": "^6.0.1", + "@capacitor/local-notifications": "^6.1.0", "@chatscope/chat-ui-kit-react": "^2.0.3", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", @@ -35,6 +36,7 @@ "@tiptap/pm": "^2.5.9", "@tiptap/react": "^2.5.9", "@tiptap/starter-kit": "^2.5.9", + "@transistorsoft/capacitor-background-fetch": "^6.0.1", "@types/chrome": "^0.0.263", "asmcrypto.js": "2.3.2", "bcryptjs": "2.4.3", @@ -515,6 +517,14 @@ "@capacitor/core": "^6.0.0" } }, + "node_modules/@capacitor/local-notifications": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@capacitor/local-notifications/-/local-notifications-6.1.0.tgz", + "integrity": "sha512-EjEnbApdFiZYeJgxKIDtgFRNSmeHySnyAeyoTn6HUTYMkTOaHFGAd7NU964k7gbX4al/CBp0cMA8iMG0c7kh1w==", + "peerDependencies": { + "@capacitor/core": "^6.0.0" + } + }, "node_modules/@chatscope/chat-ui-kit-react": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@chatscope/chat-ui-kit-react/-/chat-ui-kit-react-2.0.3.tgz", @@ -3041,6 +3051,14 @@ "url": "https://github.com/sponsors/ueberdosis" } }, + "node_modules/@transistorsoft/capacitor-background-fetch": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@transistorsoft/capacitor-background-fetch/-/capacitor-background-fetch-6.0.1.tgz", + "integrity": "sha512-/OyQ4IVu1clWsqt0eKYAX8Ynj1X1Asw5HM94HyH1il3jL5lXe6CLmRFqicXDxabYge+ACXfVLim/P2EovQ1IIg==", + "peerDependencies": { + "@capacitor/core": "^6.0.0" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", diff --git a/package.json b/package.json index e8da587..cf6d2c6 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@capacitor/cli": "^6.1.2", "@capacitor/core": "^6.1.2", "@capacitor/filesystem": "^6.0.1", + "@capacitor/local-notifications": "^6.1.0", "@chatscope/chat-ui-kit-react": "^2.0.3", "@dnd-kit/core": "^6.1.0", "@dnd-kit/sortable": "^8.0.0", @@ -39,6 +40,7 @@ "@tiptap/pm": "^2.5.9", "@tiptap/react": "^2.5.9", "@tiptap/starter-kit": "^2.5.9", + "@transistorsoft/capacitor-background-fetch": "^6.0.1", "@types/chrome": "^0.0.263", "asmcrypto.js": "2.3.2", "bcryptjs": "2.4.3", diff --git a/src/App.tsx b/src/App.tsx index 3d87362..6142e9d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -695,20 +695,18 @@ function App() { executeEvent("openDirectMessage", { from: message.payload.from, }); - } else if (message.action === "NOTIFICATION_OPEN_GROUP" && isMainWindow) { + } else if (message.action === "NOTIFICATION_OPEN_GROUP") { executeEvent("openGroupMessage", { from: message.payload.from, }); } else if ( - message.action === "NOTIFICATION_OPEN_ANNOUNCEMENT_GROUP" && - isMainWindow + message.action === "NOTIFICATION_OPEN_ANNOUNCEMENT_GROUP" ) { executeEvent("openGroupAnnouncement", { from: message.payload.from, }); } else if ( - message.action === "NOTIFICATION_OPEN_THREAD_NEW_POST" && - isMainWindow + message.action === "NOTIFICATION_OPEN_THREAD_NEW_POST" ) { executeEvent("openThreadNewPost", { data: message.payload.data, diff --git a/src/background.ts b/src/background.ts index f45e376..2c08196 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,5 +1,5 @@ // @ts-nocheck -// TODO + import "./qortalRequests"; import { isArray } from "lodash"; import { @@ -88,6 +88,16 @@ import { versionCase, } from "./background-cases"; import { getData, removeKeysAndLogout, storeData } from "./utils/chromeStorage"; +import {BackgroundFetch} from '@transistorsoft/capacitor-background-fetch'; +import { LocalNotifications } from '@capacitor/local-notifications'; + + +LocalNotifications.requestPermissions().then(permission => { + 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); + } +});