diff --git a/capacitor.config.ts b/capacitor.config.ts index 4a16982..5b646fd 100644 --- a/capacitor.config.ts +++ b/capacitor.config.ts @@ -17,6 +17,9 @@ const config: CapacitorConfig = { "androidSpinnerStyle": "large", "splashFullScreen": true, "splashImmersive": true + }, + CapacitorHttp: { + enabled: true, } } }; diff --git a/src/background-cases.ts b/src/background-cases.ts index aae6e03..8136316 100644 --- a/src/background-cases.ts +++ b/src/background-cases.ts @@ -1,5 +1,6 @@ import { addDataPublishes, + addEnteredQmailTimestamp, addTimestampEnterChat, addTimestampGroupAnnouncement, addUserSettings, @@ -20,6 +21,7 @@ import { getBalanceInfo, getCustomNodesFromStorage, getDataPublishes, + getEnteredQmailTimestamp, getGroupDataSingle, getKeyPair, getLTCBalance, @@ -1692,4 +1694,55 @@ export async function publishGroupEncryptedResourceCase(request, event) { event.origin ); } + } + + export async function addEnteredQmailTimestampCase(request, event) { + try { + const response = await addEnteredQmailTimestamp(); + + event.source.postMessage( + { + requestId: request.requestId, + action: "addEnteredQmailTimestamp", + payload: response, + type: "backgroundMessageResponse", + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: "addEnteredQmailTimestamp", + error: error?.message, + type: "backgroundMessageResponse", + }, + event.origin + ); + } + } + export async function getEnteredQmailTimestampCase(request, event) { + try { + const response = await getEnteredQmailTimestamp(); + + event.source.postMessage( + { + requestId: request.requestId, + action: "getEnteredQmailTimestamp", + payload: {timestamp: response}, + type: "backgroundMessageResponse", + }, + event.origin + ); + } catch (error) { + event.source.postMessage( + { + requestId: request.requestId, + action: "getEnteredQmailTimestamp", + error: error?.message, + type: "backgroundMessageResponse", + }, + event.origin + ); + } } \ No newline at end of file diff --git a/src/background.ts b/src/background.ts index 5e65d8c..5b61c5d 100644 --- a/src/background.ts +++ b/src/background.ts @@ -33,6 +33,7 @@ import { TradeBotRespondMultipleRequest } from "./transactions/TradeBotRespondMu import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from "./constants/resourceTypes"; import { addDataPublishesCase, + addEnteredQmailTimestampCase, addGroupNotificationTimestampCase, addTimestampEnterChatCase, addUserSettingsCase, @@ -53,6 +54,7 @@ import { getApiKeyCase, getCustomNodesFromStorageCase, getDataPublishesCase, + getEnteredQmailTimestampCase, getGroupDataSingleCase, getGroupNotificationTimestampCase, getTempPublishCase, @@ -2494,6 +2496,32 @@ export async function addTimestampGroupAnnouncement({ }); } +export async function addEnteredQmailTimestamp() { + const wallet = await getSaveWallet(); + const address = wallet.address0; + + return await new Promise((resolve, reject) => { + storeData(`qmail-entered-timestamp-${address}`, Date.now()) + .then(() => resolve(true)) + .catch((error) => { + reject(new Error(error.message || "Error saving data")); + }); + }); +} + +export async function getEnteredQmailTimestamp() { + const wallet = await getSaveWallet(); + const address = wallet.address0; + const key = `qmail-entered-timestamp-${address}`; + const res = await getData(key).catch(() => null); + if (res) { + const parsedData = res; + return parsedData; + } else { + return null + } +} + async function getGroupData() { const wallet = await getSaveWallet(); const address = wallet.address0; @@ -2788,6 +2816,12 @@ function setupMessageListener() { case "setupGroupWebsocket": setupGroupWebsocketCase(request, event); break; + case "addEnteredQmailTimestamp": + addEnteredQmailTimestampCase(request, event); + break; + case "getEnteredQmailTimestamp": + getEnteredQmailTimestampCase(request, event); + break; case "logout": { try { diff --git a/src/components/Apps/useQortalMessageListener.tsx b/src/components/Apps/useQortalMessageListener.tsx index fbd90fe..37e54f6 100644 --- a/src/components/Apps/useQortalMessageListener.tsx +++ b/src/components/Apps/useQortalMessageListener.tsx @@ -508,6 +508,11 @@ isDOMContentLoaded: false event?.data?.action === 'QDN_RESOURCE_DISPLAYED'){ const pathUrl = event?.data?.path != null ? (event?.data?.path.startsWith('/') ? '' : '/') + event?.data?.path : null setPath(pathUrl) + if(appName.toLowerCase() === 'q-mail'){ + window.sendMessage("addEnteredQmailTimestamp").catch((error) => { + // error + }); + } } else if(event?.data?.action === 'NAVIGATION_HISTORY'){ if(event?.data?.payload?.isDOMContentLoaded){ setHistory((prev)=> { diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index b1637f9..70c25c6 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -351,7 +351,6 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, } } socketRef.current.onerror = (e) => { - console.error('WebSocket error:', error); clearTimeout(groupSocketTimeoutRef.current); clearTimeout(timeoutIdRef.current); if (socketRef.current) { diff --git a/src/components/Group/Home.tsx b/src/components/Group/Home.tsx index 1324528..c114efe 100644 --- a/src/components/Group/Home.tsx +++ b/src/components/Group/Home.tsx @@ -84,6 +84,8 @@ export const Home = ({ myAddress={myAddress} name={userInfo?.name} hasGroups={groups?.length !== 0} + userInfo={userInfo} + /> diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx new file mode 100644 index 0000000..a8c7422 --- /dev/null +++ b/src/components/Group/QMailMessages.tsx @@ -0,0 +1,250 @@ +import React, { useCallback, useEffect, useState } from 'react' +import List from "@mui/material/List"; +import ListItem from "@mui/material/ListItem"; +import ListItemButton from "@mui/material/ListItemButton"; +import ListItemIcon from "@mui/material/ListItemIcon"; +import ListItemText from "@mui/material/ListItemText"; +import moment from 'moment' +import { Box, Typography } from "@mui/material"; +import { Spacer } from "../../common/Spacer"; +import { getBaseApiReact, isMobile } from "../../App"; +import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; +import MailIcon from '@mui/icons-material/Mail'; +import MailOutlineIcon from '@mui/icons-material/MailOutline'; +import { executeEvent } from '../../utils/events'; +import { CustomLoader } from '../../common/CustomLoader'; +const isLessThanOneWeekOld = (timestamp) => { + // Current time in milliseconds + const now = Date.now(); + + // One week ago in milliseconds (7 days * 24 hours * 60 minutes * 60 seconds * 1000 milliseconds) + const oneWeekAgo = now - (7 * 24 * 60 * 60 * 1000); + + // Check if the timestamp is newer than one week ago + return timestamp > oneWeekAgo; +}; +export function formatEmailDate(timestamp: number) { + const date = moment(timestamp); + const now = moment(); + + if (date.isSame(now, 'day')) { + // If the email was received today, show the time + return date.format('h:mm A'); + } else if (date.isSame(now, 'year')) { + // If the email was received this year, show the month and day + return date.format('MMM D'); + } else { + // For older emails, show the full date + return date.format('MMM D, YYYY'); + } +} +export const QMailMessages = ({userName, userAddress}) => { + const [mails, setMails] = useState([]) + const [lastEnteredTimestamp, setLastEnteredTimestamp] = useState(null) + const [loading, setLoading] = useState(true) + + console.log('lastEnteredTimestamp', lastEnteredTimestamp) + const getMails = useCallback(async () => { + try { + setLoading(true) + const query = `qortal_qmail_${userName.slice( + 0, + 20 + )}_${userAddress.slice(-6)}_mail_` + const response = await fetch(`${getBaseApiReact()}/arbitrary/resources/search?service=MAIL_PRIVATE&query=${query}&limit=10&includemetadata=false&offset=0&reverse=true&excludeblocked=true&mode=ALL`); + const mailData = await response.json(); + + + setMails(mailData); + } catch (error) { + console.error(error); + } finally { + setLoading(false) + + } + }, []) + + const getTimestamp = async () => { + try { + return new Promise((res, rej) => { + window.sendMessage("getEnteredQmailTimestamp") + .then((response) => { + if (!response?.error) { + if(response?.timestamp){ + setLastEnteredTimestamp(response?.timestamp) + } + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || "An error occurred"); + }); + + }); + } catch (error) {} + }; + + useEffect(() => { + getTimestamp() + if(!userName || !userAddress) return + getMails(); + + const interval = setInterval(() => { + getTimestamp() + getMails(); + }, 300000); + + return () => clearInterval(interval); + + }, [getMails, userName, userAddress]); + + + + console.log('mails', mails) + return ( + + + + + Latest Q-Mails + + + + + + {loading && mails.length === 0 && ( + + + + )} + {!loading && mails.length === 0 && ( + + + Nothing to display + + + )} + + + {mails?.map((mail)=> { + return ( + { + executeEvent("addTab", { data: { service: 'APP', name: 'q-mail' } }); + executeEvent("open-apps-mode", { }); + }} + > + + + + {!lastEnteredTimestamp && isLessThanOneWeekOld(mail?.created) ? ( + + ) : !lastEnteredTimestamp ? ( + + ): lastEnteredTimestamp < mail?.created ? ( + + ) : ( + + ) + } + + + + + + + ) + })} + + + + + + + + ) +} diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index ff52f35..b18c924 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -11,8 +11,9 @@ import InfoIcon from "@mui/icons-material/Info"; import { Box, Typography } from "@mui/material"; import { Spacer } from "../../common/Spacer"; import { isMobile } from "../../App"; +import { QMailMessages } from "./QMailMessages"; -export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance }) => { +export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance , userInfo}) => { const [checked1, setChecked1] = React.useState(false); const [checked2, setChecked2] = React.useState(false); const [checked3, setChecked3] = React.useState(false); @@ -47,6 +48,22 @@ export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance }) => { if (name) setChecked2(true); }, [name]); + const isLoaded = React.useMemo(()=> { + if(balance !== null && userInfo !== null) return true + return false +}, [balance, userInfo]) + +const hasDoneNameAndBalanceAndIsLoaded = React.useMemo(()=> { + if(isLoaded && checked1 && checked2) return true +return false +}, [checked1, isLoaded, checked2]) + +if(hasDoneNameAndBalanceAndIsLoaded){ +return ( + +); +} + return ( { fontWeight: 600, }} > - Getting Started: + {!isLoaded ? 'Loading...' : 'Getting Started' } +