From 99f2ae1885608f307cfe043afd333d70d0b4e1bf Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 12 Sep 2024 05:01:36 +0300 Subject: [PATCH 01/25] mobile friendly --- public/content-script.js | 20 +- src/App.tsx | 521 +++--- src/background.ts | 14 +- .../Chat/AnnouncementDiscussion.tsx | 6 +- src/components/Chat/ChatDirect.tsx | 6 +- src/components/Chat/ChatGroup.tsx | 10 +- src/components/Chat/CreateCommonSecret.tsx | 4 +- src/components/Chat/GroupAnnouncements.tsx | 16 +- src/components/Chat/GroupForum.tsx | 3 +- src/components/Drawer/Drawer.tsx | 31 + src/components/Group/AddGroup.tsx | 2 +- src/components/Group/AddGroupList.tsx | 2 +- src/components/Group/Forum/GroupMail.tsx | 7 +- src/components/Group/Forum/NewThread.tsx | 4 +- src/components/Group/Forum/ReusableModal.tsx | 3 +- src/components/Group/Group.tsx | 1411 +++++++++-------- src/components/Group/InviteMember.tsx | 2 +- src/components/Group/ListOfBans.tsx | 2 +- src/components/Group/ListOfInvites.tsx | 2 +- src/components/Group/ListOfJoinRequests.tsx | 2 +- src/components/Group/ListOfMembers.tsx | 8 +- .../Group/ListOfThreadPostsWatched.tsx | 2 +- src/components/Group/ManageMembers.tsx | 2 +- src/components/Group/UserListOfInvites.tsx | 2 +- src/components/Group/WebsocketActive.tsx | 2 +- src/index.css | 4 + 26 files changed, 1185 insertions(+), 903 deletions(-) create mode 100644 src/components/Drawer/Drawer.tsx diff --git a/public/content-script.js b/public/content-script.js index fd2b902..28c717b 100644 --- a/public/content-script.js +++ b/public/content-script.js @@ -23,7 +23,7 @@ document.addEventListener('qortalExtensionRequests', async (event) => { })); return } - chrome.runtime.sendMessage({ action: "userInfo" }, (response) => { + chrome?.runtime?.sendMessage({ action: "userInfo" }, (response) => { if (response.error) { document.dispatchEvent(new CustomEvent('qortalExtensionResponses', { detail: { type: "USER_INFO", data: { @@ -38,7 +38,7 @@ document.addEventListener('qortalExtensionRequests', async (event) => { } }); } else if (type === 'REQUEST_IS_INSTALLED') { - chrome.runtime.sendMessage({ action: "version" }, (response) => { + chrome?.runtime?.sendMessage({ action: "version" }, (response) => { if (response.error) { console.error("Error:", response.error); } else { @@ -51,7 +51,7 @@ document.addEventListener('qortalExtensionRequests', async (event) => { } else if (type === 'REQUEST_CONNECTION') { console.log('REQUEST_CONNECTION') const hostname = window.location.hostname - chrome.runtime.sendMessage({ action: "connection", payload: { + chrome?.runtime?.sendMessage({ action: "connection", payload: { hostname }, timeout }, (response) => { if (response.error) { @@ -75,7 +75,7 @@ document.addEventListener('qortalExtensionRequests', async (event) => { return } - chrome.runtime.sendMessage({ action: "oauth", payload: { + chrome?.runtime?.sendMessage({ action: "oauth", payload: { nodeBaseUrl: payload.nodeBaseUrl, senderAddress: payload.senderAddress, senderPublicKey: payload.senderPublicKey, timestamp: payload.timestamp @@ -105,7 +105,7 @@ document.addEventListener('qortalExtensionRequests', async (event) => { return } - chrome.runtime.sendMessage({ action: "buyOrder", payload: { + chrome?.runtime?.sendMessage({ action: "buyOrder", payload: { qortalAtAddress: payload.qortalAtAddress, hostname @@ -136,7 +136,7 @@ document.addEventListener('qortalExtensionRequests', async (event) => { })); return } - chrome.runtime.sendMessage({ action: "ltcBalance", payload: { + chrome?.runtime?.sendMessage({ action: "ltcBalance", payload: { hostname }, timeout }, (response) => { @@ -164,7 +164,7 @@ document.addEventListener('qortalExtensionRequests', async (event) => { })); return } - chrome.runtime.sendMessage({ action: "authentication", payload: { + chrome?.runtime?.sendMessage({ action: "authentication", payload: { hostname }, timeout }, (response) => { if (response.error) { @@ -191,7 +191,7 @@ document.addEventListener('qortalExtensionRequests', async (event) => { })); return } - chrome.runtime.sendMessage({ action: "sendQort", payload: { + chrome?.runtime?.sendMessage({ action: "sendQort", payload: { hostname, amount: payload.amount, description: payload.description, @@ -221,7 +221,7 @@ document.addEventListener('qortalExtensionRequests', async (event) => { })); return } - chrome.runtime.sendMessage({ action: "closePopup" }, (response) => { + chrome?.runtime?.sendMessage({ action: "closePopup" }, (response) => { if (response.error) { document.dispatchEvent(new CustomEvent('qortalExtensionResponses', { @@ -241,7 +241,7 @@ document.addEventListener('qortalExtensionRequests', async (event) => { }); -chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { +chrome.runtime?.onMessage.addListener(function(message, sender, sendResponse) { if (message.type === "LOGOUT") { // Notify the web page window.postMessage({ diff --git a/src/App.tsx b/src/App.tsx index 940fa7c..f9d06b0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -41,6 +41,8 @@ import Logout from "./assets/svgs/Logout.svg"; import Return from "./assets/svgs/Return.svg"; import Success from "./assets/svgs/Success.svg"; import Info from "./assets/svgs/Info.svg"; +import CloseIcon from '@mui/icons-material/Close'; + import { createAccount, generateRandomSentence, @@ -67,7 +69,7 @@ import { Spacer } from "./common/Spacer"; import { Loader } from "./components/Loader"; import { PasswordField, ErrorText } from "./components"; import { ChatGroup } from "./components/Chat/ChatGroup"; -import { Group, requestQueueMemberNames } from "./components/Group/Group"; +import { Group, requestQueueMemberNames } from "./components/Group/Group"; import { TaskManger } from "./components/TaskManager/TaskManger"; import { useModal } from "./common/useModal"; import { LoadingButton } from "@mui/lab"; @@ -83,6 +85,7 @@ import { import { executeEvent } from "./utils/events"; import { requestQueueCommentCount, requestQueuePublishedAccouncements } from "./components/Chat/GroupAnnouncements"; import { requestQueueGroupJoinRequests } from "./components/Group/GroupJoinRequests"; +import { DrawerComponent } from "./components/Drawer/Drawer"; type extStates = | "not-authenticated" @@ -126,6 +129,28 @@ const defaultValues: MyContextInterface = { message: "", }, }; +export let isMobile = false + +const isMobileDevice = () => { + const userAgent = navigator.userAgent || navigator.vendor || window.opera; + + if (/android/i.test(userAgent)) { + return true; // Android device + } + + if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) { + return true; // iOS device + } + + return false; +}; + +if (isMobileDevice()) { + isMobile = true + console.log("Running on a mobile device"); +} else { + console.log("Running on a desktop"); +} export const allQueues = { requestQueueCommentCount: requestQueueCommentCount, @@ -160,7 +185,7 @@ export const clearAllQueues = () => { export const pauseAllQueues = () => { controlAllQueues('pause'); - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "pauseAllQueues", payload: { @@ -171,7 +196,7 @@ export const pauseAllQueues = () => { } export const resumeAllQueues = () => { controlAllQueues('resume'); - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "resumeAllQueues", payload: { @@ -266,11 +291,33 @@ function App() { const [openAdvancedSettings, setOpenAdvancedSettings] = useState(false); const [useLocalNode, setUseLocalNode] = useState(false); const [confirmUseOfLocal, setConfirmUseOfLocal] = useState(false); - + const [isOpenDrawerProfile, setIsOpenDrawerProfile] = useState(false); const [apiKey, setApiKey] = useState(""); useEffect(() => { - chrome.runtime.sendMessage({ action: "getApiKey" }, (response) => { + if(!isMobile) return + // Function to set the height of the app to the viewport height + const resetHeight = () => { + const height = window.visualViewport ? window.visualViewport.height : window.innerHeight; + // Set the height to the root element (usually #root) + document.getElementById('root').style.height = height + "px"; + }; + + // Set the initial height + resetHeight(); + + // Add event listeners for resize and visualViewport changes + window.addEventListener('resize', resetHeight); + window.visualViewport?.addEventListener('resize', resetHeight); + + // Clean up the event listeners when the component unmounts + return () => { + window.removeEventListener('resize', resetHeight); + window.visualViewport?.removeEventListener('resize', resetHeight); + }; + }, []); + useEffect(() => { + chrome?.runtime?.sendMessage({ action: "getApiKey" }, (response) => { if (response) { globalApiKey = response; @@ -405,7 +452,7 @@ function App() { }; const storeWalletInfo = (wallet: any) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "storeWalletInfo", wallet }, (response) => { if (response) { @@ -427,7 +474,7 @@ function App() { const getBalanceFunc = () => { setQortBalanceLoading(true); - chrome.runtime.sendMessage({ action: "balance" }, (response) => { + chrome?.runtime?.sendMessage({ action: "balance" }, (response) => { if (!response?.error && !isNaN(+response)) { setBalance(response); } @@ -436,7 +483,7 @@ function App() { }; const getLtcBalanceFunc = () => { setLtcBalanceLoading(true); - chrome.runtime.sendMessage({ action: "ltcBalance" }, (response) => { + chrome?.runtime?.sendMessage({ action: "ltcBalance" }, (response) => { if (!response?.error && !isNaN(+response)) { setLtcBalance(response); } @@ -459,7 +506,7 @@ function App() { return; } setIsLoading(true); - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "sendCoin", payload: { @@ -487,7 +534,7 @@ function App() { useEffect(() => { // Listen for messages from the background script - chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + chrome.runtime?.onMessage.addListener((message, sender, sendResponse) => { // Check if the message is to update the state if ( message.action === "UPDATE_STATE_CONFIRM_SEND_QORT" && @@ -563,7 +610,7 @@ function App() { //param = isDecline const confirmPayment = (isDecline: boolean) => { if (isDecline) { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "sendQortConfirmation", payload: { @@ -586,7 +633,7 @@ function App() { } setIsLoading(true); - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "sendQortConfirmation", payload: { @@ -613,7 +660,7 @@ function App() { const confirmBuyOrder = (isDecline: boolean) => { if (isDecline) { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "buyOrderConfirmation", payload: { @@ -630,7 +677,7 @@ function App() { } setIsLoading(true); - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "buyOrderConfirmation", payload: { @@ -657,7 +704,7 @@ function App() { hostname: string, interactionId: string ) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "responseToConnectionRequest", payload: { isOkay, hostname, interactionId }, @@ -679,7 +726,7 @@ function App() { useEffect(() => { try { setIsLoading(true); - chrome.runtime.sendMessage({ action: "getWalletInfo" }, (response) => { + chrome?.runtime?.sendMessage({ action: "getWalletInfo" }, (response) => { if (response && response?.walletInfo) { setRawWallet(response?.walletInfo); if ( @@ -708,7 +755,7 @@ function App() { }, 10000); }); } - chrome.runtime.sendMessage({ action: "userInfo" }, (response) => { + chrome?.runtime?.sendMessage({ action: "userInfo" }, (response) => { if (response && !response.error) { setUserInfo(response); } @@ -799,7 +846,7 @@ function App() { crypto.kdfThreads, () => {} ); - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "decryptWallet", payload: { @@ -814,7 +861,7 @@ function App() { wallet, qortAddress: wallet.address0, }); - chrome.runtime.sendMessage({ action: "userInfo" }, (response2) => { + chrome?.runtime?.sendMessage({ action: "userInfo" }, (response2) => { setIsLoading(false); if (response2 && !response2.error) { setUserInfo(response); @@ -835,7 +882,7 @@ function App() { const logoutFunc = () => { try { - chrome.runtime.sendMessage({ action: "logout" }, (response) => { + chrome?.runtime?.sendMessage({ action: "logout" }, (response) => { if (response) { resetAllStates(); executeEvent("logout-event", {}); @@ -898,7 +945,7 @@ function App() { res(); }, 250); }); - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "decryptWallet", payload: { @@ -911,14 +958,14 @@ function App() { setAuthenticatePassword(""); setExtstate("authenticated"); setWalletToBeDecryptedError(""); - chrome.runtime.sendMessage({ action: "userInfo" }, (response) => { + chrome?.runtime?.sendMessage({ action: "userInfo" }, (response) => { setIsLoading(false); if (response && !response.error) { setUserInfo(response); } }); getBalanceFunc(); - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "getWalletInfo" }, (response) => { if (response && response?.walletInfo) { @@ -1023,7 +1070,7 @@ function App() { }); setIsLoadingRegisterName(true); new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "registerName", payload: { @@ -1076,8 +1123,224 @@ function App() { } }; + const renderProfile = ()=> { + return ( + + {isMobile && ( + { + setIsOpenDrawerProfile(false) + }} sx={{ + cursor: 'pointer', + color: 'white' + }} /> + )} + + + + + {authenticatedMode === "ltc" ? ( + <> + + + + + {rawWallet?.ltcAddress?.slice(0, 6)}... + {rawWallet?.ltcAddress?.slice(-4)} + + + + {ltcBalanceLoading && ( + + )} + {!isNaN(+ltcBalance) && !ltcBalanceLoading && ( + + + {ltcBalance} LTC + + + + )} + + ) : ( + <> + + + + {userInfo?.name} + + + + + {rawWallet?.address0?.slice(0, 6)}... + {rawWallet?.address0?.slice(-4)} + + + + {qortBalanceLoading && ( + + )} + {!qortBalanceLoading && balance >= 0 && ( + + + {balance?.toFixed(2)} QORT + + + + )} + + + {userInfo && !userInfo?.name && ( + { + setOpenRegisterName(true); + }} + > + REGISTER NAME + + )} + + { + setExtstate("send-qort"); + }} + > + Transfer QORT + + + )} + { + chrome.tabs.create({ url: "https://www.qort.trade" }); + }} + > + Get QORT at qort.trade + + + + + { + setExtstate("download-wallet"); + }} + src={Download} + style={{ + cursor: "pointer", + }} + /> + + + + {authenticatedMode === "qort" && ( + { + setAuthenticatedMode("ltc"); + }} + src={ltcLogo} + style={{ + cursor: "pointer", + width: "20px", + height: "auto", + }} + /> + )} + {authenticatedMode === "ltc" && ( + { + setAuthenticatedMode("qort"); + }} + src={qortLogo} + style={{ + cursor: "pointer", + width: "20px", + height: "auto", + }} + /> + )} + + + ) + } + return ( - + {/* {extState === 'group' && ( )} */} @@ -1230,7 +1493,7 @@ function App() { onClick={() => { const valueToSet = !confirmUseOfLocal const payload = valueToSet ? apiKey : null - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "setApiKey", payload }, (response) => { if (response) { @@ -1266,7 +1529,7 @@ function App() { {/* {extState !== "not-authenticated" && ( )} */} - {extState === "authenticated" && isMainWindow && ( + {extState === "authenticated" && ( - - - - - {authenticatedMode === "ltc" ? ( - <> - - - - - {rawWallet?.ltcAddress?.slice(0, 6)}... - {rawWallet?.ltcAddress?.slice(-4)} - - - - {ltcBalanceLoading && ( - - )} - {!isNaN(+ltcBalance) && !ltcBalanceLoading && ( - - - {ltcBalance} LTC - - - - )} - - ) : ( - <> - - - - {userInfo?.name} - - - - - {rawWallet?.address0?.slice(0, 6)}... - {rawWallet?.address0?.slice(-4)} - - - - {qortBalanceLoading && ( - - )} - {!qortBalanceLoading && balance >= 0 && ( - - - {balance?.toFixed(2)} QORT - - - - )} - - - {userInfo && !userInfo?.name && ( - { - setOpenRegisterName(true); - }} - > - REGISTER NAME - - )} - - { - setExtstate("send-qort"); - }} - > - Transfer QORT - - - )} - { - chrome.tabs.create({ url: "https://www.qort.trade" }); - }} - > - Get QORT at qort.trade - - - - - { - setExtstate("download-wallet"); - }} - src={Download} - style={{ - cursor: "pointer", - }} - /> - - - - {authenticatedMode === "qort" && ( - { - setAuthenticatedMode("ltc"); - }} - src={ltcLogo} - style={{ - cursor: "pointer", - width: "20px", - height: "auto", - }} - /> - )} - {authenticatedMode === "ltc" && ( - { - setAuthenticatedMode("qort"); - }} - src={qortLogo} - style={{ - cursor: "pointer", - width: "20px", - height: "auto", - }} - /> - )} - - + {!isMobile && renderProfile()} + + {renderProfile()} ); } diff --git a/src/background.ts b/src/background.ts index 9539e5b..7413cd3 100644 --- a/src/background.ts +++ b/src/background.ts @@ -2341,7 +2341,7 @@ if (res?.[key]) { throw new Error("No Chatheads saved"); } } -chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { +chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { if (request) { switch (request.action) { case "version": @@ -3581,7 +3581,7 @@ const restoreWindowBounds = (callback) => { }); }; -chrome.action.onClicked.addListener((tab) => { +chrome.action?.onClicked?.addListener((tab) => { const popupUrl = chrome.runtime.getURL("index.html?main=true"); chrome.windows.getAll( { populate: true, windowTypes: ["popup"] }, @@ -3716,7 +3716,7 @@ const checkActiveChatsForNotifications = async ()=> { } } -chrome.notifications.onClicked.addListener( (notificationId) => { +chrome.notifications?.onClicked?.addListener( (notificationId) => { const popupUrl = chrome.runtime.getURL("index.html?main=true"); const isDirect = notificationId.includes('_type=direct_'); @@ -3839,14 +3839,14 @@ chrome.notifications.onClicked.addListener( (notificationId) => { }); // Reconnect when service worker wakes up -chrome.runtime.onStartup.addListener(() => { +chrome.runtime?.onStartup.addListener(() => { console.log("Service worker started up, reconnecting WebSocket..."); // initWebsocketMessageGroup(); // listenForNewGroupAnnouncements() // listenForThreadUpdates() }); -chrome.runtime.onInstalled.addListener((details) => { +chrome.runtime?.onInstalled.addListener((details) => { if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) { console.log('Extension Installed'); // Perform tasks that should only happen on extension installation @@ -3866,14 +3866,14 @@ chrome.runtime.onInstalled.addListener((details) => { }); // Check if the alarm already exists before creating it -chrome.alarms.get("checkForNotifications", (existingAlarm) => { +chrome.alarms?.get("checkForNotifications", (existingAlarm) => { if (!existingAlarm) { // If the alarm does not exist, create it chrome.alarms.create("checkForNotifications", { periodInMinutes: 4 }); } }); -chrome.alarms.onAlarm.addListener(async (alarm) => { +chrome.alarms?.onAlarm.addListener(async (alarm) => { try { if (alarm.name === "checkForNotifications") { diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index 7a4bb9c..00c7691 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -10,7 +10,7 @@ import { decryptPublishes, getTempPublish, saveTempPublish } from "./GroupAnnoun import { AnnouncementList } from "./AnnouncementList"; import { Spacer } from "../../common/Spacer"; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; -import { getBaseApiReact, pauseAllQueues, resumeAllQueues } from "../../App"; +import { getBaseApiReact, isMobile, pauseAllQueues, resumeAllQueues } from "../../App"; const tempKey = 'accouncement-comment' @@ -66,7 +66,7 @@ export const AnnouncementDiscussion = ({ if (!selectedAnnouncement) return; return new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "publishGroupEncryptedResource", payload: { @@ -245,7 +245,7 @@ export const AnnouncementDiscussion = ({ return (
{ try { return new Promise((res, rej)=> { - chrome.runtime.sendMessage({ action: "decryptDirect", payload: { + chrome?.runtime?.sendMessage({ action: "decryptDirect", payload: { data: encryptedMessages, involvingAddress: selectedDirect?.address }}, (response) => { @@ -146,7 +146,7 @@ const sendChatDirect = async ({ chatReference = undefined, messageText}: any)=> if(!directTo) return return new Promise((res, rej)=> { - chrome.runtime.sendMessage({ action: "sendChatDirect", payload: { + chrome?.runtime?.sendMessage({ action: "sendChatDirect", payload: { directTo, chatReference, messageText }}, async (response) => { @@ -167,7 +167,7 @@ const sendChatDirect = async ({ chatReference = undefined, messageText}: any)=> "senderName": myName }) setNewChat(null) - chrome.runtime.sendMessage({ + chrome?.runtime?.sendMessage({ action: "addTimestampEnterChat", payload: { timestamp: Date.now(), diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index d0c8887..4420d94 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -10,7 +10,7 @@ import Tiptap from './TipTap' import { CustomButton } from '../../App-styles' import CircularProgress from '@mui/material/CircularProgress'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar' -import { getBaseApiReactSocket, pauseAllQueues, resumeAllQueues } from '../../App' +import { getBaseApiReactSocket, isMobile, pauseAllQueues, resumeAllQueues } from '../../App' import { CustomizedSnackbars } from '../Snackbar/Snackbar' import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes' import { useMessageQueue } from '../../MessageQueueContext' @@ -85,7 +85,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, return } return new Promise((res, rej)=> { - chrome.runtime.sendMessage({ action: "decryptSingle", payload: { + chrome?.runtime?.sendMessage({ action: "decryptSingle", payload: { data: encryptedMessages, secretKeyObject: secretKey }}, (response) => { @@ -233,7 +233,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, const encryptChatMessage = async (data: string, secretKeyObject: any)=> { try { return new Promise((res, rej)=> { - chrome.runtime.sendMessage({ action: "encryptSingle", payload: { + chrome?.runtime?.sendMessage({ action: "encryptSingle", payload: { data, secretKeyObject }}, (response) => { @@ -252,7 +252,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, const sendChatGroup = async ({groupId, typeMessage = undefined, chatReference = undefined, messageText}: any)=> { try { return new Promise((res, rej)=> { - chrome.runtime.sendMessage({ action: "sendChatGroup", payload: { + chrome?.runtime?.sendMessage({ action: "sendChatGroup", payload: { groupId, typeMessage, chatReference, messageText }}, (response) => { @@ -336,7 +336,7 @@ const clearEditorContent = () => { return (
{ @@ -142,7 +142,7 @@ export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, sec flexDirection: 'column', gap: '25px', maxWidth: '350px', - background: '#4444' + background: '#444444' }}> Re-encyrpt key {noSecretKey ? ( diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 9f8b91f..bf94e1c 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -28,7 +28,7 @@ const uid = new ShortUniqueId({ length: 8 }); import CampaignIcon from '@mui/icons-material/Campaign'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { AnnouncementDiscussion } from "./AnnouncementDiscussion"; -import { MyContext, getBaseApiReact, pauseAllQueues, resumeAllQueues } from "../../App"; +import { MyContext, getBaseApiReact, isMobile, pauseAllQueues, resumeAllQueues } from "../../App"; import { RequestQueueWithPromise } from "../../utils/queue/queue"; import { CustomizedSnackbars } from "../Snackbar/Snackbar"; @@ -39,7 +39,7 @@ export const saveTempPublish = async ({ data, key }: any) => { return new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "saveTempPublish", payload: { @@ -62,7 +62,7 @@ export const getTempPublish = async () => { return new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "getTempPublish", payload: { @@ -81,7 +81,7 @@ export const getTempPublish = async () => { export const decryptPublishes = async (encryptedMessages: any[], secretKey) => { try { return await new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "decryptSingleForPublishes", payload: { @@ -182,7 +182,7 @@ export const GroupAnnouncements = ({ const encryptChatMessage = async (data: string, secretKeyObject: any) => { try { return new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "encryptSingle", payload: { @@ -206,7 +206,7 @@ export const GroupAnnouncements = ({ return new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "publishGroupEncryptedResource", payload: { @@ -460,7 +460,7 @@ export const GroupAnnouncements = ({ return (
{ + + const toggleDrawer = (newOpen: boolean) => () => { + setOpen(newOpen); + }; + + + return ( +
+ + + + {children} + + +
+ ); +} diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index 47bbf0e..ed27d35 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -103,7 +103,7 @@ export const AddGroup = ({ address, open, setOpen }) => { }) await new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "createGroup", payload: { diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index d0b8772..2eb9495 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -108,7 +108,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { }) setIsLoading(true); await new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "joinGroup", payload: { diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index d962347..48bc62e 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -123,7 +123,7 @@ export const GroupMail = ({ const updateThreadActivity = async ({threadId, qortalName, groupId, thread}) => { try { await new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "updateThreadActivity", payload: { @@ -656,6 +656,11 @@ export const GroupMail = ({ thread?.threadData?.createdAt < hasViewedRecent?.timestamp; return ( { setCurrentThread(thread); if(thread?.threadId && thread?.threadData?.name){ diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index f4a088b..a716823 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -94,7 +94,7 @@ export const publishGroupEncryptedResource = async ({ identifier, }) => { return new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "publishGroupEncryptedResource", payload: { @@ -117,7 +117,7 @@ export const publishGroupEncryptedResource = async ({ export const encryptSingleFunc = async (data: string, secretKeyObject: any) => { try { return new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "encryptSingle", payload: { diff --git a/src/components/Group/Forum/ReusableModal.tsx b/src/components/Group/Forum/ReusableModal.tsx index b61e83e..d081293 100644 --- a/src/components/Group/Forum/ReusableModal.tsx +++ b/src/components/Group/Forum/ReusableModal.tsx @@ -1,5 +1,6 @@ import React from 'react' import { Box, Modal, useTheme } from '@mui/material' +import { isMobile } from '../../../App' interface MyModalProps { open: boolean @@ -40,7 +41,7 @@ export const ReusableModal: React.FC = ({ top: '50%', left: '50%', transform: 'translate(-50%, -50%)', - width: '75%', + width: isMobile ? '95%' : '75%', bgcolor: theme.palette.primary.main, boxShadow: 24, p: 4, diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 7edab6c..aeed4b3 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2,6 +2,7 @@ import { Avatar, Box, Button, + Grid, IconButton, List, ListItem, @@ -29,7 +30,9 @@ import MarkUnreadChatAltIcon from "@mui/icons-material/MarkUnreadChatAlt"; import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; import CreateIcon from "@mui/icons-material/Create"; import RefreshIcon from "@mui/icons-material/Refresh"; - +import AnnouncementsIcon from '@mui/icons-material/Notifications'; +import GroupIcon from '@mui/icons-material/Group'; +import PersonIcon from '@mui/icons-material/Person'; import { AuthenticatedContainerInnerRight, CustomButton, @@ -39,13 +42,21 @@ import { Spacer } from "../../common/Spacer"; import PeopleIcon from "@mui/icons-material/People"; import { ManageMembers } from "./ManageMembers"; import MarkChatUnreadIcon from "@mui/icons-material/MarkChatUnread"; -import { MyContext, clearAllQueues, getBaseApiReact, pauseAllQueues, resumeAllQueues } from "../../App"; +import { + MyContext, + clearAllQueues, + getBaseApiReact, + isMobile, + pauseAllQueues, + resumeAllQueues, +} from "../../App"; import { ChatDirect } from "../Chat/ChatDirect"; import { CustomizedSnackbars } from "../Snackbar/Snackbar"; import { LoadingButton } from "@mui/lab"; import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar"; import { GroupAnnouncements } from "../Chat/GroupAnnouncements"; import HomeIcon from "@mui/icons-material/Home"; +import CloseIcon from '@mui/icons-material/Close'; import { ThingsToDoInitial } from "./ThingsToDoInitial"; import { GroupJoinRequests } from "./GroupJoinRequests"; @@ -61,6 +72,29 @@ import { RequestQueueWithPromise } from "../../utils/queue/queue"; import { WebSocketActive } from "./WebsocketActive"; import { flushSync } from "react-dom"; import { useMessageQueue } from "../../MessageQueueContext"; +import { DrawerComponent } from "../Drawer/Drawer"; + +// let touchStartY = 0; +// let disablePullToRefresh = false; + +// // Detect when the user touches the screen +// window.addEventListener('touchstart', function(event) { +// if (event.touches.length !== 1) return; // Ignore multi-touch events + +// touchStartY = event.touches[0].clientY; +// disablePullToRefresh = window.scrollY === 0; // Only disable if at the top +// }); + +// // Detect when the user moves their finger on the screen +// window.addEventListener('touchmove', function(event) { +// let touchY = event.touches[0].clientY; + +// // If pulling down from the top of the page, prevent the default behavior +// if (disablePullToRefresh && touchY > touchStartY) { +// event.preventDefault(); +// } +// }); + interface GroupProps { myAddress: string; @@ -75,7 +109,7 @@ const timeDifferenceForNotificationChats = 900000; export const requestQueueMemberNames = new RequestQueueWithPromise(5); export const requestQueueAdminMemberNames = new RequestQueueWithPromise(5); -const audio = new Audio(chrome.runtime.getURL("msg-not1.wav")); +const audio = new Audio(chrome.runtime?.getURL("msg-not1.wav")); export const getGroupAdimnsAddress = async (groupNumber: number) => { // const validApi = await findUsableApi(); @@ -151,7 +185,7 @@ export const getGroupMembers = async (groupNumber: number) => { export const decryptResource = async (data: string) => { try { return new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "decryptGroupEncryption", payload: { @@ -159,7 +193,6 @@ export const decryptResource = async (data: string) => { }, }, (response) => { - if (!response?.error) { res(response); } @@ -276,10 +309,11 @@ export const Group = ({ isMain, userInfo, balance, + isOpenDrawerProfile, setIsOpenDrawerProfile }: GroupProps) => { const [secretKey, setSecretKey] = useState(null); const [secretKeyPublishDate, setSecretKeyPublishDate] = useState(null); - const lastFetchedSecretKey = useRef(null) + const lastFetchedSecretKey = useRef(null); const [secretKeyDetails, setSecretKeyDetails] = useState(null); const [newEncryptionNotification, setNewEncryptionNotification] = useState(null); @@ -310,11 +344,14 @@ export const Group = ({ const [isLoadingNotifyAdmin, setIsLoadingNotifyAdmin] = React.useState(false); const [isLoadingGroups, setIsLoadingGroups] = React.useState(false); const [isLoadingGroup, setIsLoadingGroup] = React.useState(false); - const [firstSecretKeyInCreation, setFirstSecretKeyInCreation] = React.useState(false) + const [firstSecretKeyInCreation, setFirstSecretKeyInCreation] = + React.useState(false); const [groupSection, setGroupSection] = React.useState("home"); const [groupAnnouncements, setGroupAnnouncements] = React.useState({}); const [defaultThread, setDefaultThread] = React.useState(null); + const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); + const [drawerMode, setDrawerMode] = React.useState('groups'); const isFocusedRef = useRef(true); const selectedGroupRef = useRef(null); const selectedDirectRef = useRef(null); @@ -325,6 +362,9 @@ export const Group = ({ const settimeoutForRefetchSecretKey = useRef(null); const { clearStatesMessageQueueProvider } = useMessageQueue(); + // useEffect(()=> { + // setFullHeight() + // }, []) useEffect(() => { isFocusedRef.current = isFocused; @@ -341,12 +381,10 @@ export const Group = ({ selectedDirectRef.current = selectedDirect; }, [selectedDirect]); - - const getTimestampEnterChat = async () => { try { return new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "getTimestampEnterChat", }, @@ -371,12 +409,11 @@ export const Group = ({ const getGroupAnnouncements = async () => { try { return new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "getGroupNotificationTimestamp", }, (response) => { - if (!response?.error) { setGroupAnnouncements(response); res(response); @@ -390,11 +427,10 @@ export const Group = ({ const getGroupOwner = async (groupId) => { try { - const url = `${getBaseApiReact()}/groups/${groupId}`; const response = await fetch(url); let data = await response.json(); - + const name = await getNameInfo(data?.owner); if (name) { data.name = name; @@ -475,11 +511,11 @@ export const Group = ({ selectedGroup?.groupId }&exactmatchnames=true&limit=0&reverse=true&${queryString}`; const response = await fetch(url); - if(!response.ok){ - throw new Error('network error') + if (!response.ok) { + throw new Error("network error"); } const adminData = await response.json(); - + const filterId = adminData.filter( (data: any) => data.identifier === `symmetric-qchat-group-${selectedGroup?.groupId}` @@ -491,18 +527,27 @@ export const Group = ({ // Get the most recent date for both a and b const dateA = a.updated ? new Date(a.updated) : new Date(a.created); const dateB = b.updated ? new Date(b.updated) : new Date(b.created); - + // Sort by most recent return dateB.getTime() - dateA.getTime(); }); - + return sortedData[0]; }; - const getSecretKey = async (loadingGroupParam?: boolean, secretKeyToPublish?: boolean) => { + const getSecretKey = async ( + loadingGroupParam?: boolean, + secretKeyToPublish?: boolean + ) => { try { - pauseAllQueues() - - if(secretKeyToPublish && secretKey && lastFetchedSecretKey.current && Date.now() - lastFetchedSecretKey.current < 1800000) return secretKey + pauseAllQueues(); + + if ( + secretKeyToPublish && + secretKey && + lastFetchedSecretKey.current && + Date.now() - lastFetchedSecretKey.current < 1800000 + ) + return secretKey; if (loadingGroupParam) { setIsLoadingGroup(true); } @@ -515,12 +560,12 @@ export const Group = ({ const prevGroupId = selectedGroupRef.current.groupId; // const validApi = await findUsableApi(); const groupAdmins = await getGroupAdimns(selectedGroup?.groupId); - setAdmins(groupAdmins) - if(!groupAdmins.length){ - throw new Error('Network error') + setAdmins(groupAdmins); + if (!groupAdmins.length) { + throw new Error("Network error"); } const publish = await getPublishesFromAdmins(groupAdmins); - + if (prevGroupId !== selectedGroupRef.current.groupId) { if (settimeoutForRefetchSecretKey.current) { clearTimeout(settimeoutForRefetchSecretKey.current); @@ -535,49 +580,45 @@ export const Group = ({ return false; } setSecretKeyPublishDate(publish?.updated || publish?.created); - + const res = await fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ publish.identifier }?encoding=base64` ); const data = await res.text(); - + const decryptedKey: any = await decryptResource(data); - + const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); - + if (!validateSecretKey(decryptedKeyToObject)) throw new Error("SecretKey is not valid"); setSecretKeyDetails(publish); setSecretKey(decryptedKeyToObject); - lastFetchedSecretKey.current = Date.now() + lastFetchedSecretKey.current = Date.now(); setMemberCountFromSecretKeyData(decryptedKey.count); if (decryptedKeyToObject) { setTriedToFetchSecretKey(true); - setFirstSecretKeyInCreation(false) + setFirstSecretKeyInCreation(false); return decryptedKeyToObject; } else { setTriedToFetchSecretKey(true); } - } catch (error) { - if(error === 'Unable to decrypt data'){ + if (error === "Unable to decrypt data") { setTriedToFetchSecretKey(true); settimeoutForRefetchSecretKey.current = setTimeout(() => { getSecretKey(); }, 120000); } - } finally { setIsLoadingGroup(false); - if(!secretKeyToPublish){ + if (!secretKeyToPublish) { await getAdmins(selectedGroup?.groupId); - } - resumeAllQueues() - + resumeAllQueues(); } }; @@ -596,7 +637,7 @@ export const Group = ({ // } // const newActiveChats= data // const oldActiveChats = await new Promise((res, rej) => { - // chrome.runtime.sendMessage( + // chrome?.runtime?.sendMessage( // { // action: "getChatHeads", // }, @@ -627,7 +668,7 @@ export const Group = ({ // if(results?.length > 0){ // if (!lastGroupNotification.current || (Date.now() - lastGroupNotification.current >= 60000)) { // console.log((Date.now() - lastGroupNotification.current >= 60000), lastGroupNotification.current) - // chrome.runtime.sendMessage( + // chrome?.runtime?.sendMessage( // { // action: "notification", // payload: { @@ -650,7 +691,7 @@ export const Group = ({ // } catch (error) { // console.log('error not', error) // if(!isFocusedRef.current){ - // chrome.runtime.sendMessage( + // chrome?.runtime?.sendMessage( // { // action: "notification", // payload: { @@ -670,7 +711,7 @@ export const Group = ({ // } finally { - // chrome.runtime.sendMessage( + // chrome?.runtime?.sendMessage( // { // action: "setChatHeads", // payload: { @@ -693,16 +734,15 @@ export const Group = ({ useEffect(() => { // Listen for messages from the background script - chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - + chrome?.runtime?.onMessage.addListener((message, sender, sendResponse) => { if (message.action === "SET_GROUPS") { // Update the component state with the received 'sendqort' state setGroups(message.payload); - + setMemberGroups(message.payload); if (selectedGroupRef.current && groupSectionRef.current === "chat") { - chrome.runtime.sendMessage({ + chrome?.runtime?.sendMessage({ action: "addTimestampEnterChat", payload: { timestamp: Date.now(), @@ -711,7 +751,7 @@ export const Group = ({ }); } if (selectedDirectRef.current) { - chrome.runtime.sendMessage({ + chrome?.runtime?.sendMessage({ action: "addTimestampEnterChat", payload: { timestamp: Date.now(), @@ -731,7 +771,7 @@ export const Group = ({ selectedGroupRef.current && groupSectionRef.current === "announcement" ) { - chrome.runtime.sendMessage({ + chrome?.runtime?.sendMessage({ action: "addGroupNotificationTimestamp", payload: { timestamp: Date.now(), @@ -748,7 +788,7 @@ export const Group = ({ setDirects(message.payload); // if (selectedGroupRef.current) { - // chrome.runtime.sendMessage({ + // chrome?.runtime?.sendMessage({ // action: "addTimestampEnterChat", // payload: { // timestamp: Date.now(), @@ -775,16 +815,15 @@ export const Group = ({ ) return; - chrome.runtime.sendMessage({ action: "setupGroupWebsocket" }); + chrome?.runtime?.sendMessage({ action: "setupGroupWebsocket" }); hasInitializedWebsocket.current = true; }, [myAddress, groups]); - const getMembers = async (groupId) => { try { const res = await getGroupMembers(groupId); - if(groupId !== selectedGroupRef.current?.groupId) return + if (groupId !== selectedGroupRef.current?.groupId) return; setMembers(res); } catch (error) {} }; @@ -795,8 +834,6 @@ export const Group = ({ } }, [selectedGroup?.groupId]); - - const shouldReEncrypt = useMemo(() => { if (triedToFetchSecretKey && !secretKeyPublishDate) return true; if ( @@ -809,13 +846,13 @@ export const Group = ({ memberCountFromSecretKeyData !== members?.memberCount && newEncryptionNotification?.text?.data?.numberOfMembers !== members?.memberCount; - + if (isDiffMemberNumber) return true; const latestJoined = members?.members.reduce((maxJoined, current) => { return current.joined > maxJoined ? current.joined : maxJoined; }, members?.members[0].joined); - + if ( secretKeyPublishDate < latestJoined && newEncryptionNotification?.data?.timestamp < latestJoined @@ -835,7 +872,7 @@ export const Group = ({ try { setIsLoadingNotifyAdmin(true); await new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "notifyAdminRegenerateSecretKey", payload: { @@ -844,7 +881,6 @@ export const Group = ({ }, }, (response) => { - if (!response?.error) { res(response); } @@ -882,7 +918,6 @@ export const Group = ({ }, [timestampEnterData, selectedGroup]); const isUnread = useMemo(() => { - if (!selectedGroup) return false; return ( groupAnnouncements?.[selectedGroup?.groupId]?.seentimestamp === false @@ -893,7 +928,7 @@ export const Group = ({ if (isLoadingOpenSectionFromNotification.current) return; isLoadingOpenSectionFromNotification.current = true; const directAddress = e.detail?.from; - + const findDirect = directs?.find( (direct) => direct?.address === directAddress ); @@ -908,7 +943,7 @@ export const Group = ({ setNewChat(false); - chrome.runtime.sendMessage({ + chrome?.runtime?.sendMessage({ action: "addTimestampEnterChat", payload: { timestamp: Date.now(), @@ -937,7 +972,7 @@ export const Group = ({ const resetAllStatesAndRefs = () => { // Reset all useState values to their initial states setSecretKey(null); - lastFetchedSecretKey.current = null + lastFetchedSecretKey.current = null; setSecretKeyPublishDate(null); setSecretKeyDetails(null); setNewEncryptionNotification(null); @@ -954,7 +989,7 @@ export const Group = ({ setOpenAddGroup(false); setIsInitialGroups(false); setOpenManageMembers(false); - setMemberGroups([]); // Assuming you're clearing the context here as well + setMemberGroups([]); // Assuming you're clearing the context here as well setTimestampEnterData({}); setChatMode("groups"); setNewChat(false); @@ -967,7 +1002,7 @@ export const Group = ({ setGroupSection("home"); setGroupAnnouncements({}); setDefaultThread(null); - + // Reset all useRef values to their initial states hasInitialized.current = false; hasInitializedWebsocket.current = false; @@ -981,12 +1016,11 @@ export const Group = ({ setupGroupWebsocketInterval.current = null; settimeoutForRefetchSecretKey.current = null; }; - - const logoutEventFunc = ()=> { - resetAllStatesAndRefs() - clearStatesMessageQueueProvider() - } + const logoutEventFunc = () => { + resetAllStatesAndRefs(); + clearStatesMessageQueueProvider(); + }; useEffect(() => { subscribeToEvent("logout-event", logoutEventFunc); @@ -1013,7 +1047,7 @@ export const Group = ({ setNewChat(false); setSecretKey(null); - lastFetchedSecretKey.current = null + lastFetchedSecretKey.current = null; setSecretKeyPublishDate(null); setAdmins([]); setSecretKeyDetails(null); @@ -1021,10 +1055,10 @@ export const Group = ({ setMembers([]); setMemberCountFromSecretKeyData(null); setTriedToFetchSecretKey(false); - setFirstSecretKeyInCreation(false) + setFirstSecretKeyInCreation(false); setGroupSection("chat"); - chrome.runtime.sendMessage({ + chrome?.runtime?.sendMessage({ action: "addTimestampEnterChat", payload: { timestamp: Date.now(), @@ -1052,7 +1086,6 @@ export const Group = ({ }, [groups, selectedGroup]); const openGroupAnnouncementFromNotification = (e) => { - const groupId = e.detail?.from; const findGroup = groups?.find((group) => +group?.groupId === +groupId); @@ -1061,7 +1094,7 @@ export const Group = ({ setChatMode("groups"); setSelectedGroup(null); setSecretKey(null); - lastFetchedSecretKey.current = null + lastFetchedSecretKey.current = null; setSecretKeyPublishDate(null); setAdmins([]); setSecretKeyDetails(null); @@ -1069,9 +1102,9 @@ export const Group = ({ setMembers([]); setMemberCountFromSecretKeyData(null); setTriedToFetchSecretKey(false); - setFirstSecretKeyInCreation(false) + setFirstSecretKeyInCreation(false); setGroupSection("announcement"); - chrome.runtime.sendMessage({ + chrome?.runtime?.sendMessage({ action: "addGroupNotificationTimestamp", payload: { timestamp: Date.now(), @@ -1118,7 +1151,7 @@ export const Group = ({ setChatMode("groups"); setSelectedGroup(null); setSecretKey(null); - lastFetchedSecretKey.current = null + lastFetchedSecretKey.current = null; setSecretKeyPublishDate(null); setAdmins([]); setSecretKeyDetails(null); @@ -1126,7 +1159,7 @@ export const Group = ({ setMembers([]); setMemberCountFromSecretKeyData(null); setTriedToFetchSecretKey(false); - setFirstSecretKeyInCreation(false) + setFirstSecretKeyInCreation(false); setGroupSection("forum"); setDefaultThread(data); @@ -1146,15 +1179,431 @@ export const Group = ({ }; }, [groups, selectedGroup]); - const handleSecretKeyCreationInProgress = ()=> { - setFirstSecretKeyInCreation(true) + const handleSecretKeyCreationInProgress = () => { + setFirstSecretKeyInCreation(true); + }; + + const goToHome = async ()=> { + setGroupSection("default"); + clearAllQueues(); + await new Promise((res) => { + setTimeout(() => { + res(null); + }, 200); + }); + setGroupSection("home"); + setSelectedGroup(null); + setNewChat(false); + setSelectedDirect(null); + setSecretKey(null); + lastFetchedSecretKey.current = null; + setSecretKeyPublishDate(null); + setAdmins([]); + setSecretKeyDetails(null); + setAdminsWithNames([]); + setMembers([]); + setMemberCountFromSecretKeyData(null); + setTriedToFetchSecretKey(false); + setFirstSecretKeyInCreation(false); } + const goToAnnouncements = async ()=> { + setGroupSection("default"); + await new Promise((res) => { + setTimeout(() => { + res(null); + }, 200); + }); + setGroupSection("announcement"); + chrome?.runtime?.sendMessage({ + action: "addGroupNotificationTimestamp", + payload: { + timestamp: Date.now(), + groupId: selectedGroupRef.current.groupId, + }, + }); + setTimeout(() => { + getGroupAnnouncements(); + }, 200); + } + const goToChat = async ()=> { + setGroupSection("default"); + await new Promise((res) => { + setTimeout(() => { + res(null); + }, 200); + }); + setGroupSection("chat"); + if (selectedGroupRef.current) { + chrome?.runtime?.sendMessage({ + action: "addTimestampEnterChat", + payload: { + timestamp: Date.now(), + groupId: selectedGroupRef.current.groupId, + }, + }); + + setTimeout(() => { + getTimestampEnterChat(); + }, 200); + } + } + + const renderGroups = ()=> { + return ( +
+
+ {isMobile && ( + { + setIsOpenDrawer(false) + }} sx={{ + cursor: 'pointer', + color: 'white' + }} /> + )} + { + setChatMode((prev) => + prev === "directs" ? "groups" : "directs" + ); + setNewChat(false); + setSelectedDirect(null); + setSelectedGroup(null); + setGroupSection("default"); + }} + > + {chatMode === "groups" && ( + <> + + + )} + {chatMode === "directs" ? "Switch to groups" : "Direct msgs"} + +
+
+ {directs.map((direct: any) => ( + + + // + // + // } + onClick={() => { + setSelectedDirect(null); + setNewChat(false); + setSelectedGroup(null); + setIsOpenDrawer(false) + chrome?.runtime?.sendMessage({ + action: "addTimestampEnterChat", + payload: { + timestamp: Date.now(), + groupId: direct.address, + }, + }); + setTimeout(() => { + setSelectedDirect(direct); + + getTimestampEnterChat(); + }, 200); + }} + sx={{ + display: "flex", + width: "100%", + flexDirection: "column", + cursor: "pointer", + border: "1px #232428 solid", + padding: "2px", + borderRadius: "2px", + background: + direct?.address === selectedDirect?.address && "white", + }} + > + + + + {(direct?.name || direct?.address)?.charAt(0)} + + + + {direct?.sender !== myAddress && + direct?.timestamp && + ((!timestampEnterData[direct?.address] && + Date.now() - direct?.timestamp < + timeDifferenceForNotificationChats) || + timestampEnterData[direct?.address] < + direct?.timestamp) && ( + + )} + + + + ))} +
+
+ {groups.map((group: any) => ( + + + // + // + // } + onClick={() => { + clearAllQueues(); + setSelectedDirect(null); + + setNewChat(false); + setSelectedGroup(null); + setSecretKey(null); + lastFetchedSecretKey.current = null; + setSecretKeyPublishDate(null); + setAdmins([]); + setSecretKeyDetails(null); + setAdminsWithNames([]); + setMembers([]); + setMemberCountFromSecretKeyData(null); + setTriedToFetchSecretKey(false); + setFirstSecretKeyInCreation(false); + setGroupSection("announcement"); + setIsOpenDrawer(false) + setTimeout(() => { + setSelectedGroup(group); + + getTimestampEnterChat(); + }, 200); + + if (groupSectionRef.current === "announcement") { + chrome?.runtime?.sendMessage({ + action: "addGroupNotificationTimestamp", + payload: { + timestamp: Date.now(), + groupId: group.groupId, + }, + }); + } + + setTimeout(() => { + getGroupAnnouncements(); + }, 600); + }} + sx={{ + display: "flex", + width: "100%", + flexDirection: "column", + cursor: "pointer", + border: "1px #232428 solid", + padding: "2px", + borderRadius: "2px", + background: + group?.groupId === selectedGroup?.groupId && "white", + }} + > + + + + {group.groupName?.charAt(0)} + + + + {groupAnnouncements[group?.groupId] && + !groupAnnouncements[group?.groupId]?.seentimestamp && ( + + )} + {group?.sender !== myAddress && + group?.timestamp && + ((!timestampEnterData[group?.groupId] && + Date.now() - group?.timestamp < + timeDifferenceForNotificationChats) || + timestampEnterData[group?.groupId] < + group?.timestamp) && ( + + )} + + + + ))} +
+
+ {chatMode === "groups" && ( + { + setOpenAddGroup(true); + }} + > + + Add Group + + )} + {chatMode === "directs" && ( + { + setNewChat(true); + setSelectedDirect(null); + setSelectedGroup(null); + }} + > + + New Chat + + )} +
+
+ ) + } return ( <> - + -
-
- { - setChatMode((prev) => - prev === "directs" ? "groups" : "directs" - ); - setNewChat(false); - setSelectedDirect(null); - setSelectedGroup(null); - setGroupSection("default"); - }} - > - {chatMode === "groups" && ( - <> - - - )} - {chatMode === "directs" ? "Switch to groups" : "Direct msgs"} - -
-
- {directs.map((direct: any) => ( - - - // - // - // } - onClick={() => { - setSelectedDirect(null); - setNewChat(false); - setSelectedGroup(null); - chrome.runtime.sendMessage({ - action: "addTimestampEnterChat", - payload: { - timestamp: Date.now(), - groupId: direct.address, - }, - }); - setTimeout(() => { - setSelectedDirect(direct); - - getTimestampEnterChat(); - }, 200); - }} - sx={{ - display: "flex", - width: "100%", - flexDirection: "column", - cursor: "pointer", - border: "1px #232428 solid", - padding: "2px", - borderRadius: "2px", - background: - direct?.address === selectedDirect?.address && "white", - }} - > - - - - {(direct?.name || direct?.address)?.charAt(0)} - - - - {direct?.sender !== myAddress && - direct?.timestamp && - ((!timestampEnterData[direct?.address] && - Date.now() - direct?.timestamp < - timeDifferenceForNotificationChats) || - timestampEnterData[direct?.address] < - direct?.timestamp) && ( - - )} - - - - ))} -
-
- {groups.map((group: any) => ( - - - // - // - // } - onClick={() => { - clearAllQueues() - setSelectedDirect(null); - - setNewChat(false); - setSelectedGroup(null); - setSecretKey(null); - lastFetchedSecretKey.current = null - setSecretKeyPublishDate(null); - setAdmins([]); - setSecretKeyDetails(null); - setAdminsWithNames([]); - setMembers([]); - setMemberCountFromSecretKeyData(null); - setTriedToFetchSecretKey(false); - setFirstSecretKeyInCreation(false) - setGroupSection("announcement"); - - setTimeout(() => { - setSelectedGroup(group); - - getTimestampEnterChat(); - }, 200); - - if (groupSectionRef.current === "announcement") { - chrome.runtime.sendMessage({ - action: "addGroupNotificationTimestamp", - payload: { - timestamp: Date.now(), - groupId: group.groupId, - }, - }); - } - - setTimeout(() => { - getGroupAnnouncements(); - }, 600); - }} - sx={{ - display: "flex", - width: "100%", - flexDirection: "column", - cursor: "pointer", - border: "1px #232428 solid", - padding: "2px", - borderRadius: "2px", - background: - group?.groupId === selectedGroup?.groupId && "white", - }} - > - - - - {group.groupName?.charAt(0)} - - - - {groupAnnouncements[group?.groupId] && - !groupAnnouncements[group?.groupId]?.seentimestamp && ( - - )} - {group?.sender !== myAddress && - group?.timestamp && - ((!timestampEnterData[group?.groupId] && - Date.now() - group?.timestamp < - timeDifferenceForNotificationChats) || - timestampEnterData[group?.groupId] < - group?.timestamp) && ( - - )} - - - - ))} -
-
- {chatMode === "groups" && ( - { - setOpenAddGroup(true); - }} - > - - Add Group - - )} - {chatMode === "directs" && ( - { - setNewChat(true); - setSelectedDirect(null); - setSelectedGroup(null); - }} - > - - New Chat - - )} -
-
+ {!isMobile && renderGroups()} - {triedToFetchSecretKey && ( + {triedToFetchSecretKey && ( + myAddress={myAddress} + selectedGroup={selectedGroup?.groupId} + getSecretKey={getSecretKey} + secretKey={secretKey} + setSecretKey={setSecretKey} + handleNewEncryptionNotification={setNewEncryptionNotification} + hide={groupSection !== "chat" || !secretKey} + handleSecretKeyCreationInProgress={ + handleSecretKeyCreationInProgress + } + triedToFetchSecretKey={triedToFetchSecretKey} + myName={userInfo?.name} + /> )} - {firstSecretKeyInCreation && triedToFetchSecretKey && !secretKeyPublishDate && ( -
- {" "} - - The group's first common encryption key is in the process of creation. Please wait a few minutes for it to be retrieved by the network. Checking every 2 minutes... - -
- )} - {!admins.includes(myAddress) && - !secretKey && - triedToFetchSecretKey ? ( - <> - {(secretKeyPublishDate || !secretKeyPublishDate && !firstSecretKeyInCreation) ? ( -
{" "} - You are not part of the encrypted group of members. Wait - until an admin re-encrypts the keys. + The group's first common encryption key is in the process + of creation. Please wait a few minutes for it to be + retrieved by the network. Checking every 2 minutes... - - - Try notifying an admin from the list of admins below: - - - {adminsWithNames.map((admin) => { - - return ( - - {admin?.name} - notifyAdmin(admin)} - > - Notify - - - ); - })}
- ) : null} + )} + {!admins.includes(myAddress) && + !secretKey && + triedToFetchSecretKey ? ( + <> + {secretKeyPublishDate || + (!secretKeyPublishDate && !firstSecretKeyInCreation) ? ( +
+ {" "} + + You are not part of the encrypted group of members. Wait + until an admin re-encrypts the keys. + + + + Try notifying an admin from the list of admins below: + + + {adminsWithNames.map((admin) => { + return ( + + {admin?.name} + notifyAdmin(admin)} + > + Notify + + + ); + })} +
+ ) : null} - ) : admins.includes(myAddress) && !secretKey && triedToFetchSecretKey ? null : !triedToFetchSecretKey ? null : ( <> - - {admins.includes(myAddress) && shouldReEncrypt && - triedToFetchSecretKey && !firstSecretKeyInCreation && ( + triedToFetchSecretKey && + !firstSecretKeyInCreation && ( )} @@ -1686,8 +1808,6 @@ export const Group = ({ isOwner={groupOwner?.owner === myAddress} /> )} - - )} @@ -1721,6 +1841,8 @@ export const Group = ({ width: "100%", flexDirection: "column", gap: "20px", + height: '100%', + overflow: 'auto' }} > )} - + + + + + Home + + + {selectedGroup && ( + <> { - setGroupSection("default"); - clearAllQueues() - await new Promise((res) => { - setTimeout(() => { - res(null); - }, 200); - }); - setGroupSection("home"); - setSelectedGroup(null); - setNewChat(false); - setSelectedDirect(null); - setSecretKey(null); - lastFetchedSecretKey.current = null - setSecretKeyPublishDate(null); - setAdmins([]); - setSecretKeyDetails(null); - setAdminsWithNames([]); - setMembers([]); - setMemberCountFromSecretKeyData(null); - setTriedToFetchSecretKey(false); - setFirstSecretKeyInCreation(false) - }} - > - - - Home - - - {selectedGroup && ( - <> - - { - setGroupSection("default"); - await new Promise((res) => { - setTimeout(() => { - res(null); - }, 200); - }); - setGroupSection("announcement"); - chrome.runtime.sendMessage({ - action: "addGroupNotificationTimestamp", - payload: { - timestamp: Date.now(), - groupId: selectedGroupRef.current.groupId, - }, - }); - setTimeout(() => { - getGroupAnnouncements(); - }, 200); + cursor: "pointer", }} + onClick={goToAnnouncements} > Announcements @@ -1899,33 +1981,11 @@ export const Group = ({ alignItems: "center", justifyContent: "flex-start", width: "100%", - cursor: 'pointer' - }} - onClick={async () => { - setGroupSection("default"); - await new Promise((res) => { - setTimeout(() => { - res(null); - }, 200); - }); - setGroupSection("chat"); - if (selectedGroupRef.current) { - chrome.runtime.sendMessage({ - action: "addTimestampEnterChat", - payload: { - timestamp: Date.now(), - groupId: selectedGroupRef.current.groupId, - }, - }); - - setTimeout(() => { - getTimestampEnterChat(); - }, 200); - } + cursor: "pointer", }} + onClick={goToChat} > Chat @@ -1959,14 +2019,13 @@ export const Group = ({ alignItems: "center", justifyContent: "flex-start", width: "100%", - cursor: 'pointer' + cursor: "pointer", }} onClick={() => { setGroupSection("forum"); }} > setOpenManageMembers(true)} + onClick={() => setOpenManageMembers(true)} sx={{ display: "flex", gap: "3px", alignItems: "center", justifyContent: "flex-start", width: "100%", - cursor: 'pointer' + cursor: "pointer", }} > - - + Members - - )} - - {/* + )} + + {/* */} - + -
+ {renderGroups()} + + + + {isMobile && ( + + + {selectedGroup && ( + <> + + + + + + + + + + + + + + )} + + {/* Second row: Groups, Home, Profile */} + + + + + + + + + + setIsOpenDrawerProfile(true)} + > + + + + + + +)} + + ); }; diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index 5a22678..12efdda 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -26,7 +26,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { setIsLoadingInvite(true) if (!expiryTime || !value) return; new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "inviteToGroup", payload: { diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 6cd1236..b25d21e 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -74,7 +74,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { }) setIsLoadingUnban(true) new Promise((res, rej)=> { - chrome.runtime.sendMessage({ action: "cancelBan", payload: { + chrome?.runtime?.sendMessage({ action: "cancelBan", payload: { groupId, qortalAddress: address, }}, (response) => { diff --git a/src/components/Group/ListOfInvites.tsx b/src/components/Group/ListOfInvites.tsx index b6a9734..ff6359e 100644 --- a/src/components/Group/ListOfInvites.tsx +++ b/src/components/Group/ListOfInvites.tsx @@ -75,7 +75,7 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => }) setIsLoadingCancelInvite(true) await new Promise((res, rej)=> { - chrome.runtime.sendMessage({ action: "cancelInvitationToGroup", payload: { + chrome?.runtime?.sendMessage({ action: "cancelInvitationToGroup", payload: { groupId, qortalAddress: address, }}, (response) => { diff --git a/src/components/Group/ListOfJoinRequests.tsx b/src/components/Group/ListOfJoinRequests.tsx index 160a7ca..cb47ab9 100644 --- a/src/components/Group/ListOfJoinRequests.tsx +++ b/src/components/Group/ListOfJoinRequests.tsx @@ -74,7 +74,7 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show } }) setIsLoadingAccept(true) await new Promise((res, rej)=> { - chrome.runtime.sendMessage({ action: "inviteToGroup", payload: { + chrome?.runtime?.sendMessage({ action: "inviteToGroup", payload: { groupId, qortalAddress: address, inviteTime: 10800, diff --git a/src/components/Group/ListOfMembers.tsx b/src/components/Group/ListOfMembers.tsx index 3662a91..6a81cf0 100644 --- a/src/components/Group/ListOfMembers.tsx +++ b/src/components/Group/ListOfMembers.tsx @@ -63,7 +63,7 @@ const ListOfMembers = ({ setIsLoadingKick(true); new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "kickFromGroup", payload: { @@ -107,7 +107,7 @@ const ListOfMembers = ({ }); setIsLoadingBan(true); await new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "banFromGroup", payload: { @@ -153,7 +153,7 @@ const ListOfMembers = ({ }); setIsLoadingMakeAdmin(true); await new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "makeAdmin", payload: { @@ -198,7 +198,7 @@ const ListOfMembers = ({ }); setIsLoadingRemoveAdmin(true); await new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "removeAdmin", payload: { diff --git a/src/components/Group/ListOfThreadPostsWatched.tsx b/src/components/Group/ListOfThreadPostsWatched.tsx index ed8e7d9..b22be24 100644 --- a/src/components/Group/ListOfThreadPostsWatched.tsx +++ b/src/components/Group/ListOfThreadPostsWatched.tsx @@ -23,7 +23,7 @@ export const ListOfThreadPostsWatched = () => { const getPosts = async ()=> { try { await new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "getThreadActivity", payload: { diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index aebb3fb..57e5712 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -77,7 +77,7 @@ export const ManageMembers = ({ }) await new Promise((res, rej) => { - chrome.runtime.sendMessage( + chrome?.runtime?.sendMessage( { action: "leaveGroup", payload: { diff --git a/src/components/Group/UserListOfInvites.tsx b/src/components/Group/UserListOfInvites.tsx index d126024..98f37f1 100644 --- a/src/components/Group/UserListOfInvites.tsx +++ b/src/components/Group/UserListOfInvites.tsx @@ -85,7 +85,7 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => { setIsLoading(true); await new Promise((res, rej)=> { - chrome.runtime.sendMessage({ action: "joinGroup", payload: { + chrome?.runtime?.sendMessage({ action: "joinGroup", payload: { groupId, }}, (response) => { diff --git a/src/components/Group/WebsocketActive.tsx b/src/components/Group/WebsocketActive.tsx index 055f590..33a6dca 100644 --- a/src/components/Group/WebsocketActive.tsx +++ b/src/components/Group/WebsocketActive.tsx @@ -74,7 +74,7 @@ export const WebSocketActive = ({ myAddress, setIsLoadingGroups }) => { ).sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0)); - chrome.runtime.sendMessage({ + chrome?.runtime?.sendMessage({ action: 'handleActiveGroupDataFromSocket', payload: { groups: sortedGroups, diff --git a/src/index.css b/src/index.css index 853afa4..543bec2 100644 --- a/src/index.css +++ b/src/index.css @@ -80,4 +80,8 @@ body { .group-list::-webkit-scrollbar-thumb:hover { background-color: whitesmoke; +} + +html, body { + overscroll-behavior:none !important; } \ No newline at end of file From f032016b83ef925a35e974ca6096cf4477af801b Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 12 Sep 2024 05:41:57 +0300 Subject: [PATCH 02/25] fixes --- src/background.ts | 40 ++++++++++++++++++++++-- src/components/Chat/AnnouncementItem.tsx | 3 +- src/components/Chat/ChatDirect.tsx | 4 +-- src/components/Group/Group.tsx | 1 + 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/background.ts b/src/background.ts index 7413cd3..f604691 100644 --- a/src/background.ts +++ b/src/background.ts @@ -32,7 +32,28 @@ export const groupApiLocal = "http://127.0.0.1:12391" export const groupApiSocketLocal = "ws://127.0.0.1:12391" const timeDifferenceForNotificationChatsBackground = 600000 const requestQueueAnnouncements = new RequestQueueWithPromise(1) +let isMobile = false +const isMobileDevice = () => { + const userAgent = navigator.userAgent || navigator.vendor || window.opera; + + if (/android/i.test(userAgent)) { + return true; // Android device + } + + if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) { + return true; // iOS device + } + + return false; +}; + +if (isMobileDevice()) { + isMobile = true + console.log("Running on a mobile device"); +} else { + console.log("Running on a desktop"); +} const allQueues = { requestQueueAnnouncements: requestQueueAnnouncements, } @@ -237,9 +258,12 @@ if (!oldestLatestTimestamp || oldChat?.timestamp > oldestLatestTimestamp?.timest // { title: 'Go to group' } // ] }); - setTimeout(() => { - chrome.notifications.clear(notificationId); - }, 7000); + if(!isMobile){ + setTimeout(() => { + chrome.notifications.clear(notificationId); + }, 7000); + } + // chrome.runtime.sendMessage( // { // action: "notification", @@ -282,9 +306,11 @@ if (!oldestLatestTimestamp || oldChat?.timestamp > oldestLatestTimestamp?.timest // { title: 'Go to group' } // ] }); + if(!isMobile){ setTimeout(() => { chrome.notifications.clear(notificationId); }, 7000); + } playNotificationSound() // audio.play(); // } @@ -427,9 +453,11 @@ const handleNotification = async (groups)=> { // { title: 'Go to group' } // ] }); + if(!isMobile){ setTimeout(() => { chrome.notifications.clear(notificationId); }, 7000); + } // chrome.runtime.sendMessage( // { // action: "notification", @@ -472,9 +500,11 @@ const handleNotification = async (groups)=> { // { title: 'Go to group' } // ] }); + if(!isMobile){ setTimeout(() => { chrome.notifications.clear(notificationId); }, 7000); + } playNotificationSound() // audio.play(); lastGroupNotification = Date.now() @@ -614,9 +644,11 @@ const checkThreads = async (bringBack) => { // { title: 'Go to group' } // ] }); + if(!isMobile){ setTimeout(() => { chrome.notifications.clear(notificationId); }, 7000); + } playNotificationSound() } const savedtimestampAfter = await getTimestampGroupAnnouncement() @@ -685,9 +717,11 @@ const checkNewMessages = // { title: 'Go to group' } // ] }); + if(!isMobile){ setTimeout(() => { chrome.notifications.clear(notificationId); }, 7000); + } playNotificationSound() } const savedtimestampAfter = await getTimestampGroupAnnouncement() diff --git a/src/components/Chat/AnnouncementItem.tsx b/src/components/Chat/AnnouncementItem.tsx index 0ab0e53..e580d77 100644 --- a/src/components/Chat/AnnouncementItem.tsx +++ b/src/components/Chat/AnnouncementItem.tsx @@ -60,7 +60,8 @@ export const AnnouncementItem = ({ message, messageData, setSelectedAnnouncement { return (
Date: Thu, 12 Sep 2024 18:12:56 +0300 Subject: [PATCH 03/25] optimizations --- src/background.ts | 108 +++++++++++++- src/components/Chat/CreateCommonSecret.tsx | 6 +- src/components/Group/Group.tsx | 164 +++++++++++++++------ src/components/Group/GroupJoinRequests.tsx | 2 +- 4 files changed, 226 insertions(+), 54 deletions(-) diff --git a/src/background.ts b/src/background.ts index f604691..edd5398 100644 --- a/src/background.ts +++ b/src/background.ts @@ -187,6 +187,25 @@ export async function findUsableApi() { throw new Error("No usable API found"); } +export function isExtMsg(data){ + let isMsgFromExtensionGroup = true + try { + const decode1 = atob(data) + const decode2 = atob(decode1) + const keyStr = decode2.slice(0, 10); + + // Convert the key string back to a number + const highestKey = parseInt(keyStr, 10); + if(isNaN(highestKey)){ + isMsgFromExtensionGroup = false + } + } catch (error) { + isMsgFromExtensionGroup = false + } +console.log('isMsgFromExtensionGroup', isMsgFromExtensionGroup) + return isMsgFromExtensionGroup +} + async function checkWebviewFocus() { return new Promise((resolve) => { @@ -440,7 +459,8 @@ const handleNotification = async (groups)=> { if(checkDifference(newestLatestTimestamp.timestamp) && !oldestLatestTimestamp || (newestLatestTimestamp && newestLatestTimestamp?.timestamp > oldestLatestTimestamp?.timestamp)){ if (!lastGroupNotification || ((Date.now() - lastGroupNotification) >= 120000)) { - + if(!newestLatestTimestamp?.data || !isExtMsg(newestLatestTimestamp?.data)) return + console.log('newestLatestTimestamp', newestLatestTimestamp) const notificationId = 'chat_notification_' + Date.now() + '_type=group' + `_from=${newestLatestTimestamp.groupId}`; chrome.notifications.create(notificationId, { @@ -2318,6 +2338,57 @@ async function addTimestampGroupAnnouncement({groupId, timestamp, seenTimestamp} }); } +async function getGroupData(){ + const wallet = await getSaveWallet(); + const address = wallet.address0; + const key = `group-data-${address}` + const res = await chrome.storage.local.get([key]); +if (res?.[key]) { + const parsedData = JSON.parse(res[key]) + return parsedData; +} else { + return {} +} +} +async function getGroupDataSingle(groupId){ + const wallet = await getSaveWallet(); + const address = wallet.address0; + const key = `group-data-${address}` + const res = await chrome.storage.local.get([key]); +if (res?.[key]) { + const parsedData = JSON.parse(res[key]) + return parsedData[groupId] || null; +} else { + return null +} +} + +async function setGroupData({groupId, + secretKeyData, + secretKeyResource, + admins}){ + const wallet = await getSaveWallet(); + const address = wallet.address0; + const data = await getGroupData() || {} + data[groupId] = { + timestampLastSet: Date.now(), + admins, + secretKeyData, + secretKeyResource, + } + const dataString = JSON.stringify(data); + return await new Promise((resolve, reject) => { + chrome.storage.local.set({ [`group-data-${address}`]: dataString }, () => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(true); + } + }); + }); +} + + async function addTimestampEnterChat({groupId, timestamp}){ const wallet = await getSaveWallet(); @@ -2821,6 +2892,41 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { }); break; } + case "setGroupData": { + + const { groupId, + secretKeyData, + secretKeyResource, + admins} = + request.payload; + setGroupData({ + groupId, + secretKeyData, + secretKeyResource, + admins + }) + .then((res) => { + sendResponse(res); + }) + .catch((error) => { + sendResponse({ error: error.message }); + console.error(error.message); + }); + break; + } + case "getGroupDataSingle": { + const {groupId} = request.payload + getGroupDataSingle(groupId) + .then((res) => { + sendResponse(res); + }) + .catch((error) => { + sendResponse({ error: error.message }); + console.error(error.message); + }); + return true + break; + } case "getTimestampEnterChat": { getTimestampEnterChat() .then((res) => { diff --git a/src/components/Chat/CreateCommonSecret.tsx b/src/components/Chat/CreateCommonSecret.tsx index 119fb52..2bfa604 100644 --- a/src/components/Chat/CreateCommonSecret.tsx +++ b/src/components/Chat/CreateCommonSecret.tsx @@ -51,11 +51,11 @@ export const CreateCommonSecret = ({groupId, secretKey, isOwner, myAddress, sec - const groupAdmins = await getGroupAdimns(groupId); - if(!groupAdmins.length){ + const {names} = await getGroupAdimns(groupId); + if(!names.length){ throw new Error('Network error') } - const publish = await getPublishesFromAdmins(groupAdmins); + const publish = await getPublishesFromAdmins(names); if (publish === false) { diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index f66b085..be246e6 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -73,6 +73,7 @@ import { WebSocketActive } from "./WebsocketActive"; import { flushSync } from "react-dom"; import { useMessageQueue } from "../../MessageQueueContext"; import { DrawerComponent } from "../Drawer/Drawer"; +import { isExtMsg } from "../../background"; // let touchStartY = 0; // let disablePullToRefresh = false; @@ -222,6 +223,8 @@ export const getGroupAdimns = async (groupNumber: number) => { ); const groupData = await response.json(); let members: any = []; + let membersAddresses = [] + let both = [] // if (groupData && Array.isArray(groupData?.members)) { // for (const member of groupData.members) { // if (member.member) { @@ -240,14 +243,16 @@ export const getGroupAdimns = async (groupNumber: number) => { }); if (name) { members.push(name); + both.push({name, address: member.member}) } + membersAddresses.push(member.member) } return true; }); await Promise.all(getMemNames); - return members; + return {names: members, addresses: membersAddresses, both}; }; export const getNames = async (listOfMembers) => { @@ -327,7 +332,7 @@ export const Group = ({ const [directs, setDirects] = useState([]); const [admins, setAdmins] = useState([]); const [adminsWithNames, setAdminsWithNames] = useState([]); - +console.log('adminsWithNames', {adminsWithNames}, {admins}) const [members, setMembers] = useState([]); const [groupOwner, setGroupOwner] = useState(null); const [triedToFetchSecretKey, setTriedToFetchSecretKey] = useState(false); @@ -342,7 +347,7 @@ export const Group = ({ const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); const [isLoadingNotifyAdmin, setIsLoadingNotifyAdmin] = React.useState(false); - const [isLoadingGroups, setIsLoadingGroups] = React.useState(false); + const [isLoadingGroups, setIsLoadingGroups] = React.useState(true); const [isLoadingGroup, setIsLoadingGroup] = React.useState(false); const [firstSecretKeyInCreation, setFirstSecretKeyInCreation] = React.useState(false); @@ -361,7 +366,7 @@ export const Group = ({ const setupGroupWebsocketInterval = useRef(null); const settimeoutForRefetchSecretKey = useRef(null); const { clearStatesMessageQueueProvider } = useMessageQueue(); - + const initiatedGetMembers = useRef(false); // useEffect(()=> { // setFullHeight() // }, []) @@ -399,6 +404,30 @@ export const Group = ({ }); } catch (error) {} }; + const getGroupDataSingle = async (groupId) => { + try { + return new Promise((res, rej) => { + chrome?.runtime?.sendMessage( + { + action: "getGroupDataSingle", + payload: { + groupId + } + }, + (response) => { + if (!response?.error) { + + res(response); + return + } + rej(response.error); + } + ); + }); + } catch (error) { + return {} + } + }; const refreshHomeDataFunc = () => { setGroupSection("default"); setTimeout(() => { @@ -539,7 +568,20 @@ export const Group = ({ secretKeyToPublish?: boolean ) => { try { + // setGroupDataLastSet(null) pauseAllQueues(); + let dataFromStorage + let publishFromStorage + let adminsFromStorage + const groupData = await getGroupDataSingle(selectedGroup?.groupId) + console.log('groupData', groupData) + if(groupData?.secretKeyData && Date.now() - groupData?.timestampLastSet < 3600000 ){ + + dataFromStorage = groupData.secretKeyData + publishFromStorage = groupData.secretKeyResource + adminsFromStorage = groupData.admins + // setGroupDataLastSet(groupData.timestampLastSet) + } if ( secretKeyToPublish && @@ -559,12 +601,13 @@ export const Group = ({ } const prevGroupId = selectedGroupRef.current.groupId; // const validApi = await findUsableApi(); - const groupAdmins = await getGroupAdimns(selectedGroup?.groupId); - setAdmins(groupAdmins); - if (!groupAdmins.length) { + const {names, addresses, both} = adminsFromStorage || await getGroupAdimns(selectedGroup?.groupId); + setAdmins(addresses); + setAdminsWithNames(both) + if (!names.length) { throw new Error("Network error"); } - const publish = await getPublishesFromAdmins(groupAdmins); + const publish = publishFromStorage || await getPublishesFromAdmins(names); if (prevGroupId !== selectedGroupRef.current.groupId) { if (settimeoutForRefetchSecretKey.current) { @@ -580,13 +623,18 @@ export const Group = ({ return false; } setSecretKeyPublishDate(publish?.updated || publish?.created); - - const res = await fetch( - `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ - publish.identifier - }?encoding=base64` - ); - const data = await res.text(); + let data + if(dataFromStorage){ + data = dataFromStorage + } else { + const res = await fetch( + `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ + publish.identifier + }?encoding=base64` + ); + data = await res.text(); + } + const decryptedKey: any = await decryptResource(data); @@ -599,6 +647,17 @@ export const Group = ({ setSecretKey(decryptedKeyToObject); lastFetchedSecretKey.current = Date.now(); setMemberCountFromSecretKeyData(decryptedKey.count); + chrome?.runtime?.sendMessage( + { + action: "setGroupData", + payload: { + groupId: selectedGroup?.groupId, + secretKeyData: data, + secretKeyResource: publish, + admins: {names, addresses, both} + }, + } + ); if (decryptedKeyToObject) { setTriedToFetchSecretKey(true); setFirstSecretKeyInCreation(false); @@ -616,7 +675,7 @@ export const Group = ({ } finally { setIsLoadingGroup(false); if (!secretKeyToPublish) { - await getAdmins(selectedGroup?.groupId); + // await getAdmins(selectedGroup?.groupId); } resumeAllQueues(); } @@ -828,11 +887,14 @@ export const Group = ({ } catch (error) {} }; useEffect(() => { - if (selectedGroup?.groupId) { + if (!initiatedGetMembers.current && selectedGroup?.groupId && secretKey && admins.includes(myAddress)) { + + console.log('goung through', admins) // getAdmins(selectedGroup?.groupId); getMembers(selectedGroup?.groupId); + initiatedGetMembers.current = true } - }, [selectedGroup?.groupId]); + }, [selectedGroup?.groupId, secretKey, myAddress, admins]); const shouldReEncrypt = useMemo(() => { if (triedToFetchSecretKey && !secretKeyPublishDate) return true; @@ -908,6 +970,7 @@ export const Group = ({ .filter((group) => group?.sender !== myAddress) .find((gr) => gr?.groupId === selectedGroup?.groupId); if (!findGroup) return false; + if(!findGroup?.data || !isExtMsg(findGroup?.data)) return false return ( findGroup?.timestamp && ((!timestampEnterData[selectedGroup?.groupId] && @@ -1015,6 +1078,7 @@ export const Group = ({ isLoadingOpenSectionFromNotification.current = false; setupGroupWebsocketInterval.current = null; settimeoutForRefetchSecretKey.current = null; + initiatedGetMembers.current = false }; const logoutEventFunc = () => { @@ -1537,7 +1601,7 @@ export const Group = ({ }} /> )} - {group?.sender !== myAddress && + {group?.data && isExtMsg(group?.data) && group?.sender !== myAddress && group?.timestamp && ((!timestampEnterData[group?.groupId] && Date.now() - group?.timestamp < @@ -1864,36 +1928,38 @@ export const Group = ({ Refresh home data - - - - - - - + {!isLoadingGroups && ( + + + + + + + )} + )} { const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([]) From eebcd8c36f21db746728516af2783e9bcbf0743d Mon Sep 17 00:00:00 2001 From: PhilReact Date: Thu, 12 Sep 2024 22:23:47 +0300 Subject: [PATCH 04/25] optimizations, see ui direct chat, fixes --- package-lock.json | 26 +++ package.json | 2 + src/App.tsx | 9 +- src/background.ts | 54 ++++-- src/components/Chat/ChatDirect.tsx | 204 +++++++++++++++------ src/components/Chat/ChatGroup.tsx | 6 +- src/components/Chat/GroupAnnouncements.tsx | 8 +- src/components/Chat/GroupForum.tsx | 2 +- src/components/Chat/MessageItem.tsx | 8 +- src/components/Group/Forum/GroupMail.tsx | 6 +- src/components/Group/Group.tsx | 36 +++- 11 files changed, 262 insertions(+), 99 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d8df9c..0fd15ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,9 +18,11 @@ "@testing-library/jest-dom": "^6.4.6", "@testing-library/user-event": "^14.5.2", "@tiptap/extension-color": "^2.5.9", + "@tiptap/extension-highlight": "^2.6.6", "@tiptap/extension-image": "^2.6.6", "@tiptap/extension-placeholder": "^2.6.2", "@tiptap/extension-text-style": "^2.5.9", + "@tiptap/extension-underline": "^2.6.6", "@tiptap/pm": "^2.5.9", "@tiptap/react": "^2.5.9", "@tiptap/starter-kit": "^2.5.9", @@ -2300,6 +2302,18 @@ "@tiptap/core": "^2.5.9" } }, + "node_modules/@tiptap/extension-highlight": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.6.6.tgz", + "integrity": "sha512-Z02AYWm1AJAfhmfT4fGCI3YitijF4uNu+eiuq7OxhCiVf9IYaq8xlH2YMxa09QvMUo70ovklxk97+vQUUHeqfQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, "node_modules/@tiptap/extension-history": { "version": "2.5.9", "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.5.9.tgz", @@ -2435,6 +2449,18 @@ "@tiptap/core": "^2.5.9" } }, + "node_modules/@tiptap/extension-underline": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.6.6.tgz", + "integrity": "sha512-3A4HqsDM/AFb2VaeWACpGexjgI257kz0yU4jNV8uyydDR2KhqeinuEnoSoOmx9T3pL006TWfPg4vaQYPO3qvrQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.6.6" + } + }, "node_modules/@tiptap/pm": { "version": "2.6.6", "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.6.6.tgz", diff --git a/package.json b/package.json index 9ca8095..2f65a5b 100644 --- a/package.json +++ b/package.json @@ -22,9 +22,11 @@ "@testing-library/jest-dom": "^6.4.6", "@testing-library/user-event": "^14.5.2", "@tiptap/extension-color": "^2.5.9", + "@tiptap/extension-highlight": "^2.6.6", "@tiptap/extension-image": "^2.6.6", "@tiptap/extension-placeholder": "^2.6.2", "@tiptap/extension-text-style": "^2.5.9", + "@tiptap/extension-underline": "^2.6.6", "@tiptap/pm": "^2.5.9", "@tiptap/react": "^2.5.9", "@tiptap/starter-kit": "^2.5.9", diff --git a/src/App.tsx b/src/App.tsx index f9d06b0..7236213 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1263,6 +1263,7 @@ function App() { { setExtstate("send-qort"); + setIsOpenDrawerProfile(false) }} > Transfer QORT @@ -1291,6 +1292,7 @@ function App() { { setExtstate("download-wallet"); + setIsOpenDrawerProfile(false) }} src={Download} style={{ @@ -1300,7 +1302,10 @@ function App() { { + logoutFunc() + setIsOpenDrawerProfile(false) + }} style={{ cursor: "pointer", }} @@ -1554,7 +1559,7 @@ function App() { { const address = wallet.address0; const dataDirects = directs.filter((direct)=> direct?.sender !== address) try { - + if(!dataDirects || dataDirects?.length === 0) return isFocused = await checkWebviewFocus() if(isFocused){ @@ -264,7 +264,7 @@ if (!oldestLatestTimestamp || oldChat?.timestamp > oldestLatestTimestamp?.timest } }); - + console.log('newestLatestTimestamp', newestLatestTimestamp) if(checkDifference(newestLatestTimestamp.timestamp) && !oldestLatestTimestamp || (newestLatestTimestamp && newestLatestTimestamp?.timestamp > oldestLatestTimestamp?.timestamp)){ const notificationId = 'chat_notification_' + Date.now() + '_type=direct' + `_from=${newestLatestTimestamp.address}`; chrome.notifications.create(notificationId, { @@ -1062,7 +1062,7 @@ async function getNameOrAddress(receiver) { } } -async function getPublicKey(receiver) { +export async function getPublicKey(receiver) { try { const validApi = await getBaseApi() @@ -1326,8 +1326,8 @@ async function sendChatGroup({ privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const balance = await getBalanceInfo(); - const hasEnoughBalance = +balance < 4 ? false : true; + // const balance = await getBalanceInfo(); + // const hasEnoughBalance = +balance < 4 ? false : true; const difficulty = 8; const tx = await createTransaction(181, keyPair, { @@ -1343,9 +1343,9 @@ async function sendChatGroup({ isText: 1, }); - if (!hasEnoughBalance) { - throw new Error("Must have at least 4 QORT to send a chat message"); - } + // if (!hasEnoughBalance) { + // throw new Error("Must have at least 4 QORT to send a chat message"); + // } const path = chrome.runtime.getURL("memory-pow.wasm.full"); const { nonce, chatBytesArray } = await computePow({ @@ -1366,15 +1366,27 @@ async function sendChatGroup({ } async function sendChatDirect({ + address, directTo, typeMessage, chatReference, messageText, + publicKeyOfRecipient }) { + let recipientPublicKey + let recipientAddress = address + if(publicKeyOfRecipient){ + recipientPublicKey = publicKeyOfRecipient + } else { + recipientAddress = await getNameOrAddress(directTo) + recipientPublicKey = await getPublicKey(recipientAddress) + } + if(!recipientAddress){ + recipientAddress = await getNameOrAddress(directTo) + } - const recipientAddress = await getNameOrAddress(directTo) - const recipientPublicKey = await getPublicKey(recipientAddress) + if(!recipientPublicKey) throw new Error('Cannot retrieve publickey') @@ -1390,8 +1402,8 @@ async function sendChatDirect({ privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const balance = await getBalanceInfo(); - const hasEnoughBalance = +balance < 4 ? false : true; + // const balance = await getBalanceInfo(); + // const hasEnoughBalance = +balance < 4 ? false : true; const difficulty = 8; @@ -1412,9 +1424,9 @@ async function sendChatDirect({ isText: 1, }); - if (!hasEnoughBalance) { - throw new Error("Must have at least 4 QORT to send a chat message"); - } + // if (!hasEnoughBalance) { + // throw new Error("Must have at least 4 QORT to send a chat message"); + // } const path = chrome.runtime.getURL("memory-pow.wasm.full"); @@ -1490,6 +1502,7 @@ async function decryptDirectFunc({ messages, involvingAddress }) { publicKey: uint8PublicKey, }; for (const message of messages) { + console.log('messagedep', message) try { const decodedMessage = decryptChatMessage( message.data, @@ -3607,9 +3620,11 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { typeMessage = undefined, chatReference = undefined, messageText, + publicKeyOfRecipient, + address } = request.payload; - sendChatDirect({ directTo, chatReference, messageText, typeMessage }) + sendChatDirect({ directTo, chatReference, messageText, typeMessage, publicKeyOfRecipient, address }) .then((res) => { sendResponse(res); @@ -3669,8 +3684,9 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { const wallet = await getSaveWallet(); const address = wallet.address0; const key1 = `tempPublish-${address}` - - chrome.storage.local.remove(["keyPair", "walletInfo", "apiKey", "active-groups-directs", key1], () => { + const key2 = `group-data-${address}` + + chrome.storage.local.remove(["keyPair", "walletInfo", "apiKey", "active-groups-directs", key1, key2], () => { if (chrome.runtime.lastError) { // Handle error console.error(chrome.runtime.lastError.message); @@ -4009,7 +4025,7 @@ chrome.runtime?.onInstalled.addListener((details) => { chrome.alarms?.get("checkForNotifications", (existingAlarm) => { if (!existingAlarm) { // If the alarm does not exist, create it - chrome.alarms.create("checkForNotifications", { periodInMinutes: 4 }); + chrome.alarms.create("checkForNotifications", { periodInMinutes: 10 }); } }); diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 6b35eec..8ab3e8c 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -11,13 +11,18 @@ import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { getNameInfo } from '../Group/Group'; import { Spacer } from '../../common/Spacer'; import { CustomizedSnackbars } from '../Snackbar/Snackbar'; -import { getBaseApiReactSocket, isMobile } from '../../App'; +import { getBaseApiReactSocket, isMobile, pauseAllQueues, resumeAllQueues } from '../../App'; +import { getPublicKey } from '../../background'; +import { useMessageQueue } from '../../MessageQueueContext'; +import { executeEvent } from '../../utils/events'; -export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDirect, setNewChat, getTimestampEnterChat, myName}) => { +export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDirect, setNewChat, getTimestampEnterChat, myName, balance}) => { + const { queueChats, addToQueue, } = useMessageQueue(); + const [messages, setMessages] = useState([]) const [isSending, setIsSending] = useState(false) const [directToValue, setDirectToValue] = useState('') @@ -25,14 +30,41 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi const [isLoading, setIsLoading] = useState(false) const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); + const [publicKeyOfRecipient, setPublicKeyOfRecipient] = React.useState("") const hasInitializedWebsocket = useRef(false) const editorRef = useRef(null); + const socketRef = useRef(null); + const timeoutIdRef = useRef(null); + const groupSocketTimeoutRef = useRef(null); + const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; - + const publicKeyOfRecipientRef = useRef(null) + const getPublicKeyFunc = async (address)=> { + try { + const publicKey = await getPublicKey(address) + if(publicKeyOfRecipientRef.current !== selectedDirect?.address) return + setPublicKeyOfRecipient(publicKey) + } catch (error) { + + } + } + const tempMessages = useMemo(()=> { + if(!selectedDirect?.address) return [] + if(queueChats[selectedDirect?.address]){ + return queueChats[selectedDirect?.address] + } + return [] + }, [selectedDirect?.address, queueChats]) + useEffect(()=> { + if(selectedDirect?.address){ + publicKeyOfRecipientRef.current = selectedDirect?.address + getPublicKeyFunc(publicKeyOfRecipientRef.current) + } + }, [selectedDirect?.address]) @@ -54,7 +86,7 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi ...item, id: item.signature, text: item.message, - unread: true + unread: item?.sender === myAddress ? false : true } } ) setMessages((prev)=> [...prev, ...formatted]) @@ -80,78 +112,106 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi } } + const forceCloseWebSocket = () => { + if (socketRef.current) { + console.log('Force closing the WebSocket'); + clearTimeout(timeoutIdRef.current); + clearTimeout(groupSocketTimeoutRef.current); + socketRef.current.close(1000, 'forced'); + socketRef.current = null; + } + }; + + const pingWebSocket = () => { + try { + if (socketRef.current?.readyState === WebSocket.OPEN) { + socketRef.current.send('ping'); + timeoutIdRef.current = setTimeout(() => { + if (socketRef.current) { + socketRef.current.close(); + clearTimeout(groupSocketTimeoutRef.current); + } + }, 5000); // Close if no pong in 5 seconds + } + } catch (error) { + console.error('Error during ping:', error); + } + }; + + const initWebsocketMessageGroup = () => { - let timeoutId - let groupSocketTimeout - - let socketTimeout: any - let socketLink = `${getBaseApiReactSocket()}/websockets/chat/messages?involving=${selectedDirect?.address}&involving=${myAddress}&encoding=BASE64&limit=100` - const socket = new WebSocket(socketLink) - - const pingGroupSocket = () => { - socket.send('ping') - timeoutId = setTimeout(() => { - socket.close() - clearTimeout(groupSocketTimeout) - }, 5000) // Close the WebSocket connection if no pong message is received within 5 seconds. - } - socket.onopen = () => { - - setTimeout(pingGroupSocket, 50) - } - socket.onmessage = (e) => { + forceCloseWebSocket(); // Close any existing connection + + if (!selectedDirect?.address || !myAddress) return; + + const socketLink = `${getBaseApiReactSocket()}/websockets/chat/messages?involving=${selectedDirect?.address}&involving=${myAddress}&encoding=BASE64&limit=100`; + socketRef.current = new WebSocket(socketLink); + + socketRef.current.onopen = () => { + console.log('WebSocket connection opened'); + setTimeout(pingWebSocket, 50); // Initial ping + }; + + socketRef.current.onmessage = (e) => { try { if (e.data === 'pong') { - - clearTimeout(timeoutId) - groupSocketTimeout = setTimeout(pingGroupSocket, 45000) - return + clearTimeout(timeoutIdRef.current); + groupSocketTimeoutRef.current = setTimeout(pingWebSocket, 45000); // Ping every 45 seconds } else { - decryptMessages(JSON.parse(e.data)) - setIsLoading(false) + decryptMessages(JSON.parse(e.data)); + setIsLoading(false); } - } catch (error) { - + console.error('Error handling WebSocket message:', error); } - - } - socket.onclose = () => { - console.log('closed') - clearTimeout(socketTimeout) - setTimeout(() => initWebsocketMessageGroup(), 50) - - } - socket.onerror = (e) => { - clearTimeout(groupSocketTimeout) - socket.close() - } - } + }; + + socketRef.current.onclose = (event) => { + clearTimeout(groupSocketTimeoutRef.current); + clearTimeout(timeoutIdRef.current); + console.warn(`WebSocket closed: ${event.reason || 'unknown reason'}`); + if (event.reason !== 'forced' && event.code !== 1000) { + setTimeout(() => initWebsocketMessageGroup(), 10000); // Retry after 10 seconds + } + }; + + socketRef.current.onerror = (error) => { + console.error('WebSocket error:', error); + clearTimeout(groupSocketTimeoutRef.current); + clearTimeout(timeoutIdRef.current); + if (socketRef.current) { + socketRef.current.close(); + } + }; + }; - useEffect(()=> { - if(hasInitializedWebsocket.current) return - setIsLoading(true) - initWebsocketMessageGroup() - hasInitializedWebsocket.current = true - }, []) + useEffect(() => { + if (hasInitializedWebsocket.current || isNewChat) return; + setIsLoading(true); + initWebsocketMessageGroup(); + hasInitializedWebsocket.current = true; + return () => { + forceCloseWebSocket(); // Clean up WebSocket on component unmount + }; + }, [selectedDirect?.address, myAddress, isNewChat]); -const sendChatDirect = async ({ chatReference = undefined, messageText}: any)=> { +const sendChatDirect = async ({ chatReference = undefined, messageText}: any, address, publicKeyOfRecipient, isNewChatVar)=> { try { - const directTo = isNewChat ? directToValue : selectedDirect.address + const directTo = isNewChatVar ? directToValue : address if(!directTo) return return new Promise((res, rej)=> { chrome?.runtime?.sendMessage({ action: "sendChatDirect", payload: { - directTo, chatReference, messageText + directTo, chatReference, messageText, publicKeyOfRecipient, address: directTo }}, async (response) => { if (!response?.error) { - if(isNewChat){ + if(isNewChatVar){ let getRecipientName = null try { @@ -186,6 +246,7 @@ const sendChatDirect = async ({ chatReference = undefined, messageText}: any)=> }) } catch (error) { throw new Error(error) + } finally { } } const clearEditorContent = () => { @@ -197,33 +258,60 @@ const clearEditorContent = () => { const sendMessage = async ()=> { try { + if(+balance < 4) throw new Error('You need at least 4 QORT to send a message') if(isSending) return if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); if(!htmlContent?.trim() || htmlContent?.trim() === '

') return setIsSending(true) + pauseAllQueues() const message = JSON.stringify(htmlContent) - const res = await sendChatDirect({ messageText: htmlContent}) + + if(isNewChat){ + await sendChatDirect({ messageText: htmlContent}, null, null, true) + return + } + const sendMessageFunc = async () => { + await sendChatDirect({ messageText: htmlContent}, selectedDirect?.address, publicKeyOfRecipient, false) + }; + + // Add the function to the queue + const messageObj = { + message: { + text: htmlContent, + timestamp: Date.now(), + senderName: myName, + sender: myAddress + }, + + } + addToQueue(sendMessageFunc, messageObj, 'chat-direct', + selectedDirect?.address ); + setTimeout(() => { + executeEvent("sent-new-message-group", {}) + }, 150); + clearEditorContent() clearEditorContent() } // send chat message } catch (error) { + const errorMsg = error?.message || error setInfoSnack({ type: "error", - message: error, + message: errorMsg === 'invalid signature' ? 'You need at least 4 QORT to send a message' : errorMsg, }); setOpenSnack(true); console.error(error) } finally { setIsSending(false) + resumeAllQueues() } } - return (
{ )} - +
{ +export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, myAddress, handleNewEncryptionNotification, hide, handleSecretKeyCreationInProgress, triedToFetchSecretKey, myName, balance}) => { const [messages, setMessages] = useState([]) const [isSending, setIsSending] = useState(false) const [isLoading, setIsLoading] = useState(false) @@ -277,6 +277,7 @@ const clearEditorContent = () => { const sendMessage = async ()=> { try { if(isSending) return + if(+balance < 4) throw new Error('You need at least 4 QORT to send a message') pauseAllQueues() if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); @@ -313,9 +314,10 @@ const clearEditorContent = () => { } // send chat message } catch (error) { + const errorMsg = error?.message || error setInfoSnack({ type: "error", - message: error, + message: errorMsg, }); setOpenSnack(true); console.error(error) diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index bf94e1c..d592523 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -335,11 +335,11 @@ export const GroupAnnouncements = ({ ); React.useEffect(() => { - if (selectedGroup && secretKey && !hasInitialized.current) { + if (selectedGroup && secretKey && !hasInitialized.current && !hide) { getAnnouncements(selectedGroup); hasInitialized.current = true } - }, [selectedGroup, secretKey]); + }, [selectedGroup, secretKey, hide]); const loadMore = async()=> { @@ -428,14 +428,14 @@ export const GroupAnnouncements = ({ }, [checkNewMessages]) useEffect(() => { - if(!secretKey) return + if(!secretKey || hide) return checkNewMessagesFunc() return () => { if (interval?.current) { clearInterval(interval.current) } } - }, [checkNewMessagesFunc]) + }, [checkNewMessagesFunc, hide]) diff --git a/src/components/Chat/GroupForum.tsx b/src/components/Chat/GroupForum.tsx index 72b4a8b..6ac300d 100644 --- a/src/components/Chat/GroupForum.tsx +++ b/src/components/Chat/GroupForum.tsx @@ -46,7 +46,7 @@ export const GroupForum = ({ left: hide && '-1000px' }} > - +
); diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index b53ce75..d940d69 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -6,7 +6,10 @@ import { Avatar, Box, Typography } from "@mui/material"; import { formatTimestamp } from "../../utils/time"; import { getBaseApi } from "../../background"; import { getBaseApiReact } from "../../App"; - +import { generateHTML } from "@tiptap/react"; +import Highlight from '@tiptap/extension-highlight' +import StarterKit from '@tiptap/starter-kit' +import Underline from '@tiptap/extension-underline' export const MessageItem = ({ message, onSeen, isLast, isTemp }) => { const { ref, inView } = useInView({ @@ -60,6 +63,9 @@ export const MessageItem = ({ message, onSeen, isLast, isTemp }) => { > {message?.senderName || message?.sender} + {message?.messageText && ( + + )} {message?.text?.type === "notification" ? ( ) : ( diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 48bc62e..d1ce85b 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -63,7 +63,8 @@ export const GroupMail = ({ getSecretKey, secretKey, defaultThread, - setDefaultThread + setDefaultThread, + hide }) => { const [viewedThreads, setViewedThreads] = React.useState({}); const [filterMode, setFilterMode] = useState("Recently active"); @@ -353,6 +354,7 @@ export const GroupMail = ({ const filterModeRef = useRef(""); useEffect(() => { + if(hide) return if (filterModeRef.current !== filterMode) { firstMount.current = false; } @@ -368,7 +370,7 @@ export const GroupMail = ({ setTempData() firstMount.current = true; } - }, [groupId, members, filterMode]); + }, [groupId, members, filterMode, hide]); const closeThread = useCallback(() => { setCurrentThread(null); diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index be246e6..6e6a14a 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -332,7 +332,6 @@ export const Group = ({ const [directs, setDirects] = useState([]); const [admins, setAdmins] = useState([]); const [adminsWithNames, setAdminsWithNames] = useState([]); -console.log('adminsWithNames', {adminsWithNames}, {admins}) const [members, setMembers] = useState([]); const [groupOwner, setGroupOwner] = useState(null); const [triedToFetchSecretKey, setTriedToFetchSecretKey] = useState(false); @@ -574,7 +573,6 @@ console.log('adminsWithNames', {adminsWithNames}, {admins}) let publishFromStorage let adminsFromStorage const groupData = await getGroupDataSingle(selectedGroup?.groupId) - console.log('groupData', groupData) if(groupData?.secretKeyData && Date.now() - groupData?.timestampLastSet < 3600000 ){ dataFromStorage = groupData.secretKeyData @@ -1518,27 +1516,42 @@ console.log('adminsWithNames', {adminsWithNames}, {admins}) setMemberCountFromSecretKeyData(null); setTriedToFetchSecretKey(false); setFirstSecretKeyInCreation(false); - setGroupSection("announcement"); + // setGroupSection("announcement"); + setGroupSection("chat"); setIsOpenDrawer(false) setTimeout(() => { setSelectedGroup(group); - getTimestampEnterChat(); + // getTimestampEnterChat(); }, 200); - if (groupSectionRef.current === "announcement") { + chrome?.runtime?.sendMessage({ - action: "addGroupNotificationTimestamp", + action: "addTimestampEnterChat", payload: { timestamp: Date.now(), groupId: group.groupId, }, }); - } + + setTimeout(() => { + getTimestampEnterChat(); + }, 200); + - setTimeout(() => { - getGroupAnnouncements(); - }, 600); + // if (groupSectionRef.current === "announcement") { + // chrome?.runtime?.sendMessage({ + // action: "addGroupNotificationTimestamp", + // payload: { + // timestamp: Date.now(), + // groupId: group.groupId, + // }, + // }); + // } + + // setTimeout(() => { + // getGroupAnnouncements(); + // }, 600); }} sx={{ display: "flex", @@ -1700,6 +1713,7 @@ console.log('adminsWithNames', {adminsWithNames}, {admins}) setSelectedDirect={setSelectedDirect} setNewChat={setNewChat} getTimestampEnterChat={getTimestampEnterChat} + balance={balance} /> )} @@ -1727,6 +1741,7 @@ console.log('adminsWithNames', {adminsWithNames}, {admins}) } triedToFetchSecretKey={triedToFetchSecretKey} myName={userInfo?.name} + balance={balance} /> )} {firstSecretKeyInCreation && @@ -1892,6 +1907,7 @@ console.log('adminsWithNames', {adminsWithNames}, {admins}) setSelectedDirect={setSelectedDirect} setNewChat={setNewChat} getTimestampEnterChat={getTimestampEnterChat} + myName={userInfo?.name} /> From 93fba38de3a21f4b5829e63a76932186eb917c24 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 14 Sep 2024 06:33:41 +0300 Subject: [PATCH 05/25] mobile update --- src/App.tsx | 63 ++++----- .../Chat/AnnouncementDiscussion.tsx | 57 +++++++- src/components/Chat/ChatDirect.tsx | 64 +++++++-- src/components/Chat/ChatGroup.tsx | 61 ++++++++- src/components/Chat/GroupAnnouncements.tsx | 72 ++++++++-- src/components/Chat/TipTap.tsx | 122 +++++++++++------ src/components/Chat/styles.css | 3 +- src/components/Group/AddGroup.tsx | 84 ++++++------ src/components/Group/AddGroupList.tsx | 2 +- src/components/Group/Forum/NewThread.tsx | 29 ++-- src/components/Group/Group.tsx | 49 ++++++- src/components/Group/ListOfBans.tsx | 2 +- src/components/Group/ListOfInvites.tsx | 2 +- src/components/Group/ListOfJoinRequests.tsx | 2 +- src/components/Group/ListOfMembers.tsx | 2 +- src/components/Group/ManageMembers.tsx | 125 ++++++++++-------- src/components/Group/UserListOfInvites.tsx | 2 +- 17 files changed, 524 insertions(+), 217 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 7236213..d6033d4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -351,25 +351,25 @@ function App() { } }; - const checkIfUserHasLocalNode = useCallback(async () => { - try { - const url = `http://127.0.0.1:12391/admin/status`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - const data = await response.json(); - if (data?.isSynchronizing === false && data?.syncPercent === 100) { - setHasLocalNode(true); - } - } catch (error) {} - }, []); + // const checkIfUserHasLocalNode = useCallback(async () => { + // try { + // const url = `http://127.0.0.1:12391/admin/status`; + // const response = await fetch(url, { + // method: "GET", + // headers: { + // "Content-Type": "application/json", + // }, + // }); + // const data = await response.json(); + // if (data?.isSynchronizing === false && data?.syncPercent === 100) { + // setHasLocalNode(true); + // } + // } catch (error) {} + // }, []); - useEffect(() => { - checkIfUserHasLocalNode(); - }, [extState]); + // useEffect(() => { + // checkIfUserHasLocalNode(); + // }, [extState]); const address = useMemo(() => { if (!rawWallet?.address0) return ""; @@ -1418,7 +1418,7 @@ function App() { }} /> - {hasLocalNode && ( + {!isMobile && ( <> - - - + {!isMobile && ( + + + + )} + )} {extState === "send-qort" && isMainWindow && ( diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index 00c7691..792695a 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -26,6 +26,8 @@ export const AnnouncementDiscussion = ({ }) => { const [isSending, setIsSending] = useState(false); const [isLoading, setIsLoading] = useState(false); + const [isFocusedParent, setIsFocusedParent] = useState(false); + const [comments, setComments] = useState([]) const [tempPublishedList, setTempPublishedList] = useState([]) const firstMountRef = useRef(false) @@ -38,6 +40,12 @@ export const AnnouncementDiscussion = ({ const clearEditorContent = () => { if (editorRef.current) { editorRef.current.chain().focus().clearContent().run(); + if(isMobile){ + setTimeout(() => { + editorRef.current?.chain().blur().run(); + setIsFocusedParent(false) + }, 200); + } } }; @@ -78,6 +86,7 @@ export const AnnouncementDiscussion = ({ if (!response?.error) { res(response); + return } rej(response.error); } @@ -281,14 +290,18 @@ export const AnnouncementDiscussion = ({ // position: 'fixed', // bottom: '0px', backgroundColor: "#232428", - minHeight: "150px", - maxHeight: "400px", + minHeight: isMobile ? "0px" : "150px", + maxHeight: isMobile ? "auto" : "400px", display: "flex", flexDirection: "column", overflow: "hidden", width: "100%", boxSizing: "border-box", - padding: "20px", + padding: isMobile ? "10px": "20px", + position: isFocusedParent ? 'fixed' : 'relative', + bottom: isFocusedParent ? '0px' : 'unset', + top: isFocusedParent ? '0px' : 'unset', + zIndex: isFocusedParent ? 5 : 'unset', flexShrink:0, }} > @@ -297,6 +310,7 @@ export const AnnouncementDiscussion = ({ display: "flex", flexDirection: "column", // height: '100%', + flexGrow: isMobile && 1, overflow: "auto", }} > @@ -304,8 +318,19 @@ export const AnnouncementDiscussion = ({ setEditorRef={setEditorRef} onEnter={publishComment} disableEnter + maxHeightOffset="60px" + isFocusedParent={isFocusedParent} setIsFocusedParent={setIsFocusedParent} + />
+ { if (isSending) return; @@ -317,6 +342,8 @@ export const AnnouncementDiscussion = ({ cursor: isSending ? "default" : "pointer", background: isSending && "rgba(0, 0, 0, 0.8)", flexShrink: 0, + padding: isMobile && '5px', + fontSize: isMobile && '14px' }} > {isSending && ( @@ -334,6 +361,30 @@ export const AnnouncementDiscussion = ({ )} {` Publish Comment`} + {isFocusedParent && ( + { + if(isSending) return + setIsFocusedParent(false) + clearEditorContent() + // Unfocus the editor + }} + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: isSending && 'rgba(0, 0, 0, 0.8)', + flexShrink: 0, + padding: isMobile && '5px', + fontSize: isMobile && '14px', + }} + > + + {` Close`} + + + )} +
{ const { queueChats, addToQueue, } = useMessageQueue(); + const [isFocusedParent, setIsFocusedParent] = useState(false); const [messages, setMessages] = useState([]) const [isSending, setIsSending] = useState(false) @@ -252,6 +254,13 @@ const sendChatDirect = async ({ chatReference = undefined, messageText}: any, ad const clearEditorContent = () => { if (editorRef.current) { editorRef.current.chain().focus().clearContent().run(); + if(isMobile){ + setTimeout(() => { + editorRef.current?.chain().blur().run(); + setIsFocusedParent(false) + executeEvent("sent-new-message-group", {}) + }, 200); + } } }; @@ -293,7 +302,6 @@ const clearEditorContent = () => { executeEvent("sent-new-message-group", {}) }, 150); clearEditorContent() - clearEditorContent() } // send chat message } catch (error) { @@ -310,7 +318,7 @@ const clearEditorContent = () => { } } - +console.log('isFocusedParent', isFocusedParent) return (
{ // position: 'fixed', // bottom: '0px', backgroundColor: "#232428", - minHeight: '150px', - maxHeight: '400px', + minHeight: isMobile ? '0px' : '150px', + maxHeight: isMobile ? 'auto' : '400px', display: 'flex', flexDirection: 'column', overflow: 'hidden', width: '100%', boxSizing: 'border-box', - padding: '20px' + padding: isMobile ? '10px' : '20px', + position: isFocusedParent ? 'fixed' : 'relative', + bottom: isFocusedParent ? '0px' : 'unset', + top: isFocusedParent ? '0px' : 'unset', + zIndex: isFocusedParent ? 5 : 'unset' }}>
- +
+ { if(isSending) return @@ -365,7 +385,8 @@ const clearEditorContent = () => { alignSelf: 'center', cursor: isSending ? 'default' : 'pointer', background: isSending && 'rgba(0, 0, 0, 0.8)', - flexShrink: 0 + flexShrink: 0, + padding: isMobile && '5px' }} > {isSending && ( @@ -383,6 +404,29 @@ const clearEditorContent = () => { )} {` Send`} + {isFocusedParent && ( + { + if(isSending) return + setIsFocusedParent(false) + clearEditorContent() + // Unfocus the editor + }} + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: isSending && 'rgba(0, 0, 0, 0.8)', + flexShrink: 0, + padding: isMobile && '5px' + }} + > + + {` Close`} + + + )} +
{ if (editorRef.current) { editorRef.current.chain().focus().clearContent().run(); + if(isMobile){ + setTimeout(() => { + editorRef.current?.chain().blur().run(); + setIsFocusedParent(false) + executeEvent("sent-new-message-group", {}) + }, 200); + } } }; @@ -354,25 +364,37 @@ const clearEditorContent = () => { // position: 'fixed', // bottom: '0px', backgroundColor: "#232428", - minHeight: '150px', - maxHeight: '400px', + minHeight: isMobile ? '0px' : '150px', + maxHeight: isMobile ? 'auto' : '400px', display: 'flex', flexDirection: 'column', overflow: 'hidden', width: '100%', boxSizing: 'border-box', - padding: '20px' + padding: isMobile ? '10px' : '20px', + position: isFocusedParent ? 'fixed' : 'relative', + bottom: isFocusedParent ? '0px' : 'unset', + top: isFocusedParent ? '0px' : 'unset', + zIndex: isFocusedParent ? 5 : 'unset' }}>
- +
+ { if(isSending) return @@ -383,7 +405,9 @@ const clearEditorContent = () => { alignSelf: 'center', cursor: isSending ? 'default' : 'pointer', background: isSending && 'rgba(0, 0, 0, 0.8)', - flexShrink: 0 + flexShrink: 0, + padding: isMobile && '5px', + }} > {isSending && ( @@ -401,6 +425,29 @@ const clearEditorContent = () => { )} {` Send`} + {isFocusedParent && ( + { + if(isSending) return + setIsFocusedParent(false) + clearEditorContent() + // Unfocus the editor + }} + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: isSending && 'rgba(0, 0, 0, 0.8)', + flexShrink: 0, + padding: isMobile && '5px' + }} + > + + {` Close`} + + + )} + {/* */}
{/* */} diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index d592523..3b11931 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -51,6 +51,7 @@ export const saveTempPublish = async ({ data, key }: any) => { if (!response?.error) { res(response); + return } rej(response.error); } @@ -71,6 +72,7 @@ export const getTempPublish = async () => { (response) => { if (!response?.error) { res(response); + return } rej(response.error); } @@ -135,6 +137,8 @@ export const GroupAnnouncements = ({ const [tempPublishedList, setTempPublishedList] = useState([]) const [announcementData, setAnnouncementData] = useState({}); const [selectedAnnouncement, setSelectedAnnouncement] = useState(null); + const [isFocusedParent, setIsFocusedParent] = useState(false); + const { show } = React.useContext(MyContext); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); @@ -226,6 +230,12 @@ export const GroupAnnouncements = ({ const clearEditorContent = () => { if (editorRef.current) { editorRef.current.chain().focus().clearContent().run(); + if(isMobile){ + setTimeout(() => { + editorRef.current?.chain().blur().run(); + setIsFocusedParent(false) + }, 200); + } } }; @@ -500,18 +510,18 @@ export const GroupAnnouncements = ({ width: "100%", display: "flex", justifyContent: "center", - padding: "25px", - fontSize: "20px", + padding: isMobile ? '8px' : "25px", + fontSize: isMobile ? '16px' : "20px", gap: '20px', alignItems: 'center' }} > Group Announcements - +
{!isLoading && combinedListTempAndReal?.length === 0 && ( @@ -541,31 +551,46 @@ export const GroupAnnouncements = ({ // position: 'fixed', // bottom: '0px', backgroundColor: "#232428", - minHeight: "150px", - maxHeight: "400px", + minHeight: isMobile ? "0px" : "150px", + maxHeight: isMobile ? "auto" : "400px", display: "flex", flexDirection: "column", overflow: "hidden", width: "100%", boxSizing: "border-box", - padding: "20px", - flexShrink: 0 + padding: isMobile ? "10px": "20px", + position: isFocusedParent ? 'fixed' : 'relative', + bottom: isFocusedParent ? '0px' : 'unset', + top: isFocusedParent ? '0px' : 'unset', + zIndex: isFocusedParent ? 5 : 'unset', + flexShrink: 0 }} >
+ { if (isSending) return; @@ -577,6 +602,9 @@ export const GroupAnnouncements = ({ cursor: isSending ? "default" : "pointer", background: isSending && "rgba(0, 0, 0, 0.8)", flexShrink: 0, + padding: isMobile && '5px', + fontSize: isMobile && '14px', + }} > {isSending && ( @@ -594,6 +622,30 @@ export const GroupAnnouncements = ({ )} {` Publish Announcement`} + {isFocusedParent && ( + { + if(isSending) return + setIsFocusedParent(false) + clearEditorContent() + // Unfocus the editor + }} + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: isSending && 'rgba(0, 0, 0, 0.8)', + flexShrink: 0, + padding: isMobile && '5px', + fontSize: isMobile && '14px', + }} + > + + {` Close`} + + + )} + )} diff --git a/src/components/Chat/TipTap.tsx b/src/components/Chat/TipTap.tsx index 4bdf3c6..be6abff 100644 --- a/src/components/Chat/TipTap.tsx +++ b/src/components/Chat/TipTap.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { EditorProvider, useCurrentEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import { Color } from '@tiptap/extension-color'; @@ -25,6 +25,7 @@ import CustomImage from './CustomImage'; import Compressor from 'compressorjs' import ImageResize from 'tiptap-extension-resize-image'; // Import the ResizeImage extension +import { isMobile } from '../../App'; const MenuBar = ({ setEditorRef, isChat }) => { const { editor } = useCurrentEditor(); const fileInputRef = useRef(null); @@ -88,10 +89,11 @@ const MenuBar = ({ setEditorRef, isChat }) => { } // color={editor.isActive('bold') ? 'white' : 'gray'} sx={{ - color: editor.isActive('bold') ? 'white' : 'gray' + color: editor.isActive('bold') ? 'white' : 'gray', + padding: isMobile ? '5px' : 'revert' }} > - + editor.chain().focus().toggleItalic().run()} @@ -104,7 +106,8 @@ const MenuBar = ({ setEditorRef, isChat }) => { } // color={editor.isActive('italic') ? 'white' : 'gray'} sx={{ - color: editor.isActive('italic') ? 'white' : 'gray' + color: editor.isActive('italic') ? 'white' : 'gray', + padding: isMobile ? '5px' : 'revert' }} > @@ -120,7 +123,8 @@ const MenuBar = ({ setEditorRef, isChat }) => { } // color={editor.isActive('strike') ? 'white' : 'gray'} sx={{ - color: editor.isActive('strike') ? 'white' : 'gray' + color: editor.isActive('strike') ? 'white' : 'gray', + padding: isMobile ? '5px' : 'revert' }} > @@ -136,19 +140,23 @@ const MenuBar = ({ setEditorRef, isChat }) => { } // color={editor.isActive('code') ? 'white' : 'gray'} sx={{ - color: editor.isActive('code') ? 'white' : 'gray' + color: editor.isActive('code') ? 'white' : 'gray', + padding: isMobile ? '5px' : 'revert' }} > - editor.chain().focus().unsetAllMarks().run()}> + editor.chain().focus().unsetAllMarks().run()}> editor.chain().focus().toggleBulletList().run()} // color={editor.isActive('bulletList') ? 'white' : 'gray'} sx={{ - color: editor.isActive('bulletList') ? 'white' : 'gray' + color: editor.isActive('bulletList') ? 'white' : 'gray', + padding: isMobile ? '5px' : 'revert' }} > @@ -157,7 +165,8 @@ const MenuBar = ({ setEditorRef, isChat }) => { onClick={() => editor.chain().focus().toggleOrderedList().run()} // color={editor.isActive('orderedList') ? 'white' : 'gray'} sx={{ - color: editor.isActive('orderedList') ? 'white' : 'gray' + color: editor.isActive('orderedList') ? 'white' : 'gray', + padding: isMobile ? '5px' : 'revert' }} > @@ -166,7 +175,8 @@ const MenuBar = ({ setEditorRef, isChat }) => { onClick={() => editor.chain().focus().toggleCodeBlock().run()} // color={editor.isActive('codeBlock') ? 'white' : 'gray'} sx={{ - color: editor.isActive('codeBlock') ? 'white' : 'gray' + color: editor.isActive('codeBlock') ? 'white' : 'gray', + padding: isMobile ? '5px' : 'revert' }} > @@ -175,7 +185,8 @@ const MenuBar = ({ setEditorRef, isChat }) => { onClick={() => editor.chain().focus().toggleBlockquote().run()} // color={editor.isActive('blockquote') ? 'white' : 'gray'} sx={{ - color: editor.isActive('blockquote') ? 'white' : 'gray' + color: editor.isActive('blockquote') ? 'white' : 'gray', + padding: isMobile ? '5px' : 'revert' }} > @@ -187,7 +198,8 @@ const MenuBar = ({ setEditorRef, isChat }) => { onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()} // color={editor.isActive('heading', { level: 1 }) ? 'white' : 'gray'} sx={{ - color: editor.isActive('heading', { level: 1 }) ? 'white' : 'gray' + color: editor.isActive('heading', { level: 1 }) ? 'white' : 'gray', + padding: isMobile ? '5px' : 'revert' }} > @@ -202,7 +214,8 @@ const MenuBar = ({ setEditorRef, isChat }) => { .run() } sx={{ - color: 'gray' + color: 'gray', + padding: isMobile ? '5px' : 'revert' }} > @@ -227,7 +240,8 @@ const MenuBar = ({ setEditorRef, isChat }) => { @@ -268,32 +282,66 @@ const extensions = [ const content = ``; -export default ({ setEditorRef, onEnter, disableEnter, isChat }) => { - +export default ({ setEditorRef, onEnter, disableEnter, isChat, maxHeightOffset, setIsFocusedParent, isFocusedParent, overrideMobile, customEditorHeight }) => { + const [isFocused, setIsFocused] = useState(false); const extensionsFiltered = isChat ? extensions.filter((item)=> item?.name !== 'image') : extensions + const editorRef = useRef(null); + const setEditorRefFunc = (editorInstance) => { + editorRef.current = editorInstance; + setEditorRef(editorInstance) + }; + const handleFocus = () => { + if(!isMobile) return + // setIsFocused(true); + setIsFocusedParent(true) + }; + + const handleBlur = () => { + const htmlContent = editorRef.current.getHTML(); + + if (!htmlContent?.trim() || htmlContent?.trim() === "

"){ + // setIsFocused(false); + // setIsFocusedParent(false) + }; + + }; + // useEffect(()=> { + // setIsFocused(isFocusedParent) + // },[isFocusedParent]) + return ( } - extensions={extensionsFiltered} - content={content} - editorProps={{ - handleKeyDown(view, event) { - if (!disableEnter && event.key === 'Enter') { - if (event.shiftKey) { - // Shift+Enter: Insert a hard break - view.dispatch(view.state.tr.replaceSelectionWith(view.state.schema.nodes.hardBreak.create())); - return true; - } else { - // Enter: Call the callback function - if (typeof onEnter === 'function') { - onEnter(); - } - return true; // Prevent the default action of adding a new line + slotBefore={(isFocusedParent || !isMobile || overrideMobile) && } + extensions={extensionsFiltered} + content={content} + onCreate={({ editor }) => { + editor.on('focus', handleFocus); // Listen for focus event + editor.on('blur', handleBlur); // Listen for blur event + }} + onUpdate={({ editor }) => { + editor.on('focus', handleFocus); // Ensure focus is updated + editor.on('blur', handleBlur); // Ensure blur is updated + }} + editorProps={{ + attributes: { + class: 'tiptap-prosemirror', + style: isMobile && `overflow: auto; min-height: ${customEditorHeight ? '200px' : '0px'}; 200px; max-height:calc(100svh - ${ customEditorHeight ? customEditorHeight : '140px'})`, + }, + handleKeyDown(view, event) { + if (!disableEnter && event.key === 'Enter') { + if (event.shiftKey) { + view.dispatch(view.state.tr.replaceSelectionWith(view.state.schema.nodes.hardBreak.create())); + return true; + } else { + if (typeof onEnter === 'function') { + onEnter(); } + return true; } - return false; // Allow default handling for other keys - }, - }} - /> - ); + } + return false; + }, + }} + /> + ) }; diff --git a/src/components/Chat/styles.css b/src/components/Chat/styles.css index f7b28ea..8766c40 100644 --- a/src/components/Chat/styles.css +++ b/src/components/Chat/styles.css @@ -118,4 +118,5 @@ .tiptap img { display: block; max-width: 100%; -} \ No newline at end of file +} + diff --git a/src/components/Group/AddGroup.tsx b/src/components/Group/AddGroup.tsx index ed27d35..288e6f3 100644 --- a/src/components/Group/AddGroup.tsx +++ b/src/components/Group/AddGroup.tsx @@ -29,7 +29,7 @@ import { AddGroupList } from "./AddGroupList"; import { UserListOfInvites } from "./UserListOfInvites"; import { CustomizedSnackbars } from "../Snackbar/Snackbar"; import { getFee } from "../../background"; -import { MyContext } from "../../App"; +import { MyContext, isMobile } from "../../App"; import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; export const Label = styled("label")( @@ -220,44 +220,50 @@ export const AddGroup = ({ address, open, setOpen }) => { }} > - - - - - + + + + + {value === 0 && ( diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index 2eb9495..4f459e0 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -247,7 +247,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { style={{ position: "relative", height: "500px", - width: "600px", + width: "100%", display: "flex", flexDirection: "column", flexShrink: 1, diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index a716823..ff1626d 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -30,7 +30,7 @@ import { formatBytes } from "../../../utils/Size"; import { CreateThreadIcon } from "../../../assets/svgs/CreateThreadIcon"; import { SendNewMessage } from "../../../assets/svgs/SendNewMessage"; import { TextEditor } from "./TextEditor"; -import { MyContext, pauseAllQueues, resumeAllQueues } from "../../../App"; +import { MyContext, isMobile, pauseAllQueues, resumeAllQueues } from "../../../App"; import { getFee } from "../../../background"; import TipTap from "../../Chat/TipTap"; import { MessageDisplay } from "../../Chat/MessageDisplay"; @@ -410,7 +410,7 @@ export const NewThread = ({ {isMessage ? "Post Message" : "New Thread"} - + )} - + {!isMobile && ( + + + )} {/* diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 6e6a14a..642119d 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -522,6 +522,36 @@ export const Group = ({ return hasUnread; }, [timestampEnterData, directs, myAddress]); + const groupChatHasUnread = useMemo(() => { + let hasUnread = false; + groups.forEach((group) => { + if ( + group?.data && isExtMsg(group?.data) && group?.sender !== myAddress && + group?.timestamp && + ((!timestampEnterData[group?.groupId] && + Date.now() - group?.timestamp < + timeDifferenceForNotificationChats) || + timestampEnterData[group?.groupId] < group?.timestamp) + ) { + hasUnread = true; + } + }); + return hasUnread; + }, [timestampEnterData, groups, myAddress]); + + const groupsAnnHasUnread = useMemo(() => { + let hasUnread = false; + groups.forEach((group) => { + if ( + groupAnnouncements[group?.groupId] && + !groupAnnouncements[group?.groupId]?.seentimestamp + ) { + hasUnread = true; + } + }); + return hasUnread; + }, [groupAnnouncements, groups]); + // useEffect(() => { // if (!myAddress) return; // checkGroupListFunc(myAddress); @@ -1660,6 +1690,7 @@ export const Group = ({ setNewChat(true); setSelectedDirect(null); setSelectedGroup(null); + setIsOpenDrawer(false) }} > } - sx={{ padding: '4px 6px' }} // Make padding smaller + sx={{ padding: '4px 6px', + color: groupSection === 'announcement' ? 'black' : 'white', + backgroundColor: isUnread + ? "red" : groupSection === 'announcement' ? 'white' : 'black', + }} onClick={goToAnnouncements} > ANN @@ -2217,7 +2252,10 @@ export const Group = ({ size="small" variant="contained" startIcon={} - sx={{ padding: '4px 6px' }} + sx={{ padding: '4px 6px', color: groupSection === 'chat' ? 'black' : 'white', + backgroundColor: isUnreadChat + ? "red" + : groupSection === 'chat' ? 'white' : 'black', }} onClick={goToChat} > Chat @@ -2229,7 +2267,8 @@ export const Group = ({ size="small" variant="contained" startIcon={} - sx={{ padding: '4px 6px' }} + sx={{ padding: '4px 6px', color: groupSection === 'forum' ? 'black' : 'white', + backgroundColor: groupSection === 'forum' ? 'white' : 'black', }} onClick={() => setGroupSection("forum")} > Forum @@ -2241,7 +2280,7 @@ export const Group = ({ size="small" variant="contained" startIcon={} - sx={{ padding: '4px 6px' }} + sx={{ padding: '4px 6px', backgroundColor: 'black' }} onClick={() => setOpenManageMembers(true)} > Members @@ -2257,7 +2296,7 @@ export const Group = ({ size="small" variant="contained" startIcon={} - sx={{ padding: '2px 4px' }} + sx={{ padding: '2px 4px', backgroundColor: groupChatHasUnread || groupsAnnHasUnread || directChatHasUnread ? "red" : 'black' }} onClick={() => { setIsOpenDrawer(true); setDrawerMode("groups"); diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index b25d21e..41ef9f4 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -168,7 +168,7 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { return (

Ban list

-
+
{({ height, width }) => ( return (

Invitees list

-
+
{({ height, width }) => (

Join request list

-
+
{({ height, width }) => ( - - - - - - - - + + + + + + + {selectedGroup?.groupId && !isOwner && ( diff --git a/src/components/Group/UserListOfInvites.tsx b/src/components/Group/UserListOfInvites.tsx index 98f37f1..20aa6fb 100644 --- a/src/components/Group/UserListOfInvites.tsx +++ b/src/components/Group/UserListOfInvites.tsx @@ -186,7 +186,7 @@ export const UserListOfInvites = ({myAddress, setInfoSnack, setOpenSnack}) => { return (

Invite list

-
+
{({ height, width }) => ( Date: Sat, 14 Sep 2024 11:42:05 +0300 Subject: [PATCH 06/25] popover for payment and message - users avatar --- src/App.tsx | 81 +- src/background.ts | 27 +- .../Chat/AnnouncementDiscussion.tsx | 48 +- src/components/Chat/AnnouncementItem.tsx | 7 +- src/components/Chat/AnnouncementList.tsx | 5 +- src/components/Chat/ChatDirect.tsx | 84 +- src/components/Chat/ChatGroup.tsx | 43 +- src/components/Chat/ChatList.tsx | 1 + src/components/Chat/GroupAnnouncements.tsx | 49 +- src/components/Chat/MessageItem.tsx | 9 +- src/components/Group/Forum/GroupMail.tsx | 5 + .../Group/Forum/ShowMessageWithoutModal.tsx | 9 +- src/components/Group/Forum/Thread.tsx | 2 + src/components/Group/Group.tsx | 1333 ++++++++++------- src/components/Loader.tsx | 2 +- src/components/WrapperUserAction.tsx | 108 ++ src/index.css | 13 + 17 files changed, 1145 insertions(+), 681 deletions(-) create mode 100644 src/components/WrapperUserAction.tsx diff --git a/src/App.tsx b/src/App.tsx index d6033d4..3c8bc1a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -82,7 +82,7 @@ import { groupApiSocket, groupApiSocketLocal, } from "./background"; -import { executeEvent } from "./utils/events"; +import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "./utils/events"; import { requestQueueCommentCount, requestQueuePublishedAccouncements } from "./components/Chat/GroupAnnouncements"; import { requestQueueGroupJoinRequests } from "./components/Group/GroupJoinRequests"; import { DrawerComponent } from "./components/Drawer/Drawer"; @@ -293,7 +293,8 @@ function App() { const [confirmUseOfLocal, setConfirmUseOfLocal] = useState(false); const [isOpenDrawerProfile, setIsOpenDrawerProfile] = useState(false); const [apiKey, setApiKey] = useState(""); - + const [isOpenSendQort, setIsOpenSendQort] = useState(false) + const [isOpenSendQortSuccess, setIsOpenSendQortSuccess] = useState(false) useEffect(() => { if(!isMobile) return // Function to set the height of the app to the viewport height @@ -519,7 +520,9 @@ function App() { if (response?.error) { setSendPaymentError(response.error); } else { - setExtstate("transfer-success-regular"); + setIsOpenSendQort(false) + setIsOpenSendQortSuccess(true) + // setExtstate("transfer-success-regular"); // setSendPaymentSuccess("Payment successfully sent"); } setIsLoading(false); @@ -901,6 +904,8 @@ function App() { setWalletToBeDownloaded(null); setWalletToBeDownloadedPassword(""); setExtstate("authenticated"); + setIsOpenSendQort(false) + setIsOpenSendQortSuccess(false) }; const resetAllStates = () => { @@ -1026,6 +1031,17 @@ function App() { // Handler for when the window gains focus const handleFocus = () => { setIsFocused(true); + if(isMobile){ + chrome?.runtime?.sendMessage( + { + action: "clearAllNotifications", + payload: { + + }, + } + ); + } + console.log("Webview is focused"); }; @@ -1043,6 +1059,16 @@ function App() { const handleVisibilityChange = () => { if (document.visibilityState === "visible") { setIsFocused(true); + if(isMobile){ + chrome?.runtime?.sendMessage( + { + action: "clearAllNotifications", + payload: { + + }, + } + ); + } console.log("Webview is visible"); } else { setIsFocused(false); @@ -1060,6 +1086,22 @@ function App() { }; }, []); + + const openPaymentInternal = (e) => { + const directAddress = e.detail?.address; + const name = e.detail?.name + setIsOpenSendQort(true) + setPaymentTo(name || directAddress) + }; + + useEffect(() => { + subscribeToEvent("openPaymentInternal", openPaymentInternal); + + return () => { + unsubscribeFromEvent("openPaymentInternal", openPaymentInternal); + }; + }, []); + const registerName = async () => { try { if (!userInfo?.address) throw new Error("Your address was not found"); @@ -1262,7 +1304,8 @@ function App() { { - setExtstate("send-qort"); + setIsOpenSendQort(true) + // setExtstate("send-qort"); setIsOpenDrawerProfile(false) }} > @@ -1584,8 +1627,17 @@ function App() { )} - {extState === "send-qort" && isMainWindow && ( - <> + {isOpenSendQort && isMainWindow && ( + Send - + )} {extState === "web-app-request-buy-order" && !isMainWindow && ( <> @@ -2225,8 +2277,17 @@ function App() { )} )} - {extState === "transfer-success-regular" && ( - <> + {isOpenSendQortSuccess && ( + @@ -2246,7 +2307,7 @@ function App() { > Continue - + )} {extState === "transfer-success-request" && ( <> diff --git a/src/background.ts b/src/background.ts index 826dc8a..c6eb1a6 100644 --- a/src/background.ts +++ b/src/background.ts @@ -202,7 +202,7 @@ export function isExtMsg(data){ } catch (error) { isMsgFromExtensionGroup = false } -console.log('isMsgFromExtensionGroup', isMsgFromExtensionGroup) + return isMsgFromExtensionGroup } @@ -264,7 +264,7 @@ if (!oldestLatestTimestamp || oldChat?.timestamp > oldestLatestTimestamp?.timest } }); - console.log('newestLatestTimestamp', newestLatestTimestamp) + if(checkDifference(newestLatestTimestamp.timestamp) && !oldestLatestTimestamp || (newestLatestTimestamp && newestLatestTimestamp?.timestamp > oldestLatestTimestamp?.timestamp)){ const notificationId = 'chat_notification_' + Date.now() + '_type=direct' + `_from=${newestLatestTimestamp.address}`; chrome.notifications.create(notificationId, { @@ -460,7 +460,7 @@ const handleNotification = async (groups)=> { if(checkDifference(newestLatestTimestamp.timestamp) && !oldestLatestTimestamp || (newestLatestTimestamp && newestLatestTimestamp?.timestamp > oldestLatestTimestamp?.timestamp)){ if (!lastGroupNotification || ((Date.now() - lastGroupNotification) >= 120000)) { if(!newestLatestTimestamp?.data || !isExtMsg(newestLatestTimestamp?.data)) return - console.log('newestLatestTimestamp', newestLatestTimestamp) + const notificationId = 'chat_notification_' + Date.now() + '_type=group' + `_from=${newestLatestTimestamp.groupId}`; chrome.notifications.create(notificationId, { @@ -859,6 +859,13 @@ async function getSaveWallet() { } } +async function clearAllNotifications(){ + const notifications = await chrome.notifications.getAll(); + for (const notificationId of Object.keys(notifications)) { + await chrome.notifications.clear(notificationId); + } +} + async function getUserInfo() { const wallet = await getSaveWallet(); const address = wallet.address0; @@ -1502,7 +1509,7 @@ async function decryptDirectFunc({ messages, involvingAddress }) { publicKey: uint8PublicKey, }; for (const message of messages) { - console.log('messagedep', message) + try { const decodedMessage = decryptChatMessage( message.data, @@ -2905,6 +2912,18 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { }); break; } + case "clearAllNotifications": { + + ; + clearAllNotifications() + .then((res) => { + + }) + .catch((error) => { + + }); + break; + } case "setGroupData": { const { groupId, diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index 792695a..9c3c1b8 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -283,6 +283,7 @@ export const AnnouncementDiscussion = ({ disableComment showLoadMore={comments.length > 0 && comments.length % 20 === 0} loadMore={loadMore} + myName={myName} />
+ {isFocusedParent && ( + { + if(isSending) return + setIsFocusedParent(false) + clearEditorContent() + // Unfocus the editor + }} + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + flexShrink: 0, + padding: isMobile && '5px', + fontSize: isMobile && '14px', + background: 'red', + }} + > + + {` Close`} + + + )} { if (isSending) return; @@ -361,29 +385,7 @@ export const AnnouncementDiscussion = ({ )} {` Publish Comment`} - {isFocusedParent && ( - { - if(isSending) return - setIsFocusedParent(false) - clearEditorContent() - // Unfocus the editor - }} - style={{ - marginTop: 'auto', - alignSelf: 'center', - cursor: isSending ? 'default' : 'pointer', - background: isSending && 'rgba(0, 0, 0, 0.8)', - flexShrink: 0, - padding: isMobile && '5px', - fontSize: isMobile && '14px', - }} - > - - {` Close`} - - - )} +
diff --git a/src/components/Chat/AnnouncementItem.tsx b/src/components/Chat/AnnouncementItem.tsx index e580d77..499ca9c 100644 --- a/src/components/Chat/AnnouncementItem.tsx +++ b/src/components/Chat/AnnouncementItem.tsx @@ -10,7 +10,8 @@ import { getBaseApi } from "../../background"; import { requestQueueCommentCount } from "./GroupAnnouncements"; import { CustomLoader } from "../../common/CustomLoader"; import { getBaseApiReact } from "../../App"; -export const AnnouncementItem = ({ message, messageData, setSelectedAnnouncement, disableComment }) => { +import { WrapperUserAction } from "../WrapperUserAction"; +export const AnnouncementItem = ({ message, messageData, setSelectedAnnouncement, disableComment, myName }) => { const [commentLength, setCommentLength] = useState(0) const getNumberOfComments = React.useCallback( @@ -63,6 +64,7 @@ export const AnnouncementItem = ({ message, messageData, setSelectedAnnouncement width: '100%', wordBreak: 'break-word' }}> + {message?.name?.charAt(0)} + + {message?.name} + {!messageData?.decryptedData && ( { const listRef = useRef(); @@ -63,7 +64,7 @@ export const AnnouncementList = ({ alignItems: "center", }} > - +
); diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index a0fd312..f975340 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -6,7 +6,7 @@ import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css"; import Tiptap from './TipTap' import { CustomButton } from '../../App-styles' import CircularProgress from '@mui/material/CircularProgress'; -import { Box, Input } from '@mui/material'; +import { Box, Input, Typography } from '@mui/material'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { getNameInfo } from '../Group/Group'; import { Spacer } from '../../common/Spacer'; @@ -14,14 +14,13 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar'; import { getBaseApiReactSocket, isMobile, pauseAllQueues, resumeAllQueues } from '../../App'; import { getPublicKey } from '../../background'; import { useMessageQueue } from '../../MessageQueueContext'; -import { executeEvent } from '../../utils/events'; -import zIndex from '@mui/material/styles/zIndex'; +import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; - -export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDirect, setNewChat, getTimestampEnterChat, myName, balance}) => { +export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDirect, setNewChat, getTimestampEnterChat, myName, balance, close}) => { const { queueChats, addToQueue, } = useMessageQueue(); const [isFocusedParent, setIsFocusedParent] = useState(false); @@ -187,7 +186,16 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi }; }; - + const setDirectChatValueFunc = async (e)=> { + setDirectToValue(e.detail.directToValue) + } + useEffect(() => { + subscribeToEvent("setDirectToValueNewChat", setDirectChatValueFunc); + + return () => { + unsubscribeFromEvent("setDirectToValueNewChat", setDirectChatValueFunc); + }; + }, []); useEffect(() => { if (hasInitializedWebsocket.current || isNewChat) return; @@ -327,6 +335,27 @@ console.log('isFocusedParent', isFocusedParent) flexDirection: 'column', width: '100%' }}> + + + Close Direct Chat + {isNewChat && ( <> @@ -375,6 +404,28 @@ console.log('isFocusedParent', isFocusedParent) flexShrink: 0, position: 'relative', }}> + {isFocusedParent && ( + { + if(isSending) return + setIsFocusedParent(false) + clearEditorContent() + // Unfocus the editor + }} + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: 'red', + flexShrink: 0, + padding: isMobile && '5px' + }} + > + + {` Close`} + + + )} { if(isSending) return @@ -404,28 +455,7 @@ console.log('isFocusedParent', isFocusedParent) )} {` Send`} - {isFocusedParent && ( - { - if(isSending) return - setIsFocusedParent(false) - clearEditorContent() - // Unfocus the editor - }} - style={{ - marginTop: 'auto', - alignSelf: 'center', - cursor: isSending ? 'default' : 'pointer', - background: isSending && 'rgba(0, 0, 0, 0.8)', - flexShrink: 0, - padding: isMobile && '5px' - }} - > - - {` Close`} - - )}
{ flexShrink: 0, position: 'relative', }}> + {isFocusedParent && ( + { + if(isSending) return + setIsFocusedParent(false) + clearEditorContent() + // Unfocus the editor + }} + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: 'red', + flexShrink: 0, + padding: isMobile && '5px' + }} + > + + {` Close`} + + + )} { if(isSending) return @@ -425,28 +447,7 @@ const clearEditorContent = () => { )} {` Send`} - {isFocusedParent && ( - { - if(isSending) return - setIsFocusedParent(false) - clearEditorContent() - // Unfocus the editor - }} - style={{ - marginTop: 'auto', - alignSelf: 'center', - cursor: isSending ? 'default' : 'pointer', - background: isSending && 'rgba(0, 0, 0, 0.8)', - flexShrink: 0, - padding: isMobile && '5px' - }} - > - - {` Close`} - - )} {/* */}
diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index b4cae82..8c636c1 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -126,6 +126,7 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages }) => { message={message} onSeen={handleMessageSeen} isTemp={!!message?.isTemp} + myAddress={myAddress} />
diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 3b11931..15de64e 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -301,6 +301,7 @@ export const GroupAnnouncements = ({ } // send chat message } catch (error) { + if(!error) return setInfoSnack({ type: "error", message: error, @@ -542,6 +543,7 @@ export const GroupAnnouncements = ({ disableComment={false} showLoadMore={announcements.length > 0 && announcements.length % 20 === 0} loadMore={loadMore} + myName={myName} /> @@ -591,6 +593,29 @@ export const GroupAnnouncements = ({ flexShrink: 0, position: 'relative', }}> + {isFocusedParent && ( + { + if(isSending) return + setIsFocusedParent(false) + clearEditorContent() + // Unfocus the editor + }} + style={{ + marginTop: 'auto', + alignSelf: 'center', + cursor: isSending ? 'default' : 'pointer', + background: 'red', + flexShrink: 0, + padding: isMobile && '5px', + fontSize: isMobile && '14px', + }} + > + + {` Close`} + + + )} { if (isSending) return; @@ -622,29 +647,7 @@ export const GroupAnnouncements = ({ )} {` Publish Announcement`} - {isFocusedParent && ( - { - if(isSending) return - setIsFocusedParent(false) - clearEditorContent() - // Unfocus the editor - }} - style={{ - marginTop: 'auto', - alignSelf: 'center', - cursor: isSending ? 'default' : 'pointer', - background: isSending && 'rgba(0, 0, 0, 0.8)', - flexShrink: 0, - padding: isMobile && '5px', - fontSize: isMobile && '14px', - }} - > - - {` Close`} - - - )} +
)} diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index d940d69..b3ac3d9 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -10,7 +10,9 @@ import { generateHTML } from "@tiptap/react"; import Highlight from '@tiptap/extension-highlight' import StarterKit from '@tiptap/starter-kit' import Underline from '@tiptap/extension-underline' -export const MessageItem = ({ message, onSeen, isLast, isTemp }) => { +import { executeEvent } from "../../utils/events"; +import { WrapperUserAction } from "../WrapperUserAction"; +export const MessageItem = ({ message, onSeen, isLast, isTemp, myAddress }) => { const { ref, inView } = useInView({ threshold: 0.7, // Fully visible @@ -36,6 +38,7 @@ export const MessageItem = ({ message, onSeen, isLast, isTemp }) => { opacity: isTemp ? 0.5 : 1 }} > + { > {message?.senderName?.charAt(0)} + { width: '100%' }} > + {message?.senderName || message?.sender} + {message?.messageText && ( )} diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index d1ce85b..3d9d82b 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -54,6 +54,7 @@ import { LoadingSnackbar } from "../../Snackbar/LoadingSnackbar"; import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../../utils/events"; import RefreshIcon from '@mui/icons-material/Refresh'; import { getBaseApiReact } from "../../../App"; +import { WrapperUserAction } from "../../WrapperUserAction"; const filterOptions = ["Recently active", "Newest", "Oldest"]; export const threadIdentifier = "DOCUMENT"; @@ -672,6 +673,7 @@ export const GroupMail = ({ } }} > + {thread?.threadData?.name?.charAt(0)} + + by {thread?.threadData?.name} + {formatTimestamp(thread?.threadData?.createdAt)} diff --git a/src/components/Group/Forum/ShowMessageWithoutModal.tsx b/src/components/Group/Forum/ShowMessageWithoutModal.tsx index c8fb5cb..9a079d6 100644 --- a/src/components/Group/Forum/ShowMessageWithoutModal.tsx +++ b/src/components/Group/Forum/ShowMessageWithoutModal.tsx @@ -19,8 +19,9 @@ import ReadOnlySlate from "./ReadOnlySlate"; import { MessageDisplay } from "../../Chat/MessageDisplay"; import { getBaseApi } from "../../../background"; import { getBaseApiReact } from "../../../App"; +import { WrapperUserAction } from "../../WrapperUserAction"; -export const ShowMessage = ({ message, openNewPostWithQuote }: any) => { +export const ShowMessage = ({ message, openNewPostWithQuote, myName }: any) => { const [expandAttachments, setExpandAttachments] = useState(false); let cleanHTML = ""; @@ -53,13 +54,17 @@ export const ShowMessage = ({ message, openNewPostWithQuote }: any) => { }} > - + {message?.name?.charAt(0)} + + + {message?.name} + {formatTimestampForum(message?.created)} diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 6dd0dbb..c386da0 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -531,6 +531,7 @@ export const Thread = ({ key={message?.identifier} message={fullMessage} openNewPostWithQuote={openNewPostWithQuote} + myName={userInfo?.name} /> ); } else if(message?.tempData){ @@ -539,6 +540,7 @@ export const Thread = ({ key={message?.identifier} message={message?.tempData} openNewPostWithQuote={openNewPostWithQuote} + myName={userInfo?.name} /> ); } diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 642119d..160367f 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -30,9 +30,9 @@ import MarkUnreadChatAltIcon from "@mui/icons-material/MarkUnreadChatAlt"; import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; import CreateIcon from "@mui/icons-material/Create"; import RefreshIcon from "@mui/icons-material/Refresh"; -import AnnouncementsIcon from '@mui/icons-material/Notifications'; -import GroupIcon from '@mui/icons-material/Group'; -import PersonIcon from '@mui/icons-material/Person'; +import AnnouncementsIcon from "@mui/icons-material/Notifications"; +import GroupIcon from "@mui/icons-material/Group"; +import PersonIcon from "@mui/icons-material/Person"; import { AuthenticatedContainerInnerRight, CustomButton, @@ -56,7 +56,7 @@ import { LoadingButton } from "@mui/lab"; import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar"; import { GroupAnnouncements } from "../Chat/GroupAnnouncements"; import HomeIcon from "@mui/icons-material/Home"; -import CloseIcon from '@mui/icons-material/Close'; +import CloseIcon from "@mui/icons-material/Close"; import { ThingsToDoInitial } from "./ThingsToDoInitial"; import { GroupJoinRequests } from "./GroupJoinRequests"; @@ -96,7 +96,6 @@ import { isExtMsg } from "../../background"; // } // }); - interface GroupProps { myAddress: string; isFocused: boolean; @@ -223,8 +222,8 @@ export const getGroupAdimns = async (groupNumber: number) => { ); const groupData = await response.json(); let members: any = []; - let membersAddresses = [] - let both = [] + let membersAddresses = []; + let both = []; // if (groupData && Array.isArray(groupData?.members)) { // for (const member of groupData.members) { // if (member.member) { @@ -243,16 +242,16 @@ export const getGroupAdimns = async (groupNumber: number) => { }); if (name) { members.push(name); - both.push({name, address: member.member}) + both.push({ name, address: member.member }); } - membersAddresses.push(member.member) + membersAddresses.push(member.member); } return true; }); await Promise.all(getMemNames); - return {names: members, addresses: membersAddresses, both}; + return { names: members, addresses: membersAddresses, both }; }; export const getNames = async (listOfMembers) => { @@ -314,7 +313,8 @@ export const Group = ({ isMain, userInfo, balance, - isOpenDrawerProfile, setIsOpenDrawerProfile + isOpenDrawerProfile, + setIsOpenDrawerProfile, }: GroupProps) => { const [secretKey, setSecretKey] = useState(null); const [secretKeyPublishDate, setSecretKeyPublishDate] = useState(null); @@ -355,7 +355,7 @@ export const Group = ({ const [defaultThread, setDefaultThread] = React.useState(null); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); - const [drawerMode, setDrawerMode] = React.useState('groups'); + const [drawerMode, setDrawerMode] = React.useState("groups"); const isFocusedRef = useRef(true); const selectedGroupRef = useRef(null); const selectedDirectRef = useRef(null); @@ -410,21 +410,20 @@ export const Group = ({ { action: "getGroupDataSingle", payload: { - groupId - } + groupId, + }, }, (response) => { if (!response?.error) { - res(response); - return + return; } rej(response.error); } ); }); } catch (error) { - return {} + return {}; } }; const refreshHomeDataFunc = () => { @@ -526,11 +525,12 @@ export const Group = ({ let hasUnread = false; groups.forEach((group) => { if ( - group?.data && isExtMsg(group?.data) && group?.sender !== myAddress && + group?.data && + isExtMsg(group?.data) && + group?.sender !== myAddress && group?.timestamp && ((!timestampEnterData[group?.groupId] && - Date.now() - group?.timestamp < - timeDifferenceForNotificationChats) || + Date.now() - group?.timestamp < timeDifferenceForNotificationChats) || timestampEnterData[group?.groupId] < group?.timestamp) ) { hasUnread = true; @@ -544,7 +544,7 @@ export const Group = ({ groups.forEach((group) => { if ( groupAnnouncements[group?.groupId] && - !groupAnnouncements[group?.groupId]?.seentimestamp + !groupAnnouncements[group?.groupId]?.seentimestamp ) { hasUnread = true; } @@ -599,15 +599,17 @@ export const Group = ({ try { // setGroupDataLastSet(null) pauseAllQueues(); - let dataFromStorage - let publishFromStorage - let adminsFromStorage - const groupData = await getGroupDataSingle(selectedGroup?.groupId) - if(groupData?.secretKeyData && Date.now() - groupData?.timestampLastSet < 3600000 ){ - - dataFromStorage = groupData.secretKeyData - publishFromStorage = groupData.secretKeyResource - adminsFromStorage = groupData.admins + let dataFromStorage; + let publishFromStorage; + let adminsFromStorage; + const groupData = await getGroupDataSingle(selectedGroup?.groupId); + if ( + groupData?.secretKeyData && + Date.now() - groupData?.timestampLastSet < 3600000 + ) { + dataFromStorage = groupData.secretKeyData; + publishFromStorage = groupData.secretKeyResource; + adminsFromStorage = groupData.admins; // setGroupDataLastSet(groupData.timestampLastSet) } @@ -629,13 +631,15 @@ export const Group = ({ } const prevGroupId = selectedGroupRef.current.groupId; // const validApi = await findUsableApi(); - const {names, addresses, both} = adminsFromStorage || await getGroupAdimns(selectedGroup?.groupId); + const { names, addresses, both } = + adminsFromStorage || (await getGroupAdimns(selectedGroup?.groupId)); setAdmins(addresses); - setAdminsWithNames(both) + setAdminsWithNames(both); if (!names.length) { throw new Error("Network error"); } - const publish = publishFromStorage || await getPublishesFromAdmins(names); + const publish = + publishFromStorage || (await getPublishesFromAdmins(names)); if (prevGroupId !== selectedGroupRef.current.groupId) { if (settimeoutForRefetchSecretKey.current) { @@ -651,18 +655,17 @@ export const Group = ({ return false; } setSecretKeyPublishDate(publish?.updated || publish?.created); - let data - if(dataFromStorage){ - data = dataFromStorage + let data; + if (dataFromStorage) { + data = dataFromStorage; } else { const res = await fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ publish.identifier }?encoding=base64` ); - data = await res.text(); + data = await res.text(); } - const decryptedKey: any = await decryptResource(data); @@ -675,17 +678,15 @@ export const Group = ({ setSecretKey(decryptedKeyToObject); lastFetchedSecretKey.current = Date.now(); setMemberCountFromSecretKeyData(decryptedKey.count); - chrome?.runtime?.sendMessage( - { - action: "setGroupData", - payload: { - groupId: selectedGroup?.groupId, - secretKeyData: data, - secretKeyResource: publish, - admins: {names, addresses, both} - }, - } - ); + chrome?.runtime?.sendMessage({ + action: "setGroupData", + payload: { + groupId: selectedGroup?.groupId, + secretKeyData: data, + secretKeyResource: publish, + admins: { names, addresses, both }, + }, + }); if (decryptedKeyToObject) { setTriedToFetchSecretKey(true); setFirstSecretKeyInCreation(false); @@ -915,12 +916,16 @@ export const Group = ({ } catch (error) {} }; useEffect(() => { - if (!initiatedGetMembers.current && selectedGroup?.groupId && secretKey && admins.includes(myAddress)) { - - console.log('goung through', admins) + if ( + !initiatedGetMembers.current && + selectedGroup?.groupId && + secretKey && + admins.includes(myAddress) + ) { + console.log("goung through", admins); // getAdmins(selectedGroup?.groupId); getMembers(selectedGroup?.groupId); - initiatedGetMembers.current = true + initiatedGetMembers.current = true; } }, [selectedGroup?.groupId, secretKey, myAddress, admins]); @@ -998,7 +1003,7 @@ export const Group = ({ .filter((group) => group?.sender !== myAddress) .find((gr) => gr?.groupId === selectedGroup?.groupId); if (!findGroup) return false; - if(!findGroup?.data || !isExtMsg(findGroup?.data)) return false + if (!findGroup?.data || !isExtMsg(findGroup?.data)) return false; return ( findGroup?.timestamp && ((!timestampEnterData[selectedGroup?.groupId] && @@ -1030,7 +1035,7 @@ export const Group = ({ if (findDirect) { setChatMode("directs"); setSelectedDirect(null); - setSelectedGroup(null); + // setSelectedGroup(null); setNewChat(false); @@ -1052,6 +1057,52 @@ export const Group = ({ } }; + const openDirectChatFromInternal = (e) => { + const directAddress = e.detail?.address; + const name = e.detail?.name + const findDirect = directs?.find( + (direct) => direct?.address === directAddress || direct?.name === name + ); + + if (findDirect) { + setChatMode("directs"); + setSelectedDirect(null); + // setSelectedGroup(null); + + setNewChat(false); + + chrome?.runtime?.sendMessage({ + action: "addTimestampEnterChat", + payload: { + timestamp: Date.now(), + groupId: findDirect.address, + }, + }); + + setTimeout(() => { + setSelectedDirect(findDirect); + getTimestampEnterChat(); + }, 200); + + } else { + setChatMode('directs') + setNewChat(true); + setTimeout(() => { + executeEvent("setDirectToValueNewChat", { + directToValue: name || directAddress + }); + }, 500); + } + }; + + useEffect(() => { + subscribeToEvent("openDirectMessageInternal", openDirectChatFromInternal); + + return () => { + unsubscribeFromEvent("openDirectMessageInternal", openDirectChatFromInternal); + }; + }, [directs, selectedDirect]); + useEffect(() => { subscribeToEvent("openDirectMessage", openDirectChatFromNotification); @@ -1106,7 +1157,7 @@ export const Group = ({ isLoadingOpenSectionFromNotification.current = false; setupGroupWebsocketInterval.current = null; settimeoutForRefetchSecretKey.current = null; - initiatedGetMembers.current = false + initiatedGetMembers.current = false; }; const logoutEventFunc = () => { @@ -1275,51 +1326,51 @@ export const Group = ({ setFirstSecretKeyInCreation(true); }; - const goToHome = async ()=> { + const goToHome = async () => { setGroupSection("default"); - clearAllQueues(); - await new Promise((res) => { - setTimeout(() => { - res(null); - }, 200); - }); - setGroupSection("home"); - setSelectedGroup(null); - setNewChat(false); - setSelectedDirect(null); - setSecretKey(null); - lastFetchedSecretKey.current = null; - setSecretKeyPublishDate(null); - setAdmins([]); - setSecretKeyDetails(null); - setAdminsWithNames([]); - setMembers([]); - setMemberCountFromSecretKeyData(null); - setTriedToFetchSecretKey(false); - setFirstSecretKeyInCreation(false); - } + clearAllQueues(); + await new Promise((res) => { + setTimeout(() => { + res(null); + }, 200); + }); + setGroupSection("home"); + setSelectedGroup(null); + setNewChat(false); + setSelectedDirect(null); + setSecretKey(null); + lastFetchedSecretKey.current = null; + setSecretKeyPublishDate(null); + setAdmins([]); + setSecretKeyDetails(null); + setAdminsWithNames([]); + setMembers([]); + setMemberCountFromSecretKeyData(null); + setTriedToFetchSecretKey(false); + setFirstSecretKeyInCreation(false); + }; - const goToAnnouncements = async ()=> { + const goToAnnouncements = async () => { setGroupSection("default"); - await new Promise((res) => { - setTimeout(() => { - res(null); - }, 200); - }); - setGroupSection("announcement"); - chrome?.runtime?.sendMessage({ - action: "addGroupNotificationTimestamp", - payload: { - timestamp: Date.now(), - groupId: selectedGroupRef.current.groupId, - }, - }); - setTimeout(() => { - getGroupAnnouncements(); - }, 200); - } + await new Promise((res) => { + setTimeout(() => { + res(null); + }, 200); + }); + setGroupSection("announcement"); + chrome?.runtime?.sendMessage({ + action: "addGroupNotificationTimestamp", + payload: { + timestamp: Date.now(), + groupId: selectedGroupRef.current.groupId, + }, + }); + setTimeout(() => { + getGroupAnnouncements(); + }, 200); + }; - const goToChat = async ()=> { + const goToChat = async () => { setGroupSection("default"); await new Promise((res) => { setTimeout(() => { @@ -1340,222 +1391,228 @@ export const Group = ({ getTimestampEnterChat(); }, 200); } - } + }; - const renderGroups = ()=> { + const renderGroups = () => { return ( -
-
- {isMobile && ( - { - setIsOpenDrawer(false) - }} sx={{ - cursor: 'pointer', - color: 'white' - }} /> - )} - { - setChatMode((prev) => - prev === "directs" ? "groups" : "directs" - ); - setNewChat(false); - setSelectedDirect(null); - setSelectedGroup(null); - setGroupSection("default"); - }} - > - {chatMode === "groups" && ( - <> - - - )} - {chatMode === "directs" ? "Switch to groups" : "Direct msgs"} - -
- {directs.map((direct: any) => ( - - - // - // - // } - onClick={() => { - setSelectedDirect(null); - setNewChat(false); - setSelectedGroup(null); - setIsOpenDrawer(false) - chrome?.runtime?.sendMessage({ - action: "addTimestampEnterChat", - payload: { - timestamp: Date.now(), - groupId: direct.address, - }, - }); - setTimeout(() => { - setSelectedDirect(direct); - - getTimestampEnterChat(); - }, 200); - }} +
+ {isMobile && ( + - { + setIsOpenDrawer(false); + }} + sx={{ + cursor: "pointer", + color: "white", + }} + /> + + )} + { + setChatMode((prev) => + prev === "directs" ? "groups" : "directs" + ); + // setNewChat(false); + // setSelectedDirect(null); + // setSelectedGroup(null); + // setGroupSection("default"); + }} + > + {chatMode === "groups" && ( + <> + + + )} + {chatMode === "directs" ? "Switch to groups" : "Direct msgs"} + +
+
+ {directs.map((direct: any) => ( + + + // + // + // } + onClick={() => { + setSelectedDirect(null); + setNewChat(false); + // setSelectedGroup(null); + setIsOpenDrawer(false); + chrome?.runtime?.sendMessage({ + action: "addTimestampEnterChat", + payload: { + timestamp: Date.now(), + groupId: direct.address, + }, + }); + setTimeout(() => { + setSelectedDirect(direct); + + getTimestampEnterChat(); + }, 200); + }} sx={{ display: "flex", width: "100%", + flexDirection: "column", + cursor: "pointer", + border: "1px #232428 solid", + padding: "2px", + borderRadius: "2px", + background: + direct?.address === selectedDirect?.address && "white", }} > - - - {(direct?.name || direct?.address)?.charAt(0)} - - - - {direct?.sender !== myAddress && - direct?.timestamp && - ((!timestampEnterData[direct?.address] && - Date.now() - direct?.timestamp < - timeDifferenceForNotificationChats) || - timestampEnterData[direct?.address] < - direct?.timestamp) && ( - + + - )} - - - - ))} -
-
- {groups.map((group: any) => ( - - - // - // - // } - onClick={() => { - clearAllQueues(); - setSelectedDirect(null); + alt={direct?.name || direct?.address} + // src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${groupOwner?.name}/qortal_group_avatar_${group.groupId}?async=true`} + > + {(direct?.name || direct?.address)?.charAt(0)} + + + + {direct?.sender !== myAddress && + direct?.timestamp && + ((!timestampEnterData[direct?.address] && + Date.now() - direct?.timestamp < + timeDifferenceForNotificationChats) || + timestampEnterData[direct?.address] < + direct?.timestamp) && ( + + )} + + + + ))} +
+
+ {groups.map((group: any) => ( + + + // + // + // } + onClick={() => { + clearAllQueues(); + setSelectedDirect(null); - setNewChat(false); - setSelectedGroup(null); - setSecretKey(null); - lastFetchedSecretKey.current = null; - setSecretKeyPublishDate(null); - setAdmins([]); - setSecretKeyDetails(null); - setAdminsWithNames([]); - setMembers([]); - setMemberCountFromSecretKeyData(null); - setTriedToFetchSecretKey(false); - setFirstSecretKeyInCreation(false); - // setGroupSection("announcement"); - setGroupSection("chat"); - setIsOpenDrawer(false) - setTimeout(() => { - setSelectedGroup(group); + setNewChat(false); + setSelectedGroup(null); + setSecretKey(null); + lastFetchedSecretKey.current = null; + setSecretKeyPublishDate(null); + setAdmins([]); + setSecretKeyDetails(null); + setAdminsWithNames([]); + setMembers([]); + setMemberCountFromSecretKeyData(null); + setTriedToFetchSecretKey(false); + setFirstSecretKeyInCreation(false); + // setGroupSection("announcement"); + setGroupSection("chat"); + setIsOpenDrawer(false); + setTimeout(() => { + setSelectedGroup(group); - // getTimestampEnterChat(); - }, 200); + // getTimestampEnterChat(); + }, 200); - chrome?.runtime?.sendMessage({ action: "addTimestampEnterChat", payload: { @@ -1563,148 +1620,147 @@ export const Group = ({ groupId: group.groupId, }, }); - + setTimeout(() => { getTimestampEnterChat(); }, 200); - - // if (groupSectionRef.current === "announcement") { - // chrome?.runtime?.sendMessage({ - // action: "addGroupNotificationTimestamp", - // payload: { - // timestamp: Date.now(), - // groupId: group.groupId, - // }, - // }); - // } + // if (groupSectionRef.current === "announcement") { + // chrome?.runtime?.sendMessage({ + // action: "addGroupNotificationTimestamp", + // payload: { + // timestamp: Date.now(), + // groupId: group.groupId, + // }, + // }); + // } - // setTimeout(() => { - // getGroupAnnouncements(); - // }, 600); - }} - sx={{ - display: "flex", - width: "100%", - flexDirection: "column", - cursor: "pointer", - border: "1px #232428 solid", - padding: "2px", - borderRadius: "2px", - background: - group?.groupId === selectedGroup?.groupId && "white", - }} - > - { + // getGroupAnnouncements(); + // }, 600); + }} sx={{ display: "flex", width: "100%", + flexDirection: "column", + cursor: "pointer", + border: "1px #232428 solid", + padding: "2px", + borderRadius: "2px", + background: + group?.groupId === selectedGroup?.groupId && "white", }} > - - - {group.groupName?.charAt(0)} - - - - {groupAnnouncements[group?.groupId] && - !groupAnnouncements[group?.groupId]?.seentimestamp && ( - + + - )} - {group?.data && isExtMsg(group?.data) && group?.sender !== myAddress && - group?.timestamp && - ((!timestampEnterData[group?.groupId] && - Date.now() - group?.timestamp < - timeDifferenceForNotificationChats) || - timestampEnterData[group?.groupId] < - group?.timestamp) && ( - - )} - - - - ))} -
-
- {chatMode === "groups" && ( - { - setOpenAddGroup(true); - }} - > - + {group.groupName?.charAt(0)} + + + + {groupAnnouncements[group?.groupId] && + !groupAnnouncements[group?.groupId]?.seentimestamp && ( + + )} + {group?.data && + isExtMsg(group?.data) && + group?.sender !== myAddress && + group?.timestamp && + ((!timestampEnterData[group?.groupId] && + Date.now() - group?.timestamp < + timeDifferenceForNotificationChats) || + timestampEnterData[group?.groupId] < + group?.timestamp) && ( + + )} + + + + ))} +
+
+ {chatMode === "groups" && ( + { + setOpenAddGroup(true); }} - /> - Add Group - - )} - {chatMode === "directs" && ( - { - setNewChat(true); - setSelectedDirect(null); - setSelectedGroup(null); - setIsOpenDrawer(false) - }} - > - + + Add Group + + )} + {chatMode === "directs" && ( + { + setNewChat(true); + setSelectedDirect(null); + // setSelectedGroup(null); + setIsOpenDrawer(false); }} - /> - New Chat - - )} + > + + New Chat + + )} +
-
- ) - } + ); + }; return ( <> @@ -1723,12 +1779,17 @@ export const Group = ({ style={{ display: "flex", width: "100%", - height: isMobile ? "calc(100% - 75px)" : "100%", + height: isMobile ? "calc(100% - 75px)" : "100%", flexDirection: "row", alignItems: "flex-start", }} > - {!isMobile && renderGroups()} + {!isMobile && renderGroups()} + {newChat && ( <> + { + setSelectedDirect(null); + + setNewChat(false); + }} /> + )} - {selectedGroup && !newChat && ( + {selectedGroup && ( <> + + + + + { + setSelectedDirect(null); + + setNewChat(false); + }} /> + )} - {!selectedDirect && + { !selectedGroup && - !newChat && groupSection === "home" && ( - {!isLoadingGroups && ( - - - - - - - )} - + {!isLoadingGroups && ( + + + + + + + + )} )} + @@ -2209,124 +2309,225 @@ export const Group = ({ message: "Setting up groups... please wait.", }} /> +
- {renderGroups()} - - - - {isMobile && ( - - - {selectedGroup && ( - <> - - - - - - - - - - - - - - )} + + {renderGroups()} + - {/* Second row: Groups, Home, Profile */} - - - - - - - - - - setIsOpenDrawerProfile(true)} - > - - - - - - -)} - + + {selectedGroup && ( + <> + + + + + + + + + + + + + + )} + {/* Second row: Groups, Home, Profile */} + + + + + + + + + + setIsOpenDrawerProfile(true)} + > + + + + + + )} ); }; diff --git a/src/components/Loader.tsx b/src/components/Loader.tsx index fe0a05e..8db908e 100644 --- a/src/components/Loader.tsx +++ b/src/components/Loader.tsx @@ -14,7 +14,7 @@ export const Loader = () => { left:'0px', right: '0px', bottom: '0px', - zIndex: 2, + zIndex: 10, background: 'rgba(0, 0, 0, 0.4)' }}> diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx new file mode 100644 index 0000000..cfdb82f --- /dev/null +++ b/src/components/WrapperUserAction.tsx @@ -0,0 +1,108 @@ +import React, { useState } from 'react'; +import { Popover, Button, Box } from '@mui/material'; +import { executeEvent } from '../utils/events'; + +export const WrapperUserAction = ({ children, address, name, disabled }) => { + const [anchorEl, setAnchorEl] = useState(null); + + // Handle child element click to open Popover + const handleChildClick = (event) => { + event.stopPropagation(); // Prevent parent onClick from firing + setAnchorEl(event.currentTarget); + }; + + // Handle closing the Popover + const handleClose = () => { + setAnchorEl(null); + }; + + // Determine if the popover is open + const open = Boolean(anchorEl); + const id = open ? address || name : undefined; + + if(disabled){ + return children + } + + return ( + <> + + {/* Render the child without altering dimensions */} + {children} + + + {/* Popover */} + event.stopPropagation(), // Stop propagation inside popover + }, + }} + > + + {/* Option 1: Message */} + + + {/* Option 2: Send QORT */} + + + + + ); +}; diff --git a/src/index.css b/src/index.css index 543bec2..956f995 100644 --- a/src/index.css +++ b/src/index.css @@ -78,6 +78,19 @@ body { border: 4px solid transparent; } +/* Mobile-specific scrollbar styles */ +@media only screen and (max-width: 600px) { + ::-webkit-scrollbar { + width: 8px; /* Narrower scrollbar width on mobile */ + height: 6px; /* Narrower scrollbar height on mobile */ + } + + ::-webkit-scrollbar-thumb { + border-radius: 4px; /* Adjust the radius for a narrower thumb */ + border: 2px solid transparent; /* Narrower thumb border */ + } +} + .group-list::-webkit-scrollbar-thumb:hover { background-color: whitesmoke; } From 0e516e4ce45190f786509d06f7943818754f38fd Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 14 Sep 2024 12:02:49 +0300 Subject: [PATCH 07/25] fixes --- src/components/Chat/AnnouncementList.tsx | 1 + src/components/Group/Group.tsx | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/Chat/AnnouncementList.tsx b/src/components/Chat/AnnouncementList.tsx index 286a920..b55ebb5 100644 --- a/src/components/Chat/AnnouncementList.tsx +++ b/src/components/Chat/AnnouncementList.tsx @@ -56,6 +56,7 @@ export const AnnouncementList = ({ return (
prev === "directs" ? "groups" : "directs" ); - // setNewChat(false); - // setSelectedDirect(null); - // setSelectedGroup(null); - // setGroupSection("default"); + + }} + sx={{ + backgroundColor: chatMode === 'directs' && ( groupChatHasUnread || groupsAnnHasUnread) ? 'red' : 'revert' }} > {chatMode === "groups" && ( @@ -1453,6 +1453,7 @@ export const Group = ({ /> )} + {chatMode === "directs" ? "Switch to groups" : "Direct msgs"}
From 753ba200d45be46e206f426acd0484f13b9201c7 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 14 Sep 2024 18:15:23 +0300 Subject: [PATCH 08/25] fixes --- src/App.tsx | 4 ++-- src/components/Group/Group.tsx | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 3c8bc1a..71db923 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1461,7 +1461,7 @@ function App() { }} /> - {!isMobile && ( + <> - )} + )} {/* {extState !== "not-authenticated" && ( diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index b4434ef..2f8c974 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1357,6 +1357,7 @@ export const Group = ({ res(null); }, 200); }); + setSelectedDirect(null); setGroupSection("announcement"); chrome?.runtime?.sendMessage({ action: "addGroupNotificationTimestamp", @@ -1378,6 +1379,7 @@ export const Group = ({ }, 200); }); setGroupSection("chat"); + setSelectedDirect(null); if (selectedGroupRef.current) { chrome?.runtime?.sendMessage({ action: "addTimestampEnterChat", @@ -2238,6 +2240,7 @@ export const Group = ({ }} onClick={() => { setGroupSection("forum"); + setSelectedDirect(null); }} > setGroupSection("forum")} + onClick={() => { + setSelectedDirect(null); + setGroupSection("forum") + } } > Forum From a6db86f0513561322ac0184e694cb58f1731f934 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 14 Sep 2024 19:51:31 +0300 Subject: [PATCH 09/25] forum scroll down --- src/components/Group/Forum/NewThread.tsx | 7 +- src/components/Group/Forum/Thread.tsx | 306 +++++++++++++++-------- 2 files changed, 207 insertions(+), 106 deletions(-) diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index ff1626d..22eaef6 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -147,7 +147,8 @@ export const NewThread = ({ getSecretKey, closeCallback, postReply, - myName + myName, + setPostReply }: NewMessageProps) => { const { show } = React.useContext(MyContext); @@ -171,6 +172,7 @@ export const NewThread = ({ const closeModal = () => { setIsOpen(false); setValue(""); + setPostReply(null) }; async function publishQDNResource() { @@ -399,7 +401,8 @@ export const NewThread = ({ > setIsOpen(true)} > diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index c386da0..6b8f476 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -1,10 +1,6 @@ import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "react"; - import { Box, Button, IconButton, Skeleton } from "@mui/material"; import { ShowMessage } from "./ShowMessageWithoutModal"; -// import { -// setIsLoadingCustom, -// } from '../../state/features/globalSlice' import { ComposeP, GroupContainer, @@ -24,8 +20,8 @@ import { decryptPublishes, getTempPublish } from "../../Chat/GroupAnnouncements" import { LoadingSnackbar } from "../../Snackbar/LoadingSnackbar"; import { subscribeToEvent, unsubscribeFromEvent } from "../../../utils/events"; import RefreshIcon from "@mui/icons-material/Refresh"; -import { getBaseApi } from "../../../background"; -import { getBaseApiReact } from "../../../App"; +import { getBaseApiReact, isMobile } from "../../../App"; +import { ArrowDownward as ArrowDownwardIcon, ArrowUpward as ArrowUpwardIcon } from '@mui/icons-material'; interface ThreadProps { currentThread: any; @@ -56,7 +52,6 @@ export const Thread = ({ updateThreadActivityCurrentThread }: ThreadProps) => { const [tempPublishedList, setTempPublishedList] = useState([]) - const [messages, setMessages] = useState([]); const [hashMapMailMessages, setHashMapMailMessages] = useState({}); const [hasFirstPage, setHasFirstPage] = useState(false); @@ -66,9 +61,17 @@ export const Thread = ({ const [postReply, setPostReply] = useState(null); const [hasLastPage, setHasLastPage] = useState(false); + // Update: Use a new ref for the scrollable container + const threadContainerRef = useRef(null); + + // New state variables + const [showScrollButton, setShowScrollButton] = useState(false); + const [isAtBottom, setIsAtBottom] = useState(false); + const secretKeyRef = useRef(null); const currentThreadRef = useRef(null); const containerRef = useRef(null); + useEffect(() => { currentThreadRef.current = currentThread; }, [currentThread]); @@ -84,7 +87,6 @@ export const Thread = ({ name: message.name, secretKey, }); - const fullObject = { ...message, @@ -97,35 +99,34 @@ export const Thread = ({ [message.identifier]: fullObject, }; }); - } catch (error) {} + } catch (error) { } }; - const setTempData = async ()=> { + const setTempData = async () => { try { let threadId = currentThread.threadId; - + const keyTemp = 'thread-post' const getTempAnnouncements = await getTempPublish() - - if(getTempAnnouncements?.[keyTemp]){ - - let tempData = [] - Object.keys(getTempAnnouncements?.[keyTemp] || {}).map((key)=> { - const value = getTempAnnouncements?.[keyTemp][key] - - if(value.data?.threadId === threadId){ - tempData.push(value.data) - } - - }) - setTempPublishedList(tempData) - } - } catch (error) { - - } - - } + if (getTempAnnouncements?.[keyTemp]) { + + let tempData = [] + Object.keys(getTempAnnouncements?.[keyTemp] || {}).map((key) => { + const value = getTempAnnouncements?.[keyTemp][key] + + if (value.data?.threadId === threadId) { + tempData.push(value.data) + } + + }) + setTempPublishedList(tempData) + } + } catch (error) { + + } + + } const getMailMessages = React.useCallback( async (groupInfo: any, before, after, isReverse) => { @@ -137,7 +138,7 @@ export const Thread = ({ setHasLastPage(false); setHasNextPage(false); let threadId = groupInfo.threadId; - + const identifier = `thmsg-${threadId}`; let url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&prefix=true`; if (!isReverse) { @@ -160,7 +161,7 @@ export const Thread = ({ }, }); const responseData = await response.json(); - + let fullArrayMsg = [...responseData]; if (isReverse) { @@ -175,13 +176,13 @@ export const Thread = ({ setTimeout(() => { containerRef.current.scrollIntoView({ behavior: "smooth" }); }, 300); - + } - - if (fullArrayMsg.length === 0){ + + if (fullArrayMsg.length === 0) { setTempData() return; - } + } // check if there are newer posts const urlNewer = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=1&includemetadata=false&reverse=false&prefix=true&before=${fullArrayMsg[0].created}`; const responseNewer = await fetch(urlNewer, { @@ -201,7 +202,7 @@ export const Thread = ({ // check if there are older posts const urlOlder = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=1&includemetadata=false&reverse=false&prefix=true&after=${ fullArrayMsg[fullArrayMsg.length - 1].created - }`; + }`; const responseOlder = await fetch(urlOlder, { method: "GET", headers: { @@ -221,13 +222,12 @@ export const Thread = ({ } catch (error) { } finally { setIsLoading(false); - } }, [messages, secretKey] ); const getMessages = React.useCallback(async () => { - + if (!currentThread || !secretKey) return; await getMailMessages(currentThread, null, null, false); }, [getMailMessages, currentThread, secretKey]); @@ -287,8 +287,6 @@ export const Thread = ({ } if (currentThread && secretKey && !firstMount.current) { getMessagesMiddleware(); - - // saveTimestamp(currentThread, user.name) } }, [currentThread, secretKey]); const messageCallback = useCallback((msg: any) => { @@ -350,7 +348,7 @@ export const Thread = ({ } else { fullArrayMsg.unshift(fullObject); } - } catch (error) {} + } catch (error) { } } setMessages(fullArrayMsg); } catch (error) { @@ -360,25 +358,6 @@ export const Thread = ({ [messages] ); - // const checkNewMessagesFunc = useCallback(() => { - // let isCalling = false - // interval.current = setInterval(async () => { - // if (isCalling) return - // isCalling = true - // const res = await checkNewMessages(currentThread) - // isCalling = false - // }, 8000) - // }, [checkNewMessages, currentThrefirstMount.current = truead]) - - // useEffect(() => { - // checkNewMessagesFunc() - // return () => { - // if (interval?.current) { - // clearInterval(interval.current) - // } - // } - // }, [checkNewMessagesFunc]) - const openNewPostWithQuote = useCallback((reply) => { setPostReply(reply); }, []); @@ -406,44 +385,153 @@ export const Thread = ({ const combinedListTempAndReal = useMemo(() => { // Combine the two lists const combined = [...tempPublishedList, ...messages]; - + // Remove duplicates based on the "identifier" const uniqueItems = new Map(); combined.forEach(item => { uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence }); - + // Convert the map back to an array and sort by "created" timestamp in descending order const sortedList = Array.from(uniqueItems.values()).sort((a, b) => a.created - b.created); - + return sortedList; }, [tempPublishedList, messages]); + // Updated useEffect to handle scroll and overflow + useEffect(() => { + const container = threadContainerRef.current; // Updated reference + if (!container) return; + + const handleScroll = () => { + const { scrollTop, scrollHeight, clientHeight } = container; + // Check if user is at the bottom + if (scrollTop + clientHeight >= scrollHeight - 5) { + setIsAtBottom(true); + } else { + setIsAtBottom(false); + } + + // Initial check if content overflows + if (container.scrollHeight > container.clientHeight) { + setShowScrollButton(true); + } else { + setShowScrollButton(false); + } + }; + setTimeout(() => { + handleScroll() + }, 400); + + container.addEventListener('scroll', handleScroll); + + // Cleanup + return () => { + container.removeEventListener('scroll', handleScroll); + }; + }, [messages]); + + // Function to scroll to the top or bottom of the container + const scrollToPosition = () => { + const container = threadContainerRef.current; // Updated reference + if (!container) return; + + if (isAtBottom) { + container.scrollTo({ top: 0, behavior: 'smooth' }); // Scroll to top + } else { + container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); // Scroll to bottom + } + }; + + console.log('showScrollButton', showScrollButton) + if (!currentThread) return null; return ( - - - - + + + + { + setMessages([]); + closeThread(); + }} + > + + {!isMobile && ( + Return to Threads + + )} + + {/* Conditionally render the scroll buttons */} + {showScrollButton && ( + isAtBottom ? ( + + ) : ( + + ) + )} + + + + + + - {currentThread?.threadData?.title} - - { - setMessages([]); - closeThread(); - }} - > - - Return to Threads - + {currentThread?.threadData?.title} + + - - {combinedListTempAndReal.map((message) => { - let fullMessage = message; + + {combinedListTempAndReal.map((message, index, list) => { + let fullMessage = message; if (hashMapMailMessages[message?.identifier]) { fullMessage = hashMapMailMessages[message.identifier]; @@ -534,7 +636,7 @@ export const Thread = ({ myName={userInfo?.name} /> ); - } else if(message?.tempData){ + } else if (message?.tempData) { return ( - {/* {messages.length >= 20 && ( - getMailMessages(currentThread, false, true)}> - - )} */} Date: Sat, 14 Sep 2024 22:26:40 +0300 Subject: [PATCH 10/25] more using friendly for forum posts --- src/components/Chat/ChatDirect.tsx | 3 +- src/components/Group/Forum/Thread.tsx | 124 +++++++++++++++----------- src/components/Group/Group.tsx | 34 +++++-- 3 files changed, 103 insertions(+), 58 deletions(-) diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index f975340..f935eff 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -360,7 +360,8 @@ console.log('isFocusedParent', isFocusedParent) <> setDirectToValue(e.target.value)} /> diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 6b8f476..365c322 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -1,5 +1,5 @@ import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Box, Button, IconButton, Skeleton } from "@mui/material"; +import { Box, Button, ButtonBase, IconButton, Skeleton } from "@mui/material"; import { ShowMessage } from "./ShowMessageWithoutModal"; import { ComposeP, @@ -479,8 +479,9 @@ export const Thread = ({ /> + ) : ( + + ) )} @@ -701,51 +705,71 @@ export const Thread = ({ }} > - - - + sx={{ + padding: isMobile && '5px', + fontSize: isMobile && '14px', + textTransformation: 'capitalize' + }} + onClick={() => { + getMailMessages(currentThread, null, null, false); + }} + disabled={!hasFirstPage} + variant="contained" + > + First + + + + diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 2f8c974..f382150 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1358,6 +1358,7 @@ export const Group = ({ }, 200); }); setSelectedDirect(null); + setNewChat(false) setGroupSection("announcement"); chrome?.runtime?.sendMessage({ action: "addGroupNotificationTimestamp", @@ -1379,6 +1380,7 @@ export const Group = ({ }, 200); }); setGroupSection("chat"); + setNewChat(false) setSelectedDirect(null); if (selectedGroupRef.current) { chrome?.runtime?.sendMessage({ @@ -2241,6 +2243,7 @@ export const Group = ({ onClick={() => { setGroupSection("forum"); setSelectedDirect(null); + setNewChat(false) }} > {selectedGroup && ( <> - + - + + diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index fc0fc1e..4c9e0ce 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -28,7 +28,7 @@ const uid = new ShortUniqueId({ length: 8 }); import CampaignIcon from '@mui/icons-material/Campaign'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import { AnnouncementDiscussion } from "./AnnouncementDiscussion"; -import { MyContext, getBaseApiReact, isMobile, pauseAllQueues, resumeAllQueues } from "../../App"; +import { MyContext, getArbitraryEndpointReact, getBaseApiReact, isMobile, pauseAllQueues, resumeAllQueues } from "../../App"; import { RequestQueueWithPromise } from "../../utils/queue/queue"; import { CustomizedSnackbars } from "../Snackbar/Snackbar"; import { addDataPublishesFunc, getDataPublishesFunc } from "../Group/Group"; @@ -168,6 +168,7 @@ export const GroupAnnouncements = ({ `${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64` ); }) + if(!res?.ok) return data = await res.text(); await addDataPublishesFunc({...resource, data}, selectedGroup, 'anc') } else { @@ -339,7 +340,7 @@ export const GroupAnnouncements = ({ // dispatch(setIsLoadingGlobal(true)) const identifier = `grp-${selectedGroup}-anc-`; - const url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; const response = await fetch(url, { method: "GET", headers: { @@ -376,7 +377,7 @@ export const GroupAnnouncements = ({ const offset = announcements.length const identifier = `grp-${selectedGroup}-anc-`; - const url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; const response = await fetch(url, { method: "GET", headers: { @@ -403,7 +404,7 @@ export const GroupAnnouncements = ({ try { const identifier = `grp-${selectedGroup}-anc-`; - const url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true`; + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true`; const response = await fetch(url, { method: 'GET', headers: { diff --git a/src/components/ContextMenu.tsx b/src/components/ContextMenu.tsx new file mode 100644 index 0000000..6779bb0 --- /dev/null +++ b/src/components/ContextMenu.tsx @@ -0,0 +1,165 @@ +import React, { useState, useRef, useMemo, useEffect } from 'react'; +import { ListItemIcon, Menu, MenuItem, Typography, styled } from '@mui/material'; +import MailOutlineIcon from '@mui/icons-material/MailOutline'; +import NotificationsOffIcon from '@mui/icons-material/NotificationsOff'; +import { executeEvent } from '../utils/events'; + +const CustomStyledMenu = styled(Menu)(({ theme }) => ({ + '& .MuiPaper-root': { + backgroundColor: '#f9f9f9', + borderRadius: '12px', + padding: theme.spacing(1), + boxShadow: '0 5px 15px rgba(0, 0, 0, 0.2)', + }, + '& .MuiMenuItem-root': { + fontSize: '14px', // Smaller font size for the menu item text + color: '#444', + transition: '0.3s background-color', + '&:hover': { + backgroundColor: '#f0f0f0', // Explicit hover state + }, + + }, + })); + +export const ContextMenu = ({ children, groupId, getUserSettings, mutedGroups }) => { + const [menuPosition, setMenuPosition] = useState(null); + const longPressTimeout = useRef(null); + const preventClick = useRef(false); // Flag to prevent click after long-press or right-click + + const isMuted = useMemo(()=> { + return mutedGroups.includes(groupId) + }, [mutedGroups, groupId]) + + // Handle right-click (context menu) for desktop + const handleContextMenu = (event) => { + event.preventDefault(); + event.stopPropagation(); // Prevent parent click + + // Set flag to prevent any click event after right-click + preventClick.current = true; + + setMenuPosition({ + mouseX: event.clientX, + mouseY: event.clientY, + }); + }; + + // Handle long-press for mobile + const handleTouchStart = (event) => { + longPressTimeout.current = setTimeout(() => { + preventClick.current = true; // Prevent the next click after long-press + event.stopPropagation(); // Prevent parent click + setMenuPosition({ + mouseX: event.touches[0].clientX, + mouseY: event.touches[0].clientY, + }); + }, 500); // Long press duration + }; + + const handleTouchEnd = (event) => { + clearTimeout(longPressTimeout.current); + + if (preventClick.current) { + event.preventDefault(); + event.stopPropagation(); // Prevent synthetic click after long-press + preventClick.current = false; // Reset the flag + } + }; + + + + const handleSetGroupMute = ()=> { + try { + let value = [...mutedGroups] + if(isMuted){ + value = value.filter((group)=> group !== groupId) + } else { + value.push(groupId) + } + chrome?.runtime?.sendMessage( + { + action: "addUserSettings", + payload: { + keyValue: { + key: 'mutedGroups', + value + }, + }, + } + ); + setTimeout(() => { + getUserSettings() + }, 400); + + } catch (error) { + + } + } + + + + const handleClose = (e) => { + e.preventDefault(); + e.stopPropagation(); + setMenuPosition(null); + }; + + return ( +
+ {children} + + { + e.stopPropagation(); + }} + > + { + handleClose(e) + executeEvent("markAsRead", { + groupId + }); + }}> + + + + + Mark As Read + + + { + + handleClose(e) + handleSetGroupMute() + + }}> + + + + + {isMuted ? 'Unmute ' : 'Mute '}Push Notifications + + + +
+ ); +}; + + diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 3eaee0f..6d26848 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -53,7 +53,7 @@ import ArrowDownSVG from "../../../assets/svgs/ArrowDown.svg"; import { LoadingSnackbar } from "../../Snackbar/LoadingSnackbar"; import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../../utils/events"; import RefreshIcon from '@mui/icons-material/Refresh'; -import { getBaseApiReact } from "../../../App"; +import { getArbitraryEndpointReact, getBaseApiReact } from "../../../App"; import { WrapperUserAction } from "../../WrapperUserAction"; import { addDataPublishesFunc, getDataPublishesFunc } from "../Group"; const filterOptions = ["Recently active", "Newest", "Oldest"]; @@ -126,6 +126,7 @@ export const GroupMail = ({ const res = await fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64` ); + if(!res?.ok) return data = await res.text(); await addDataPublishesFunc({...resource, data}, groupId, 'thread') @@ -176,7 +177,7 @@ export const GroupMail = ({ } const identifier = `grp-${groupId}-thread-`; - const url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=${20}&includemetadata=false&offset=${offset}&reverse=${isReverse}&prefix=true`; + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=${20}&includemetadata=false&offset=${offset}&reverse=${isReverse}&prefix=true`; const response = await fetch(url, { method: "GET", headers: { @@ -263,7 +264,7 @@ export const GroupMail = ({ // dispatch(setIsLoadingCustom("Loading recent threads")); const identifier = `thmsg-grp-${groupId}-thread-`; - const url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=100&includemetadata=false&offset=${0}&reverse=true&prefix=true`; + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=100&includemetadata=false&offset=${0}&reverse=true&prefix=true`; const response = await fetch(url, { method: "GET", headers: { @@ -299,7 +300,7 @@ export const GroupMail = ({ const getMessageForThreads = newArray.map(async (message: any) => { try { const identifierQuery = message.threadId; - const url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifierQuery}&limit=1&includemetadata=false&offset=${0}&reverse=true&prefix=true`; + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifierQuery}&limit=1&includemetadata=false&offset=${0}&reverse=true&prefix=true`; const response = await fetch(url, { method: "GET", headers: { diff --git a/src/components/Group/Forum/Thread copy.tsx b/src/components/Group/Forum/Thread copy.tsx index 7aadabd..9091a2c 100644 --- a/src/components/Group/Forum/Thread copy.tsx +++ b/src/components/Group/Forum/Thread copy.tsx @@ -24,7 +24,7 @@ import ReturnSVG from '../../../assets/svgs/Return.svg' import { NewThread } from './NewThread' import { decryptPublishes } from '../../Chat/GroupAnnouncements' import { getBaseApi } from '../../../background' -import { getBaseApiReact } from '../../../App' +import { getArbitraryEndpointReact, getBaseApiReact } from '../../../App' interface ThreadProps { currentThread: any groupInfo: any @@ -91,7 +91,7 @@ export const Thread = ({ const offset = messages.length const identifier = `thmsg-${threadId}` - const url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true` + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true` const response = await fetch(url, { method: 'GET', headers: { @@ -180,7 +180,7 @@ export const Thread = ({ let threadId = groupInfo.threadId const identifier = `thmsg-${threadId}` - const url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true` + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true` const response = await fetch(url, { method: 'GET', headers: { diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index 33a9444..f393d5d 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -1,5 +1,20 @@ -import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Box, Button, ButtonBase, IconButton, Skeleton } from "@mui/material"; +import React, { + FC, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { + Avatar, + Box, + Button, + ButtonBase, + IconButton, + Skeleton, + Typography, +} from "@mui/material"; import { ShowMessage } from "./ShowMessageWithoutModal"; import { ComposeP, @@ -10,22 +25,38 @@ import { SingleThreadParent, ThreadContainer, ThreadContainerFullWidth, + ThreadInfoColumn, + ThreadInfoColumnNameP, + ThreadInfoColumnTime, } from "./Mail-styles"; import { Spacer } from "../../../common/Spacer"; import { threadIdentifier } from "./GroupMail"; import LazyLoad from "../../../common/LazyLoad"; import ReturnSVG from "../../../assets/svgs/Return.svg"; import { NewThread } from "./NewThread"; -import { decryptPublishes, getTempPublish } from "../../Chat/GroupAnnouncements"; +import { + decryptPublishes, + getTempPublish, +} from "../../Chat/GroupAnnouncements"; import { LoadingSnackbar } from "../../Snackbar/LoadingSnackbar"; import { subscribeToEvent, unsubscribeFromEvent } from "../../../utils/events"; import RefreshIcon from "@mui/icons-material/Refresh"; -import { getBaseApiReact, isMobile } from "../../../App"; -import { ArrowDownward as ArrowDownwardIcon, ArrowUpward as ArrowUpwardIcon } from '@mui/icons-material'; +import { + getArbitraryEndpointReact, + getBaseApiReact, + isMobile, +} from "../../../App"; +import { + ArrowDownward as ArrowDownwardIcon, + ArrowUpward as ArrowUpwardIcon, +} from "@mui/icons-material"; import { addDataPublishesFunc, getDataPublishesFunc } from "../Group"; import { RequestQueueWithPromise } from "../../../utils/queue/queue"; -const requestQueueSaveToLocal = new RequestQueueWithPromise(1) -const requestQueueDownloadPost = new RequestQueueWithPromise(3) +import { CustomLoader } from "../../../common/CustomLoader"; +import { WrapperUserAction } from "../../WrapperUserAction"; +import { formatTimestampForum } from "../../../utils/time"; +const requestQueueSaveToLocal = new RequestQueueWithPromise(1); +const requestQueueDownloadPost = new RequestQueueWithPromise(3); interface ThreadProps { currentThread: any; groupInfo: any; @@ -33,22 +64,41 @@ interface ThreadProps { members: any; } -const getEncryptedResource = async ({ name, identifier, secretKey, resource, groupId, dataPublishes }) => { - let data = dataPublishes[`${name}-${identifier}`] - if(!data || (data?.update || data?.created !== (resource?.updated || resource?.created))){ - const res = await requestQueueDownloadPost.enqueue(()=> { - return fetch( - `${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64` - ); - }) - data = await res.text(); - await requestQueueSaveToLocal.enqueue(()=> { - return addDataPublishesFunc({...resource, data}, groupId, 'thmsg') - }) - - } else { - data = data.data +const getEncryptedResource = async ({ + name, + identifier, + secretKey, + resource, + groupId, + dataPublishes, +}) => { + let data = dataPublishes[`${name}-${identifier}`]; + if ( + !data || + data?.update || + data?.created !== (resource?.updated || resource?.created) + ) { + const res = await requestQueueDownloadPost.enqueue(() => { + return fetch( + `${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64` + ); + }); + if (!res.ok) { + const errorData = await res.json(); + + return { + error: errorData?.message, + }; } + data = await res.text(); + + if (data?.error || typeof data !== "string") return; + await requestQueueSaveToLocal.enqueue(() => { + return addDataPublishesFunc({ ...resource, data }, groupId, "thmsg"); + }); + } else { + data = data.data; + } const response = await decryptPublishes([{ data }], secretKey); const messageData = response[0]; @@ -63,9 +113,9 @@ export const Thread = ({ userInfo, secretKey, getSecretKey, - updateThreadActivityCurrentThread + updateThreadActivityCurrentThread, }: ThreadProps) => { - const [tempPublishedList, setTempPublishedList] = useState([]) + const [tempPublishedList, setTempPublishedList] = useState([]); const [messages, setMessages] = useState([]); const [hashMapMailMessages, setHashMapMailMessages] = useState({}); const [hasFirstPage, setHasFirstPage] = useState(false); @@ -77,7 +127,7 @@ export const Thread = ({ // Update: Use a new ref for the scrollable container const threadContainerRef = useRef(null); - + const threadBeginningRef = useRef(null) // New state variables const [showScrollButton, setShowScrollButton] = useState(false); const [isAtBottom, setIsAtBottom] = useState(false); @@ -85,18 +135,17 @@ export const Thread = ({ const secretKeyRef = useRef(null); const currentThreadRef = useRef(null); const containerRef = useRef(null); - const dataPublishes = useRef({}) + const dataPublishes = useRef({}); - - const getSavedData = useCallback(async (groupId)=> { - const res = await getDataPublishesFunc(groupId, 'thmsg') - dataPublishes.current = res || {} - }, []) + const getSavedData = useCallback(async (groupId) => { + const res = await getDataPublishesFunc(groupId, "thmsg"); + dataPublishes.current = res || {}; + }, []); - useEffect(()=> { - if(!groupInfo?.groupId) return - getSavedData(groupInfo?.groupId) - }, [groupInfo?.groupId]) + useEffect(() => { + if (!groupInfo?.groupId) return; + getSavedData(groupInfo?.groupId); + }, [groupInfo?.groupId]); useEffect(() => { currentThreadRef.current = currentThread; @@ -114,9 +163,24 @@ export const Thread = ({ secretKey, resource: message, groupId: groupInfo?.groupId, - dataPublishes: dataPublishes.current + dataPublishes: dataPublishes.current, }); + if (responseDataMessage?.error) { + const fullObject = { + ...message, + error: responseDataMessage?.error, + id: message.identifier, + }; + setHashMapMailMessages((prev) => { + return { + ...prev, + [message.identifier]: fullObject, + }; + }); + return; + } + const fullObject = { ...message, ...(responseDataMessage || {}), @@ -128,39 +192,34 @@ export const Thread = ({ [message.identifier]: fullObject, }; }); - } catch (error) { } + } catch (error) {} }; const setTempData = async () => { try { let threadId = currentThread.threadId; - const keyTemp = 'thread-post' - const getTempAnnouncements = await getTempPublish() + const keyTemp = "thread-post"; + const getTempAnnouncements = await getTempPublish(); if (getTempAnnouncements?.[keyTemp]) { - - let tempData = [] + let tempData = []; Object.keys(getTempAnnouncements?.[keyTemp] || {}).map((key) => { - const value = getTempAnnouncements?.[keyTemp][key] + const value = getTempAnnouncements?.[keyTemp][key]; if (value.data?.threadId === threadId) { - tempData.push(value.data) + tempData.push(value.data); } - - }) - setTempPublishedList(tempData) + }); + setTempPublishedList(tempData); } - } catch (error) { - - } - - } + } catch (error) {} + }; const getMailMessages = React.useCallback( async (groupInfo: any, before, after, isReverse, groupId) => { try { - setTempPublishedList([]) + setTempPublishedList([]); setIsLoading(true); setHasFirstPage(false); setHasPreviousPage(false); @@ -169,7 +228,7 @@ export const Thread = ({ let threadId = groupInfo.threadId; const identifier = `thmsg-${threadId}`; - let url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&prefix=true`; + let url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&prefix=true`; if (!isReverse) { url = url + "&reverse=false"; } @@ -191,29 +250,35 @@ export const Thread = ({ }); const responseData = await response.json(); - let fullArrayMsg = [...responseData]; if (isReverse) { fullArrayMsg = fullArrayMsg.reverse(); } // let newMessages: any[] = [] for (const message of responseData) { - getIndividualMsg(message); + getIndividualMsg(message); } setMessages(fullArrayMsg); if (before === null && after === null && isReverse) { setTimeout(() => { containerRef.current.scrollIntoView({ behavior: "smooth" }); }, 300); - + } + if(after || before === null && after === null && !isReverse){ + setTimeout(() => { + threadBeginningRef.current.scrollIntoView(); + }, 100); + } if (fullArrayMsg.length === 0) { - setTempData() + setTempData(); return; } // check if there are newer posts - const urlNewer = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=1&includemetadata=false&reverse=false&prefix=true&before=${fullArrayMsg[0].created}`; + const urlNewer = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=1&includemetadata=false&reverse=false&prefix=true&before=${ + fullArrayMsg[0].created + }`; const responseNewer = await fetch(urlNewer, { method: "GET", headers: { @@ -229,9 +294,9 @@ export const Thread = ({ setHasPreviousPage(false); } // check if there are older posts - const urlOlder = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=1&includemetadata=false&reverse=false&prefix=true&after=${ + const urlOlder = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=1&includemetadata=false&reverse=false&prefix=true&after=${ fullArrayMsg[fullArrayMsg.length - 1].created - }`; + }`; const responseOlder = await fetch(urlOlder, { method: "GET", headers: { @@ -245,14 +310,14 @@ export const Thread = ({ } else { setHasLastPage(false); setHasNextPage(false); - setTempData() - updateThreadActivityCurrentThread() + setTempData(); + updateThreadActivityCurrentThread(); } } catch (error) { - console.log('error', error) + console.log("error", error); } finally { setIsLoading(false); - getSavedData(groupId) + getSavedData(groupId); } }, [messages, secretKey] @@ -332,7 +397,7 @@ export const Thread = ({ let threadId = groupInfo.threadId; const identifier = `thmsg-${threadId}`; - const url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true`; + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=${threadIdentifier}&identifier=${identifier}&limit=20&includemetadata=false&offset=${0}&reverse=true&prefix=true`; const response = await fetch(url, { method: "GET", headers: { @@ -359,7 +424,7 @@ export const Thread = ({ secretKey: secretKeyRef.current, resource: message, groupId: groupInfo?.groupId, - dataPublishes: dataPublishes.current + dataPublishes: dataPublishes.current, }); const fullObject = { @@ -381,7 +446,7 @@ export const Thread = ({ } else { fullArrayMsg.unshift(fullObject); } - } catch (error) { } + } catch (error) {} } setMessages(fullArrayMsg); } catch (error) { @@ -421,12 +486,14 @@ export const Thread = ({ // Remove duplicates based on the "identifier" const uniqueItems = new Map(); - combined.forEach(item => { - uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence + combined.forEach((item) => { + uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence }); // Convert the map back to an array and sort by "created" timestamp in descending order - const sortedList = Array.from(uniqueItems.values()).sort((a, b) => a.created - b.created); + const sortedList = Array.from(uniqueItems.values()).sort( + (a, b) => a.created - b.created + ); return sortedList; }, [tempPublishedList, messages]); @@ -452,15 +519,15 @@ export const Thread = ({ setShowScrollButton(false); } }; - setTimeout(() => { - handleScroll() - }, 400); + setTimeout(() => { + handleScroll(); + }, 400); - container.addEventListener('scroll', handleScroll); + container.addEventListener("scroll", handleScroll); // Cleanup return () => { - container.removeEventListener('scroll', handleScroll); + container.removeEventListener("scroll", handleScroll); }; }, [messages]); @@ -470,31 +537,32 @@ export const Thread = ({ if (!container) return; if (isAtBottom) { - container.scrollTo({ top: 0, behavior: 'smooth' }); // Scroll to top + container.scrollTo({ top: 0, behavior: "smooth" }); // Scroll to top } else { - container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' }); // Scroll to bottom + container.scrollTo({ top: container.scrollHeight, behavior: "smooth" }); // Scroll to bottom } }; - if (!currentThread) return null; return ( - + - - + { setMessages([]); closeThread(); }} > - {!isMobile && ( - Return to Threads - - )} + {!isMobile && Return to Threads} {/* Conditionally render the scroll buttons */} - {showScrollButton && ( - isAtBottom ? ( + {showScrollButton && + (isAtBottom ? ( - + ) : ( - + - ) - )} + ))} +
- + - {currentThread?.threadData?.title} + + {currentThread?.threadData?.title} + - + - + {combinedListTempAndReal.map((message, index, list) => { let fullMessage = message; if (hashMapMailMessages[message?.identifier]) { fullMessage = hashMapMailMessages[message.identifier]; + + + if (fullMessage?.error) { + return ( + + + + + + {message?.name?.charAt(0)} + + + + + + {message?.name} + + + + {formatTimestampForum(message?.created)} + + + + + + {fullMessage?.error} + + + + + + ); + } return ( - + + > + + + + {message?.name?.charAt(0)} + + + + + + {message?.name} + + + + {formatTimestampForum(message?.created)} + + + + + + + Downloading from QDN + + + + ); })} -
+ {!hasLastPage && !isLoading && ( <> @@ -714,7 +942,13 @@ export const Thread = ({ variant="outlined" startIcon={} onClick={() => { - getMailMessages(currentThread, null, null, true, groupInfo?.groupId); + getMailMessages( + currentThread, + null, + null, + true, + groupInfo?.groupId + ); }} sx={{ color: "white", @@ -726,8 +960,11 @@ export const Thread = ({ )} - {messages?.length > 4 && ( - <> + + 4 ? 'visible' : 'hidden' + }}> - - - + sx={{ + padding: isMobile && "5px", + fontSize: isMobile && "14px", + textTransformation: "capitalize", + }} + onClick={() => { + getMailMessages( + currentThread, + null, + null, + false, + groupInfo?.groupId + ); + }} + disabled={!hasFirstPage} + variant="contained" + > + First + + + + - - )} + + +
{ (response) => { if (!response?.error) { res(response); + return } rej(response.error); } @@ -399,8 +403,11 @@ export const Group = ({ const [groupAnnouncements, setGroupAnnouncements] = React.useState({}); const [defaultThread, setDefaultThread] = React.useState(null); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); - + const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false) + const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState('') const [drawerMode, setDrawerMode] = React.useState("groups"); + const [mutedGroups, setMutedGroups] = useState([]) + const isFocusedRef = useRef(true); const selectedGroupRef = useRef(null); const selectedDirectRef = useRef(null); @@ -430,6 +437,37 @@ export const Group = ({ selectedDirectRef.current = selectedDirect; }, [selectedDirect]); + const getUserSettings = async () => { + try { + return new Promise((res, rej) => { + chrome?.runtime?.sendMessage( + { + action: "getUserSettings", + payload: { + key: 'mutedGroups' + } + }, + (response) => { + if (!response?.error) { + + setMutedGroups(response || []); + res(response); + return + } + rej(response.error); + } + ); + }); + } catch (error) { + console.log('error', error) + } + }; + + useEffect(()=> { + + getUserSettings() + }, []) + const getTimestampEnterChat = async () => { try { return new Promise((res, rej) => { @@ -610,7 +648,7 @@ export const Group = ({ const getPublishesFromAdmins = async (admins: string[]) => { // const validApi = await findUsableApi(); const queryString = admins.map((name) => `name=${name}`).join("&"); - const url = `${getBaseApiReact()}/arbitrary/resources/search?mode=ALL&service=DOCUMENT_PRIVATE&identifier=symmetric-qchat-group-${ + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=symmetric-qchat-group-${ selectedGroup?.groupId }&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`; const response = await fetch(url); @@ -642,21 +680,22 @@ export const Group = ({ secretKeyToPublish?: boolean ) => { try { + setIsLoadingGroupMessage('Locating encryption keys') // setGroupDataLastSet(null) pauseAllQueues(); let dataFromStorage; let publishFromStorage; let adminsFromStorage; - const groupData = await getGroupDataSingle(selectedGroup?.groupId); - if ( - groupData?.secretKeyData && - Date.now() - groupData?.timestampLastSet < 3600000 - ) { - dataFromStorage = groupData.secretKeyData; - publishFromStorage = groupData.secretKeyResource; - adminsFromStorage = groupData.admins; - // setGroupDataLastSet(groupData.timestampLastSet) - } + // const groupData = await getGroupDataSingle(selectedGroup?.groupId); + // if ( + // groupData?.secretKeyData && + // Date.now() - groupData?.timestampLastSet < 3600000 + // ) { + // dataFromStorage = groupData.secretKeyData; + // publishFromStorage = groupData.secretKeyResource; + // adminsFromStorage = groupData.admins; + // // setGroupDataLastSet(groupData.timestampLastSet) + // } if ( secretKeyToPublish && @@ -704,6 +743,7 @@ export const Group = ({ if (dataFromStorage) { data = dataFromStorage; } else { + setIsLoadingGroupMessage('Downloading encryption keys') const res = await fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ publish.identifier @@ -748,6 +788,7 @@ export const Group = ({ } } finally { setIsLoadingGroup(false); + setIsLoadingGroupMessage('') if (!secretKeyToPublish) { // await getAdmins(selectedGroup?.groupId); } @@ -967,7 +1008,7 @@ export const Group = ({ secretKey && admins.includes(myAddress) ) { - console.log("goung through", admins); + // getAdmins(selectedGroup?.groupId); getMembers(selectedGroup?.groupId); initiatedGetMembers.current = true; @@ -1156,6 +1197,41 @@ export const Group = ({ }; }, [directs, selectedDirect]); + const handleMarkAsRead = (e)=> { + const {groupId} = e.detail + chrome?.runtime?.sendMessage({ + action: "addTimestampEnterChat", + payload: { + timestamp: Date.now(), + groupId, + }, + }); + + + + chrome?.runtime?.sendMessage({ + action: "addGroupNotificationTimestamp", + payload: { + timestamp: Date.now(), + groupId, + }, + }); + setTimeout(() => { + getGroupAnnouncements(); + getTimestampEnterChat(); + }, 200); + } + + + useEffect(() => { + subscribeToEvent("markAsRead", handleMarkAsRead); + + return () => { + unsubscribeFromEvent("markAsRead", handleMarkAsRead); + }; + }, []); + + const resetAllStatesAndRefs = () => { // Reset all useState values to their initial states setSecretKey(null); @@ -1173,6 +1249,7 @@ export const Group = ({ setMembers([]); setGroupOwner(null); setTriedToFetchSecretKey(false); + setHideCommonKeyPopup(false) setOpenAddGroup(false); setIsInitialGroups(false); setOpenManageMembers(false); @@ -1641,7 +1718,7 @@ export const Group = ({ onClick={() => { clearAllQueues(); setSelectedDirect(null); - + setTriedToFetchSecretKey(false); setNewChat(false); setSelectedGroup(null); setSecretKey(null); @@ -1652,7 +1729,7 @@ export const Group = ({ setAdminsWithNames([]); setMembers([]); setMemberCountFromSecretKeyData(null); - setTriedToFetchSecretKey(false); + setHideCommonKeyPopup(false); setFirstSecretKeyInCreation(false); // setGroupSection("announcement"); setGroupSection("chat"); @@ -1701,6 +1778,7 @@ export const Group = ({ group?.groupId === selectedGroup?.groupId && "white", }} > + )} + ))} @@ -2019,8 +2098,9 @@ export const Group = ({ {admins.includes(myAddress) && shouldReEncrypt && triedToFetchSecretKey && - !firstSecretKeyInCreation && ( + !firstSecretKeyInCreation && !hideCommonKeyPopup && ( diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx index cfdb82f..2d1f04a 100644 --- a/src/components/WrapperUserAction.tsx +++ b/src/components/WrapperUserAction.tsx @@ -75,7 +75,7 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => { name, }); handleClose(); - console.log('Message clicked'); + }} sx={{ color: 'white' @@ -93,7 +93,7 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => { name, }); handleClose(); - console.log('Send QORT clicked'); + }} sx={{ color: 'white' From b59a00440612a359d324c874b754948e95f2f2de Mon Sep 17 00:00:00 2001 From: PhilReact Date: Mon, 16 Sep 2024 13:49:56 +0300 Subject: [PATCH 14/25] comment out noise for notification --- src/background.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/background.ts b/src/background.ts index 4c0e39c..8c74424 100644 --- a/src/background.ts +++ b/src/background.ts @@ -244,7 +244,7 @@ async function checkWebviewFocus() { } function playNotificationSound() { - chrome.runtime.sendMessage({ action: "PLAY_NOTIFICATION_SOUND" }); + // chrome.runtime.sendMessage({ action: "PLAY_NOTIFICATION_SOUND" }); } const handleNotificationDirect = async (directs) => { From e60c7ff7046cd8bee1c3a837a3dc5eba69052b38 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 18 Sep 2024 00:08:19 +0300 Subject: [PATCH 15/25] add ability to use local node to trade --- public/content-script.js | 36 +++++- src/App.tsx | 23 +++- src/background.ts | 119 +++++++++++++++--- .../TradeBotRespondMultipleRequest.ts | 42 +++++++ 4 files changed, 197 insertions(+), 23 deletions(-) create mode 100644 src/transactions/TradeBotRespondMultipleRequest.ts diff --git a/public/content-script.js b/public/content-script.js index 28c717b..e84cd7d 100644 --- a/public/content-script.js +++ b/public/content-script.js @@ -49,7 +49,6 @@ document.addEventListener('qortalExtensionRequests', async (event) => { } }); } else if (type === 'REQUEST_CONNECTION') { - console.log('REQUEST_CONNECTION') const hostname = window.location.hostname chrome?.runtime?.sendMessage({ action: "connection", payload: { hostname @@ -106,8 +105,9 @@ document.addEventListener('qortalExtensionRequests', async (event) => { } chrome?.runtime?.sendMessage({ action: "buyOrder", payload: { - qortalAtAddress: payload.qortalAtAddress, - hostname + qortalAtAddresses: payload.qortalAtAddresses, + hostname, + useLocal: payload?.useLocal }, timeout}, (response) => { if (response.error) { @@ -153,6 +153,36 @@ document.addEventListener('qortalExtensionRequests', async (event) => { })); } }); + } else if(type === 'CHECK_IF_LOCAL'){ + + + const hostname = window.location.hostname + const res = await connection(hostname) + if(!res){ + document.dispatchEvent(new CustomEvent('qortalExtensionResponses', { + detail: { type: "USER_INFO", data: { + error: "Not authorized" + }, requestId } + })); + return + } + chrome?.runtime?.sendMessage({ action: "checkLocal", payload: { + hostname + }, timeout }, (response) => { + + if (response.error) { + document.dispatchEvent(new CustomEvent('qortalExtensionResponses', { + detail: { type: "CHECK_IF_LOCAL", data: { + error: response.error + }, requestId } + })); + } else { + // Include the requestId in the detail when dispatching the response + document.dispatchEvent(new CustomEvent('qortalExtensionResponses', { + detail: { type: "CHECK_IF_LOCAL", data: response, requestId } + })); + } + }); } else if (type === 'REQUEST_AUTHENTICATION') { const hostname = window.location.hostname const res = await connection(hostname) diff --git a/src/App.tsx b/src/App.tsx index 25e21db..44d7c5d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -236,7 +236,7 @@ export const getArbitraryEndpointReact = () => { if (globalApiKey) { - return `/arbitrary/resources/searchsimple`; + return `/arbitrary/resources/search`; } else { return `/arbitrary/resources/searchsimple`; } @@ -688,6 +688,7 @@ function App() { crosschainAtInfo: requestBuyOrder?.crosschainAtInfo, interactionId: requestBuyOrder?.interactionId, isDecline: true, + useLocal: requestBuyOrder?.useLocal }, }, (response) => { @@ -705,6 +706,7 @@ function App() { crosschainAtInfo: requestBuyOrder?.crosschainAtInfo, interactionId: requestBuyOrder?.interactionId, isDecline: false, + useLocal: requestBuyOrder?.useLocal }, }, (response) => { @@ -959,6 +961,11 @@ function App() { setMemberGroups([]) }; + function roundUpToDecimals(number, decimals = 8) { + const factor = Math.pow(10, decimals); // Create a factor based on the number of decimals + return Math.ceil(+number * factor) / factor; + } + const authenticateWallet = async () => { try { setIsLoading(true); @@ -1595,7 +1602,7 @@ function App() { {/* {extState !== "not-authenticated" && ( )} */} - {extState === "authenticated" && ( + {extState === "authenticated" && isMainWindow && ( The Application

{" "} {requestBuyOrder?.hostname}

- is requesting a buy order + is requesting {requestBuyOrder?.crosschainAtInfo?.length} {`buy order${requestBuyOrder?.crosschainAtInfo.length === 1 ? '' : 's'}`} - {+requestBuyOrder?.crosschainAtInfo?.qortAmount} QORT + {requestBuyOrder?.crosschainAtInfo?.reduce((latest, cur)=> { + return latest + +cur?.qortAmount + }, 0)} QORT - {requestBuyOrder?.crosschainAtInfo?.expectedForeignAmount}{" "} - {requestBuyOrder?.crosschainAtInfo?.foreignBlockchain} + {roundUpToDecimals(requestBuyOrder?.crosschainAtInfo?.reduce((latest, cur)=> { + return latest + +cur?.expectedForeignAmount + }, 0))} + {` ${requestBuyOrder?.crosschainAtInfo?.[0]?.foreignBlockchain}`} {/* diff --git a/src/background.ts b/src/background.ts index 8c74424..00ceffc 100644 --- a/src/background.ts +++ b/src/background.ts @@ -25,6 +25,7 @@ import PhraseWallet from "./utils/generateWallet/phrase-wallet"; import { RequestQueueWithPromise } from "./utils/queue/queue"; import { validateAddress } from "./utils/validateAddress"; import { Sha256 } from "asmcrypto.js"; +import { TradeBotRespondMultipleRequest } from "./transactions/TradeBotRespondMultipleRequest"; let lastGroupNotification; export const groupApi = "https://ext-node.qortal.link"; @@ -112,7 +113,7 @@ const getApiKeyFromStorage = async () => { const getArbitraryEndpoint = async () => { const apiKey = await getApiKeyFromStorage(); // Retrieve apiKey asynchronously if (apiKey) { - return `/arbitrary/resources/searchsimple`; + return `/arbitrary/resources/search`; } else { return `/arbitrary/resources/searchsimple`; } @@ -963,6 +964,13 @@ async function getTradeInfo(qortalAtAddress) { const data = await response.json(); return data; } +async function getTradesInfo(qortalAtAddresses) { + // Use Promise.all to fetch data for all addresses concurrently + const trades = await Promise.all( + qortalAtAddresses.map((address) => getTradeInfo(address)) + ); + return trades; // Return the array of trade info objects +} async function getBalanceInfo() { const wallet = await getSaveWallet(); @@ -1467,7 +1475,7 @@ async function sendChat({ qortAddress, recipientPublicKey, message }) { const hasEnoughBalance = +balance < 4 ? false : true; const difficulty = 8; const jsonData = { - atAddress: message.atAddress, + addresses: message.addresses, foreignKey: message.foreignKey, receivingAddress: message.receivingAddress, }; @@ -1714,15 +1722,80 @@ async function decryptDirectFunc({ messages, involvingAddress }) { return holdMessages; } -async function createBuyOrderTx({ crosschainAtInfo }) { +async function createBuyOrderTx({ crosschainAtInfo, useLocal }) { try { + if(useLocal){ + const wallet = await getSaveWallet(); + + const address = wallet.address0; + + const resKeyPair = await getKeyPair(); + const parsedData = JSON.parse(resKeyPair); + const message = { + addresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), + foreignKey: parsedData.ltcPrivateKey, + receivingAddress: address, + }; + let responseVar + const txn = new TradeBotRespondMultipleRequest().createTransaction(message) + const apiKey = await getApiKeyFromStorage(); + const responseFetch = await fetch(`http://127.0.0.1:12391/crosschain/tradebot/respondmultiple?apiKey=${apiKey}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(txn), + }); + + const res = await responseFetch.json(); + + if(res === false){ + responseVar = { response: "Unable to execute buy order", success: false }; + } else { + responseVar = { response: res, success: true }; + } + const { response, success } = responseVar + let responseMessage; + if (success) { + responseMessage = { + callResponse: response, + extra: { + message: 'Transaction processed successfully!', + atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), + + } + }; + } else { + responseMessage = { + callResponse: 'ERROR', + extra: { + message: response, + atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), + + } + }; + } + + setTimeout(() => { + chrome.tabs.query({}, function (tabs) { + tabs.forEach((tab) => { + chrome.tabs.sendMessage(tab.id, { + type: "RESPONSE_FOR_TRADES", + message: responseMessage, + }); + }); + }); + }, 5000); + + return + } const wallet = await getSaveWallet(); const address = wallet.address0; const resKeyPair = await getKeyPair(); const parsedData = JSON.parse(resKeyPair); const message = { - atAddress: crosschainAtInfo.qortalAtAddress, + addresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), foreignKey: parsedData.ltcPrivateKey, receivingAddress: address, }; @@ -1740,7 +1813,7 @@ async function createBuyOrderTx({ crosschainAtInfo }) { }); if (res?.encryptedMessageToBase58) { return { - atAddress: crosschainAtInfo.qortalAtAddress, + atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), encryptedMessageToBase58: res?.encryptedMessageToBase58, node: buyTradeNodeBaseUrl, qortAddress: address, @@ -1751,7 +1824,7 @@ async function createBuyOrderTx({ crosschainAtInfo }) { }; } return { - atAddress: crosschainAtInfo.qortalAtAddress, + atAddresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), chatSignature: res?.signature, node: buyTradeNodeBaseUrl, qortAddress: address, @@ -2237,6 +2310,7 @@ async function fetchMessagesForBuyOrders(apiCall, signature, senderPublicKey) { try { const response = await fetch(apiCall); let data = await response.json(); + data = data.filter( (item) => !triedChatMessage.includes(item.signature) ); @@ -2250,7 +2324,7 @@ async function fetchMessagesForBuyOrders(apiCall, signature, senderPublicKey) { privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - + const decodedMessage = decryptChatMessage( encodedMessageObj.data, keyPair.privateKey, @@ -2427,6 +2501,12 @@ async function setChatHeads(data) { }); } +async function checkLocalFunc(){ + const apiKey = await getApiKeyFromStorage() + return !!apiKey + +} + async function getTempPublish() { const wallet = await getSaveWallet(); const address = wallet.address0; @@ -3349,8 +3429,8 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { break; case "buyOrder": { - const { qortalAtAddress, hostname } = request.payload; - getTradeInfo(qortalAtAddress) + const { qortalAtAddresses, hostname, useLocal } = request.payload; + getTradesInfo(qortalAtAddresses) .then((crosschainAtInfo) => { const popupUrl = chrome.runtime.getURL( "index.html?secondary=true" @@ -3420,6 +3500,7 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { hostname, crosschainAtInfo, interactionId, + useLocal }, }); }, 500); @@ -3665,7 +3746,7 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { break; case "buyOrderConfirmation": { - const { crosschainAtInfo, isDecline } = request.payload; + const { crosschainAtInfo, isDecline, useLocal } = request.payload; const interactionId2 = request.payload.interactionId; // Retrieve the stored sendResponse callback const originalSendResponse = pendingResponses.get(interactionId2); @@ -3677,7 +3758,7 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { pendingResponses.delete(interactionId2); return; } - createBuyOrderTx({ crosschainAtInfo }) + createBuyOrderTx({ crosschainAtInfo, useLocal }) .then((res) => { sendResponse(true); originalSendResponse(res); @@ -3839,6 +3920,19 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { resumeAllQueues(); sendResponse(true); + break; + } + case "checkLocal": { + checkLocalFunc() + .then((res) => { + sendResponse(res); + }) + .catch((error) => { + console.error(error.message); + sendResponse({ error: error.message }); + }); + + break; } case "decryptSingleForPublishes": { @@ -4037,7 +4131,6 @@ chrome.action?.onClicked?.addListener((tab) => { chrome.windows.getAll( { populate: true, windowTypes: ["popup"] }, (windows) => { - console.log("windows", windows); // Attempt to find an existing popup window that has a tab with the correct URL const existingPopup = windows.find((w) => { return ( @@ -4046,7 +4139,6 @@ chrome.action?.onClicked?.addListener((tab) => { ); }); if (existingPopup) { - console.log("Focusing existing popup window", existingPopup.id); // If the popup exists but is minimized or not focused, focus it if (isMobile) { @@ -4067,7 +4159,6 @@ chrome.action?.onClicked?.addListener((tab) => { }); } } else { - console.log("No existing popup, creating new window..."); // No existing popup found, restore the saved bounds or create a new one restoreWindowBounds((savedBounds) => { chrome.system.display.getInfo((displays) => { diff --git a/src/transactions/TradeBotRespondMultipleRequest.ts b/src/transactions/TradeBotRespondMultipleRequest.ts new file mode 100644 index 0000000..a7eb4d8 --- /dev/null +++ b/src/transactions/TradeBotRespondMultipleRequest.ts @@ -0,0 +1,42 @@ +// @ts-nocheck + +/** + * CrossChain - TradeBot Respond Multiple Request (Buy Action) + * + * These are special types of transactions (JSON ENCODED) + */ + +export class TradeBotRespondMultipleRequest { + constructor() { + // ... + } + + createTransaction(txnReq) { + this.addresses(txnReq.addresses) + this.foreignKey(txnReq.foreignKey) + this.receivingAddress(txnReq.receivingAddress) + + return this.txnRequest() + } + + addresses(addresses) { + this._addresses = addresses + } + + foreignKey(foreignKey) { + this._foreignKey = foreignKey + } + + receivingAddress(receivingAddress) { + this._receivingAddress = receivingAddress + } + + txnRequest() { + return { + addresses: this._addresses, + foreignKey: this._foreignKey, + receivingAddress: this._receivingAddress + } + } +} + From a7ccaf53e0559e53c464815931999bfc9e47aa12 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Wed, 18 Sep 2024 06:59:39 +0300 Subject: [PATCH 16/25] leave sending msg until new one --- src/MessageQueueContext.tsx | 66 ++++++++++++++++++++---------- src/background.ts | 6 ++- src/components/Chat/ChatDirect.tsx | 19 ++++++--- src/components/Chat/ChatGroup.tsx | 31 ++++++++++---- 4 files changed, 88 insertions(+), 34 deletions(-) diff --git a/src/MessageQueueContext.tsx b/src/MessageQueueContext.tsx index 2284da0..166a4c3 100644 --- a/src/MessageQueueContext.tsx +++ b/src/MessageQueueContext.tsx @@ -12,12 +12,11 @@ export const MessageQueueProvider = ({ children }) => { const [queueChats, setQueueChats] = useState({}); // Stores chats and status for display const isProcessingRef = useRef(false); // To track if the queue is being processed const maxRetries = 4; - const clearStatesMessageQueueProvider = useCallback(() => { - setQueueChats({}) - messageQueue = [] - isProcessingRef.current = false - }, []) + setQueueChats({}); + messageQueue = []; + isProcessingRef.current = false; + }, []); // Function to add a message to the queue const addToQueue = useCallback((sendMessageFunc, messageObj, type, groupDirectId) => { @@ -40,16 +39,43 @@ export const MessageQueueProvider = ({ children }) => { // Add the message to the global messageQueue messageQueue = [ ...messageQueue, - { func: sendMessageFunc, identifier: tempId, groupDirectId } + { func: sendMessageFunc, identifier: tempId, groupDirectId, specialId: messageObj?.message?.specialId } ]; // Start processing the queue if not already processing processQueue(); }, []); - // Function to process the messageQueue - // Function to process the messageQueue -const processQueue = useCallback(async () => { + // Method to process with new messages and groupDirectId + const processWithNewMessages = (newMessages, groupDirectId) => { + processQueue(newMessages, groupDirectId); + }; + + // Function to process the messageQueue and handle new messages + const processQueue = useCallback(async (newMessages = [], groupDirectId) => { + // Filter out any message in the queue that matches the specialId from newMessages + messageQueue = messageQueue.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); + }); + + // If no more chats for this group, delete the groupDirectId entry + if (updatedChats[groupDirectId].length === 0) { + delete updatedChats[groupDirectId]; + } + } + return updatedChats; + }); + // If currently processing or the queue is empty, return if (isProcessingRef.current || messageQueue.length === 0) return; @@ -77,18 +103,17 @@ const processQueue = useCallback(async () => { // Execute the function stored in the messageQueue await currentMessage.func(); - // Remove the message from the messageQueue after successful sending - messageQueue = messageQueue.slice(1); + messageQueue = messageQueue.slice(1); // 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; - }); + // setQueueChats((prev) => { + // const updatedChats = { ...prev }; + // updatedChats[groupDirectId] = updatedChats[groupDirectId].filter( + // (item) => item.identifier !== identifier + // ); + // return updatedChats; + // }); } catch (error) { console.error('Message sending failed', error); @@ -109,7 +134,7 @@ const processQueue = useCallback(async () => { updatedChats[groupDirectId][chatIndex].status = 'failed-permanent'; // Remove the message from the messageQueue after max retries - messageQueue = messageQueue.slice(1); + messageQueue = messageQueue.slice(1); // Slice for failed messages after max retries // Remove the message from queueChats after failure updatedChats[groupDirectId] = updatedChats[groupDirectId].filter( @@ -129,9 +154,8 @@ const processQueue = useCallback(async () => { isProcessingRef.current = false; }, []); - return ( - + {children} ); diff --git a/src/background.ts b/src/background.ts index 00ceffc..86b1f11 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1583,6 +1583,7 @@ async function sendChatDirect({ chatReference, messageText, publicKeyOfRecipient, + otherData }) { let recipientPublicKey; let recipientAddress = address; @@ -1618,6 +1619,7 @@ async function sendChatDirect({ const finalJson = { message: messageText, version: 2, + ...(otherData || {}) }; const messageStringified = JSON.stringify(finalJson); const tx = await createTransaction(18, keyPair, { @@ -1667,7 +1669,7 @@ async function decryptSingleFunc({ const decryptToUnit8Array = base64ToUint8Array(res); const responseData = uint8ArrayToObject(decryptToUnit8Array); - holdMessages.push({ ...message, text: responseData }); + holdMessages.push({ ...message, decryptedData: responseData }); } catch (error) {} } return holdMessages; @@ -3996,6 +3998,7 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { messageText, publicKeyOfRecipient, address, + otherData } = request.payload; sendChatDirect({ @@ -4005,6 +4008,7 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { typeMessage, publicKeyOfRecipient, address, + otherData }) .then((res) => { sendResponse(res); diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index da49ba4..33325c8 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -16,12 +16,14 @@ import { getPublicKey } from '../../background'; import { useMessageQueue } from '../../MessageQueueContext'; import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import ShortUniqueId from "short-unique-id"; +const uid = new ShortUniqueId({ length: 5 }); export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDirect, setNewChat, getTimestampEnterChat, myName, balance, close}) => { - const { queueChats, addToQueue, } = useMessageQueue(); + const { queueChats, addToQueue, processWithNewMessages} = useMessageQueue(); const [isFocusedParent, setIsFocusedParent] = useState(false); const [messages, setMessages] = useState([]) @@ -79,6 +81,9 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi }}, (response) => { if (!response?.error) { + + processWithNewMessages(response, selectedDirect?.address) + res(response) if(hasInitialized.current){ @@ -210,14 +215,14 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi -const sendChatDirect = async ({ chatReference = undefined, messageText}: any, address, publicKeyOfRecipient, isNewChatVar)=> { +const sendChatDirect = async ({ chatReference = undefined, messageText, otherData}: any, address, publicKeyOfRecipient, isNewChatVar)=> { try { const directTo = isNewChatVar ? directToValue : address if(!directTo) return return new Promise((res, rej)=> { chrome?.runtime?.sendMessage({ action: "sendChatDirect", payload: { - directTo, chatReference, messageText, publicKeyOfRecipient, address: directTo + directTo, chatReference, messageText, otherData, publicKeyOfRecipient, address: directTo }}, async (response) => { if (!response?.error) { @@ -290,8 +295,11 @@ const clearEditorContent = () => { await sendChatDirect({ messageText: htmlContent}, null, null, true) return } + const otherData = { + specialId: uid.rnd() + } const sendMessageFunc = async () => { - await sendChatDirect({ messageText: htmlContent}, selectedDirect?.address, publicKeyOfRecipient, false) + await sendChatDirect({ messageText: htmlContent, otherData}, selectedDirect?.address, publicKeyOfRecipient, false) }; // Add the function to the queue @@ -300,7 +308,8 @@ const clearEditorContent = () => { text: htmlContent, timestamp: Date.now(), senderName: myName, - sender: myAddress + sender: myAddress, + ...(otherData || {}) }, } diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index d7de9c4..162e415 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -16,6 +16,10 @@ import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes import { useMessageQueue } from '../../MessageQueueContext' import { executeEvent } from '../../utils/events' import { Box } from '@mui/material' +import ShortUniqueId from "short-unique-id"; + + +const uid = new ShortUniqueId({ length: 5 }); @@ -36,7 +40,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, const timeoutIdRef = useRef(null); // Timeout ID reference const groupSocketTimeoutRef = useRef(null); // Group Socket Timeout reference const editorRef = useRef(null); - const { queueChats, addToQueue, } = useMessageQueue(); + const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; @@ -92,8 +96,13 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, data: encryptedMessages, secretKeyObject: secretKey }}, (response) => { - if (!response?.error) { + processWithNewMessages(response?.map((item)=> { + return { + ...item, + ...(item?.decryptedData || {}) + } + }), selectedGroup) res(response) if(hasInitialized.current){ @@ -101,7 +110,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, return { ...item, id: item.signature, - text: item.text, + text: item?.decryptedData?.message || "", unread: item?.sender === myAddress ? false : true } } ) @@ -111,7 +120,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, return { ...item, id: item.signature, - text: item.text, + text: item?.decryptedData?.message || "", unread: false } } ) @@ -296,11 +305,18 @@ const clearEditorContent = () => { setIsSending(true) const message = htmlContent const secretKeyObject = await getSecretKey(false, true) - const message64: any = await objectToBase64(message) + const otherData = { + specialId: uid.rnd() + } + const objectMessage = { + message, + ...(otherData || {}) + } + const message64: any = await objectToBase64(objectMessage) const encryptSingle = await encryptChatMessage(message64, secretKeyObject) // const res = await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle}) - + const sendMessageFunc = async () => { await sendChatGroup({groupId: selectedGroup,messageText: encryptSingle}) }; @@ -311,7 +327,8 @@ const clearEditorContent = () => { text: message, timestamp: Date.now(), senderName: myName, - sender: myAddress + sender: myAddress, + ...(otherData || {}) }, } From f9c6f4c5fd94ae2367a56ada2e8f21f1fd24cdea Mon Sep 17 00:00:00 2001 From: PhilReact Date: Fri, 20 Sep 2024 03:57:10 +0300 Subject: [PATCH 17/25] add more mobile styles --- src/App.tsx | 17 +- src/assets/Icons/ArrowDownIcon.tsx | 14 + src/assets/Icons/ChatIcon.tsx | 12 + src/assets/Icons/HomeIcon.tsx | 12 + src/assets/Icons/HubsIcon.tsx | 12 + src/assets/Icons/LogoutIcon.tsx | 12 + src/assets/Icons/MembersIcon.tsx | 19 + src/assets/Icons/MessagingIcon.tsx | 13 + src/assets/Icons/NotificationIcon.tsx | 13 + src/assets/Icons/NotificationIcon2.tsx | 11 + src/assets/Icons/ThreadsIcon.tsx | 13 + src/assets/Icons/TradingIcon.tsx | 11 + src/assets/Icons/WalletIcon.tsx | 12 + src/assets/svgs/BottomLogo.svg | 9 + src/assets/svgs/BottomLogo2.svg | 9 + src/assets/svgs/BottomLogo3.svg | 9 + src/assets/svgs/BottomLogo4.svg | 9 + src/assets/svgs/BottomLogo5.svg | 9 + src/common/CustomSvg.tsx | 17 + .../Chat/AnnouncementDiscussion.tsx | 4 +- src/components/Chat/GroupAnnouncements.tsx | 42 +- src/components/Group/Group.tsx | 827 +++++++++++------- src/components/Group/GroupInvites.tsx | 209 +++-- src/components/Group/GroupJoinRequests.tsx | 94 +- src/components/Group/GroupMenu.tsx | 200 +++++ src/components/Group/Home.tsx | 85 ++ .../Group/ListOfThreadPostsWatched.tsx | 212 +++-- src/components/Group/ThingsToDoInitial.tsx | 328 ++++--- src/components/Mobile/MobileFooter.tsx | 127 +++ src/components/Mobile/MobileHeader.tsx | 186 ++++ src/index.css | 4 + src/utils/mobile/mobileUtils.ts | 5 + 32 files changed, 1903 insertions(+), 653 deletions(-) create mode 100644 src/assets/Icons/ArrowDownIcon.tsx create mode 100644 src/assets/Icons/ChatIcon.tsx create mode 100644 src/assets/Icons/HomeIcon.tsx create mode 100644 src/assets/Icons/HubsIcon.tsx create mode 100644 src/assets/Icons/LogoutIcon.tsx create mode 100644 src/assets/Icons/MembersIcon.tsx create mode 100644 src/assets/Icons/MessagingIcon.tsx create mode 100644 src/assets/Icons/NotificationIcon.tsx create mode 100644 src/assets/Icons/NotificationIcon2.tsx create mode 100644 src/assets/Icons/ThreadsIcon.tsx create mode 100644 src/assets/Icons/TradingIcon.tsx create mode 100644 src/assets/Icons/WalletIcon.tsx create mode 100644 src/assets/svgs/BottomLogo.svg create mode 100644 src/assets/svgs/BottomLogo2.svg create mode 100644 src/assets/svgs/BottomLogo3.svg create mode 100644 src/assets/svgs/BottomLogo4.svg create mode 100644 src/assets/svgs/BottomLogo5.svg create mode 100644 src/common/CustomSvg.tsx create mode 100644 src/components/Group/GroupMenu.tsx create mode 100644 src/components/Group/Home.tsx create mode 100644 src/components/Mobile/MobileFooter.tsx create mode 100644 src/components/Mobile/MobileHeader.tsx create mode 100644 src/utils/mobile/mobileUtils.ts diff --git a/src/App.tsx b/src/App.tsx index 44d7c5d..b43d357 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -129,7 +129,7 @@ const defaultValues: MyContextInterface = { message: "", }, }; -export let isMobile = false +export let isMobile = true const isMobileDevice = () => { const userAgent = navigator.userAgent || navigator.vendor || window.opera; @@ -1192,7 +1192,7 @@ function App() { const renderProfile = ()=> { return ( - + {isMobile && ( { - chrome.tabs.create({ url: "https://www.qort.trade" }); + chrome.tabs.create({ url: "https://www.qort.trade", active: true }); }} > Get QORT at qort.trade @@ -1367,7 +1367,9 @@ function App() { cursor: "pointer", }} /> - + {!isMobile && ( + <> + { @@ -1378,6 +1380,9 @@ function App() { cursor: "pointer", }} /> + + )} + {authenticatedMode === "qort" && ( { + return ( + + + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/ChatIcon.tsx b/src/assets/Icons/ChatIcon.tsx new file mode 100644 index 0000000..fdc5945 --- /dev/null +++ b/src/assets/Icons/ChatIcon.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export const ChatIcon= ({ color = 'white', height, width }) => { + return ( + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/HomeIcon.tsx b/src/assets/Icons/HomeIcon.tsx new file mode 100644 index 0000000..38f880d --- /dev/null +++ b/src/assets/Icons/HomeIcon.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export const HomeIcon= ({ color, height = 20, width = 23 }) => { + return ( + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/HubsIcon.tsx b/src/assets/Icons/HubsIcon.tsx new file mode 100644 index 0000000..00f6cd4 --- /dev/null +++ b/src/assets/Icons/HubsIcon.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export const HubsIcon= ({ color, height, width }) => { + return ( + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/LogoutIcon.tsx b/src/assets/Icons/LogoutIcon.tsx new file mode 100644 index 0000000..9288de1 --- /dev/null +++ b/src/assets/Icons/LogoutIcon.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export const LogoutIcon= ({ color, height = 20, width = 18}) => { + return ( + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/MembersIcon.tsx b/src/assets/Icons/MembersIcon.tsx new file mode 100644 index 0000000..a37680e --- /dev/null +++ b/src/assets/Icons/MembersIcon.tsx @@ -0,0 +1,19 @@ +import React from 'react'; + +export const MembersIcon= ({ color = 'white', height, width }) => { + return ( + + + + + + + + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/MessagingIcon.tsx b/src/assets/Icons/MessagingIcon.tsx new file mode 100644 index 0000000..0de459b --- /dev/null +++ b/src/assets/Icons/MessagingIcon.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +export const MessagingIcon= ({ color, height = 31, width = 31 }) => { + return ( + + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/NotificationIcon.tsx b/src/assets/Icons/NotificationIcon.tsx new file mode 100644 index 0000000..d577954 --- /dev/null +++ b/src/assets/Icons/NotificationIcon.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +export const NotificationIcon= ({ color, height, width }) => { + return ( + + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/NotificationIcon2.tsx b/src/assets/Icons/NotificationIcon2.tsx new file mode 100644 index 0000000..e670888 --- /dev/null +++ b/src/assets/Icons/NotificationIcon2.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +export const NotificationIcon2= ({ color = 'white', height, width }) => { + return ( + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/ThreadsIcon.tsx b/src/assets/Icons/ThreadsIcon.tsx new file mode 100644 index 0000000..7a42763 --- /dev/null +++ b/src/assets/Icons/ThreadsIcon.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +export const ThreadsIcon= ({ color = 'white', height, width }) => { + return ( + + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/TradingIcon.tsx b/src/assets/Icons/TradingIcon.tsx new file mode 100644 index 0000000..2c72300 --- /dev/null +++ b/src/assets/Icons/TradingIcon.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +export const TradingIcon= ({ color, height, width }) => { + return ( + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/WalletIcon.tsx b/src/assets/Icons/WalletIcon.tsx new file mode 100644 index 0000000..ff993d4 --- /dev/null +++ b/src/assets/Icons/WalletIcon.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +export const WalletIcon= ({ color, height, width }) => { + return ( + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/svgs/BottomLogo.svg b/src/assets/svgs/BottomLogo.svg new file mode 100644 index 0000000..74848da --- /dev/null +++ b/src/assets/svgs/BottomLogo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/svgs/BottomLogo2.svg b/src/assets/svgs/BottomLogo2.svg new file mode 100644 index 0000000..3a7fcea --- /dev/null +++ b/src/assets/svgs/BottomLogo2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/svgs/BottomLogo3.svg b/src/assets/svgs/BottomLogo3.svg new file mode 100644 index 0000000..67a0480 --- /dev/null +++ b/src/assets/svgs/BottomLogo3.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/svgs/BottomLogo4.svg b/src/assets/svgs/BottomLogo4.svg new file mode 100644 index 0000000..562ff64 --- /dev/null +++ b/src/assets/svgs/BottomLogo4.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/assets/svgs/BottomLogo5.svg b/src/assets/svgs/BottomLogo5.svg new file mode 100644 index 0000000..89256fe --- /dev/null +++ b/src/assets/svgs/BottomLogo5.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/common/CustomSvg.tsx b/src/common/CustomSvg.tsx new file mode 100644 index 0000000..c1d8f09 --- /dev/null +++ b/src/common/CustomSvg.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +export const CustomSvg = ({ src, color = 'black', size = 24 }) => { + return ( + + {src} + + ); + }; + \ No newline at end of file diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index 923c159..a162c8b 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -269,7 +269,9 @@ export const AnnouncementDiscussion = ({ flexShrink: 0, }}> - + setSelectedAnnouncement(null)} sx={{ cursor: 'pointer' }} /> diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 4c9e0ce..56b1b4a 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -32,6 +32,7 @@ import { MyContext, getArbitraryEndpointReact, getBaseApiReact, isMobile, pauseA import { RequestQueueWithPromise } from "../../utils/queue/queue"; import { CustomizedSnackbars } from "../Snackbar/Snackbar"; import { addDataPublishesFunc, getDataPublishesFunc } from "../Group/Group"; +import { getRootHeight } from "../../utils/mobile/mobileUtils"; export const requestQueueCommentCount = new RequestQueueWithPromise(3) export const requestQueuePublishedAccouncements = new RequestQueueWithPromise(3) @@ -489,7 +490,7 @@ export const GroupAnnouncements = ({ return (
- - - Group Announcements - - + {!isMobile && ( + + + Group Announcements + + )} + +
{!isLoading && combinedListTempAndReal?.length === 0 && ( diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 013f74b..8484fea 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -77,6 +77,11 @@ import { useMessageQueue } from "../../MessageQueueContext"; import { DrawerComponent } from "../Drawer/Drawer"; import { isExtMsg } from "../../background"; import { ContextMenu } from "../ContextMenu"; +import { MobileFooter } from "../Mobile/MobileFooter"; +import Header from "../Mobile/MobileHeader"; +import { Home } from "./Home"; +import { GroupMenu } from "./GroupMenu"; +import { getRootHeight } from "../../utils/mobile/mobileUtils"; // let touchStartY = 0; // let disablePullToRefresh = false; @@ -364,6 +369,7 @@ export const Group = ({ balance, isOpenDrawerProfile, setIsOpenDrawerProfile, + logoutFunc }: GroupProps) => { const [secretKey, setSecretKey] = useState(null); const [secretKeyPublishDate, setSecretKeyPublishDate] = useState(null); @@ -407,6 +413,8 @@ export const Group = ({ const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState('') const [drawerMode, setDrawerMode] = React.useState("groups"); const [mutedGroups, setMutedGroups] = useState([]) + const [mobileViewMode, setMobileViewMode] = useState('home') + const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState('') const isFocusedRef = useRef(true); const selectedGroupRef = useRef(null); @@ -1266,7 +1274,7 @@ export const Group = ({ setGroupSection("home"); setGroupAnnouncements({}); setDefaultThread(null); - + setMobileViewMode('home') // Reset all useRef values to their initial states hasInitialized.current = false; hasInitializedWebsocket.current = false; @@ -1333,7 +1341,7 @@ export const Group = ({ setTimeout(() => { setSelectedGroup(findGroup); - + setMobileViewMode('group') getTimestampEnterChat(); isLoadingOpenSectionFromNotification.current = false; }, 200); @@ -1378,6 +1386,7 @@ export const Group = ({ }); setTimeout(() => { setSelectedGroup(findGroup); + setMobileViewMode('group') getGroupAnnouncements(); }, 200); @@ -1430,7 +1439,7 @@ export const Group = ({ setTimeout(() => { setSelectedGroup(findGroup); - + setMobileViewMode('group') getGroupAnnouncements(); }, 200); } @@ -1449,6 +1458,12 @@ export const Group = ({ }; const goToHome = async () => { + if(isMobile){ + setMobileViewMode('home') + } + if(!isMobile){ + + } setGroupSection("default"); clearAllQueues(); await new Promise((res) => { @@ -1494,6 +1509,17 @@ export const Group = ({ }, 200); }; + const openDrawerGroups = ()=> { + setIsOpenDrawer(true); + setDrawerMode("groups"); + } + + const goToThreads = ()=> { + setSelectedDirect(null); + setNewChat(false) + setGroupSection("forum") + } + const goToChat = async () => { setGroupSection("default"); await new Promise((res) => { @@ -1519,18 +1545,175 @@ export const Group = ({ } }; + + const renderDirects = ()=> { + return ( +
+
+ {directs.map((direct: any) => ( + + + // + // + // } + onClick={() => { + setSelectedDirect(null); + setNewChat(false); + // setSelectedGroup(null); + setIsOpenDrawer(false); + chrome?.runtime?.sendMessage({ + action: "addTimestampEnterChat", + payload: { + timestamp: Date.now(), + groupId: direct.address, + }, + }); + setTimeout(() => { + setSelectedDirect(direct); + + getTimestampEnterChat(); + }, 200); + }} + sx={{ + display: "flex", + width: "100%", + flexDirection: "column", + cursor: "pointer", + border: "1px #232428 solid", + padding: "2px", + borderRadius: "2px", + background: + direct?.address === selectedDirect?.address && "white", + }} + > + + + + {(direct?.name || direct?.address)?.charAt(0)} + + + + {direct?.sender !== myAddress && + direct?.timestamp && + ((!timestampEnterData[direct?.address] && + Date.now() - direct?.timestamp < + timeDifferenceForNotificationChats) || + timestampEnterData[direct?.address] < + direct?.timestamp) && ( + + )} + + + + ))} +
+
+ + { + setNewChat(true); + setSelectedDirect(null); + // setSelectedGroup(null); + setIsOpenDrawer(false); + }} + > + + New Chat + + +
+
+ ) + } + const renderGroups = () => { return (
-
-
-
*/} + {/*
{directs.map((direct: any) => ( - + @@ -1693,11 +1878,11 @@ export const Group = ({ ))} -
+
*/}
{groups.map((group: any) => ( - + @@ -1716,6 +1903,7 @@ export const Group = ({ // // } onClick={() => { + setMobileViewMode('group') clearAllQueues(); setSelectedDirect(null); setTriedToFetchSecretKey(false); @@ -1783,6 +1971,7 @@ export const Group = ({ sx={{ display: "flex", width: "100%", + alignItems: 'center' }} > @@ -1887,6 +2076,7 @@ export const Group = ({ )}
+
); }; @@ -1903,12 +2093,13 @@ export const Group = ({ info={infoSnack} setInfo={setInfoSnack} /> - +
+
+ + {mobileViewMode === 'groups' && ( + renderGroups() + )} + +{mobileViewModeKeepOpen === 'messaging' && ( + renderDirects() + )} {newChat && ( <> + {mobileViewMode === 'group' && ( + + )} {triedToFetchSecretKey && ( @@ -2139,7 +2344,8 @@ export const Group = ({ bottom: '0px', top: '0px', background: '#27282c', - zIndex: 5 + zIndex: 5, + height: isMobile && 'calc(100% - 30px)' }} > @@ -2173,71 +2379,41 @@ export const Group = ({ )} - { + + {mobileViewMode === 'home' && ( + + )} + {/* { !selectedGroup && groupSection === "home" && ( - - - - - {!isLoadingGroups && ( - - - - - - - - )} - - )} + + + )} */}
- + {mobileViewMode === 'home' && !mobileViewModeKeepOpen && ( + <> +
+ {/* {renderGroups()} - - + */} {isMobile && ( - - - {selectedGroup && ( - <> - - - - - - - - - - - - - - )} - - {/* Second row: Groups, Home, Profile */} - - - - - - - - - - setIsOpenDrawerProfile(true)} - > - - - - - + )} + + )} + + + ); }; + + + +// {isMobile && ( +// +// +// {selectedGroup && ( +// <> +// +// +// +// +// +// +// +// +// +// +// +// +// +// )} + +// {/* Second row: Groups, Home, Profile */} +// +// +// +// +// +// +// +// +// +// setIsOpenDrawerProfile(true)} +// > +// +// +// +// +// +// )} \ No newline at end of file diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index 06e176d..d451576 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -8,7 +8,7 @@ import Checkbox from "@mui/material/Checkbox"; import IconButton from "@mui/material/IconButton"; import CommentIcon from "@mui/icons-material/Comment"; import InfoIcon from "@mui/icons-material/Info"; -import GroupAddIcon from '@mui/icons-material/GroupAdd'; +import GroupAddIcon from "@mui/icons-material/GroupAdd"; import { executeEvent } from "../../utils/events"; import { Box, Typography } from "@mui/material"; import { Spacer } from "../../common/Spacer"; @@ -17,98 +17,155 @@ import { CustomLoader } from "../../common/CustomLoader"; import { getBaseApiReact } from "../../App"; export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { - const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([]) - const [loading, setLoading] = React.useState(true) + const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState( + [] + ); + const [loading, setLoading] = React.useState(true); - const getJoinRequests = async ()=> { + const getJoinRequests = async () => { try { - setLoading(true) - const response = await fetch(`${getBaseApiReact()}/groups/invites/${myAddress}/?limit=0`); + setLoading(true); + const response = await fetch( + `${getBaseApiReact()}/groups/invites/${myAddress}/?limit=0` + ); const data = await response.json(); - const resMoreData = await getGroupNames(data) + const resMoreData = await getGroupNames(data); - setGroupsWithJoinRequests(resMoreData) + setGroupsWithJoinRequests(resMoreData); } catch (error) { - } finally { - setLoading(false) + setLoading(false); } - } + }; React.useEffect(() => { if (myAddress) { - getJoinRequests() + getJoinRequests(); } }, [myAddress]); - return ( - - Group Invites - - {loading && groupsWithJoinRequests.length === 0 && ( - - - - )} - {!loading && groupsWithJoinRequests.length === 0 && ( - - No invites - - )} - - {groupsWithJoinRequests?.map((group)=> { - return ( - { - setOpenAddGroup(true) - setTimeout(() => { - executeEvent("openGroupInvitesRequest", {}); - - }, 300); + + + - - - } > - - - - - - ) + Hub Invites: + + + - })} - - - - + + {loading && groupsWithJoinRequests.length === 0 && ( + + + + )} + {!loading && groupsWithJoinRequests.length === 0 && ( + + + Nothing to display + + + )} + + {groupsWithJoinRequests?.map((group) => { + return ( + { + setOpenAddGroup(true); + setTimeout(() => { + executeEvent("openGroupInvitesRequest", {}); + }, 300); + }} + disablePadding + secondaryAction={ + + + + } + > + + + + + ); + })} + + ); }; diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 9430b19..bdbb7d8 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -18,7 +18,7 @@ import { getBaseApi } from "../../background"; import { getBaseApiReact } from "../../App"; export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2) -export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, getTimestampEnterChat, setSelectedGroup, setGroupSection }) => { +export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, getTimestampEnterChat, setSelectedGroup, setGroupSection, setMobileViewMode }) => { const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([]) const [loading, setLoading] = React.useState(true) @@ -95,17 +95,43 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get return ( - - Join Requests - + + + Join Requests: + + + + + {loading && groupsWithJoinRequests.length === 0 && ( )} {!loading && groupsWithJoinRequests.length === 0 && ( - - No join requests - + + + Nothing to display + + )} {groupsWithJoinRequests?.map((group)=> { @@ -134,6 +171,7 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get key={group?.groupId} onClick={()=> { setSelectedGroup(group?.group) + setMobileViewMode('group') getTimestampEnterChat() setGroupSection("announcement") setOpenManageMembers(true) @@ -142,20 +180,31 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get }, 300); }} + sx={{ + marginBottom: '20px' + }} disablePadding secondaryAction={ } > - + - + ) @@ -166,5 +215,6 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get + ); }; diff --git a/src/components/Group/GroupMenu.tsx b/src/components/Group/GroupMenu.tsx new file mode 100644 index 0000000..f113bb5 --- /dev/null +++ b/src/components/Group/GroupMenu.tsx @@ -0,0 +1,200 @@ +import React, { useState } from "react"; +import { + Button, + Menu, + MenuItem, + ListItemIcon, + ListItemText, + Badge, + Box, +} from "@mui/material"; +import ForumIcon from "@mui/icons-material/Forum"; +import GroupIcon from "@mui/icons-material/Group"; +import { ArrowDownIcon } from "../../assets/Icons/ArrowDownIcon"; +import { NotificationIcon2 } from "../../assets/Icons/NotificationIcon2"; +import { ChatIcon } from "../../assets/Icons/ChatIcon"; +import { ThreadsIcon } from "../../assets/Icons/ThreadsIcon"; +import { MembersIcon } from "../../assets/Icons/MembersIcon"; + +export const GroupMenu = ({ setGroupSection, groupSection }) => { + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( + + + + { + setGroupSection("chat"); + handleClose(); + }} + > + + + + + + { + setGroupSection("announcement"); + handleClose(); + }} + > + + + + + + { + setGroupSection("forum"); + handleClose(); + }} + > + + + + + + + { + // setGroupSection("") + handleClose(); + }} + > + + + + + + + + + ); +}; diff --git a/src/components/Group/Home.tsx b/src/components/Group/Home.tsx new file mode 100644 index 0000000..7dcc557 --- /dev/null +++ b/src/components/Group/Home.tsx @@ -0,0 +1,85 @@ +import { Box, Button, Typography } from '@mui/material' +import React from 'react' +import { Spacer } from '../../common/Spacer' +import { ListOfThreadPostsWatched } from './ListOfThreadPostsWatched' +import { ThingsToDoInitial } from './ThingsToDoInitial' +import { GroupJoinRequests } from './GroupJoinRequests' +import { GroupInvites } from './GroupInvites' +import RefreshIcon from "@mui/icons-material/Refresh"; + +export const Home = ({refreshHomeDataFunc, myAddress, isLoadingGroups, balance, userInfo, groups, setGroupSection, setSelectedGroup, getTimestampEnterChat, setOpenManageMembers, setOpenAddGroup, setMobileViewMode}) => { + return ( + + + + + Welcome + + + + {/* + + */} + {!isLoadingGroups && ( + + + + + + + + + )} + + + ) +} diff --git a/src/components/Group/ListOfThreadPostsWatched.tsx b/src/components/Group/ListOfThreadPostsWatched.tsx index b22be24..3e794b0 100644 --- a/src/components/Group/ListOfThreadPostsWatched.tsx +++ b/src/components/Group/ListOfThreadPostsWatched.tsx @@ -8,131 +8,173 @@ import Checkbox from "@mui/material/Checkbox"; import IconButton from "@mui/material/IconButton"; import CommentIcon from "@mui/icons-material/Comment"; import InfoIcon from "@mui/icons-material/Info"; -import GroupAddIcon from '@mui/icons-material/GroupAdd'; +import GroupAddIcon from "@mui/icons-material/GroupAdd"; import { executeEvent } from "../../utils/events"; import { Box, Typography } from "@mui/material"; import { Spacer } from "../../common/Spacer"; import { getGroupNames } from "./UserListOfInvites"; import { CustomLoader } from "../../common/CustomLoader"; -import VisibilityIcon from '@mui/icons-material/Visibility'; +import VisibilityIcon from "@mui/icons-material/Visibility"; export const ListOfThreadPostsWatched = () => { - const [posts, setPosts] = React.useState([]) - const [loading, setLoading] = React.useState(true) + const [posts, setPosts] = React.useState([]); + const [loading, setLoading] = React.useState(true); - const getPosts = async ()=> { + const getPosts = async () => { try { await new Promise((res, rej) => { chrome?.runtime?.sendMessage( { action: "getThreadActivity", - payload: { - - }, + payload: {}, }, (response) => { - if (!response?.error) { - if(!response) { - res(null) - return + if (!response) { + res(null); + return; } const uniquePosts = response.reduce((acc, current) => { - const x = acc.find(item => item?.thread?.threadId === current?.thread?.threadId); + const x = acc.find( + (item) => item?.thread?.threadId === current?.thread?.threadId + ); if (!x) { return acc.concat([current]); } else { return acc; } }, []); - setPosts(uniquePosts) + setPosts(uniquePosts); res(uniquePosts); - return + return; } rej(response.error); } ); }); } catch (error) { - } finally { - setLoading(false) + setLoading(false); } - } + }; React.useEffect(() => { - - getPosts() - + getPosts(); }, []); - - return ( - - New Thread Posts - - {loading && posts.length === 0 && ( - - - - )} - {!loading && posts.length === 0 && ( - - No thread post notifications - - )} - - {posts?.map((post)=> { - return ( - { - executeEvent("openThreadNewPost", { - data: post - }); - }} - disablePadding - secondaryAction={ - - - - } - > - - - - - - ) + + }} + > + + New Thread Posts: + + + + + + {loading && posts.length === 0 && ( + + + + )} + {!loading && posts.length === 0 && ( + + + Nothing to display + + + )} + {posts?.length > 0 && ( + + {posts?.map((post) => { + return ( + { + executeEvent("openThreadNewPost", { + data: post, + }); + }} + disablePadding + secondaryAction={ + + + + } + > + + + + + ); + })} + + )} + + ); }; diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index 18f75b5..fb8a289 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -16,151 +16,219 @@ export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance }) => { const [checked2, setChecked2] = React.useState(false); const [checked3, setChecked3] = React.useState(false); -// const getAddressInfo = async (address) => { -// const response = await fetch(getBaseApiReact() + "/addresses/" + address); -// const data = await response.json(); -// if (data.error && data.error === 124) { -// setChecked1(false); -// } else if (data.address) { -// setChecked1(true); -// } -// }; + // const getAddressInfo = async (address) => { + // const response = await fetch(getBaseApiReact() + "/addresses/" + address); + // const data = await response.json(); + // if (data.error && data.error === 124) { + // setChecked1(false); + // } else if (data.address) { + // setChecked1(true); + // } + // }; -// const checkInfo = async () => { -// try { -// getAddressInfo(myAddress); -// } catch (error) {} -// }; - - + // const checkInfo = async () => { + // try { + // getAddressInfo(myAddress); + // } catch (error) {} + // }; React.useEffect(() => { if (balance && +balance >= 6) { - setChecked1(true) + setChecked1(true); } }, [balance]); - React.useEffect(()=> { - if(hasGroups) setChecked3(true) - }, [hasGroups]) + React.useEffect(() => { + if (hasGroups) setChecked3(true); + }, [hasGroups]); - React.useEffect(()=> { - if(name) setChecked2(true) - }, [name]) + React.useEffect(() => { + if (name) setChecked2(true); + }, [name]); return ( - - Suggestion: Complete the following - - - - // - // - // } - disablePadding + + - - - - - - - - - // - // - // } - disablePadding + + Getting Started: + + + + + - - - + + // + // + // } + disablePadding + sx={{ + marginBottom: '20px' + }} + > + - - - - - - // - // - // } - disablePadding - > - - - - - - - - + role={undefined} + dense + > + + + + {/* */} + + + + + // + // + // } + disablePadding + > + + + + + + + + + + // + // + // } + disablePadding + > + + + + + + + + + + ); }; diff --git a/src/components/Mobile/MobileFooter.tsx b/src/components/Mobile/MobileFooter.tsx new file mode 100644 index 0000000..1f025f4 --- /dev/null +++ b/src/components/Mobile/MobileFooter.tsx @@ -0,0 +1,127 @@ +import * as React from 'react'; +import { BottomNavigation, BottomNavigationAction, Typography } from '@mui/material'; +import { Home, Groups, Message, ShowChart } from '@mui/icons-material'; +import Box from '@mui/material/Box'; +import BottomLogo from '../../assets/svgs/BottomLogo5.svg' +import { CustomSvg } from '../../common/CustomSvg'; +import { WalletIcon } from '../../assets/Icons/WalletIcon'; +import { HubsIcon } from '../../assets/Icons/HubsIcon'; +import { TradingIcon } from '../../assets/Icons/TradingIcon'; +import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; + +const IconWrapper = ({children, label, color})=> { + + return + {children} + {label} + +} + +export const MobileFooter =({ +selectedGroup, + groupSection, + isUnread, + goToAnnouncements, + isUnreadChat, + goToChat, + goToThreads, + setOpenManageMembers, + groupChatHasUnread, + groupsAnnHasUnread, + directChatHasUnread, + chatMode, + openDrawerGroups, + goToHome, + setIsOpenDrawerProfile, + mobileViewMode, + setMobileViewMode, + setMobileViewModeKeepOpen +}) => { + const [value, setValue] = React.useState(0); + return ( + + setValue(newValue)} + sx={{ backgroundColor: 'transparent', flexGrow: 1 }} + > + { + // setMobileViewMode('wallet') + setIsOpenDrawerProfile(true) + }} icon={} sx={{ color: value === 0 ? 'white' : 'gray', padding: '0px 10px' }} /> + { + setMobileViewMode('groups') + }} icon={} sx={{ color: value === 0 ? 'white' : 'gray', paddingLeft: '10px', paddingRight: '42px' }} /> + + + + {/* Floating Center Button */} + + + {/* Custom Center Icon */} + center-icon + + + + setValue(newValue)} + sx={{ backgroundColor: 'transparent', flexGrow: 1 }} + > + { + setMobileViewModeKeepOpen('messaging') + }} icon={} sx={{ color: value === 2 ? 'white' : 'gray', paddingLeft: '55px', paddingRight: '10px' }} /> + { + chrome.tabs.create({ url: "https://www.qort.trade", active: true }); + }} icon={} sx={{ color: value === 3 ? 'white' : 'gray' , padding: '0px 10px'}} /> + + + ); + } \ No newline at end of file diff --git a/src/components/Mobile/MobileHeader.tsx b/src/components/Mobile/MobileHeader.tsx new file mode 100644 index 0000000..da9d07e --- /dev/null +++ b/src/components/Mobile/MobileHeader.tsx @@ -0,0 +1,186 @@ +import React from 'react'; +import { AppBar, Toolbar, IconButton, Typography, Box, MenuItem, Select, ButtonBase } from '@mui/material'; +import { HomeIcon } from '../../assets/Icons/HomeIcon'; +import { LogoutIcon } from '../../assets/Icons/LogoutIcon'; +import { NotificationIcon } from '../../assets/Icons/NotificationIcon'; +import { ArrowDownIcon } from '../../assets/Icons/ArrowDownIcon'; +import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; + +const Header = ({ + logoutFunc, + goToHome, + setIsOpenDrawerProfile, + isThin, + setMobileViewModeKeepOpen + // selectedGroup, + // onHomeClick, + // onLogoutClick, + // onGroupChange, + // onWalletClick, + // onNotificationClick, +}) => { + if(isThin){ + + + return ( + + + {/* Left Home Icon */} + + { + setMobileViewModeKeepOpen('') + goToHome() +}} + // onClick={onHomeClick} + > + + + { + setMobileViewModeKeepOpen() + goToHome() +}} + // onClick={onHomeClick} + > + + + + + + {/* Center Title */} + + QORTAL + + + {/* Right Logout Icon */} + + { + setMobileViewModeKeepOpen('messaging') + }} edge="end" color="inherit" aria-label="logout" + +// onClick={onLogoutClick} +> + + + + + + + + + ) + } + return ( + <> + {/* Main Header */} + + + {/* Left Home Icon */} + + + + + {/* Center Title */} + + QORTAL + + + {/* Right Logout Icon */} + + + + + + + {/* Secondary Section */} + + + + + Palmas + +{/* + */} + + + + + + + + + + + {/* Right Dropdown */} + { + setIsOpenDrawerProfile(true) + }}> + + + View Wallet + + + + + + + + ); +}; + +export default Header; diff --git a/src/index.css b/src/index.css index 956f995..68a7ee3 100644 --- a/src/index.css +++ b/src/index.css @@ -29,6 +29,10 @@ --color-instance-popover-bg: #222222; --Mail-Background: rgba(49, 51, 56, 1); --new-message-text: black; + + --bg-primary : rgba(31, 32, 35, 1); + --bg-2: #27282c; + --bg-3: rgba(0, 0, 0, 0.1); } body { diff --git a/src/utils/mobile/mobileUtils.ts b/src/utils/mobile/mobileUtils.ts new file mode 100644 index 0000000..144c8c1 --- /dev/null +++ b/src/utils/mobile/mobileUtils.ts @@ -0,0 +1,5 @@ + +export const getRootHeight = ()=> { + + return document?.getElementById('root')?.style?.height || '100%' +} \ No newline at end of file From 38545e3da5fa79503c8df5136f5f80b377eb40dc Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 21 Sep 2024 07:48:54 +0300 Subject: [PATCH 18/25] mobile new design --- src/assets/Icons/ExitIcon.tsx | 13 + src/assets/Icons/HubsIcon.tsx | 4 +- src/assets/Icons/MessagingIcon2.tsx | 14 + src/assets/Icons/NotificationIcon.tsx | 11 +- src/assets/Icons/ReturnIcon.tsx | 14 + src/components/Chat/ChatDirect.tsx | 92 +- src/components/Chat/ChatGroup.tsx | 2 +- src/components/Chat/ChatList.tsx | 17 +- src/components/Chat/GroupAnnouncements.tsx | 720 ++++++------- src/components/Chat/GroupForum.tsx | 4 +- src/components/Group/Forum/NewThread.tsx | 2 +- src/components/Group/Group.tsx | 1079 ++++++++++++-------- src/components/Group/GroupMenu.tsx | 4 +- src/components/Group/Home.tsx | 149 +-- src/components/Group/ManageMembers.tsx | 11 +- src/components/Mobile/MobileFooter.tsx | 264 +++-- src/components/Mobile/MobileHeader.tsx | 498 ++++++--- src/index.css | 2 + 18 files changed, 1778 insertions(+), 1122 deletions(-) create mode 100644 src/assets/Icons/ExitIcon.tsx create mode 100644 src/assets/Icons/MessagingIcon2.tsx create mode 100644 src/assets/Icons/ReturnIcon.tsx diff --git a/src/assets/Icons/ExitIcon.tsx b/src/assets/Icons/ExitIcon.tsx new file mode 100644 index 0000000..cfbeefa --- /dev/null +++ b/src/assets/Icons/ExitIcon.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +export const ExitIcon= ({ color = 'white', height, width }) => { + return ( + + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/HubsIcon.tsx b/src/assets/Icons/HubsIcon.tsx index 00f6cd4..b96ba8a 100644 --- a/src/assets/Icons/HubsIcon.tsx +++ b/src/assets/Icons/HubsIcon.tsx @@ -1,8 +1,8 @@ import React from 'react'; -export const HubsIcon= ({ color, height, width }) => { +export const HubsIcon= ({ color, height = 31, width = 32 }) => { return ( - + diff --git a/src/assets/Icons/MessagingIcon2.tsx b/src/assets/Icons/MessagingIcon2.tsx new file mode 100644 index 0000000..5cbdb98 --- /dev/null +++ b/src/assets/Icons/MessagingIcon2.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +export const MessagingIcon2= ({ color = '#8F8F91', height = 24, width =24 }) => { + return ( + + + + + + + + ); + }; + \ No newline at end of file diff --git a/src/assets/Icons/NotificationIcon.tsx b/src/assets/Icons/NotificationIcon.tsx index d577954..cc9092c 100644 --- a/src/assets/Icons/NotificationIcon.tsx +++ b/src/assets/Icons/NotificationIcon.tsx @@ -1,11 +1,12 @@ import React from 'react'; -export const NotificationIcon= ({ color, height, width }) => { +export const NotificationIcon= ({ color = 'white', height = 16, width = 14 }) => { return ( - - - - + + + + + ); diff --git a/src/assets/Icons/ReturnIcon.tsx b/src/assets/Icons/ReturnIcon.tsx new file mode 100644 index 0000000..633eafc --- /dev/null +++ b/src/assets/Icons/ReturnIcon.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +export const ReturnIcon= ({ color = 'white', height, width }) => { + return ( + + + + + + + + ); + }; + \ No newline at end of file diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 33325c8..c499eb3 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -6,7 +6,7 @@ import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css"; import Tiptap from './TipTap' import { CustomButton } from '../../App-styles' import CircularProgress from '@mui/material/CircularProgress'; -import { Box, Input, Typography } from '@mui/material'; +import { Box, ButtonBase, Input, Typography } from '@mui/material'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { getNameInfo } from '../Group/Group'; import { Spacer } from '../../common/Spacer'; @@ -17,12 +17,14 @@ import { useMessageQueue } from '../../MessageQueueContext'; import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import ShortUniqueId from "short-unique-id"; +import { ReturnIcon } from '../../assets/Icons/ReturnIcon'; +import { ExitIcon } from '../../assets/Icons/ExitIcon'; const uid = new ShortUniqueId({ length: 5 }); -export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDirect, setNewChat, getTimestampEnterChat, myName, balance, close}) => { +export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDirect, setNewChat, getTimestampEnterChat, myName, balance, close, setMobileViewModeKeepOpen}) => { const { queueChats, addToQueue, processWithNewMessages} = useMessageQueue(); const [isFocusedParent, setIsFocusedParent] = useState(false); @@ -344,27 +346,69 @@ const clearEditorContent = () => { flexDirection: 'column', width: '100%' }}> - - - Close Direct Chat - + {isMobile && ( + + + + { + close() + }} + > + + + + + {isNewChat ? '' : selectedDirect?.name || (selectedDirect?.address?.slice(0,10) + '...')} + + + { + setSelectedDirect(null) + setMobileViewModeKeepOpen('') + setNewChat(false) + }} + > + + + + + + )} {isNewChat && ( <> @@ -376,7 +420,7 @@ const clearEditorContent = () => { )} - +
{ left: hide && '-100000px', }}> - +
{ +export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId }) => { const hasLoadedInitialRef = useRef(false); const listRef = useRef(); const [messages, setMessages] = useState(initialMessages); const [showScrollButton, setShowScrollButton] = useState(false); - + const cache = useMemo(() => new CellMeasurerCache({ + fixedWidth: true, + defaultHeight: 50, + }), [chatId]); // Recreate cache when chatId changes useEffect(() => { cache.clearAll(); @@ -175,7 +178,7 @@ let uniqueInitialMessages = Array.from(uniqueInitialMessagesMap.values()).sort(( // }, [messages, myAddress]); return ( -
+
{({ height, width }) => ( { - - return new Promise((res, rej) => { chrome?.runtime?.sendMessage( { @@ -50,10 +57,9 @@ export const saveTempPublish = async ({ data, key }: any) => { }, }, (response) => { - if (!response?.error) { res(response); - return + return; } rej(response.error); } @@ -62,19 +68,16 @@ export const saveTempPublish = async ({ data, key }: any) => { }; export const getTempPublish = async () => { - - return new Promise((res, rej) => { chrome?.runtime?.sendMessage( { action: "getTempPublish", - payload: { - }, + payload: {}, }, (response) => { if (!response?.error) { res(response); - return + return; } rej(response.error); } @@ -95,7 +98,6 @@ export const decryptPublishes = async (encryptedMessages: any[], secretKey) => { }, }, (response) => { - if (!response?.error) { res(response); // if(hasInitialized.current){ @@ -130,13 +132,13 @@ export const GroupAnnouncements = ({ handleNewEncryptionNotification, isAdmin, hide, - myName + myName, }) => { const [messages, setMessages] = useState([]); const [isSending, setIsSending] = useState(false); const [isLoading, setIsLoading] = useState(true); const [announcements, setAnnouncements] = useState([]); - const [tempPublishedList, setTempPublishedList] = useState([]) + const [tempPublishedList, setTempPublishedList] = useState([]); const [announcementData, setAnnouncementData] = useState({}); const [selectedAnnouncement, setSelectedAnnouncement] = useState(null); const [isFocusedParent, setIsFocusedParent] = useState(false); @@ -147,37 +149,45 @@ export const GroupAnnouncements = ({ const hasInitialized = useRef(false); const hasInitializedWebsocket = useRef(false); const editorRef = useRef(null); - const dataPublishes = useRef({}) + const dataPublishes = useRef({}); const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; + const [, forceUpdate] = React.useReducer((x) => x + 1, 0); - useEffect(()=> { - if(!selectedGroup) return - (async ()=> { - const res = await getDataPublishesFunc(selectedGroup, 'anc') - dataPublishes.current = res || {} - })() - }, [selectedGroup]) + const triggerRerender = () => { + forceUpdate(); // Trigger re-render by updating the state + }; + useEffect(() => { + if (!selectedGroup) return; + (async () => { + const res = await getDataPublishesFunc(selectedGroup, "anc"); + dataPublishes.current = res || {}; + })(); + }, [selectedGroup]); const getAnnouncementData = async ({ identifier, name, resource }) => { try { - let data = dataPublishes.current[`${name}-${identifier}`] - if(!data || (data?.update || data?.created !== (resource?.updated || resource?.created))){ - const res = await requestQueuePublishedAccouncements.enqueue(()=> { + let data = dataPublishes.current[`${name}-${identifier}`]; + if ( + !data || + data?.update || + data?.created !== (resource?.updated || resource?.created) + ) { + const res = await requestQueuePublishedAccouncements.enqueue(() => { return fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT/${name}/${identifier}?encoding=base64` ); - }) - if(!res?.ok) return - data = await res.text(); - await addDataPublishesFunc({...resource, data}, selectedGroup, 'anc') + }); + if (!res?.ok) return; + data = await res.text(); + await addDataPublishesFunc({ ...resource, data }, selectedGroup, "anc"); } else { - data = data.data + data = data.data; } - + const response = await decryptPublishes([{ data }], secretKey); - + const messageData = response[0]; setAnnouncementData((prev) => { return { @@ -185,16 +195,11 @@ export const GroupAnnouncements = ({ [`${identifier}-${name}`]: messageData, }; }); - } catch (error) { - console.log('error', error) + console.log("error", error); } }; - - - - useEffect(() => { if (!secretKey || hasInitializedWebsocket.current) return; setIsLoading(true); @@ -226,64 +231,61 @@ export const GroupAnnouncements = ({ }; const publishAnc = async ({ encryptedData, identifier }: any) => { - - - return new Promise((res, rej) => { - chrome?.runtime?.sendMessage( - { - action: "publishGroupEncryptedResource", - payload: { - encryptedData, - identifier, - }, + return new Promise((res, rej) => { + chrome?.runtime?.sendMessage( + { + action: "publishGroupEncryptedResource", + payload: { + encryptedData, + identifier, }, - (response) => { - if (!response?.error) { - res(response); - } - rej(response.error); + }, + (response) => { + if (!response?.error) { + res(response); } - ); - }); + rej(response.error); + } + ); + }); }; const clearEditorContent = () => { if (editorRef.current) { editorRef.current.chain().focus().clearContent().run(); - if(isMobile){ + if (isMobile) { setTimeout(() => { - editorRef.current?.chain().blur().run(); - setIsFocusedParent(false) + editorRef.current?.chain().blur().run(); + setIsFocusedParent(false); + setTimeout(() => { + triggerRerender(); + }, 300); }, 200); } } }; - const setTempData = async ()=> { + const setTempData = async () => { try { - const getTempAnnouncements = await getTempPublish() - if(getTempAnnouncements?.announcement){ - let tempData = [] - Object.keys(getTempAnnouncements?.announcement || {}).map((key)=> { - const value = getTempAnnouncements?.announcement[key] - tempData.push(value.data) - }) - setTempPublishedList(tempData) - } - } catch (error) { - - } - - } + const getTempAnnouncements = await getTempPublish(); + if (getTempAnnouncements?.announcement) { + let tempData = []; + Object.keys(getTempAnnouncements?.announcement || {}).map((key) => { + const value = getTempAnnouncements?.announcement[key]; + tempData.push(value.data); + }); + setTempPublishedList(tempData); + } + } catch (error) {} + }; const publishAnnouncement = async () => { try { - - pauseAllQueues() - const fee = await getFee('ARBITRARY') + pauseAllQueues(); + const fee = await getFee("ARBITRARY"); await show({ - message: "Would you like to perform a ARBITRARY transaction?" , - publishFee: fee.fee + ' QORT' - }) + message: "Would you like to perform a ARBITRARY transaction?", + publishFee: fee.fee + " QORT", + }); if (isSending) return; if (editorRef.current) { const htmlContent = editorRef.current.getHTML(); @@ -292,8 +294,8 @@ export const GroupAnnouncements = ({ const message = { version: 1, extra: {}, - message: htmlContent - } + message: htmlContent, + }; const secretKeyObject = await getSecretKey(false, true); const message64: any = await objectToBase64(message); const encryptSingle = await encryptChatMessage( @@ -304,36 +306,37 @@ export const GroupAnnouncements = ({ const identifier = `grp-${selectedGroup}-anc-${randomUid}`; const res = await publishAnc({ encryptedData: encryptSingle, - identifier + identifier, }); const dataToSaveToStorage = { name: myName, identifier, - service: 'DOCUMENT', + service: "DOCUMENT", tempData: message, - created: Date.now() - } - await saveTempPublish({data: dataToSaveToStorage, key: 'announcement'}) - setTempData() + created: Date.now(), + }; + await saveTempPublish({ + data: dataToSaveToStorage, + key: "announcement", + }); + setTempData(); clearEditorContent(); } // send chat message } catch (error) { - if(!error) return + if (!error) return; setInfoSnack({ type: "error", message: error, }); - setOpenSnack(true) + setOpenSnack(true); } finally { - resumeAllQueues() + resumeAllQueues(); setIsSending(false); } }; - - const getAnnouncements = React.useCallback( async (selectedGroup) => { try { @@ -349,12 +352,16 @@ export const GroupAnnouncements = ({ }, }); const responseData = await response.json(); - - setTempData() + + setTempData(); setAnnouncements(responseData); setIsLoading(false); for (const data of responseData) { - getAnnouncementData({ name: data.name, identifier: data.identifier, resource: data }); + getAnnouncementData({ + name: data.name, + identifier: data.identifier, + resource: data, + }); } } catch (error) { } finally { @@ -363,199 +370,206 @@ export const GroupAnnouncements = ({ }, [secretKey] ); - + React.useEffect(() => { if (selectedGroup && secretKey && !hasInitialized.current && !hide) { getAnnouncements(selectedGroup); - hasInitialized.current = true + hasInitialized.current = true; } }, [selectedGroup, secretKey, hide]); - - const loadMore = async()=> { + const loadMore = async () => { try { setIsLoading(true); - const offset = announcements.length - const identifier = `grp-${selectedGroup}-anc-`; - const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - const responseData = await response.json(); + const offset = announcements.length; + const identifier = `grp-${selectedGroup}-anc-`; + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&includemetadata=false&offset=${offset}&reverse=true&prefix=true`; + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + const responseData = await response.json(); - setAnnouncements((prev)=> [...prev, ...responseData]); - setIsLoading(false); + setAnnouncements((prev) => [...prev, ...responseData]); + setIsLoading(false); + for (const data of responseData) { + getAnnouncementData({ name: data.name, identifier: data.identifier }); + } + } catch (error) {} + }; + + const interval = useRef(null); + + const checkNewMessages = React.useCallback(async () => { + try { + const identifier = `grp-${selectedGroup}-anc-`; + const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&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 = announcements[0]; + if (!latestMessage) { for (const data of responseData) { - getAnnouncementData({ name: data.name, identifier: data.identifier }); - } - } catch (error) { - - } - - } - - const interval = useRef(null) - - const checkNewMessages = React.useCallback( - async () => { - try { - - const identifier = `grp-${selectedGroup}-anc-`; - const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=20&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 = announcements[0] - if (!latestMessage) { - for (const data of responseData) { - try { - - getAnnouncementData({ name: data.name, identifier: data.identifier }); - - } catch (error) {} - } - setAnnouncements(responseData) - return - } - const findMessage = responseData?.findIndex( - (item: any) => item?.identifier === latestMessage?.identifier - ) - - if(findMessage === -1) return - const newArray = responseData.slice(0, findMessage) - - for (const data of newArray) { try { - - getAnnouncementData({ name: data.name, identifier: data.identifier }); - + getAnnouncementData({ + name: data.name, + identifier: data.identifier, + }); } catch (error) {} } - setAnnouncements((prev)=> [...newArray, ...prev]) - } catch (error) { - } finally { + setAnnouncements(responseData); + return; } - }, - [announcements, secretKey, selectedGroup] - ) + const findMessage = responseData?.findIndex( + (item: any) => item?.identifier === latestMessage?.identifier + ); + + if (findMessage === -1) return; + const newArray = responseData.slice(0, findMessage); + + for (const data of newArray) { + try { + getAnnouncementData({ name: data.name, identifier: data.identifier }); + } catch (error) {} + } + setAnnouncements((prev) => [...newArray, ...prev]); + } catch (error) { + } finally { + } + }, [announcements, secretKey, selectedGroup]); const checkNewMessagesFunc = useCallback(() => { - let isCalling = false + let isCalling = false; interval.current = setInterval(async () => { - if (isCalling) return - isCalling = true - const res = await checkNewMessages() - isCalling = false - }, 20000) - }, [checkNewMessages]) + if (isCalling) return; + isCalling = true; + const res = await checkNewMessages(); + isCalling = false; + }, 20000); + }, [checkNewMessages]); useEffect(() => { - if(!secretKey || hide) return - checkNewMessagesFunc() + if (!secretKey || hide) return; + checkNewMessagesFunc(); return () => { if (interval?.current) { - clearInterval(interval.current) + clearInterval(interval.current); } - } - }, [checkNewMessagesFunc, hide]) - - + }; + }, [checkNewMessagesFunc, hide]); const combinedListTempAndReal = useMemo(() => { // Combine the two lists const combined = [...tempPublishedList, ...announcements]; - + // Remove duplicates based on the "identifier" const uniqueItems = new Map(); - combined.forEach(item => { - uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence + combined.forEach((item) => { + uniqueItems.set(item.identifier, item); // This will overwrite duplicates, keeping the last occurrence }); - + // Convert the map back to an array and sort by "created" timestamp in descending order - const sortedList = Array.from(uniqueItems.values()).sort((a, b) => b.created - a.created); - + const sortedList = Array.from(uniqueItems.values()).sort( + (a, b) => b.created - a.created + ); + return sortedList; }, [tempPublishedList, announcements]); - - if(selectedAnnouncement){ + if (selectedAnnouncement) { return (
- + style={{ + // reference to change height + height: isMobile ? `calc(${getRootHeight()} - 127px` : "100vh", + display: "flex", + flexDirection: "column", + width: "100%", + visibility: hide && "hidden", + position: hide && "fixed", + left: hide && "-1000px", + }} + > +
- ) + ); } - - return (
-
- {!isMobile && ( - - - Group Announcements - - )} - - +
+ {!isMobile && ( + + + Group Announcements + + )} +
{!isLoading && combinedListTempAndReal?.length === 0 && ( - - No announcements + + + No announcements + )} 0 && announcements.length % 20 === 0} + showLoadMore={ + announcements.length > 0 && announcements.length % 20 === 0 + } loadMore={loadMore} myName={myName} /> - - -{isAdmin && ( -
-
- -
- - {isFocusedParent && ( - { - if(isSending) return - setIsFocusedParent(false) - clearEditorContent() - // Unfocus the editor - }} - style={{ - marginTop: 'auto', - alignSelf: 'center', - cursor: isSending ? 'default' : 'pointer', - background: 'red', - flexShrink: 0, - padding: isMobile && '5px', - fontSize: isMobile && '14px', - }} - > - - {` Close`} - - + + {isAdmin && ( +
+
+ +
+ + {isFocusedParent && ( + { + if (isSending) return; + setIsFocusedParent(false); + clearEditorContent(); + setTimeout(() => { + triggerRerender(); + }, 300); + // Unfocus the editor + }} + style={{ + marginTop: "auto", + alignSelf: "center", + cursor: isSending ? "default" : "pointer", + background: "red", + flexShrink: 0, + padding: isMobile && "5px", + fontSize: isMobile && "14px", + }} + > + {` Close`} + )} - { - if (isSending) return; - publishAnnouncement(); - }} - style={{ - marginTop: "auto", - alignSelf: "center", - cursor: isSending ? "default" : "pointer", - background: isSending && "rgba(0, 0, 0, 0.8)", - flexShrink: 0, - padding: isMobile && '5px', - fontSize: isMobile && '14px', + { + if (isSending) return; + publishAnnouncement(); + }} + style={{ + marginTop: "auto", + alignSelf: "center", + cursor: isSending ? "default" : "pointer", + background: isSending && "rgba(0, 0, 0, 0.8)", + flexShrink: 0, + padding: isMobile && "5px", + fontSize: isMobile && "14px", + }} + > + {isSending && ( + + )} + {` Publish Announcement`} + + +
+ )} - }} - > - {isSending && ( - - )} - {` Publish Announcement`} - - -
-
-)} - - + diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 8484fea..58312cb 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2,6 +2,7 @@ import { Avatar, Box, Button, + ButtonBase, Grid, IconButton, List, @@ -82,6 +83,8 @@ import Header from "../Mobile/MobileHeader"; import { Home } from "./Home"; import { GroupMenu } from "./GroupMenu"; import { getRootHeight } from "../../utils/mobile/mobileUtils"; +import { ReturnIcon } from "../../assets/Icons/ReturnIcon"; +import { ExitIcon } from "../../assets/Icons/ExitIcon"; // let touchStartY = 0; // let disablePullToRefresh = false; @@ -203,7 +206,7 @@ export const decryptResource = async (data: string) => { (response) => { if (!response?.error) { res(response); - return + return; } rej(response.error); } @@ -221,7 +224,7 @@ export const addDataPublishesFunc = async (data: string, groupId, type) => { payload: { data, groupId, - type + type, }, }, (response) => { @@ -243,7 +246,7 @@ export const getDataPublishesFunc = async (groupId, type) => { action: "getDataPublishes", payload: { groupId, - type + type, }, }, (response) => { @@ -369,7 +372,7 @@ export const Group = ({ balance, isOpenDrawerProfile, setIsOpenDrawerProfile, - logoutFunc + logoutFunc, }: GroupProps) => { const [secretKey, setSecretKey] = useState(null); const [secretKeyPublishDate, setSecretKeyPublishDate] = useState(null); @@ -409,12 +412,12 @@ export const Group = ({ const [groupAnnouncements, setGroupAnnouncements] = React.useState({}); const [defaultThread, setDefaultThread] = React.useState(null); const [isOpenDrawer, setIsOpenDrawer] = React.useState(false); - const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false) - const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState('') + const [hideCommonKeyPopup, setHideCommonKeyPopup] = React.useState(false); + const [isLoadingGroupMessage, setIsLoadingGroupMessage] = React.useState(""); const [drawerMode, setDrawerMode] = React.useState("groups"); - const [mutedGroups, setMutedGroups] = useState([]) - const [mobileViewMode, setMobileViewMode] = useState('home') - const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState('') + const [mutedGroups, setMutedGroups] = useState([]); + const [mobileViewMode, setMobileViewMode] = useState("home"); + const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState(""); const isFocusedRef = useRef(true); const selectedGroupRef = useRef(null); @@ -452,29 +455,27 @@ export const Group = ({ { action: "getUserSettings", payload: { - key: 'mutedGroups' - } + key: "mutedGroups", + }, }, (response) => { if (!response?.error) { - - setMutedGroups(response || []); + setMutedGroups(response || []); res(response); - return + return; } rej(response.error); } ); }); } catch (error) { - console.log('error', error) + console.log("error", error); } }; - useEffect(()=> { - - getUserSettings() - }, []) + useEffect(() => { + getUserSettings(); + }, []); const getTimestampEnterChat = async () => { try { @@ -688,7 +689,7 @@ export const Group = ({ secretKeyToPublish?: boolean ) => { try { - setIsLoadingGroupMessage('Locating encryption keys') + setIsLoadingGroupMessage("Locating encryption keys"); // setGroupDataLastSet(null) pauseAllQueues(); let dataFromStorage; @@ -751,7 +752,7 @@ export const Group = ({ if (dataFromStorage) { data = dataFromStorage; } else { - setIsLoadingGroupMessage('Downloading encryption keys') + setIsLoadingGroupMessage("Downloading encryption keys"); const res = await fetch( `${getBaseApiReact()}/arbitrary/DOCUMENT_PRIVATE/${publish.name}/${ publish.identifier @@ -796,7 +797,7 @@ export const Group = ({ } } finally { setIsLoadingGroup(false); - setIsLoadingGroupMessage('') + setIsLoadingGroupMessage(""); if (!secretKeyToPublish) { // await getAdmins(selectedGroup?.groupId); } @@ -1016,7 +1017,6 @@ export const Group = ({ secretKey && admins.includes(myAddress) ) { - // getAdmins(selectedGroup?.groupId); getMembers(selectedGroup?.groupId); initiatedGetMembers.current = true; @@ -1153,11 +1153,11 @@ export const Group = ({ const openDirectChatFromInternal = (e) => { const directAddress = e.detail?.address; - const name = e.detail?.name + const name = e.detail?.name; const findDirect = directs?.find( (direct) => direct?.address === directAddress || direct?.name === name ); - + if (findDirect) { setChatMode("directs"); setSelectedDirect(null); @@ -1177,15 +1177,14 @@ export const Group = ({ setSelectedDirect(findDirect); getTimestampEnterChat(); }, 200); - } else { - setChatMode('directs') + setChatMode("directs"); setNewChat(true); setTimeout(() => { - executeEvent("setDirectToValueNewChat", { - directToValue: name || directAddress - }); - }, 500); + executeEvent("setDirectToValueNewChat", { + directToValue: name || directAddress, + }); + }, 500); } }; @@ -1193,7 +1192,10 @@ export const Group = ({ subscribeToEvent("openDirectMessageInternal", openDirectChatFromInternal); return () => { - unsubscribeFromEvent("openDirectMessageInternal", openDirectChatFromInternal); + unsubscribeFromEvent( + "openDirectMessageInternal", + openDirectChatFromInternal + ); }; }, [directs, selectedDirect]); @@ -1205,8 +1207,8 @@ export const Group = ({ }; }, [directs, selectedDirect]); - const handleMarkAsRead = (e)=> { - const {groupId} = e.detail + const handleMarkAsRead = (e) => { + const { groupId } = e.detail; chrome?.runtime?.sendMessage({ action: "addTimestampEnterChat", payload: { @@ -1215,8 +1217,6 @@ export const Group = ({ }, }); - - chrome?.runtime?.sendMessage({ action: "addGroupNotificationTimestamp", payload: { @@ -1228,8 +1228,7 @@ export const Group = ({ getGroupAnnouncements(); getTimestampEnterChat(); }, 200); - } - + }; useEffect(() => { subscribeToEvent("markAsRead", handleMarkAsRead); @@ -1239,7 +1238,6 @@ export const Group = ({ }; }, []); - const resetAllStatesAndRefs = () => { // Reset all useState values to their initial states setSecretKey(null); @@ -1257,7 +1255,7 @@ export const Group = ({ setMembers([]); setGroupOwner(null); setTriedToFetchSecretKey(false); - setHideCommonKeyPopup(false) + setHideCommonKeyPopup(false); setOpenAddGroup(false); setIsInitialGroups(false); setOpenManageMembers(false); @@ -1274,7 +1272,7 @@ export const Group = ({ setGroupSection("home"); setGroupAnnouncements({}); setDefaultThread(null); - setMobileViewMode('home') + setMobileViewMode("home"); // Reset all useRef values to their initial states hasInitialized.current = false; hasInitializedWebsocket.current = false; @@ -1341,7 +1339,7 @@ export const Group = ({ setTimeout(() => { setSelectedGroup(findGroup); - setMobileViewMode('group') + setMobileViewMode("group"); getTimestampEnterChat(); isLoadingOpenSectionFromNotification.current = false; }, 200); @@ -1386,7 +1384,7 @@ export const Group = ({ }); setTimeout(() => { setSelectedGroup(findGroup); - setMobileViewMode('group') + setMobileViewMode("group"); getGroupAnnouncements(); }, 200); @@ -1439,7 +1437,7 @@ export const Group = ({ setTimeout(() => { setSelectedGroup(findGroup); - setMobileViewMode('group') + setMobileViewMode("group"); getGroupAnnouncements(); }, 200); } @@ -1458,11 +1456,10 @@ export const Group = ({ }; const goToHome = async () => { - if(isMobile){ - setMobileViewMode('home') + if (isMobile) { + setMobileViewMode("home"); } - if(!isMobile){ - + if (!isMobile) { } setGroupSection("default"); clearAllQueues(); @@ -1495,7 +1492,7 @@ export const Group = ({ }, 200); }); setSelectedDirect(null); - setNewChat(false) + setNewChat(false); setGroupSection("announcement"); chrome?.runtime?.sendMessage({ action: "addGroupNotificationTimestamp", @@ -1509,16 +1506,16 @@ export const Group = ({ }, 200); }; - const openDrawerGroups = ()=> { + const openDrawerGroups = () => { setIsOpenDrawer(true); - setDrawerMode("groups"); - } + setDrawerMode("groups"); + }; - const goToThreads = ()=> { + const goToThreads = () => { setSelectedDirect(null); - setNewChat(false) - setGroupSection("forum") - } + setNewChat(false); + setGroupSection("forum"); + }; const goToChat = async () => { setGroupSection("default"); @@ -1528,7 +1525,7 @@ export const Group = ({ }, 200); }); setGroupSection("chat"); - setNewChat(false) + setNewChat(false); setSelectedDirect(null); if (selectedGroupRef.current) { chrome?.runtime?.sendMessage({ @@ -1545,19 +1542,66 @@ export const Group = ({ } }; - - const renderDirects = ()=> { + const renderDirects = () => { return (
-
+ + + + + + + { + setMobileViewModeKeepOpen('') + }} + > + + + + + + )} +
{directs.map((direct: any) => ( - + @@ -1614,7 +1662,7 @@ export const Group = ({ sx={{ display: "flex", width: "100%", - alignItems: 'center' + alignItems: "center", }} > @@ -1670,7 +1718,7 @@ export const Group = ({ ))} -
+
- - { - setNewChat(true); - setSelectedDirect(null); - // setSelectedGroup(null); - setIsOpenDrawer(false); + { + setNewChat(true); + setSelectedDirect(null); + // setSelectedGroup(null); + setIsOpenDrawer(false); + }} + > + - - New Chat - - + /> + New Chat +
- ) - } + ); + }; const renderGroups = () => { return (
- {/*
{groups.map((group: any) => ( - + @@ -1903,7 +1952,7 @@ export const Group = ({ // // } onClick={() => { - setMobileViewMode('group') + setMobileViewMode("group"); clearAllQueues(); setSelectedDirect(null); setTriedToFetchSecretKey(false); @@ -1966,71 +2015,77 @@ export const Group = ({ group?.groupId === selectedGroup?.groupId && "white", }} > - - - - - {group.groupName?.charAt(0)} - - - - {groupAnnouncements[group?.groupId] && - !groupAnnouncements[group?.groupId]?.seentimestamp && ( - + + - )} - {group?.data && - isExtMsg(group?.data) && - group?.sender !== myAddress && - group?.timestamp && - ((!timestampEnterData[group?.groupId] && - Date.now() - group?.timestamp < - timeDifferenceForNotificationChats) || - timestampEnterData[group?.groupId] < - group?.timestamp) && ( - - )} - + alt={group?.groupName} + // src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${groupOwner?.name}/qortal_group_avatar_${group.groupId}?async=true`} + > + {group.groupName?.charAt(0)} + + + + {groupAnnouncements[group?.groupId] && + !groupAnnouncements[group?.groupId]?.seentimestamp && ( + + )} + {group?.data && + isExtMsg(group?.data) && + group?.sender !== myAddress && + group?.timestamp && + ((!timestampEnterData[group?.groupId] && + Date.now() - group?.timestamp < + timeDifferenceForNotificationChats) || + timestampEnterData[group?.groupId] < + group?.timestamp) && ( + + )} + @@ -2076,7 +2131,6 @@ export const Group = ({ )}
-
); }; @@ -2093,8 +2147,22 @@ export const Group = ({ info={infoSnack} setInfo={setInfoSnack} /> -
- +
+
{!isMobile && renderGroups()} - - + + - {mobileViewMode === 'groups' && ( - renderGroups() - )} + {mobileViewMode === "groups" && !mobileViewModeKeepOpen && renderGroups()} -{mobileViewModeKeepOpen === 'messaging' && ( - renderDirects() - )} - {newChat && ( - <> - - { - setSelectedDirect(null); - - setNewChat(false); - }} - /> - - - )} - {selectedGroup && ( - <> - {mobileViewMode === 'group' && ( - - )} - - {triedToFetchSecretKey && ( - - )} - {firstSecretKeyInCreation && - triedToFetchSecretKey && - !secretKeyPublishDate && ( -
+ + + - {" "} - - The group's first common encryption key is in the process - of creation. Please wait a few minutes for it to be - retrieved by the network. Checking every 2 minutes... - -
- )} - {!admins.includes(myAddress) && - !secretKey && - triedToFetchSecretKey ? ( + { + close() + }} + > + + +
+ + + { + setSelectedDirect(null) + setMobileViewModeKeepOpen('') + }} + > + + + +
+
+ + { + setSelectedDirect(null); + + setNewChat(false); + }} + setMobileViewModeKeepOpen={setMobileViewModeKeepOpen} + /> + + + )} + {selectedGroup && ( + <> + + + + { + setMobileViewMode("groups"); + }} + > + + + + + {selectedGroup?.groupName} + + + {/* */} + + + + {mobileViewMode === "group" && ( <> - {secretKeyPublishDate || - (!secretKeyPublishDate && !firstSecretKeyInCreation) ? ( + + + )} + + {triedToFetchSecretKey && ( + + )} + {firstSecretKeyInCreation && + triedToFetchSecretKey && + !secretKeyPublishDate && (
{" "} - You are not part of the encrypted group of members. Wait - until an admin re-encrypts the keys. + The group's first common encryption key is in the + process of creation. Please wait a few minutes for it to + be retrieved by the network. Checking every 2 minutes... - - - Try notifying an admin from the list of admins below: - - - {adminsWithNames.map((admin) => { - return ( - - {admin?.name} - notifyAdmin(admin)} - > - Notify - - - ); - })}
- ) : null} - - ) : admins.includes(myAddress) && + )} + {!admins.includes(myAddress) && !secretKey && - triedToFetchSecretKey ? null : !triedToFetchSecretKey ? null : ( - <> - - - - )} + triedToFetchSecretKey ? ( + <> + {secretKeyPublishDate || + (!secretKeyPublishDate && !firstSecretKeyInCreation) ? ( +
+ {" "} + + You are not part of the encrypted group of members. + Wait until an admin re-encrypts the keys. + + + + Try notifying an admin from the list of admins below: + + + {adminsWithNames.map((admin) => { + return ( + + {admin?.name} + notifyAdmin(admin)} + > + Notify + + + ); + })} +
+ ) : null} + + ) : admins.includes(myAddress) && + !secretKey && + triedToFetchSecretKey ? null : !triedToFetchSecretKey ? null : ( + <> + + + + )} + + {admins.includes(myAddress) && + shouldReEncrypt && + triedToFetchSecretKey && + !firstSecretKeyInCreation && + !hideCommonKeyPopup && ( + + )} + +
+ {openManageMembers && ( + + )} + + )} + + {selectedDirect && !newChat && ( + <> - {admins.includes(myAddress) && - shouldReEncrypt && - triedToFetchSecretKey && - !firstSecretKeyInCreation && !hideCommonKeyPopup && ( - - )} + + { + setSelectedDirect(null); + + setNewChat(false); + }} + setMobileViewModeKeepOpen={setMobileViewModeKeepOpen} + /> + - - {openManageMembers && ( - - )} - - )} + + )} - {selectedDirect && !newChat && ( - <> - - - - - - - { - setSelectedDirect(null); - - setNewChat(false); - }} - /> - - - - )} - - {mobileViewMode === 'home' && ( - - )} - {/* { + {mobileViewMode === "home" && ( + + )} + {/* { !selectedGroup && groupSection === "home" && ( @@ -2414,7 +2591,7 @@ export const Group = ({ setOpenAddGroup={setOpenAddGroup} /> )} */} - + { setGroupSection("forum"); setSelectedDirect(null); - setNewChat(false) + setNewChat(false); }} > @@ -2617,36 +2795,53 @@ export const Group = ({ message: "Setting up groups... please wait.", }} /> -
- {mobileViewMode === 'home' && !mobileViewModeKeepOpen && ( + {mobileViewMode === "home" && !mobileViewModeKeepOpen && ( <> -
- {/* +
+ {/* {renderGroups()} */} - {isMobile && ( - - )} + {isMobile && ( + + )} )} - - - ); }; - - // {isMobile && ( // // // -// )} \ No newline at end of file +// )} diff --git a/src/components/Group/GroupMenu.tsx b/src/components/Group/GroupMenu.tsx index f113bb5..5bc99c6 100644 --- a/src/components/Group/GroupMenu.tsx +++ b/src/components/Group/GroupMenu.tsx @@ -16,7 +16,7 @@ import { ChatIcon } from "../../assets/Icons/ChatIcon"; import { ThreadsIcon } from "../../assets/Icons/ThreadsIcon"; import { MembersIcon } from "../../assets/Icons/MembersIcon"; -export const GroupMenu = ({ setGroupSection, groupSection }) => { +export const GroupMenu = ({ setGroupSection, groupSection, setOpenManageMembers }) => { const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); @@ -177,7 +177,7 @@ export const GroupMenu = ({ setGroupSection, groupSection }) => { { - // setGroupSection("") + setOpenManageMembers(true) handleClose(); }} > diff --git a/src/components/Group/Home.tsx b/src/components/Group/Home.tsx index 7dcc557..4db465d 100644 --- a/src/components/Group/Home.tsx +++ b/src/components/Group/Home.tsx @@ -1,32 +1,58 @@ -import { Box, Button, Typography } from '@mui/material' -import React from 'react' -import { Spacer } from '../../common/Spacer' -import { ListOfThreadPostsWatched } from './ListOfThreadPostsWatched' -import { ThingsToDoInitial } from './ThingsToDoInitial' -import { GroupJoinRequests } from './GroupJoinRequests' -import { GroupInvites } from './GroupInvites' +import { Box, Button, Typography } from "@mui/material"; +import React from "react"; +import { Spacer } from "../../common/Spacer"; +import { ListOfThreadPostsWatched } from "./ListOfThreadPostsWatched"; +import { ThingsToDoInitial } from "./ThingsToDoInitial"; +import { GroupJoinRequests } from "./GroupJoinRequests"; +import { GroupInvites } from "./GroupInvites"; import RefreshIcon from "@mui/icons-material/Refresh"; -export const Home = ({refreshHomeDataFunc, myAddress, isLoadingGroups, balance, userInfo, groups, setGroupSection, setSelectedGroup, getTimestampEnterChat, setOpenManageMembers, setOpenAddGroup, setMobileViewMode}) => { +export const Home = ({ + refreshHomeDataFunc, + myAddress, + isLoadingGroups, + balance, + userInfo, + groups, + setGroupSection, + setSelectedGroup, + getTimestampEnterChat, + setOpenManageMembers, + setOpenAddGroup, + setMobileViewMode, +}) => { return ( - - - - Welcome - - + sx={{ + display: "flex", + width: "100%", + flexDirection: "column", + height: "100%", + overflow: "auto", + alignItems: "center", + }} + > + + 15 ? "16px" : "20px", + padding: '10px' + }} + > + Welcome{" "} + {userInfo?.name ? ( + {`, ${userInfo?.name}`} + ) : null} + + - {/* */} - {!isLoadingGroups && ( - + {!isLoadingGroups && ( + + + - - - - - - - )} - - - ) -} + + + + )} + + + ); +}; diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index 078cecc..96a46ba 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -253,6 +253,7 @@ export const ManageMembers = ({ sx={{ width: "100%", padding: "25px", + maxWidth: '750px' }} > @@ -281,7 +283,8 @@ export const ManageMembers = ({ @@ -293,7 +296,8 @@ export const ManageMembers = ({ @@ -304,7 +308,8 @@ export const ManageMembers = ({ diff --git a/src/components/Mobile/MobileFooter.tsx b/src/components/Mobile/MobileFooter.tsx index 1f025f4..778cac3 100644 --- a/src/components/Mobile/MobileFooter.tsx +++ b/src/components/Mobile/MobileFooter.tsx @@ -1,35 +1,46 @@ -import * as React from 'react'; -import { BottomNavigation, BottomNavigationAction, Typography } from '@mui/material'; -import { Home, Groups, Message, ShowChart } from '@mui/icons-material'; -import Box from '@mui/material/Box'; -import BottomLogo from '../../assets/svgs/BottomLogo5.svg' -import { CustomSvg } from '../../common/CustomSvg'; -import { WalletIcon } from '../../assets/Icons/WalletIcon'; -import { HubsIcon } from '../../assets/Icons/HubsIcon'; -import { TradingIcon } from '../../assets/Icons/TradingIcon'; -import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; +import * as React from "react"; +import { + BottomNavigation, + BottomNavigationAction, + Typography, +} from "@mui/material"; +import { Home, Groups, Message, ShowChart } from "@mui/icons-material"; +import Box from "@mui/material/Box"; +import BottomLogo from "../../assets/svgs/BottomLogo5.svg"; +import { CustomSvg } from "../../common/CustomSvg"; +import { WalletIcon } from "../../assets/Icons/WalletIcon"; +import { HubsIcon } from "../../assets/Icons/HubsIcon"; +import { TradingIcon } from "../../assets/Icons/TradingIcon"; +import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; -const IconWrapper = ({children, label, color})=> { - - return - {children} - {label} +const IconWrapper = ({ children, label, color }) => { + return ( + + {children} + + {label} + -} + ); +}; -export const MobileFooter =({ -selectedGroup, +export const MobileFooter = ({ + selectedGroup, groupSection, isUnread, goToAnnouncements, @@ -44,84 +55,131 @@ selectedGroup, openDrawerGroups, goToHome, setIsOpenDrawerProfile, - mobileViewMode, + mobileViewMode, setMobileViewMode, - setMobileViewModeKeepOpen + setMobileViewModeKeepOpen, + hasUnreadGroups, + hasUnreadDirects }) => { - const [value, setValue] = React.useState(0); - return ( - - setValue(newValue)} - sx={{ backgroundColor: 'transparent', flexGrow: 1 }} - > - { + borderTopRightRadius: "25px", + borderTopLeftRadius: "25px", + boxShadow: '0px -2px 10px rgba(0, 0, 0, 0.1)', + }} + > + setValue(newValue)} + sx={{ backgroundColor: "transparent", flexGrow: 1 }} + > + { // setMobileViewMode('wallet') - setIsOpenDrawerProfile(true) - }} icon={} sx={{ color: value === 0 ? 'white' : 'gray', padding: '0px 10px' }} /> - { - setMobileViewMode('groups') - }} icon={} sx={{ color: value === 0 ? 'white' : 'gray', paddingLeft: '10px', paddingRight: '42px' }} /> + setIsOpenDrawerProfile(true); + }} + icon={ + + + + } + sx={{ color: value === 0 ? "white" : "gray", padding: "0px 10px" }} + /> + { + setMobileViewMode("groups"); + }} + icon={ + + + + } + sx={{ + color: value === 0 ? "white" : "gray", + paddingLeft: "10px", + paddingRight: "42px", + }} + /> + - - - {/* Floating Center Button */} - - - {/* Custom Center Icon */} - center-icon - - - - setValue(newValue)} - sx={{ backgroundColor: 'transparent', flexGrow: 1 }} + {/* Floating Center Button */} + + - { - setMobileViewModeKeepOpen('messaging') - }} icon={} sx={{ color: value === 2 ? 'white' : 'gray', paddingLeft: '55px', paddingRight: '10px' }} /> - { - chrome.tabs.create({ url: "https://www.qort.trade", active: true }); - }} icon={} sx={{ color: value === 3 ? 'white' : 'gray' , padding: '0px 10px'}} /> - + {/* Custom Center Icon */} + center-icon + - ); - } \ No newline at end of file + + setValue(newValue)} + sx={{ backgroundColor: "transparent", flexGrow: 1 }} + > + { + setMobileViewModeKeepOpen("messaging"); + }} + icon={ + + + + } + sx={{ + color: value === 2 ? "white" : "gray", + paddingLeft: "55px", + paddingRight: "10px", + }} + /> + { + chrome.tabs.create({ url: "https://www.qort.trade", active: true }); + }} + icon={ + + + + } + sx={{ color: value === 3 ? "white" : "gray", padding: "0px 10px" }} + /> + + + ); +}; diff --git a/src/components/Mobile/MobileHeader.tsx b/src/components/Mobile/MobileHeader.tsx index da9d07e..914e8eb 100644 --- a/src/components/Mobile/MobileHeader.tsx +++ b/src/components/Mobile/MobileHeader.tsx @@ -1,17 +1,35 @@ -import React from 'react'; -import { AppBar, Toolbar, IconButton, Typography, Box, MenuItem, Select, ButtonBase } from '@mui/material'; -import { HomeIcon } from '../../assets/Icons/HomeIcon'; -import { LogoutIcon } from '../../assets/Icons/LogoutIcon'; -import { NotificationIcon } from '../../assets/Icons/NotificationIcon'; -import { ArrowDownIcon } from '../../assets/Icons/ArrowDownIcon'; -import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; +import React, { useState } from "react"; +import { + AppBar, + Toolbar, + IconButton, + Typography, + Box, + MenuItem, + Select, + ButtonBase, + Menu, + ListItemIcon, + ListItemText, +} from "@mui/material"; +import { HomeIcon } from "../../assets/Icons/HomeIcon"; +import { LogoutIcon } from "../../assets/Icons/LogoutIcon"; +import { NotificationIcon } from "../../assets/Icons/NotificationIcon"; +import { ArrowDownIcon } from "../../assets/Icons/ArrowDownIcon"; +import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; +import { MessagingIcon2 } from "../../assets/Icons/MessagingIcon2"; +import { HubsIcon } from "../../assets/Icons/HubsIcon"; const Header = ({ logoutFunc, goToHome, setIsOpenDrawerProfile, isThin, - setMobileViewModeKeepOpen + setMobileViewModeKeepOpen, + hasUnreadGroups, + hasUnreadDirects, + setMobileViewMode, + // selectedGroup, // onHomeClick, // onLogoutClick, @@ -19,94 +37,243 @@ const Header = ({ // onWalletClick, // onNotificationClick, }) => { - if(isThin){ + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + + if (isThin) { return ( - - - {/* Left Home Icon */} - - { - setMobileViewModeKeepOpen('') - goToHome() -}} - // onClick={onHomeClick} + + - - - { - setMobileViewModeKeepOpen() - goToHome() -}} - // onClick={onHomeClick} + {/* Left Home Icon */} + + { + setMobileViewModeKeepOpen(""); + goToHome(); + }} + // onClick={onHomeClick} + > + + + + + + + + {/* Center Title */} + + QORTAL + + + {/* Right Logout Icon */} + + { + setMobileViewModeKeepOpen("messaging"); + }} + edge="end" + color="inherit" + aria-label="logout" + + // onClick={onLogoutClick} + > + + + + + + + + + { + setMobileViewMode("groups"); + setMobileViewModeKeepOpen("") + handleClose(); + }} > - - - - + + + + + + { + setMobileViewModeKeepOpen("messaging"); - {/* Center Title */} - - QORTAL - - - {/* Right Logout Icon */} - - { - setMobileViewModeKeepOpen('messaging') - }} edge="end" color="inherit" aria-label="logout" - -// onClick={onLogoutClick} -> - - - - - - - - - ) + + + + + + + + ); } return ( <> {/* Main Header */} - - + + {/* Left Home Icon */} - - + {/* Center Title */} - + QORTAL {/* Right Logout Icon */} - @@ -116,69 +283,160 @@ onClick={()=> { {/* Secondary Section */} - - - + + Palmas -{/* + {/* */} - - - - + + {/* Right Dropdown */} - { - setIsOpenDrawerProfile(true) - }}> - - - View Wallet - + {/* { + setIsOpenDrawerProfile(true); + }} + > + + + View Wallet + - - - + + + */} + + { + setMobileViewMode("groups"); + setMobileViewModeKeepOpen("") + handleClose(); + }} + > + + + + + + { + setMobileViewModeKeepOpen("messaging"); + + handleClose(); + }} + > + + + + + + ); }; diff --git a/src/index.css b/src/index.css index 68a7ee3..9d6fa90 100644 --- a/src/index.css +++ b/src/index.css @@ -25,6 +25,7 @@ padding: 0px; margin: 0px; box-sizing: border-box !important; + word-break: break-word; --color-instance : #1E1E20; --color-instance-popover-bg: #222222; --Mail-Background: rgba(49, 51, 56, 1); @@ -33,6 +34,7 @@ --bg-primary : rgba(31, 32, 35, 1); --bg-2: #27282c; --bg-3: rgba(0, 0, 0, 0.1); + --unread: rgba(255, 0, 0, 1); } body { From 7205f8c9adf2d1a6527f54b835b6c11af4ae2465 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 21 Sep 2024 09:57:35 +0300 Subject: [PATCH 19/25] started on desktop --- src/App-styles.ts | 2 +- src/App.tsx | 2 +- src/components/Desktop/DesktopFooter.tsx | 118 ++++++++++ src/components/Group/Group.tsx | 205 +++++++++++------- src/components/Group/GroupInvites.tsx | 5 +- src/components/Group/GroupJoinRequests.tsx | 5 +- src/components/Group/HomeDesktop.tsx | 150 +++++++++++++ .../Group/ListOfThreadPostsWatched.tsx | 3 +- src/components/Group/ThingsToDoInitial.tsx | 3 +- 9 files changed, 405 insertions(+), 88 deletions(-) create mode 100644 src/components/Desktop/DesktopFooter.tsx create mode 100644 src/components/Group/HomeDesktop.tsx diff --git a/src/App-styles.ts b/src/App-styles.ts index b33bb0a..c2a036f 100644 --- a/src/App-styles.ts +++ b/src/App-styles.ts @@ -77,7 +77,7 @@ display: flex; border: 1px solid var(--50-white, rgba(255, 255, 255, 0.5)); justify-content: space-between; align-items: center; -width: 132px; +width: 140px; height: 25px; padding: 5px 15px 5px 15px; gap: 5px; diff --git a/src/App.tsx b/src/App.tsx index b43d357..3872eaa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -129,7 +129,7 @@ const defaultValues: MyContextInterface = { message: "", }, }; -export let isMobile = true +export let isMobile = false const isMobileDevice = () => { const userAgent = navigator.userAgent || navigator.vendor || window.opera; diff --git a/src/components/Desktop/DesktopFooter.tsx b/src/components/Desktop/DesktopFooter.tsx new file mode 100644 index 0000000..4eafe8b --- /dev/null +++ b/src/components/Desktop/DesktopFooter.tsx @@ -0,0 +1,118 @@ +import * as React from "react"; +import { + BottomNavigation, + BottomNavigationAction, + ButtonBase, + Typography, +} from "@mui/material"; +import { Home, Groups, Message, ShowChart } from "@mui/icons-material"; +import Box from "@mui/material/Box"; +import BottomLogo from "../../assets/svgs/BottomLogo5.svg"; +import { CustomSvg } from "../../common/CustomSvg"; +import { WalletIcon } from "../../assets/Icons/WalletIcon"; +import { HubsIcon } from "../../assets/Icons/HubsIcon"; +import { TradingIcon } from "../../assets/Icons/TradingIcon"; +import { MessagingIcon } from "../../assets/Icons/MessagingIcon"; +import { HomeIcon } from "../../assets/Icons/HomeIcon"; + +const IconWrapper = ({ children, label, color, selected }) => { + return ( + + {children} + + {label} + + + ); +}; + +export const DesktopFooter = ({ + selectedGroup, + groupSection, + isUnread, + goToAnnouncements, + isUnreadChat, + goToChat, + goToThreads, + setOpenManageMembers, + groupChatHasUnread, + groupsAnnHasUnread, + directChatHasUnread, + chatMode, + openDrawerGroups, + goToHome, + setIsOpenDrawerProfile, + mobileViewMode, + setMobileViewMode, + setMobileViewModeKeepOpen, + hasUnreadGroups, + hasUnreadDirects, + isHome, + isGroups, + isDirects, + setDesktopSideView +}) => { + const [value, setValue] = React.useState(0); + return ( + + + { + goToHome() + }}> + + + + + { + setDesktopSideView('groups') + }}> + + + + + { + setDesktopSideView('directs') + }}> + + + + + + + + + ); +}; diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 58312cb..a38eff1 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -85,6 +85,8 @@ import { GroupMenu } from "./GroupMenu"; import { getRootHeight } from "../../utils/mobile/mobileUtils"; import { ReturnIcon } from "../../assets/Icons/ReturnIcon"; import { ExitIcon } from "../../assets/Icons/ExitIcon"; +import { HomeDesktop } from "./HomeDesktop"; +import { DesktopFooter } from "../Desktop/DesktopFooter"; // let touchStartY = 0; // let disablePullToRefresh = false; @@ -418,7 +420,7 @@ export const Group = ({ const [mutedGroups, setMutedGroups] = useState([]); const [mobileViewMode, setMobileViewMode] = useState("home"); const [mobileViewModeKeepOpen, setMobileViewModeKeepOpen] = useState(""); - + const [desktopSideView, setDesktopSideView] = useState('groups') const isFocusedRef = useRef(true); const selectedGroupRef = useRef(null); const selectedDirectRef = useRef(null); @@ -1547,10 +1549,12 @@ export const Group = ({
{isMobile && ( @@ -1752,10 +1756,12 @@ export const Group = ({
{/*
-
+ {isMobile && ( +
+ )} +
- {!isMobile && renderGroups()} + {!isMobile && desktopSideView === 'groups' && renderGroups()} + {!isMobile && desktopSideView === 'directs' && renderDirects()} + - - - - { - setMobileViewMode("groups"); - }} - > - - - - - {selectedGroup?.groupName} - - - {/* */} - - - - {mobileViewMode === "group" && ( + {isMobile && ( + + + + { + setMobileViewMode("groups"); + }} + > + + + + + {selectedGroup?.groupName} + + + {/* */} + + + + )} + + {isMobile && mobileViewMode === "group" && ( <> )} - - {mobileViewMode === "home" && ( + {!isMobile && ( + + )} + {isMobile && mobileViewMode === "home" && ( )} - {/* { - !selectedGroup && + { + !isMobile && !selectedGroup && groupSection === "home" && ( - - )} */} + )}
- {mobileViewMode === "home" && !mobileViewModeKeepOpen && ( + + {isMobile && mobileViewMode === "home" && !mobileViewModeKeepOpen && ( <>
{ const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState( @@ -75,7 +75,8 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { { @@ -124,7 +124,8 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get { + return ( + + + + 15 ? "16px" : "20px", + padding: '10px' + }} + > + Welcome{" "} + {userInfo?.name ? ( + {`, ${userInfo?.name}`} + ) : null} + + + {!isLoadingGroups && ( + + + + + + + + + + + + + + + )} + + + + + {/* + + */} + + + + ); +}; diff --git a/src/components/Group/ListOfThreadPostsWatched.tsx b/src/components/Group/ListOfThreadPostsWatched.tsx index 3e794b0..4817805 100644 --- a/src/components/Group/ListOfThreadPostsWatched.tsx +++ b/src/components/Group/ListOfThreadPostsWatched.tsx @@ -15,6 +15,7 @@ import { Spacer } from "../../common/Spacer"; import { getGroupNames } from "./UserListOfInvites"; import { CustomLoader } from "../../common/CustomLoader"; import VisibilityIcon from "@mui/icons-material/Visibility"; +import { isMobile } from "../../App"; export const ListOfThreadPostsWatched = () => { const [posts, setPosts] = React.useState([]); @@ -92,7 +93,7 @@ export const ListOfThreadPostsWatched = () => { { const [checked1, setChecked1] = React.useState(false); @@ -77,7 +78,7 @@ export const ThingsToDoInitial = ({ myAddress, name, hasGroups, balance }) => { Date: Sat, 21 Sep 2024 10:25:28 +0300 Subject: [PATCH 20/25] fix bugs --- src/components/Chat/ChatDirect.tsx | 26 ++++++++++- src/components/Group/Group.tsx | 72 +++++++++++++++--------------- 2 files changed, 62 insertions(+), 36 deletions(-) diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index c499eb3..5341fd9 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -344,8 +344,32 @@ const clearEditorContent = () => { height: isMobile ? '100%' : '100vh', display: 'flex', flexDirection: 'column', - width: '100%' + width: '100%', + background: !isMobile && 'var(--bg-2)' }}> + {!isMobile && ( + + + Close Direct Chat + + )} {isMobile && ( - - - { - close() + - - - - - { - setSelectedDirect(null) - setMobileViewModeKeepOpen('') + onClick={() => { + close() + }} + > + + + + + - - + { + setSelectedDirect(null) + setMobileViewModeKeepOpen('') + }} + > + + + - + )} Date: Sat, 21 Sep 2024 13:16:22 +0300 Subject: [PATCH 21/25] added reply --- src/App.tsx | 2 +- src/background.ts | 13 +- src/components/Chat/ChatDirect.tsx | 43 ++++- src/components/Chat/ChatGroup.tsx | 40 +++- src/components/Chat/ChatList.tsx | 23 ++- src/components/Chat/MessageDisplay.tsx | 4 +- src/components/Chat/MessageItem.tsx | 257 ++++++++++++++++++++----- src/components/Chat/styles.css | 3 + src/components/Mobile/MobileFooter.tsx | 1 + 9 files changed, 320 insertions(+), 66 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 3872eaa..b43d357 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -129,7 +129,7 @@ const defaultValues: MyContextInterface = { message: "", }, }; -export let isMobile = false +export let isMobile = true const isMobileDevice = () => { const userAgent = navigator.userAgent || navigator.vendor || window.opera; diff --git a/src/background.ts b/src/background.ts index 86b1f11..053bb0e 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1622,17 +1622,22 @@ async function sendChatDirect({ ...(otherData || {}) }; const messageStringified = JSON.stringify(finalJson); - const tx = await createTransaction(18, keyPair, { + console.log('chatReferencefinal', chatReference) + const txBody = { timestamp: Date.now(), recipient: recipientAddress, recipientPublicKey: recipientPublicKey, - hasChatReference: 0, + hasChatReference: chatReference ? 1 : 0, message: messageStringified, lastReference: reference, proofOfWorkNonce: 0, isEncrypted: 1, isText: 1, - }); + } + if(chatReference){ + txBody['chatReference'] = chatReference + } + const tx = await createTransaction(18, keyPair, txBody); // if (!hasEnoughBalance) { // throw new Error("Must have at least 4 QORT to send a chat message"); @@ -4000,7 +4005,7 @@ chrome?.runtime?.onMessage.addListener((request, sender, sendResponse) => { address, otherData } = request.payload; - + console.log('chatReferencebg', chatReference) sendChatDirect({ directTo, chatReference, diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 5341fd9..1fe05ae 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -19,6 +19,7 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import ShortUniqueId from "short-unique-id"; import { ReturnIcon } from '../../assets/Icons/ReturnIcon'; import { ExitIcon } from '../../assets/Icons/ExitIcon'; +import { MessageItem, ReplyPreview } from './MessageItem'; const uid = new ShortUniqueId({ length: 5 }); @@ -41,12 +42,12 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi const socketRef = useRef(null); const timeoutIdRef = useRef(null); const groupSocketTimeoutRef = useRef(null); - + const [replyMessage, setReplyMessage] = useState(null) const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; const publicKeyOfRecipientRef = useRef(null) - + console.log({messages}) const getPublicKeyFunc = async (address)=> { try { const publicKey = await getPublicKey(address) @@ -219,6 +220,7 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi const sendChatDirect = async ({ chatReference = undefined, messageText, otherData}: any, address, publicKeyOfRecipient, isNewChatVar)=> { try { + console.log('chatReferencedirect', chatReference) const directTo = isNewChatVar ? directToValue : address if(!directTo) return @@ -282,6 +284,8 @@ const clearEditorContent = () => { const sendMessage = async ()=> { try { + + if(+balance < 4) throw new Error('You need at least 4 QORT to send a message') if(isSending) return if (editorRef.current) { @@ -297,12 +301,20 @@ const clearEditorContent = () => { await sendChatDirect({ messageText: htmlContent}, null, null, true) return } + let repliedTo = replyMessage?.signature + + if (replyMessage?.chatReference) { + repliedTo = replyMessage?.chatReference + } const otherData = { - specialId: uid.rnd() + specialId: uid.rnd(), + repliedTo } const sendMessageFunc = async () => { - await sendChatDirect({ messageText: htmlContent, otherData}, selectedDirect?.address, publicKeyOfRecipient, false) + await sendChatDirect({ chatReference: undefined, messageText: htmlContent, otherData}, selectedDirect?.address, publicKeyOfRecipient, false) }; + + // Add the function to the queue const messageObj = { @@ -321,6 +333,7 @@ const clearEditorContent = () => { executeEvent("sent-new-message-group", {}) }, 150); clearEditorContent() + setReplyMessage(null) } // send chat message } catch (error) { @@ -337,6 +350,9 @@ const clearEditorContent = () => { } } + const onReply = useCallback((message)=> { + setReplyMessage(message) + }, []) return ( @@ -444,7 +460,7 @@ const clearEditorContent = () => { )} - +
{ flexGrow: isMobile && 1, overflow: !isMobile && "auto", }}> + {replyMessage && ( + + + { + setReplyMessage(null) + }} + > + + + + )}
diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 97c06ac..2ffad67 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -15,8 +15,10 @@ import { CustomizedSnackbars } from '../Snackbar/Snackbar' import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from '../../constants/codes' import { useMessageQueue } from '../../MessageQueueContext' import { executeEvent } from '../../utils/events' -import { Box } from '@mui/material' +import { Box, ButtonBase } from '@mui/material' import ShortUniqueId from "short-unique-id"; +import { ReplyPreview } from './MessageItem' +import { ExitIcon } from '../../assets/Icons/ExitIcon' const uid = new ShortUniqueId({ length: 5 }); @@ -34,6 +36,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, const [infoSnack, setInfoSnack] = React.useState(null); const hasInitialized = useRef(false) const [isFocusedParent, setIsFocusedParent] = useState(false); + const [replyMessage, setReplyMessage] = useState(null) const hasInitializedWebsocket = useRef(false) const socketRef = useRef(null); // WebSocket reference @@ -111,6 +114,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, ...item, id: item.signature, text: item?.decryptedData?.message || "", + repliedTo: item?.decryptedData?.repliedTo, unread: item?.sender === myAddress ? false : true } } ) @@ -121,6 +125,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, ...item, id: item.signature, text: item?.decryptedData?.message || "", + repliedTo: item?.decryptedData?.repliedTo, unread: false } } ) @@ -305,8 +310,15 @@ const clearEditorContent = () => { setIsSending(true) const message = htmlContent const secretKeyObject = await getSecretKey(false, true) + + let repliedTo = replyMessage?.signature + + if (replyMessage?.chatReference) { + repliedTo = replyMessage?.chatReference + } const otherData = { - specialId: uid.rnd() + specialId: uid.rnd(), + repliedTo } const objectMessage = { message, @@ -338,6 +350,7 @@ const clearEditorContent = () => { executeEvent("sent-new-message-group", {}) }, 150); clearEditorContent() + setReplyMessage(null) } // send chat message } catch (error) { @@ -362,6 +375,9 @@ const clearEditorContent = () => { } }, [hide]); + const onReply = useCallback((message)=> { + setReplyMessage(message) + }, []) return (
{ left: hide && '-100000px', }}> - +
{ flexGrow: isMobile && 1, overflow: !isMobile && "auto", }}> + {replyMessage && ( + + + { + setReplyMessage(null) + }} + > + + + + )} +
diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 73b35f4..4a4e4f4 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -8,7 +8,7 @@ import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; // defaultHeight: 50, // }); -export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId }) => { +export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onReply }) => { const hasLoadedInitialRef = useRef(false); const listRef = useRef(); @@ -18,7 +18,7 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId }) = fixedWidth: true, defaultHeight: 50, }), [chatId]); // Recreate cache when chatId changes - + console.log('messages2', messages) useEffect(() => { cache.clearAll(); }, []); @@ -68,6 +68,10 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId }) = } }; + const scrollToItem = useCallback((index) => { + listRef.current.scrollToRow(index); // This scrolls to the specific index + }, []); + const recomputeListHeights = () => { if (listRef.current) { listRef.current.recomputeRowHeights(); @@ -93,7 +97,18 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId }) = let message = messages[index]; const isLargeMessage = message.text?.length > 200; // Adjust based on your message size threshold + // const reply = message?.repliedTo ? messages.find((msg)=> msg?.signature === message?.repliedTo) : undefined + let replyIndex = messages.findIndex((msg)=> msg?.signature === message?.repliedTo) + let reply + if(message?.repliedTo && replyIndex !== -1){ + reply = messages[replyIndex] + } if(message?.message && message?.groupDirectId){ + replyIndex = messages.findIndex((msg)=> msg?.signature === message?.message?.repliedTo) + reply + if(message?.message?.repliedTo && replyIndex !== -1){ + reply = messages[replyIndex] + } message = { ...(message?.message || {}), isTemp: true, @@ -130,6 +145,10 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId }) = onSeen={handleMessageSeen} isTemp={!!message?.isTemp} myAddress={myAddress} + onReply={onReply} + reply={reply} + scrollToItem={scrollToItem} + replyIndex={replyIndex} />
diff --git a/src/components/Chat/MessageDisplay.tsx b/src/components/Chat/MessageDisplay.tsx index 885f986..f4cdce7 100644 --- a/src/components/Chat/MessageDisplay.tsx +++ b/src/components/Chat/MessageDisplay.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import DOMPurify from 'dompurify'; import './styles.css'; // Ensure this CSS file is imported -export const MessageDisplay = ({ htmlContent }) => { +export const MessageDisplay = ({ htmlContent , isReply}) => { const linkify = (text) => { // Regular expression to find URLs starting with https://, http://, or www. @@ -53,7 +53,7 @@ export const MessageDisplay = ({ htmlContent }) => { }; return (
{ // Delegate click handling to the parent div diff --git a/src/components/Chat/MessageItem.tsx b/src/components/Chat/MessageItem.tsx index b3ac3d9..7df771c 100644 --- a/src/components/Chat/MessageItem.tsx +++ b/src/components/Chat/MessageItem.tsx @@ -2,18 +2,30 @@ import { Message } from "@chatscope/chat-ui-kit-react"; import React, { useEffect } from "react"; import { useInView } from "react-intersection-observer"; import { MessageDisplay } from "./MessageDisplay"; -import { Avatar, Box, Typography } from "@mui/material"; +import { Avatar, Box, ButtonBase, Typography } from "@mui/material"; import { formatTimestamp } from "../../utils/time"; import { getBaseApi } from "../../background"; import { getBaseApiReact } from "../../App"; import { generateHTML } from "@tiptap/react"; -import Highlight from '@tiptap/extension-highlight' -import StarterKit from '@tiptap/starter-kit' -import Underline from '@tiptap/extension-underline' +import Highlight from "@tiptap/extension-highlight"; +import StarterKit from "@tiptap/starter-kit"; +import Underline from "@tiptap/extension-underline"; import { executeEvent } from "../../utils/events"; import { WrapperUserAction } from "../WrapperUserAction"; -export const MessageItem = ({ message, onSeen, isLast, isTemp, myAddress }) => { +import ReplyIcon from "@mui/icons-material/Reply"; +export const MessageItem = ({ + message, + onSeen, + isLast, + isTemp, + myAddress, + onReply, + isShowingAsReply, + reply, + replyIndex, + scrollToItem +}) => { const { ref, inView } = useInView({ threshold: 0.7, // Fully visible triggerOnce: true, // Only trigger once when it becomes visible @@ -34,70 +46,169 @@ export const MessageItem = ({ message, onSeen, isLast, isTemp, myAddress }) => { borderRadius: "7px", width: "95%", display: "flex", - gap: '7px', - opacity: isTemp ? 0.5 : 1 + gap: "7px", + opacity: isTemp ? 0.5 : 1, }} + id={message?.signature} > - - - {message?.senderName?.charAt(0)} - - + {isShowingAsReply ? ( + + ) : ( + + + {message?.senderName?.charAt(0)} + + + )} + - - - {message?.senderName || message?.sender} - - + + + {message?.senderName || message?.sender} + + + {!isShowingAsReply && ( + { + onReply(message); + }} + > + + + )} + + {reply && ( + { + scrollToItem(replyIndex) + + + }} + > + + + Replied to {reply?.senderName || reply?.senderAddress} + {reply?.messageText && ( + + )} + {reply?.text?.type === "notification" ? ( + + ) : ( + + )} + + + )} {message?.messageText && ( - + )} {message?.text?.type === "notification" ? ( ) : ( )} - + {isTemp ? ( - Sending... - ): ( - {formatTimestamp(message.timestamp)} - ) } - + + Sending... + + ) : ( + + {formatTimestamp(message.timestamp)} + + )} @@ -115,3 +226,51 @@ export const MessageItem = ({ message, onSeen, isLast, isTemp, myAddress }) => {
); }; + + +export const ReplyPreview = ({message})=> { + + return ( + + + + Replied to {message?.senderName || message?.senderAddress} + {message?.messageText && ( + + )} + {message?.text?.type === "notification" ? ( + + ) : ( + + )} + + + ) +} \ No newline at end of file diff --git a/src/components/Chat/styles.css b/src/components/Chat/styles.css index 8766c40..7c65eb0 100644 --- a/src/components/Chat/styles.css +++ b/src/components/Chat/styles.css @@ -120,3 +120,6 @@ max-width: 100%; } +.isReply p { + font-size: 12px !important; +} diff --git a/src/components/Mobile/MobileFooter.tsx b/src/components/Mobile/MobileFooter.tsx index 778cac3..95bf553 100644 --- a/src/components/Mobile/MobileFooter.tsx +++ b/src/components/Mobile/MobileFooter.tsx @@ -31,6 +31,7 @@ const IconWrapper = ({ children, label, color }) => { fontSize: "12px", fontWeight: 500, color: color, + wordBreak: 'normal' }} > {label} From 5a8f21bfb9a0bed792bf40cfb81b958b3ab82f6c Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 21 Sep 2024 15:07:37 +0300 Subject: [PATCH 22/25] fix height --- src/App.tsx | 5 ++++- src/components/Chat/ChatDirect.tsx | 10 +++++++++- src/components/Chat/ChatGroup.tsx | 11 +++++++++-- src/components/Chat/GroupAnnouncements.tsx | 6 +++--- src/components/Chat/GroupForum.tsx | 6 ++++-- src/components/Group/Group.tsx | 8 ++++---- 6 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index b43d357..f7f17b7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -129,7 +129,7 @@ const defaultValues: MyContextInterface = { message: "", }, }; -export let isMobile = true +export let isMobile = false const isMobileDevice = () => { const userAgent = navigator.userAgent || navigator.vendor || window.opera; @@ -313,6 +313,7 @@ function App() { const [apiKey, setApiKey] = useState(""); const [isOpenSendQort, setIsOpenSendQort] = useState(false) const [isOpenSendQortSuccess, setIsOpenSendQortSuccess] = useState(false) + const [rootHeight, setRootHeight] = useState('100%') useEffect(() => { if(!isMobile) return // Function to set the height of the app to the viewport height @@ -320,6 +321,7 @@ function App() { const height = window.visualViewport ? window.visualViewport.height : window.innerHeight; // Set the height to the root element (usually #root) document.getElementById('root').style.height = height + "px"; + setRootHeight(height + "px") }; // Set the initial height @@ -1619,6 +1621,7 @@ function App() { onOk, show, message, + rootHeight }} > { editorRef.current = editorInstance; }; + const [, forceUpdate] = useReducer((x) => x + 1, 0); + + const triggerRerender = () => { + forceUpdate(); // Trigger re-render by updating the state + }; const publicKeyOfRecipientRef = useRef(null) console.log({messages}) const getPublicKeyFunc = async (address)=> { @@ -276,6 +281,9 @@ const clearEditorContent = () => { editorRef.current?.chain().blur().run(); setIsFocusedParent(false) executeEvent("sent-new-message-group", {}) + setTimeout(() => { + triggerRerender(); + }, 300); }, 200); } } diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 2ffad67..8dbf927 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react' import { CreateCommonSecret } from './CreateCommonSecret' import { reusableGet } from '../../qdn/publish/pubish' import { uint8ArrayToObject } from '../../backgroundFunctions/encryption' @@ -44,7 +44,11 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, const groupSocketTimeoutRef = useRef(null); // Group Socket Timeout reference const editorRef = useRef(null); const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); - + const [, forceUpdate] = useReducer((x) => x + 1, 0); + + const triggerRerender = () => { + forceUpdate(); // Trigger re-render by updating the state + }; const setEditorRef = (editorInstance) => { editorRef.current = editorInstance; }; @@ -292,6 +296,9 @@ const clearEditorContent = () => { editorRef.current?.chain().blur().run(); setIsFocusedParent(false) executeEvent("sent-new-message-group", {}) + setTimeout(() => { + triggerRerender(); + }, 300); }, 200); } } diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 9721adf..ff4ce82 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -143,7 +143,7 @@ export const GroupAnnouncements = ({ const [selectedAnnouncement, setSelectedAnnouncement] = useState(null); const [isFocusedParent, setIsFocusedParent] = useState(false); - const { show } = React.useContext(MyContext); + const { show, rootHeight } = React.useContext(MyContext); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); const hasInitialized = useRef(false); @@ -488,7 +488,7 @@ export const GroupAnnouncements = ({
{ + const { rootHeight } = useContext(MyContext); const [isMoved, setIsMoved] = useState(false); useEffect(() => { if (hide) { @@ -38,7 +40,7 @@ export const GroupForum = ({
(null); const [timestampEnterData, setTimestampEnterData] = useState({}); const [chatMode, setChatMode] = useState("groups"); @@ -1552,7 +1552,7 @@ export const Group = ({ width: isMobile ? "100%" : "380px", flexDirection: "column", alignItems: "flex-start", - height: isMobile ? `calc(${getRootHeight()} - 45px)` : "100%", + height: isMobile ? `calc(${rootHeight} - 45px)` : "100%", background: !isMobile && 'var(--bg-primary)', borderRadius: !isMobile && '0px 15px 15px 0px' }} @@ -2266,7 +2266,7 @@ export const Group = ({ top: "0px", background: "#27282c", zIndex: 5, - height: isMobile && `calc(${getRootHeight()} - 45px)`, + height: isMobile && `calc(${rootHeight} - 45px)`, }} > Date: Sat, 21 Sep 2024 21:29:29 +0300 Subject: [PATCH 23/25] added desktop styles --- src/assets/Icons/ChatIcon.tsx | 4 +- src/assets/Icons/MembersIcon.tsx | 4 +- src/assets/Icons/NotificationIcon2.tsx | 4 +- src/assets/Icons/ThreadsIcon.tsx | 10 +- src/assets/svgs/Forum.svg | 3 + src/components/Chat/ChatGroup.tsx | 2 +- src/components/Chat/GroupAnnouncements.tsx | 4 +- src/components/Chat/GroupForum.tsx | 2 +- src/components/Desktop/DesktopHeader.tsx | 280 +++++++++++++++++++++ src/components/Group/Group.tsx | 70 +++++- src/components/Group/GroupJoinRequests.tsx | 4 +- src/components/Group/HomeDesktop.tsx | 2 +- 12 files changed, 362 insertions(+), 27 deletions(-) create mode 100644 src/assets/svgs/Forum.svg create mode 100644 src/components/Desktop/DesktopHeader.tsx diff --git a/src/assets/Icons/ChatIcon.tsx b/src/assets/Icons/ChatIcon.tsx index fdc5945..f68ed35 100644 --- a/src/assets/Icons/ChatIcon.tsx +++ b/src/assets/Icons/ChatIcon.tsx @@ -1,8 +1,8 @@ import React from 'react'; -export const ChatIcon= ({ color = 'white', height, width }) => { +export const ChatIcon= ({ color = 'white', height = 15, width = 15 }) => { return ( - + diff --git a/src/assets/Icons/MembersIcon.tsx b/src/assets/Icons/MembersIcon.tsx index a37680e..8bbc0e1 100644 --- a/src/assets/Icons/MembersIcon.tsx +++ b/src/assets/Icons/MembersIcon.tsx @@ -1,8 +1,8 @@ import React from 'react'; -export const MembersIcon= ({ color = 'white', height, width }) => { +export const MembersIcon= ({ color = 'white', height = 9, width = 15 }) => { return ( - + diff --git a/src/assets/Icons/NotificationIcon2.tsx b/src/assets/Icons/NotificationIcon2.tsx index e670888..10d7a03 100644 --- a/src/assets/Icons/NotificationIcon2.tsx +++ b/src/assets/Icons/NotificationIcon2.tsx @@ -1,8 +1,8 @@ import React from 'react'; -export const NotificationIcon2= ({ color = 'white', height, width }) => { +export const NotificationIcon2= ({ color = 'white', height = 15, width = 15 }) => { return ( - + diff --git a/src/assets/Icons/ThreadsIcon.tsx b/src/assets/Icons/ThreadsIcon.tsx index 7a42763..86dfeaa 100644 --- a/src/assets/Icons/ThreadsIcon.tsx +++ b/src/assets/Icons/ThreadsIcon.tsx @@ -1,11 +1,11 @@ import React from 'react'; -export const ThreadsIcon= ({ color = 'white', height, width }) => { +export const ThreadsIcon= ({ color = 'white', height = 11, width = 15 }) => { return ( - - - - + + + + ); diff --git a/src/assets/svgs/Forum.svg b/src/assets/svgs/Forum.svg new file mode 100644 index 0000000..7957b06 --- /dev/null +++ b/src/assets/svgs/Forum.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 8dbf927..44446cf 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -388,7 +388,7 @@ const clearEditorContent = () => { return (
{ + return ( + + {children} + + {label} + + + ); +}; + +export const DesktopHeader = ({ + selectedGroup, + groupSection, + isUnread, + goToAnnouncements, + isUnreadChat, + goToChat, + goToThreads, + setOpenManageMembers, + groupChatHasUnread, + groupsAnnHasUnread, + directChatHasUnread, + chatMode, + openDrawerGroups, + goToHome, + setIsOpenDrawerProfile, + mobileViewMode, + setMobileViewMode, + setMobileViewModeKeepOpen, + hasUnreadGroups, + hasUnreadDirects, + isHome, + isGroups, + isDirects, + setDesktopSideView, + hasUnreadAnnouncements, + isAnnouncement, + hasUnreadChat, + isChat, + isForum, + setGroupSection +}) => { + const [value, setValue] = React.useState(0); + return ( + + + + {selectedGroup?.groupName} + + + + { + goToHome(); + }} + > + + + + + { + setDesktopSideView("groups"); + }} + > + + + + + { + setDesktopSideView("directs"); + }} + > + + + + + + { + goToAnnouncements() + }} + > + + + + + + { + goToChat() + }} + > + + + + + + { + setGroupSection("forum"); + + }} + > + + + + + { + setOpenManageMembers(true) + + }} + > + + + + + + + ); +}; diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 365e094..e407acc 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -87,6 +87,7 @@ import { ReturnIcon } from "../../assets/Icons/ReturnIcon"; import { ExitIcon } from "../../assets/Icons/ExitIcon"; import { HomeDesktop } from "./HomeDesktop"; import { DesktopFooter } from "../Desktop/DesktopFooter"; +import { DesktopHeader } from "../Desktop/DesktopHeader"; // let touchStartY = 0; // let disablePullToRefresh = false; @@ -807,13 +808,20 @@ export const Group = ({ } }; + useEffect(()=> { + if(!selectedGroup) return + getGroupOwner(selectedGroup?.groupId); + }, [selectedGroup]) + + console.log('groupOwner', groupOwner) + useEffect(() => { - if (selectedGroup) { + if (selectedGroup && groupOwner && groupOwner?.isOpen === false) { setTriedToFetchSecretKey(false); getSecretKey(true); - getGroupOwner(selectedGroup?.groupId); + // getGroupOwner(selectedGroup?.groupId); } - }, [selectedGroup]); + }, [selectedGroup, groupOwner]); // const handleNotification = async (data)=> { // try { @@ -1320,6 +1328,7 @@ export const Group = ({ setNewChat(false); setSecretKey(null); + setGroupOwner(null) lastFetchedSecretKey.current = null; setSecretKeyPublishDate(null); setAdmins([]); @@ -1367,6 +1376,7 @@ export const Group = ({ setChatMode("groups"); setSelectedGroup(null); setSecretKey(null); + setGroupOwner(null) lastFetchedSecretKey.current = null; setSecretKeyPublishDate(null); setAdmins([]); @@ -1425,6 +1435,7 @@ export const Group = ({ setChatMode("groups"); setSelectedGroup(null); setSecretKey(null); + setGroupOwner(null) lastFetchedSecretKey.current = null; setSecretKeyPublishDate(null); setAdmins([]); @@ -1475,6 +1486,7 @@ export const Group = ({ setNewChat(false); setSelectedDirect(null); setSecretKey(null); + setGroupOwner(null) lastFetchedSecretKey.current = null; setSecretKeyPublishDate(null); setAdmins([]); @@ -1970,6 +1982,7 @@ export const Group = ({ setAdmins([]); setSecretKeyDetails(null); setAdminsWithNames([]); + setGroupOwner(null) setMembers([]); setMemberCountFromSecretKeyData(null); setHideCommonKeyPopup(false); @@ -2153,6 +2166,7 @@ export const Group = ({ info={infoSnack} setInfo={setInfoSnack} /> + {isMobile && (
+ {!isMobile && selectedGroup && ( + + + + )} {isMobile && ( {triedToFetchSecretKey && ( @@ -2572,7 +2624,7 @@ export const Group = ({ )} - {!isMobile && ( + {!isMobile && groupSection === "home" && ( - + {/* - )} + )} */} {/* )} - {!loading && groupsWithJoinRequests.length === 0 && ( + {!loading && (groupsWithJoinRequests.length === 0 || groupsWithJoinRequests?.filter((group)=> group?.data?.length > 0).length === 0) && ( {`, ${userInfo?.name}`} ) : null} - + {!isLoadingGroups && ( Date: Sat, 21 Sep 2024 21:30:54 +0300 Subject: [PATCH 24/25] remove logs --- src/components/Chat/ChatDirect.tsx | 2 -- src/components/Chat/ChatList.tsx | 1 - src/components/Group/Group.tsx | 1 - src/components/Group/GroupJoinRequests.tsx | 1 - 4 files changed, 5 deletions(-) diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 1197770..280a42d 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -52,7 +52,6 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi forceUpdate(); // Trigger re-render by updating the state }; const publicKeyOfRecipientRef = useRef(null) - console.log({messages}) const getPublicKeyFunc = async (address)=> { try { const publicKey = await getPublicKey(address) @@ -225,7 +224,6 @@ export const ChatDirect = ({ myAddress, isNewChat, selectedDirect, setSelectedDi const sendChatDirect = async ({ chatReference = undefined, messageText, otherData}: any, address, publicKeyOfRecipient, isNewChatVar)=> { try { - console.log('chatReferencedirect', chatReference) const directTo = isNewChatVar ? directToValue : address if(!directTo) return diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 4a4e4f4..3ebb362 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -18,7 +18,6 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR fixedWidth: true, defaultHeight: 50, }), [chatId]); // Recreate cache when chatId changes - console.log('messages2', messages) useEffect(() => { cache.clearAll(); }, []); diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index e407acc..f07515c 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -813,7 +813,6 @@ export const Group = ({ getGroupOwner(selectedGroup?.groupId); }, [selectedGroup]) - console.log('groupOwner', groupOwner) useEffect(() => { if (selectedGroup && groupOwner && groupOwner?.isOpen === false) { diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index ae6ced1..1f9d2ca 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -92,7 +92,6 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get } }, [myAddress, groups]); - console.log('groupsWithJoinRequests', groupsWithJoinRequests) return ( Date: Sat, 21 Sep 2024 22:21:45 +0300 Subject: [PATCH 25/25] fixes --- src/App.tsx | 2 +- src/components/Group/Group.tsx | 1 + src/components/Mobile/MobileFooter.tsx | 2 +- src/components/Mobile/MobileHeader.tsx | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index f7f17b7..7208d66 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1351,7 +1351,7 @@ function App() { textDecoration: "underline", }} onClick={() => { - chrome.tabs.create({ url: "https://www.qort.trade", active: true }); + chrome.tabs.create({ url: "https://www.qort.trade" }); }} > Get QORT at qort.trade diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index f07515c..b7e0625 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -2181,6 +2181,7 @@ export const Group = ({ groupsAnnHasUnread} hasUnreadDirects={directChatHasUnread} setMobileViewMode={setMobileViewMode} + myName={userInfo?.name} /> )} diff --git a/src/components/Mobile/MobileFooter.tsx b/src/components/Mobile/MobileFooter.tsx index 95bf553..ca31331 100644 --- a/src/components/Mobile/MobileFooter.tsx +++ b/src/components/Mobile/MobileFooter.tsx @@ -171,7 +171,7 @@ export const MobileFooter = ({ /> { - chrome.tabs.create({ url: "https://www.qort.trade", active: true }); + chrome.tabs.create({ url: "https://www.qort.trade"}); }} icon={ diff --git a/src/components/Mobile/MobileHeader.tsx b/src/components/Mobile/MobileHeader.tsx index 914e8eb..f49291e 100644 --- a/src/components/Mobile/MobileHeader.tsx +++ b/src/components/Mobile/MobileHeader.tsx @@ -29,7 +29,7 @@ const Header = ({ hasUnreadGroups, hasUnreadDirects, setMobileViewMode, - + myName // selectedGroup, // onHomeClick, // onLogoutClick, @@ -307,7 +307,7 @@ const Header = ({ fontSize: "11px", }} > - Palmas + {myName} {/* */}