diff --git a/src/MessageQueueContext.tsx b/src/MessageQueueContext.tsx index ac822cf..499b10b 100644 --- a/src/MessageQueueContext.tsx +++ b/src/MessageQueueContext.tsx @@ -10,14 +10,16 @@ const uid = new ShortUniqueId({ length: 8 }); export const MessageQueueProvider = ({ children }) => { const messageQueueRef = useRef([]); const [queueChats, setQueueChats] = useState({}); // Stores chats and status for display - const isProcessingRef = useRef(false); // To track if the queue is being processed const maxRetries = 2; + const clearStatesMessageQueueProvider = useCallback(() => { setQueueChats({}); messageQueueRef.current = []; - isProcessingRef.current = false; }, []); + // Promise-based lock to prevent concurrent executions + const processingPromiseRef = useRef(Promise.resolve()); + // Function to add a message to the queue const addToQueue = useCallback((sendMessageFunc, messageObj, type, groupDirectId) => { const tempId = uid.rnd(); @@ -25,8 +27,8 @@ export const MessageQueueProvider = ({ children }) => { ...messageObj, type, groupDirectId, + signature: uid.rnd(), identifier: tempId, - signature: uid.rnd(), retries: 0, // Retry count for display purposes status: 'pending' // Initial status is 'pending' }; @@ -37,59 +39,63 @@ export const MessageQueueProvider = ({ children }) => { [groupDirectId]: [...(prev[groupDirectId] || []), chatData] })); - // Add the message to the global messageQueueRef.current - messageQueueRef.current = [ - ...messageQueueRef.current, - { func: sendMessageFunc, identifier: tempId, groupDirectId, specialId: messageObj?.message?.specialId } - ]; + // Add the message to the global messageQueueRef + messageQueueRef.current.push({ + func: sendMessageFunc, + identifier: tempId, + groupDirectId, + specialId: messageObj?.message?.specialId + }); - // Start processing the queue if not already processing + // Start processing the queue processQueue([], groupDirectId); }, []); - // Method to process with new messages and groupDirectId - const processWithNewMessages = (newMessages, groupDirectId) => { - processQueue(newMessages, groupDirectId); - }; + // Function to process the message queue + const processQueue = useCallback((newMessages = [], groupDirectId) => { + processingPromiseRef.current = processingPromiseRef.current + .then(() => processQueueInternal(newMessages, groupDirectId)) + .catch((err) => console.error('Error in processQueue:', err)); + }, []); - // Function to process the messageQueueRef.current and handle new messages - const processQueue = useCallback(async (newMessages = [], groupDirectId) => { - // Filter out any message in the queue that matches the specialId from newMessages - messageQueueRef.current = messageQueueRef.current.filter((msg) => { - return !newMessages.some(newMsg => newMsg?.specialId === msg?.specialId); - }); + // Internal function to handle queue processing + const processQueueInternal = async (newMessages, groupDirectId) => { + // Remove any messages from the queue that match the specialId from newMessages + if (newMessages.length > 0) { + messageQueueRef.current = messageQueueRef.current.filter((msg) => { + return !newMessages.some(newMsg => newMsg?.specialId === msg?.specialId); + }); - // Remove any corresponding entries in queueChats for the provided groupDirectId - setQueueChats((prev) => { - const updatedChats = { ...prev }; - if (updatedChats[groupDirectId]) { - // Remove any message in queueChats that has a matching specialId - updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => { - - return !newMessages.some(newMsg => newMsg?.specialId === chat?.message?.specialId); - }); + // Remove corresponding entries in queueChats for the provided groupDirectId + setQueueChats((prev) => { + const updatedChats = { ...prev }; + if (updatedChats[groupDirectId]) { + updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => { + return !newMessages.some(newMsg => newMsg?.specialId === chat?.message?.specialId); + }); - updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => { - return chat?.status !== 'failed-permanent' - }); + // Remove messages with status 'failed-permanent' + updatedChats[groupDirectId] = updatedChats[groupDirectId].filter((chat) => { + return chat?.status !== 'failed-permanent'; + }); - // If no more chats for this group, delete the groupDirectId entry - if (updatedChats[groupDirectId].length === 0) { - delete updatedChats[groupDirectId]; + // If no more chats for this group, delete the groupDirectId entry + if (updatedChats[groupDirectId].length === 0) { + delete updatedChats[groupDirectId]; + } } - } - return updatedChats; - }); + return updatedChats; + }); + } - // If currently processing or the queue is empty, return - if (isProcessingRef.current || messageQueueRef.current.length === 0) return; - - isProcessingRef.current = true; // Lock the queue for processing - + // If the queue is empty, no need to process + if (messageQueueRef.current.length === 0) return; + + // Process messages sequentially while (messageQueueRef.current.length > 0) { const currentMessage = messageQueueRef.current[0]; // Get the first message in the queue const { groupDirectId, identifier } = currentMessage; - + // Update the chat status to 'sending' setQueueChats((prev) => { const updatedChats = { ...prev }; @@ -103,24 +109,33 @@ export const MessageQueueProvider = ({ children }) => { } return updatedChats; }); - + try { - // Execute the function stored in the messageQueueRef.current + // Execute the function stored in the messageQueueRef await currentMessage.func(); - // Remove the message from the messageQueueRef.current after successful sending - messageQueueRef.current.shift(); // Slice here remains for successful messages - - // Remove the message from queueChats after success - // setQueueChats((prev) => { - // const updatedChats = { ...prev }; - // updatedChats[groupDirectId] = updatedChats[groupDirectId].filter( - // (item) => item.identifier !== identifier - // ); - // return updatedChats; - // }); + + // Remove the message from the queue after successful sending + messageQueueRef.current.shift(); + + // Remove the message from queueChats + setQueueChats((prev) => { + const updatedChats = { ...prev }; + if (updatedChats[groupDirectId]) { + updatedChats[groupDirectId] = updatedChats[groupDirectId].filter( + (item) => item.identifier !== identifier + ); + + // If no more chats for this group, delete the groupDirectId entry + if (updatedChats[groupDirectId].length === 0) { + delete updatedChats[groupDirectId]; + } + } + return updatedChats; + }); + } catch (error) { console.error('Message sending failed', error); - + // Retry logic setQueueChats((prev) => { const updatedChats = { ...prev }; @@ -136,28 +151,25 @@ export const MessageQueueProvider = ({ children }) => { } else { // Max retries reached, set status to 'failed-permanent' updatedChats[groupDirectId][chatIndex].status = 'failed-permanent'; - - // Remove the message from the messageQueueRef.current after max retries - messageQueueRef.current.shift();// Slice for failed messages after max retries - - // // Remove the message from queueChats after failure - // updatedChats[groupDirectId] = updatedChats[groupDirectId].filter( - // (item) => item.identifier !== identifier - // ); + + // Remove the message from the queue after max retries + messageQueueRef.current.shift(); } } return updatedChats; }); - } - - // Delay between processing each message to avoid overlap - await new Promise((res) => setTimeout(res, 5000)); + } + + // Optional delay between processing messages + // await new Promise((res) => setTimeout(res, 5000)); } - - // Reset the processing lock once all messages are processed - isProcessingRef.current = false; - }, []); - + }; + + // Method to process with new messages and groupDirectId + const processWithNewMessages = (newMessages, groupDirectId) => { + processQueue(newMessages, groupDirectId); + }; + return ( {children} diff --git a/src/background.ts b/src/background.ts index bbc81f5..1c651cb 100644 --- a/src/background.ts +++ b/src/background.ts @@ -100,6 +100,7 @@ import { import { getData, removeKeysAndLogout, storeData } from "./utils/chromeStorage"; import {BackgroundFetch} from '@transistorsoft/capacitor-background-fetch'; import { LocalNotifications } from '@capacitor/local-notifications'; +import { executeEvent } from "./utils/events"; const uid = new ShortUniqueId({ length: 9, dictionary: 'number' }); @@ -3255,7 +3256,10 @@ LocalNotifications.addListener('localNotificationActionPerformed', async (event) const initializeBackButton = () => { CapacitorApp.addListener('backButton', (event) => { + console.log('event', event) // Prevent the app from closing on back button press + executeEvent("handleMobileNativeBack", { + }); event.preventDefault(); }); diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index ed60ea7..32a73e9 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -92,6 +92,7 @@ import { Apps } from "../Apps/Apps"; import { AppsNavBar } from "../Apps/AppsNavBar"; import { AppsDesktop } from "../Apps/AppsDesktop"; import { formatEmailDate } from "./QMailMessages"; +import { useHandleMobileNativeBack } from "../../hooks/useHandleMobileNativeBack"; // let touchStartY = 0; // let disablePullToRefresh = false; @@ -380,7 +381,6 @@ export const Group = ({ desktopViewMode }: GroupProps) => { const [desktopSideView, setDesktopSideView] = useState('groups') - const [secretKey, setSecretKey] = useState(null); const [secretKeyPublishDate, setSecretKeyPublishDate] = useState(null); const lastFetchedSecretKey = useRef(null); @@ -1535,6 +1535,10 @@ export const Group = ({ setIsOpenSideViewGroups(false) }; + + useHandleMobileNativeBack({mobileViewMode, goToHome, setMobileViewMode}) + + const goToAnnouncements = async () => { setGroupSection("default"); await new Promise((res) => { diff --git a/src/hooks/useHandleMobileNativeBack.tsx b/src/hooks/useHandleMobileNativeBack.tsx new file mode 100644 index 0000000..370e8e0 --- /dev/null +++ b/src/hooks/useHandleMobileNativeBack.tsx @@ -0,0 +1,23 @@ +import React, { useCallback, useEffect } from 'react' +import { subscribeToEvent, unsubscribeFromEvent } from '../utils/events'; + +export const useHandleMobileNativeBack = ({mobileViewMode, goToHome, setMobileViewMode}) => { + + + const handleMobileNativeBackFunc = useCallback((e) => { + if(mobileViewMode === 'groups'){ + goToHome() + } else if(mobileViewMode === "group"){ + setMobileViewMode('groups') + } + }, [mobileViewMode]); + + useEffect(() => { + subscribeToEvent("handleMobileNativeBack", handleMobileNativeBackFunc); + + return () => { + unsubscribeFromEvent("handleMobileNativeBack", handleMobileNativeBackFunc); + }; + }, [handleMobileNativeBackFunc]); + return null +} diff --git a/src/qdn/encryption/group-encryption.ts b/src/qdn/encryption/group-encryption.ts index 4f61dd8..1c16763 100644 --- a/src/qdn/encryption/group-encryption.ts +++ b/src/qdn/encryption/group-encryption.ts @@ -193,7 +193,7 @@ export const decodeBase64ForUIChatMessages = (messages)=> { for(const msg of messages){ try { const decoded = atob(msg?.data); - const parseDecoded = JSON.parse(decoded) + const parseDecoded =JSON.parse(decodeURIComponent(escape(decoded))) if(parseDecoded?.messageText){ msgs.push({ ...msg,