diff --git a/src/assets/Icons/ExitIcon.tsx b/src/assets/Icons/ExitIcon.tsx index cfbeefa..eaa0cbb 100644 --- a/src/assets/Icons/ExitIcon.tsx +++ b/src/assets/Icons/ExitIcon.tsx @@ -1,13 +1,20 @@ -import React from 'react'; +import { useTheme } from '@mui/material'; -export const ExitIcon= ({ color = 'white', height, width }) => { - return ( - - - - +export const ExitIcon = () => { + const theme = useTheme(); - - ); - }; - \ No newline at end of file + return ( + + + + ); +}; diff --git a/src/assets/Icons/ReturnIcon.tsx b/src/assets/Icons/ReturnIcon.tsx index 633eafc..98e0fea 100644 --- a/src/assets/Icons/ReturnIcon.tsx +++ b/src/assets/Icons/ReturnIcon.tsx @@ -1,14 +1,20 @@ -import React from 'react'; +import { useTheme } from '@mui/material'; -export const ReturnIcon= ({ color = 'white', height, width }) => { - return ( - - - - - +export const ReturnIcon = () => { + const theme = useTheme(); - - ); - }; - \ No newline at end of file + return ( + + + + ); +}; diff --git a/src/background.ts b/src/background.ts index 5bd922b..dad784a 100644 --- a/src/background.ts +++ b/src/background.ts @@ -1,37 +1,37 @@ // @ts-nocheck -import "./qortalRequests"; -import { isArray } from "lodash"; +import './qortalRequests'; +import { isArray } from 'lodash'; import { decryptGroupEncryption, encryptAndPublishSymmetricKeyGroupChat, publishGroupEncryptedResource, publishOnQDN, uint8ArrayToObject, -} from "./backgroundFunctions/encryption"; -import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from "./constants/codes"; -import Base58 from "./deps/Base58"; -import axios from 'axios' +} from './backgroundFunctions/encryption'; +import { PUBLIC_NOTIFICATION_CODE_FIRST_SECRET_KEY } from './constants/codes'; +import Base58 from './deps/Base58'; +import axios from 'axios'; import { base64ToUint8Array, decryptSingle, encryptDataGroup, encryptSingle, objectToBase64, -} from "./qdn/encryption/group-encryption"; +} from './qdn/encryption/group-encryption'; import ChatComputePowWorker from './chatComputePow.worker.js?worker'; -import { reusableGet } from "./qdn/publish/pubish"; -import { signChat } from "./transactions/signChat"; -import { createTransaction } from "./transactions/transactions"; -import { decryptChatMessage } from "./utils/decryptChatMessage"; -import { decryptStoredWallet } from "./utils/decryptWallet"; -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"; +import { reusableGet } from './qdn/publish/pubish'; +import { signChat } from './transactions/signChat'; +import { createTransaction } from './transactions/transactions'; +import { decryptChatMessage } from './utils/decryptChatMessage'; +import { decryptStoredWallet } from './utils/decryptWallet'; +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'; -import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from "./constants/resourceTypes"; +import { RESOURCE_TYPE_NUMBER_GROUP_CHAT_REACTIONS } from './constants/resourceTypes'; import { addDataPublishesCase, addEnteredQmailTimestampCase, @@ -101,34 +101,33 @@ import { validApiCase, versionCase, voteOnPollCase, -} from "./background-cases"; -import { getData, removeKeysAndLogout, storeData } from "./utils/chromeStorage"; -import TradeBotRespondRequest from "./transactions/TradeBotRespondRequest"; +} from './background-cases'; +import { getData, removeKeysAndLogout, storeData } from './utils/chromeStorage'; +import TradeBotRespondRequest from './transactions/TradeBotRespondRequest'; // import {BackgroundFetch} from '@transistorsoft/capacitor-background-fetch'; -export let groupSecretkeys = {} +export let groupSecretkeys = {}; export function cleanUrl(url) { - return url?.replace(/^(https?:\/\/)?(www\.)?/, ""); + return url?.replace(/^(https?:\/\/)?(www\.)?/, ''); } export function getProtocol(url) { - if (url?.startsWith("https://")) { - return "https"; - } else if (url?.startsWith("http://")) { - return "http"; + if (url?.startsWith('https://')) { + return 'https'; + } else if (url?.startsWith('http://')) { + return 'http'; } else { - return "unknown"; // If neither protocol is present + return 'unknown'; // If neither protocol is present } } -export const gateways = ['ext-node.qortal.link'] - +export const gateways = ['ext-node.qortal.link']; let lastGroupNotification; -export const groupApi = "https://ext-node.qortal.link"; -export const groupApiSocket = "wss://ext-node.qortal.link"; -export const groupApiLocal = "http://127.0.0.1:12391"; -export const groupApiSocketLocal = "ws://127.0.0.1:12391"; +export const groupApi = 'https://ext-node.qortal.link'; +export const groupApiSocket = 'wss://ext-node.qortal.link'; +export const groupApiLocal = 'http://127.0.0.1:12391'; +export const groupApiSocketLocal = 'ws://127.0.0.1:12391'; const timeDifferenceForNotificationChatsBackground = 86400000; const requestQueueAnnouncements = new RequestQueueWithPromise(1); let isMobile = true; @@ -138,10 +137,12 @@ function handleNotificationClick(notificationId) { const decodedNotificationId = decodeURIComponent(notificationId); // Determine the type of notification by parsing decodedNotificationId - const isDirect = decodedNotificationId.includes("_type=direct_"); - const isGroup = decodedNotificationId.includes("_type=group_"); - const isGroupAnnouncement = decodedNotificationId.includes("_type=group-announcement_"); - const isNewThreadPost = decodedNotificationId.includes("_type=thread-post_"); + const isDirect = decodedNotificationId.includes('_type=direct_'); + const isGroup = decodedNotificationId.includes('_type=group_'); + const isGroupAnnouncement = decodedNotificationId.includes( + '_type=group-announcement_' + ); + const isNewThreadPost = decodedNotificationId.includes('_type=thread-post_'); // Helper function to extract parameter values safely function getParameterValue(id, key) { @@ -151,45 +152,47 @@ function handleNotificationClick(notificationId) { const targetOrigin = window.location.origin; // Handle specific notification types and post the message accordingly if (isDirect) { - const fromValue = getParameterValue(decodedNotificationId, "_from"); + const fromValue = getParameterValue(decodedNotificationId, '_from'); window.postMessage( - { action: "NOTIFICATION_OPEN_DIRECT", payload: { from: fromValue } }, + { action: 'NOTIFICATION_OPEN_DIRECT', payload: { from: fromValue } }, targetOrigin ); } else if (isGroup) { - const fromValue = getParameterValue(decodedNotificationId, "_from"); + const fromValue = getParameterValue(decodedNotificationId, '_from'); window.postMessage( - { action: "NOTIFICATION_OPEN_GROUP", payload: { from: fromValue } }, + { action: 'NOTIFICATION_OPEN_GROUP', payload: { from: fromValue } }, targetOrigin ); } else if (isGroupAnnouncement) { - const fromValue = getParameterValue(decodedNotificationId, "_from"); + const fromValue = getParameterValue(decodedNotificationId, '_from'); window.postMessage( { - action: "NOTIFICATION_OPEN_ANNOUNCEMENT_GROUP", + action: 'NOTIFICATION_OPEN_ANNOUNCEMENT_GROUP', payload: { from: fromValue }, }, targetOrigin ); } else if (isNewThreadPost) { - const dataValue = getParameterValue(decodedNotificationId, "_data"); + const dataValue = getParameterValue(decodedNotificationId, '_data'); try { const targetOrigin = window.location.origin; const dataParsed = JSON.parse(dataValue); window.postMessage( { - action: "NOTIFICATION_OPEN_THREAD_NEW_POST", + action: 'NOTIFICATION_OPEN_THREAD_NEW_POST', payload: { data: dataParsed }, }, targetOrigin ); } catch (error) { - console.error("Error parsing JSON data for thread post notification:", error); + console.error( + 'Error parsing JSON data for thread post notification:', + error + ); } } } - const isMobileDevice = () => { const userAgent = navigator.userAgent || navigator.vendor || window.opera; @@ -206,9 +209,9 @@ const isMobileDevice = () => { if (isMobileDevice()) { isMobile = true; - console.log("Running on a mobile device"); + console.log('Running on a mobile device'); } else { - console.log("Running on a desktop"); + console.log('Running on a desktop'); } const allQueues = { requestQueueAnnouncements: requestQueueAnnouncements, @@ -218,7 +221,7 @@ const controlAllQueues = (action) => { Object.keys(allQueues).forEach((key) => { const val = allQueues[key]; try { - if (typeof val[action] === "function") { + if (typeof val[action] === 'function') { val[action](); } } catch (error) { @@ -238,42 +241,43 @@ export const clearAllQueues = () => { }); }; -export const getForeignKey = async (foreignBlockchain)=> { +export const getForeignKey = async (foreignBlockchain) => { const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; switch (foreignBlockchain) { - case "LITECOIN": - return parsedData.ltcPrivateKey - case "DOGECOIN": - return parsedData.dogePrivateKey - case "BITCOIN": - return parsedData.btcPrivateKey - case "DIGIBYTE": - return parsedData.dgbPrivateKey - case "RAVENCOIN": - return parsedData.rvnPrivateKey - case "PIRATECHAIN": - return parsedData.arrrSeed58 - default: - return null + case 'LITECOIN': + return parsedData.ltcPrivateKey; + case 'DOGECOIN': + return parsedData.dogePrivateKey; + case 'BITCOIN': + return parsedData.btcPrivateKey; + case 'DIGIBYTE': + return parsedData.dgbPrivateKey; + case 'RAVENCOIN': + return parsedData.rvnPrivateKey; + case 'PIRATECHAIN': + return parsedData.arrrSeed58; + default: + return null; } -} +}; -export const pauseAllQueues = () => controlAllQueues("pause"); -export const resumeAllQueues = () => controlAllQueues("resume"); -export const checkDifference = (createdTimestamp, diff = timeDifferenceForNotificationChatsBackground) => { - return ( - Date.now() - createdTimestamp < diff - ); +export const pauseAllQueues = () => controlAllQueues('pause'); +export const resumeAllQueues = () => controlAllQueues('resume'); +export const checkDifference = ( + createdTimestamp, + diff = timeDifferenceForNotificationChatsBackground +) => { + return Date.now() - createdTimestamp < diff; }; export const getApiKeyFromStorage = async (): Promise => { - return getData("apiKey").catch(() => null); + return getData('apiKey').catch(() => null); }; export const getCustomNodesFromStorage = async (): Promise => { - return getData("customNodes").catch(() => null); + return getData('customNodes').catch(() => null); }; const getArbitraryEndpoint = async () => { @@ -291,7 +295,7 @@ export const getBaseApi = async (customApi?: string) => { } const apiKey = await getApiKeyFromStorage(); // Retrieve apiKey asynchronously - + if (apiKey?.url) { return apiKey?.url; } else { @@ -316,7 +320,7 @@ export const createEndpoint = async (endpoint, customApi?: string) => { if (apiKey?.url) { // Check if the endpoint already contains a query string - const separator = endpoint.includes("?") ? "&" : "?"; + const separator = endpoint.includes('?') ? '&' : '?'; return `${apiKey?.url}${endpoint}${separator}apiKey=${apiKey?.apikey}`; } else { return `${groupApi}${endpoint}`; @@ -326,19 +330,19 @@ export const createEndpoint = async (endpoint, customApi?: string) => { export const walletVersion = 2; // List of your API endpoints const apiEndpoints = [ - "https://api.qortal.org", - "https://api2.qortal.org", - "https://appnode.qortal.org", - "https://apinode.qortalnodes.live", - "https://apinode1.qortalnodes.live", - "https://apinode2.qortalnodes.live", - "https://apinode3.qortalnodes.live", - "https://apinode4.qortalnodes.live", + 'https://api.qortal.org', + 'https://api2.qortal.org', + 'https://appnode.qortal.org', + 'https://apinode.qortalnodes.live', + 'https://apinode1.qortalnodes.live', + 'https://apinode2.qortalnodes.live', + 'https://apinode3.qortalnodes.live', + 'https://apinode4.qortalnodes.live', ]; -const buyTradeNodeBaseUrl = "https://appnode.qortal.org"; -const proxyAccountAddress = "QXPejUe5Za1KD3zCMViWCX35AreMQ9H7ku"; -const proxyAccountPublicKey = "5hP6stDWybojoDw5t8z9D51nV945oMPX7qBd29rhX1G7"; +const buyTradeNodeBaseUrl = 'https://appnode.qortal.org'; +const proxyAccountAddress = 'QXPejUe5Za1KD3zCMViWCX35AreMQ9H7ku'; +const proxyAccountPublicKey = '5hP6stDWybojoDw5t8z9D51nV945oMPX7qBd29rhX1G7'; const pendingResponses = new Map(); let groups = null; @@ -353,7 +357,7 @@ export async function findUsableApi() { for (const endpoint of apiEndpoints) { try { const response = await fetch(`${endpoint}/admin/status`); - if (!response.ok) throw new Error("Failed to fetch"); + if (!response.ok) throw new Error('Failed to fetch'); const data = await response.json(); if (data.isSynchronizing === false && data.syncPercent === 100) { @@ -367,7 +371,7 @@ export async function findUsableApi() { } } - throw new Error("No usable API found"); + throw new Error('No usable API found'); } export function isExtMsg(data) { @@ -417,21 +421,21 @@ async function checkWebviewFocus() { }, 1000); const targetOrigin = window.location.origin; // Send a message to check focus - window.postMessage({ action: "CHECK_FOCUS" }, targetOrigin); + window.postMessage({ action: 'CHECK_FOCUS' }, targetOrigin); // Listen for the response const handleMessage = (event) => { - if (event.data?.action === "CHECK_FOCUS_RESPONSE") { + if (event.data?.action === 'CHECK_FOCUS_RESPONSE') { clearTimeout(timeout); - window.removeEventListener("message", handleMessage); // Clean up listener + window.removeEventListener('message', handleMessage); // Clean up listener resolve(event.data.isFocused); // Resolve with the response } }; - window.addEventListener("message", handleMessage); + window.addEventListener('message', handleMessage); }); } -const worker = new ChatComputePowWorker() +const worker = new ChatComputePowWorker(); export async function performPowTask(chatBytes, difficulty) { return new Promise((resolve, reject) => { @@ -465,7 +469,7 @@ const handleNotificationDirect = async (directs) => { const wallet = await getSaveWallet(); const address = wallet.address0; let isDisableNotifications = - (await getUserSettings({ key: "disable-push-notifications" })) || false; + (await getUserSettings({ key: 'disable-push-notifications' })) || false; const dataDirects = directs.filter((direct) => direct?.sender !== address); try { if (isDisableNotifications) return; @@ -473,7 +477,7 @@ const handleNotificationDirect = async (directs) => { isFocused = await checkWebviewFocus(); if (isFocused) { - throw new Error("isFocused"); + throw new Error('isFocused'); } const newActiveChats = dataDirects; const oldActiveChats = await getChatHeadsDirect(); @@ -510,17 +514,18 @@ const handleNotificationDirect = async (directs) => { ) { // Create the notification and assign the onclick handler const title = `New Direct message! ${ - newestLatestTimestamp?.name ? `from ${newestLatestTimestamp.name}` : "" + newestLatestTimestamp?.name ? `from ${newestLatestTimestamp.name}` : '' }`; - const body = "You have received a new direct message"; - const notificationId = - encodeURIComponent("chat_notification_" + - Date.now() + - "_type=direct" + - `_from=${newestLatestTimestamp.address}`); + const body = 'You have received a new direct message'; + const notificationId = encodeURIComponent( + 'chat_notification_' + + Date.now() + + '_type=direct' + + `_from=${newestLatestTimestamp.address}` + ); const notification = new window.Notification(title, { body, - icon: window.location.origin + "/qortal192.png", + icon: window.location.origin + '/qortal192.png', data: { id: notificationId }, }); @@ -537,7 +542,7 @@ const handleNotificationDirect = async (directs) => { } catch (error) { if (!isFocused) { window - .sendMessage("notification", {}) + .sendMessage('notification', {}) .then((response) => { if (!response?.error) { // Handle success if needed @@ -545,24 +550,22 @@ const handleNotificationDirect = async (directs) => { }) .catch((error) => { console.error( - "Failed to send notification:", - error.message || "An error occurred" + 'Failed to send notification:', + error.message || 'An error occurred' ); }); // Create a unique notification ID with type and sender information - const notificationId = - encodeURIComponent("chat_notification_" + - Date.now() + - "_type=direct" + - `_from=""`); + const notificationId = encodeURIComponent( + 'chat_notification_' + Date.now() + '_type=direct' + `_from=""` + ); - const title = "New Direct message!"; - const body = "You have received a new direct message"; + const title = 'New Direct message!'; + const body = 'You have received a new direct message'; const notification = new window.Notification(title, { body, - icon: window.location.origin + "/qortal192.png", + icon: window.location.origin + '/qortal192.png', data: { id: notificationId }, }); @@ -674,16 +677,14 @@ const handleNotification = async (groups) => { const wallet = await getSaveWallet(); const address = wallet.address0; let isDisableNotifications = - (await getUserSettings({ key: "disable-push-notifications" })) || false; + (await getUserSettings({ key: 'disable-push-notifications' })) || false; - let mutedGroups = (await getUserSettings({ key: "mutedGroups" })) || []; + let mutedGroups = (await getUserSettings({ key: 'mutedGroups' })) || []; if (!isArray(mutedGroups)) mutedGroups = []; - mutedGroups.push('0') + mutedGroups.push('0'); let isFocused; const data = groups.filter( - (group) => - group?.sender !== address && - !mutedGroups.includes(group.groupId) + (group) => group?.sender !== address && !mutedGroups.includes(group.groupId) ); const dataWithUpdates = groups.filter( (group) => group?.sender !== address && !mutedGroups.includes(group.groupId) @@ -695,7 +696,7 @@ const handleNotification = async (groups) => { isFocused = await checkWebviewFocus(); if (isFocused) { - throw new Error("isFocused"); + throw new Error('isFocused'); } const newActiveChats = data; const oldActiveChats = await getChatHeads(); @@ -733,24 +734,22 @@ const handleNotification = async (groups) => { !lastGroupNotification || Date.now() - lastGroupNotification >= 120000 ) { - if ( - !newestLatestTimestamp?.data - ) - return; + if (!newestLatestTimestamp?.data) return; // Create a unique notification ID with type and group information - const notificationId = - encodeURIComponent("chat_notification_" + - Date.now() + - "_type=group" + - `_from=${newestLatestTimestamp.groupId}`); + const notificationId = encodeURIComponent( + 'chat_notification_' + + Date.now() + + '_type=group' + + `_from=${newestLatestTimestamp.groupId}` + ); - const title = "New Group Message!"; + const title = 'New Group Message!'; const body = `You have received a new message from ${newestLatestTimestamp?.groupName}`; const notification = new window.Notification(title, { body, - icon: window.location.origin + "/qortal192.png", + icon: window.location.origin + '/qortal192.png', data: { id: notificationId }, }); @@ -771,7 +770,7 @@ const handleNotification = async (groups) => { } catch (error) { if (!isFocused) { window - .sendMessage("notification", {}) + .sendMessage('notification', {}) .then((response) => { if (!response?.error) { // Handle success if needed @@ -779,21 +778,23 @@ const handleNotification = async (groups) => { }) .catch((error) => { console.error( - "Failed to send notification:", - error.message || "An error occurred" + 'Failed to send notification:', + error.message || 'An error occurred' ); }); // Generate a unique notification ID - const notificationId = encodeURIComponent("chat_notification_" + Date.now()); + const notificationId = encodeURIComponent( + 'chat_notification_' + Date.now() + ); - const title = "New Group Message!"; - const body = "You have received a new message from one of your groups"; + const title = 'New Group Message!'; + const body = 'You have received a new message from one of your groups'; // Create and show the notification immediately const notification = new window.Notification(title, { body, - icon: window.location.origin + "/qortal192.png", + icon: window.location.origin + '/qortal192.png', data: { id: notificationId }, }); @@ -823,7 +824,7 @@ const forceCloseWebSocket = () => { clearTimeout(socketTimeout); timeoutId = null; groupSocketTimeout = null; - socket.close(1000, "forced"); + socket.close(1000, 'forced'); socket = null; } }; @@ -832,32 +833,32 @@ export async function getNameInfo() { const wallet = await getSaveWallet(); const address = wallet.address0; const validApi = await getBaseApi(); - const response = await fetch(validApi + "/names/address/" + address); + const response = await fetch(validApi + '/names/address/' + address); const nameData = await response.json(); if (nameData?.length > 0) { return nameData[0].name; } else { - return ""; + return ''; } } export async function getNameInfoForOthers(address) { const validApi = await getBaseApi(); - const response = await fetch(validApi + "/names/address/" + address); + const response = await fetch(validApi + '/names/address/' + address); const nameData = await response.json(); if (nameData?.length > 0) { return nameData[0].name; } else { - return ""; + return ''; } } export async function getAddressInfo(address) { const validApi = await getBaseApi(); - const response = await fetch(validApi + "/addresses/" + address); + const response = await fetch(validApi + '/addresses/' + address); const data = await response.json(); if (!response?.ok && data?.error !== 124) - throw new Error("Cannot fetch address info"); + throw new Error('Cannot fetch address info'); if (data?.error === 124) { return { address, @@ -867,40 +868,38 @@ export async function getAddressInfo(address) { } export async function getKeyPair() { - const res = await getData("keyPair").catch(() => null); + const res = await getData('keyPair').catch(() => null); if (res) { return res; } else { - throw new Error("Wallet not authenticated"); + throw new Error('Wallet not authenticated'); } } export async function getSaveWallet() { - const res = await getData("walletInfo").catch(() => null); + const res = await getData('walletInfo').catch(() => null); if (res) { return res; } else { - throw new Error("No wallet saved"); + throw new Error('No wallet saved'); } } export async function getWallets() { - const res = await getData("wallets").catch(() => null); + const res = await getData('wallets').catch(() => null); if (res) { return res; } else { - return null + return null; } } export async function storeWallets(wallets) { - storeData("wallets", wallets) - .catch((error) => { - console.error(error) - }); + storeData('wallets', wallets).catch((error) => { + console.error(error); + }); } - export async function clearAllNotifications() { // const notifications = await chrome.notifications.getAll(); // for (const notificationId of Object.keys(notifications)) { @@ -927,9 +926,9 @@ async function connection(hostname: string) { async function getTradeInfo(qortalAtAddress) { const response = await fetch( - buyTradeNodeBaseUrl + "/crosschain/trade/" + qortalAtAddress + buyTradeNodeBaseUrl + '/crosschain/trade/' + qortalAtAddress ); - if (!response?.ok) throw new Error("Cannot crosschain trade information"); + if (!response?.ok) throw new Error('Cannot crosschain trade information'); const data = await response.json(); return data; } @@ -946,9 +945,9 @@ export async function getBalanceInfo() { const address = wallet.address0; const validApi = await getBaseApi(); - const response = await fetch(validApi + "/addresses/balance/" + address); + const response = await fetch(validApi + '/addresses/balance/' + address); - if (!response?.ok) throw new Error("0 QORT in your balance"); + if (!response?.ok) throw new Error('0 QORT in your balance'); const data = await response.json(); return data; } @@ -959,9 +958,9 @@ export async function getLTCBalance() { const parsedKeyPair = keyPair; let _body = parsedKeyPair.ltcPublicKey; const response = await fetch(_url, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, body: _body, }); @@ -969,10 +968,13 @@ export async function getLTCBalance() { const data = await response.text(); const dataLTCBalance = (Number(data) / 1e8).toFixed(8); return +dataLTCBalance; - } else throw new Error("Onable to get LTC balance"); + } else throw new Error('Onable to get LTC balance'); } -export async function parseErrorResponse(response, defaultMessage = "Request failed") { +export async function parseErrorResponse( + response, + defaultMessage = 'Request failed' +) { let message = defaultMessage; try { @@ -998,15 +1000,14 @@ export async function parseErrorResponse(response, defaultMessage = "Request fai return message; } - const processTransactionVersion2Chat = async (body: any, customApi) => { // const validApi = await findUsableApi(); const url = await createEndpoint( - "/transactions/process?apiVersion=2", + '/transactions/process?apiVersion=2', customApi ); return fetch(url, { - method: "POST", + method: 'POST', headers: {}, body: Base58.encode(body), }).then(async (response) => { @@ -1024,9 +1025,9 @@ export const processTransactionVersion2 = async (body: any) => { try { const response = await fetch(url, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "application/json", // Ensure the body is correctly parsed + 'Content-Type': 'application/json', // Ensure the body is correctly parsed }, body, // Convert body to JSON string }); @@ -1048,7 +1049,7 @@ export const processTransactionVersion2 = async (body: any) => { } } } catch (error) { - console.error("Error processing transaction:", error); + console.error('Error processing transaction:', error); throw error; // Re-throw the error after logging it } }; @@ -1106,20 +1107,20 @@ export const getLastRef = async () => { const address = wallet.address0; const validApi = await getBaseApi(); const response = await fetch( - validApi + "/addresses/lastreference/" + address + validApi + '/addresses/lastreference/' + address ); - if (!response?.ok) throw new Error("0 QORT in your balance"); + if (!response?.ok) throw new Error('0 QORT in your balance'); const data = await response.text(); return data; }; export const sendQortFee = async (): Promise => { const validApi = await getBaseApi(); const response = await fetch( - validApi + "/transactions/unitfee?txType=PAYMENT" + validApi + '/transactions/unitfee?txType=PAYMENT' ); if (!response.ok) { - throw new Error("Error when fetching join fee"); + throw new Error('Error when fetching join fee'); } const data = await response.json(); @@ -1135,16 +1136,16 @@ export async function getNameOrAddress(receiver) { } const validApi = await getBaseApi(); - const response = await fetch(validApi + "/names/" + receiver); + const response = await fetch(validApi + '/names/' + receiver); const data = await response.json(); if (data?.owner) return data.owner; if (data?.error) { - throw new Error("Name does not exist"); + throw new Error('Name does not exist'); } - if (!response?.ok) throw new Error("Cannot fetch name"); - return { error: "cannot validate address or name" }; + if (!response?.ok) throw new Error('Cannot fetch name'); + return { error: 'cannot validate address or name' }; } catch (error) { - throw new Error(error?.message || "cannot validate address or name"); + throw new Error(error?.message || 'cannot validate address or name'); } } @@ -1152,17 +1153,17 @@ export async function getPublicKey(receiver) { try { const validApi = await getBaseApi(); - const response = await fetch(validApi + "/addresses/publickey/" + receiver); + const response = await fetch(validApi + '/addresses/publickey/' + receiver); if (!response?.ok) throw new Error("Cannot fetch recipient's public key"); const data = await response.text(); - if (!data?.error && data !== "false") return data; + if (!data?.error && data !== 'false') return data; if (data?.error) { throw new Error("Cannot fetch recipient's public key"); } throw new Error("Cannot fetch recipient's public key"); } catch (error) { - throw new Error(error?.message || "cannot validate address or name"); + throw new Error(error?.message || 'cannot validate address or name'); } } @@ -1182,7 +1183,7 @@ export async function getDataPublishes(groupId, type) { resolve(typeData); // Resolve with the data inside the specific type }) .catch((error) => { - console.error("Error retrieving data:", error); + console.error('Error retrieving data:', error); resolve(null); // Return null in case of an error }); }); @@ -1240,16 +1241,16 @@ export async function addDataPublishes(newData, groupId, type) { storeData(`${address}-publishData`, storedData) .then(() => res(true)) // Successfully added .catch((error) => { - console.error("Error saving data:", error); + console.error('Error saving data:', error); res(false); // Save failed }); } else { - console.error("Failed to add data, still exceeds storage limit."); + console.error('Failed to add data, still exceeds storage limit.'); res(false); // Failure due to storage limit } }) .catch((error) => { - console.error("Error retrieving data:", error); + console.error('Error retrieving data:', error); res(false); // Failure due to retrieval error }); }); @@ -1293,12 +1294,12 @@ export async function addUserSettings({ keyValue }) { storeData(`${address}-userSettings`, storedData) .then(() => res(true)) // Data successfully added .catch((error) => { - console.error("Error saving data:", error); + console.error('Error saving data:', error); res(false); // Save failed }); }) .catch((error) => { - console.error("Error retrieving data:", error); + console.error('Error retrieving data:', error); res(false); // Failure due to retrieval error }); }); @@ -1338,10 +1339,10 @@ export async function decryptWallet({ password, wallet, walletVersion }) { rvnPrivateKey: wallet2._addresses[0].rvnWallet.derivedMasterPrivateKey, }; await new Promise((resolve, reject) => { - storeData("keyPair", toSave) + storeData('keyPair', toSave) .then(() => resolve(true)) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); const newWallet = { @@ -1350,10 +1351,10 @@ export async function decryptWallet({ password, wallet, walletVersion }) { ltcAddress: ltcAddress, }; await new Promise((resolve, reject) => { - storeData("walletInfo", newWallet) + storeData('walletInfo', newWallet) .then(() => resolve(true)) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); @@ -1389,7 +1390,7 @@ function sbrk(size, heap) { let brk = 512 * 1024; // stack top let old = brk; brk += size; - if (brk > heap.length) throw new Error("heap exhausted"); + if (brk > heap.length) throw new Error('heap exhausted'); return old; } @@ -1453,14 +1454,14 @@ export async function handleActiveGroupDataFromSocket({ groups, directs }) { const targetOrigin = window.location.origin; window.postMessage( { - action: "SET_GROUPS", + action: 'SET_GROUPS', payload: groups, }, targetOrigin ); window.postMessage( { - action: "SET_DIRECTS", + action: 'SET_DIRECTS', payload: directs, }, targetOrigin @@ -1473,18 +1474,28 @@ export async function handleActiveGroupDataFromSocket({ groups, directs }) { directs: directs || [], // Your directs data here }; // Save the active data to localStorage - storeData("active-groups-directs", activeData).catch((error) => { - console.error("Error saving data:", error); + storeData('active-groups-directs', activeData).catch((error) => { + console.error('Error saving data:', error); }); try { handleNotification(groups); handleNotificationDirect(directs); - } catch (error) {} - } catch (error) {} + } catch (error) { + console.log(error); + } + } catch (error) { + console.log(error); + } } -async function sendChatForBuyOrder({ qortAddress, recipientPublicKey, message, atAddresses, isSingle }) { +async function sendChatForBuyOrder({ + qortAddress, + recipientPublicKey, + message, + atAddresses, + isSingle, +}) { let _reference = new Uint8Array(64); self.crypto.getRandomValues(_reference); @@ -1510,7 +1521,7 @@ async function sendChatForBuyOrder({ qortAddress, recipientPublicKey, message, a const finalJson = { callRequest: jsonData, extra: { - type: isSingle ? "single" : "multiple" + type: isSingle ? 'single' : 'multiple', }, }; const messageStringified = JSON.stringify(finalJson); @@ -1528,50 +1539,63 @@ async function sendChatForBuyOrder({ qortAddress, recipientPublicKey, message, a }); //TODO // if (!hasEnoughBalance) { - if(!hasEnoughBalance){ + if (!hasEnoughBalance) { const _encryptedMessage = tx._encryptedMessage; const encryptedMessageToBase58 = Base58.encode(_encryptedMessage); - const signature = "id-" + Date.now() + "-" + Math.floor(Math.random() * 1000) - const checkGatewayStatusRes = await fetch(`${buyTradeNodeBaseUrl}/admin/status`) - const checkGatewayStatusData = await checkGatewayStatusRes.json() - if(+checkGatewayStatusData?.syncPercent !== 100 || checkGatewayStatusData?.isSynchronizing !== false){ - throw new Error("Cannot make trade. Gateway node is synchronizing") + const signature = + 'id-' + Date.now() + '-' + Math.floor(Math.random() * 1000); + const checkGatewayStatusRes = await fetch( + `${buyTradeNodeBaseUrl}/admin/status` + ); + const checkGatewayStatusData = await checkGatewayStatusRes.json(); + if ( + +checkGatewayStatusData?.syncPercent !== 100 || + checkGatewayStatusData?.isSynchronizing !== false + ) { + throw new Error('Cannot make trade. Gateway node is synchronizing'); } - const healthCheckRes = await fetch('https://www.qort.trade/api/transaction/healthcheck') - const healthcheckData = await healthCheckRes.json() - if(healthcheckData?.dbConnection !== 'healthy'){ - throw new Error('Could not connect to db. Try again later.') + const healthCheckRes = await fetch( + 'https://www.qort.trade/api/transaction/healthcheck' + ); + const healthcheckData = await healthCheckRes.json(); + if (healthcheckData?.dbConnection !== 'healthy') { + throw new Error('Could not connect to db. Try again later.'); } const res = await axios.post( `https://www.qort.trade/api/transaction/updatetxgateway`, { - qortalAtAddresses: atAddresses, qortAddress: address, node: buyTradeNodeBaseUrl, status: "message-sent", encryptedMessageToBase58, signature , - reference, senderPublicKey: parsedData.publicKey, + qortalAtAddresses: atAddresses, + qortAddress: address, + node: buyTradeNodeBaseUrl, + status: 'message-sent', + encryptedMessageToBase58, + signature, + reference, + senderPublicKey: parsedData.publicKey, sender: address, }, { headers: { - "Content-Type": "application/json" + 'Content-Type': 'application/json', }, } ); return { encryptedMessageToBase58, - status: "message-sent", - signature - } + status: 'message-sent', + signature, + }; } - const chatBytes = tx.chatBytes; const difficulty = 8; - const { nonce, chatBytesArray } = await performPowTask(chatBytes, difficulty); + const { nonce, chatBytesArray } = await performPowTask(chatBytes, difficulty); let _response = await signChatFunc( chatBytesArray, nonce, - "https://appnode.qortal.org", + 'https://appnode.qortal.org', keyPair ); if (_response?.error) { @@ -1580,9 +1604,6 @@ async function sendChatForBuyOrder({ qortAddress, recipientPublicKey, message, a return _response; } - - - export async function sendChatGroup({ groupId, typeMessage, @@ -1604,7 +1625,6 @@ export async function sendChatGroup({ // const balance = await getBalanceInfo(); // const hasEnoughBalance = +balance < 4 ? false : true; - const txBody = { timestamp: Date.now(), groupID: Number(groupId), @@ -1618,7 +1638,7 @@ export async function sendChatGroup({ }; if (chatReference) { - txBody["chatReference"] = chatReference; + txBody['chatReference'] = chatReference; } const tx = await createTransaction(181, keyPair, txBody); @@ -1629,9 +1649,8 @@ export async function sendChatGroup({ const chatBytes = tx.chatBytes; const difficulty = 8; - const { nonce, chatBytesArray } = await performPowTask(chatBytes, difficulty); + const { nonce, chatBytesArray } = await performPowTask(chatBytes, difficulty); - let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair); if (_response?.error) { throw new Error(_response?.message); @@ -1660,7 +1679,7 @@ export async function sendChatDirect({ recipientAddress = await getNameOrAddress(directTo); } - if (!recipientPublicKey) throw new Error("Cannot retrieve publickey"); + if (!recipientPublicKey) throw new Error('Cannot retrieve publickey'); let _reference = new Uint8Array(64); self.crypto.getRandomValues(_reference); @@ -1677,7 +1696,6 @@ export async function sendChatDirect({ // const balance = await getBalanceInfo(); // const hasEnoughBalance = +balance < 4 ? false : true; - const finalJson = { message: messageText, version: 2, @@ -1697,7 +1715,7 @@ export async function sendChatDirect({ isText: 1, }; if (chatReference) { - txBody["chatReference"] = chatReference; + txBody['chatReference'] = chatReference; } const tx = await createTransaction(18, keyPair, txBody); @@ -1707,7 +1725,7 @@ export async function sendChatDirect({ const chatBytes = tx.chatBytes; const difficulty = 8; - const { nonce, chatBytesArray } = await performPowTask(chatBytes, difficulty); + const { nonce, chatBytesArray } = await performPowTask(chatBytes, difficulty); let _response = await signChatFunc(chatBytesArray, nonce, null, keyPair); if (_response?.error) { @@ -1734,7 +1752,9 @@ export async function decryptSingleFunc({ const decryptToUnit8Array = base64ToUint8Array(res); const responseData = uint8ArrayToObject(decryptToUnit8Array); holdMessages.push({ ...message, decryptedData: responseData }); - } catch (error) {} + } catch (error) { + console.log(error); + } } return holdMessages; } @@ -1756,12 +1776,13 @@ export async function decryptSingleForPublishes({ const decryptToUnit8Array = base64ToUint8Array(res); const responseData = uint8ArrayToObject(decryptToUnit8Array); holdMessages.push({ ...message, decryptedData: responseData }); - } catch (error) {} + } catch (error) { + console.log(error); + } } return holdMessages; } - export async function decryptDirectFunc({ messages, involvingAddress }) { const senderPublicKey = await getPublicKey(involvingAddress); let holdMessages = []; @@ -1784,72 +1805,68 @@ export async function decryptDirectFunc({ messages, involvingAddress }) { ); const parsedMessage = JSON.parse(decodedMessage); holdMessages.push({ ...message, ...parsedMessage }); - } catch (error) {} + } catch (error) { + console.log(error); + } } return holdMessages; } -export async function createBuyOrderTx({ crosschainAtInfo, isGateway, foreignBlockchain }) { +export async function createBuyOrderTx({ + crosschainAtInfo, + isGateway, + foreignBlockchain, +}) { try { - if (!isGateway) { const wallet = await getSaveWallet(); const address = wallet.address0; - let message - if(foreignBlockchain === 'PIRATECHAIN'){ - message = { + let message; + if (foreignBlockchain === 'PIRATECHAIN') { + message = { atAddress: crosschainAtInfo[0].qortalAtAddress, foreignKey: await getForeignKey(foreignBlockchain), receivingAddress: address, }; } else { - message = { - addresses: crosschainAtInfo.map((order)=> order.qortalAtAddress), + message = { + addresses: crosschainAtInfo.map((order) => order.qortalAtAddress), foreignKey: await getForeignKey(foreignBlockchain), receivingAddress: address, }; } - + let responseVar; - let txn - let url - if(foreignBlockchain === 'PIRATECHAIN'){ - txn = new TradeBotRespondRequest().createTransaction( - message - ); - - - url = await createEndpoint('/crosschain/tradebot/respond') + let txn; + let url; + if (foreignBlockchain === 'PIRATECHAIN') { + txn = new TradeBotRespondRequest().createTransaction(message); + + url = await createEndpoint('/crosschain/tradebot/respond'); } else { - txn = new TradeBotRespondMultipleRequest().createTransaction( - message - ); - - - url = await createEndpoint('/crosschain/tradebot/respondmultiple') + txn = new TradeBotRespondMultipleRequest().createTransaction(message); + + url = await createEndpoint('/crosschain/tradebot/respondmultiple'); } - - const responseFetch = await fetch( - url, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(txn), - } - ); + + const responseFetch = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(txn), + }); const res = await responseFetch.json(); - if(res?.error && res?.message){ - throw new Error(res?.message) + if (res?.error && res?.message) { + throw new Error(res?.message); } - if(!responseFetch?.ok) throw new Error('Failed to submit buy order') + if (!responseFetch?.ok) throw new Error('Failed to submit buy order'); if (res === false) { responseVar = { - response: "Unable to execute buy order", + response: 'Unable to execute buy order', success: false, }; } else { @@ -1861,32 +1878,40 @@ export async function createBuyOrderTx({ crosschainAtInfo, isGateway, foreignBlo responseMessage = { callResponse: response, extra: { - message: "Transaction processed successfully!", - atAddresses: foreignBlockchain === 'PIRATECHAIN' ? [crosschainAtInfo[0].qortalAtAddress] : crosschainAtInfo.map((order)=> order.qortalAtAddress), + message: 'Transaction processed successfully!', + atAddresses: + foreignBlockchain === 'PIRATECHAIN' + ? [crosschainAtInfo[0].qortalAtAddress] + : crosschainAtInfo.map((order) => order.qortalAtAddress), senderAddress: address, - node: url + node: url, }, }; } else { responseMessage = { - callResponse: "ERROR", + callResponse: 'ERROR', extra: { message: response, - atAddresses: foreignBlockchain === 'PIRATECHAIN' ? [crosschainAtInfo[0].qortalAtAddress] : crosschainAtInfo.map((order)=> order.qortalAtAddress), + atAddresses: + foreignBlockchain === 'PIRATECHAIN' + ? [crosschainAtInfo[0].qortalAtAddress] + : crosschainAtInfo.map((order) => order.qortalAtAddress), senderAddress: address, - node: url + node: url, }, }; } - return responseMessage + return responseMessage; } const wallet = await getSaveWallet(); const address = wallet.address0; - const message = { - addresses: foreignBlockchain === 'PIRATECHAIN' ? [crosschainAtInfo[0].qortalAtAddress] : crosschainAtInfo.map((order)=> order.qortalAtAddress), + addresses: + foreignBlockchain === 'PIRATECHAIN' + ? [crosschainAtInfo[0].qortalAtAddress] + : crosschainAtInfo.map((order) => order.qortalAtAddress), foreignKey: await getForeignKey(foreignBlockchain), receivingAddress: address, }; @@ -1894,33 +1919,37 @@ export async function createBuyOrderTx({ crosschainAtInfo, isGateway, foreignBlo qortAddress: proxyAccountAddress, recipientPublicKey: proxyAccountPublicKey, message, - atAddresses: foreignBlockchain === 'PIRATECHAIN' ? [crosschainAtInfo[0].qortalAtAddress] : crosschainAtInfo.map((order)=> order.qortalAtAddress), - isSingle: foreignBlockchain === 'PIRATECHAIN' + atAddresses: + foreignBlockchain === 'PIRATECHAIN' + ? [crosschainAtInfo[0].qortalAtAddress] + : crosschainAtInfo.map((order) => order.qortalAtAddress), + isSingle: foreignBlockchain === 'PIRATECHAIN', }); - if (res?.signature) { - - const message = await listenForChatMessageForBuyOrder({ - nodeBaseUrl: buyTradeNodeBaseUrl, - senderAddress: proxyAccountAddress, - senderPublicKey: proxyAccountPublicKey, - signature: res?.signature, - }); + const message = await listenForChatMessageForBuyOrder({ + nodeBaseUrl: buyTradeNodeBaseUrl, + senderAddress: proxyAccountAddress, + senderPublicKey: proxyAccountPublicKey, + signature: res?.signature, + }); const responseMessage = { - callResponse: message.callResponse, - extra: { - message: message?.extra?.message, - senderAddress: address, - node: buyTradeNodeBaseUrl, - atAddresses: foreignBlockchain === 'PIRATECHAIN' ? [crosschainAtInfo[0].qortalAtAddress] : crosschainAtInfo.map((order)=> order.qortalAtAddress), - } - } - - return responseMessage + callResponse: message.callResponse, + extra: { + message: message?.extra?.message, + senderAddress: address, + node: buyTradeNodeBaseUrl, + atAddresses: + foreignBlockchain === 'PIRATECHAIN' + ? [crosschainAtInfo[0].qortalAtAddress] + : crosschainAtInfo.map((order) => order.qortalAtAddress), + }, + }; + + return responseMessage; } else { - throw new Error("Unable to send buy order message"); + throw new Error('Unable to send buy order message'); } } catch (error) { throw new Error(error.message); @@ -1935,8 +1964,8 @@ export async function sendChatNotification( ) { try { const data = await objectToBase64({ - type: "notification", - subType: "new-group-encryption", + type: 'notification', + subType: 'new-group-encryption', data: { timestamp: res.timestamp, name: res.name, @@ -1959,16 +1988,18 @@ export async function sendChatNotification( }) .then(() => {}) .catch((error) => { - console.error("1", error.message); + console.error('1', error.message); }) .finally(() => { resumeAllQueues(); }); }) .catch((error) => { - console.error("2", error.message); + console.error('2', error.message); }); - } catch (error) {} + } catch (error) { + console.log(error); + } } export const getFee = async (txType) => { @@ -1996,7 +2027,7 @@ export async function leaveGroup({ groupId }) { privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const feeres = await getFee("LEAVE_GROUP"); + const feeres = await getFee('LEAVE_GROUP'); const tx = await createTransaction(32, keyPair, { fee: feeres.fee, @@ -2009,7 +2040,7 @@ export async function leaveGroup({ groupId }) { const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } @@ -2025,7 +2056,7 @@ export async function joinGroup({ groupId }) { privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const feeres = await getFee("JOIN_GROUP"); + const feeres = await getFee('JOIN_GROUP'); const tx = await createTransaction(31, keyPair, { fee: feeres.fee, @@ -2038,7 +2069,7 @@ export async function joinGroup({ groupId }) { const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } @@ -2052,7 +2083,7 @@ export async function cancelInvitationToGroup({ groupId, qortalAddress }) { privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const feeres = await getFee("CANCEL_GROUP_INVITE"); + const feeres = await getFee('CANCEL_GROUP_INVITE'); const tx = await createTransaction(30, keyPair, { fee: feeres.fee, @@ -2065,7 +2096,7 @@ export async function cancelInvitationToGroup({ groupId, qortalAddress }) { const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } @@ -2079,7 +2110,7 @@ export async function cancelBan({ groupId, qortalAddress }) { privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const feeres = await getFee("CANCEL_GROUP_BAN"); + const feeres = await getFee('CANCEL_GROUP_BAN'); const tx = await createTransaction(27, keyPair, { fee: feeres.fee, @@ -2092,10 +2123,10 @@ export async function cancelBan({ groupId, qortalAddress }) { const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } -export async function registerName({ name, description = "" }) { +export async function registerName({ name, description = '' }) { const lastReference = await getLastRef(); const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; @@ -2105,12 +2136,12 @@ export async function registerName({ name, description = "" }) { privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const feeres = await getFee("REGISTER_NAME"); + const feeres = await getFee('REGISTER_NAME'); const tx = await createTransaction(3, keyPair, { fee: feeres.fee, name, - value: description || "", + value: description || '', lastReference: lastReference, }); @@ -2118,7 +2149,7 @@ export async function registerName({ name, description = "" }) { const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } export async function updateName({ newName, oldName, description }) { @@ -2131,13 +2162,13 @@ export async function updateName({ newName, oldName, description }) { privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const feeres = await getFee("UPDATE_NAME"); + const feeres = await getFee('UPDATE_NAME'); const tx = await createTransaction(4, keyPair, { fee: feeres.fee, name: oldName, newName, - newData: description || "", + newData: description || '', lastReference: lastReference, }); @@ -2145,7 +2176,7 @@ export async function updateName({ newName, oldName, description }) { const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } export async function makeAdmin({ groupId, qortalAddress }) { @@ -2158,7 +2189,7 @@ export async function makeAdmin({ groupId, qortalAddress }) { privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const feeres = await getFee("ADD_GROUP_ADMIN"); + const feeres = await getFee('ADD_GROUP_ADMIN'); const tx = await createTransaction(24, keyPair, { fee: feeres.fee, @@ -2171,7 +2202,7 @@ export async function makeAdmin({ groupId, qortalAddress }) { const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } @@ -2185,7 +2216,7 @@ export async function removeAdmin({ groupId, qortalAddress }) { privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const feeres = await getFee("REMOVE_GROUP_ADMIN"); + const feeres = await getFee('REMOVE_GROUP_ADMIN'); const tx = await createTransaction(25, keyPair, { fee: feeres.fee, @@ -2198,14 +2229,14 @@ export async function removeAdmin({ groupId, qortalAddress }) { const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } export async function banFromGroup({ groupId, qortalAddress, - rBanReason = "", + rBanReason = '', rBanTime, }) { const lastReference = await getLastRef(); @@ -2217,7 +2248,7 @@ export async function banFromGroup({ privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const feeres = await getFee("GROUP_BAN"); + const feeres = await getFee('GROUP_BAN'); const tx = await createTransaction(26, keyPair, { fee: feeres.fee, @@ -2232,14 +2263,14 @@ export async function banFromGroup({ const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } export async function kickFromGroup({ groupId, qortalAddress, - rBanReason = "", + rBanReason = '', }) { const lastReference = await getLastRef(); const resKeyPair = await getKeyPair(); @@ -2250,7 +2281,7 @@ export async function kickFromGroup({ privateKey: uint8PrivateKey, publicKey: uint8PublicKey, }; - const feeres = await getFee("GROUP_KICK"); + const feeres = await getFee('GROUP_KICK'); const tx = await createTransaction(28, keyPair, { fee: feeres.fee, @@ -2264,7 +2295,7 @@ export async function kickFromGroup({ const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } @@ -2278,9 +2309,9 @@ export async function createGroup({ }) { const wallet = await getSaveWallet(); const address = wallet.address0; - if (!address) throw new Error("Cannot find user"); + if (!address) throw new Error('Cannot find user'); const lastReference = await getLastRef(); - const feeres = await getFee("CREATE_GROUP"); + const feeres = await getFee('CREATE_GROUP'); const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; const uint8PrivateKey = Base58.decode(parsedData.privateKey); @@ -2306,18 +2337,15 @@ export async function createGroup({ const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } -export async function sellName({ - name, - sellPrice -}) { +export async function sellName({ name, sellPrice }) { const wallet = await getSaveWallet(); const address = wallet.address0; - if (!address) throw new Error("Cannot find user"); + if (!address) throw new Error('Cannot find user'); const lastReference = await getLastRef(); - const feeres = await getFee("SELL_NAME"); + const feeres = await getFee('SELL_NAME'); const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; const uint8PrivateKey = Base58.decode(parsedData.privateKey); @@ -2338,18 +2366,16 @@ export async function sellName({ const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } -export async function cancelSellName({ - name -}) { +export async function cancelSellName({ name }) { const wallet = await getSaveWallet(); const address = wallet.address0; - if (!address) throw new Error("Cannot find user"); + if (!address) throw new Error('Cannot find user'); const lastReference = await getLastRef(); - const feeres = await getFee("SELL_NAME"); + const feeres = await getFee('SELL_NAME'); const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; const uint8PrivateKey = Base58.decode(parsedData.privateKey); @@ -2369,20 +2395,16 @@ export async function cancelSellName({ const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } -export async function buyName({ - name, - sellerAddress, - sellPrice -}) { +export async function buyName({ name, sellerAddress, sellPrice }) { const wallet = await getSaveWallet(); const address = wallet.address0; - if (!address) throw new Error("Cannot find user"); + if (!address) throw new Error('Cannot find user'); const lastReference = await getLastRef(); - const feeres = await getFee("BUY_NAME"); + const feeres = await getFee('BUY_NAME'); const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; const uint8PrivateKey = Base58.decode(parsedData.privateKey); @@ -2395,8 +2417,8 @@ export async function buyName({ const tx = await createTransaction(7, keyPair, { fee: feeres.fee, name, - sellPrice, - recipient: sellerAddress, + sellPrice, + recipient: sellerAddress, lastReference: lastReference, }); @@ -2404,7 +2426,7 @@ export async function buyName({ const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } export async function updateGroup({ @@ -2414,13 +2436,13 @@ export async function updateGroup({ newDescription, newApprovalThreshold, newMinimumBlockDelay, - newMaximumBlockDelay + newMaximumBlockDelay, }) { const wallet = await getSaveWallet(); const address = wallet.address0; - if (!address) throw new Error("Cannot find user"); + if (!address) throw new Error('Cannot find user'); const lastReference = await getLastRef(); - const feeres = await getFee("UPDATE_GROUP"); + const feeres = await getFee('UPDATE_GROUP'); const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; const uint8PrivateKey = Base58.decode(parsedData.privateKey); @@ -2433,8 +2455,8 @@ export async function updateGroup({ const tx = await createTransaction(23, keyPair, { fee: feeres.fee, _groupId: groupId, - newOwner, - newIsOpen, + newOwner, + newIsOpen, newDescription, newApprovalThreshold, newMinimumBlockDelay, @@ -2446,14 +2468,14 @@ export async function updateGroup({ const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error(res?.message || "Transaction was not able to be processed"); + throw new Error(res?.message || 'Transaction was not able to be processed'); return res; } export async function inviteToGroup({ groupId, qortalAddress, inviteTime }) { const address = await getNameOrAddress(qortalAddress); - if (!address) throw new Error("Cannot find user"); + if (!address) throw new Error('Cannot find user'); const lastReference = await getLastRef(); - const feeres = await getFee("GROUP_INVITE"); + const feeres = await getFee('GROUP_INVITE'); const resKeyPair = await getKeyPair(); const parsedData = resKeyPair; const uint8PrivateKey = Base58.decode(parsedData.privateKey); @@ -2475,7 +2497,7 @@ export async function inviteToGroup({ groupId, qortalAddress, inviteTime }) { const res = await processTransactionVersion2(signedBytes); if (!res?.signature) - throw new Error("Transaction was not able to be processed"); + throw new Error('Transaction was not able to be processed'); return res; } @@ -2486,12 +2508,12 @@ export async function sendCoin( try { const confirmReceiver = await getNameOrAddress(receiver); if (confirmReceiver.error) - throw new Error("Invalid receiver address or name"); + throw new Error('Invalid receiver address or name'); const wallet = await getSaveWallet(); - let keyPair = ""; + let keyPair = ''; if (skipConfirmPassword) { const resKeyPair = await getKeyPair(); - + const parsedData = resKeyPair; const uint8PrivateKey = Base58.decode(parsedData.privateKey); const uint8PublicKey = Base58.decode(parsedData.publicKey); @@ -2501,7 +2523,10 @@ export async function sendCoin( }; } else { const response = await decryptStoredWallet(password, wallet); - const wallet2 = new PhraseWallet(response, wallet?.version || walletVersion); + const wallet2 = new PhraseWallet( + response, + wallet?.version || walletVersion + ); keyPair = wallet2._addresses[0].keyPair; } @@ -2534,7 +2559,7 @@ function fetchMessages(apiCall) { return new Promise((resolve, reject) => { const attemptFetch = async () => { if (Date.now() - startTime > maxDuration) { - return reject(new Error("Maximum polling time exceeded")); + return reject(new Error('Maximum polling time exceeded')); } try { @@ -2569,7 +2594,7 @@ async function fetchMessagesForBuyOrders(apiCall, signature, senderPublicKey) { return new Promise((resolve, reject) => { const attemptFetch = async () => { if (Date.now() - startTime > maxDuration) { - return reject(new Error("Maximum polling time exceeded")); + return reject(new Error('Maximum polling time exceeded')); } try { @@ -2625,7 +2650,7 @@ async function listenForChatMessage({ timestamp, }) { try { - let validApi = ""; + let validApi = ''; const checkIfNodeBaseUrlIsAcceptable = apiEndpoints.find( (item) => item === nodeBaseUrl ); @@ -2670,7 +2695,7 @@ async function listenForChatMessageForBuyOrder({ signature, }) { try { - let validApi = ""; + let validApi = ''; const checkIfNodeBaseUrlIsAcceptable = apiEndpoints.find( (item) => item === nodeBaseUrl ); @@ -2690,7 +2715,7 @@ async function listenForChatMessageForBuyOrder({ senderPublicKey ); - return parsedMessageObj + return parsedMessageObj; // chrome.tabs.query({}, function (tabs) { // tabs.forEach((tab) => { @@ -2748,7 +2773,7 @@ export async function setChatHeads(data) { storeData(`chatheads-${address}`, data) .then(() => resolve(true)) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); } @@ -2811,7 +2836,7 @@ export async function saveTempPublish({ data, key }) { storeData(`tempPublish-${address}`, newTemp) .then(() => resolve(newTemp[key])) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); } @@ -2823,7 +2848,7 @@ async function setChatHeadsDirect(data) { storeData(`chatheads-direct-${address}`, data) .then(() => resolve(true)) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); } @@ -2881,7 +2906,7 @@ export async function addTimestampGroupAnnouncement({ storeData(`group-announcement-${address}`, data) .then(() => resolve(true)) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); } @@ -2894,32 +2919,31 @@ export async function getTimestampLatestPayment() { if (res) { const parsedData = res; return parsedData; - } else return 0 + } else return 0; } export async function addTimestampLatestPayment(timestamp) { const wallet = await getSaveWallet(); const address = wallet.address0; - + return await new Promise((resolve, reject) => { storeData(`latest-payment-${address}`, timestamp) .then(() => resolve(true)) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); } - export async function addEnteredQmailTimestamp() { const wallet = await getSaveWallet(); const address = wallet.address0; - + return await new Promise((resolve, reject) => { storeData(`qmail-entered-timestamp-${address}`, Date.now()) .then(() => resolve(true)) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); } @@ -2933,7 +2957,7 @@ export async function getEnteredQmailTimestamp() { const parsedData = res; return parsedData; } else { - return null + return null; } } @@ -2981,7 +3005,7 @@ export async function setGroupData({ storeData(`group-data-${address}`, data) .then(() => resolve(true)) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); } @@ -2995,7 +3019,7 @@ export async function addTimestampEnterChat({ groupId, timestamp }) { storeData(`enter-chat-timestamp-${address}`, data) .then(() => resolve(true)) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); } @@ -3009,12 +3033,11 @@ export async function addTimestampMention({ groupId, timestamp }) { storeData(`enter-mention-timestamp-${address}`, data) .then(() => resolve(true)) .catch((error) => { - reject(new Error(error.message || "Error saving data")); + reject(new Error(error.message || 'Error saving data')); }); }); } - export async function notifyAdminRegenerateSecretKey({ groupName, adminAddress, @@ -3041,7 +3064,7 @@ async function getChatHeads() { const parsedData = res; return parsedData; } else { - throw new Error("No Chatheads saved"); + throw new Error('No Chatheads saved'); } } @@ -3054,22 +3077,22 @@ async function getChatHeadsDirect() { const parsedData = res; return parsedData; } else { - throw new Error("No Chatheads saved"); + throw new Error('No Chatheads saved'); } } function setupMessageListener() { - window.addEventListener("message", async (event) => { + window.addEventListener('message', async (event) => { if (event.origin !== window.location.origin) { return; } const request = event.data; // Check if the message is intended for this listener - if (request?.type !== "backgroundMessage") return; // Only process messages of type 'backgroundMessage' + if (request?.type !== 'backgroundMessage') return; // Only process messages of type 'backgroundMessage' switch (request.action) { - case "version": + case 'version': versionCase(request, event); break; @@ -3077,208 +3100,208 @@ function setupMessageListener() { // storeWalletInfoCase(request, event); // break; - case "getWalletInfo": + case 'getWalletInfo': getWalletInfoCase(request, event); break; - case "validApi": + case 'validApi': validApiCase(request, event); break; - case "name": + case 'name': nameCase(request, event); break; - case "userInfo": + case 'userInfo': userInfoCase(request, event); break; - case "decryptWallet": + case 'decryptWallet': decryptWalletCase(request, event); break; - case "balance": + case 'balance': balanceCase(request, event); break; - case "ltcBalance": + case 'ltcBalance': ltcBalanceCase(request, event); break; - case "sendCoin": + case 'sendCoin': sendCoinCase(request, event); break; - case "inviteToGroup": + case 'inviteToGroup': inviteToGroupCase(request, event); break; - case "saveTempPublish": + case 'saveTempPublish': saveTempPublishCase(request, event); break; - case "getTempPublish": + case 'getTempPublish': getTempPublishCase(request, event); break; - case "createGroup": + case 'createGroup': createGroupCase(request, event); break; - case "cancelInvitationToGroup": + case 'cancelInvitationToGroup': cancelInvitationToGroupCase(request, event); break; - case "leaveGroup": + case 'leaveGroup': leaveGroupCase(request, event); break; - case "joinGroup": + case 'joinGroup': joinGroupCase(request, event); break; - case "kickFromGroup": + case 'kickFromGroup': kickFromGroupCase(request, event); break; - case "banFromGroup": + case 'banFromGroup': banFromGroupCase(request, event); break; - case "addDataPublishes": + case 'addDataPublishes': addDataPublishesCase(request, event); break; - case "getDataPublishes": + case 'getDataPublishes': getDataPublishesCase(request, event); break; - case "addUserSettings": + case 'addUserSettings': addUserSettingsCase(request, event); break; - case "cancelBan": + case 'cancelBan': cancelBanCase(request, event); break; - case "registerName": + case 'registerName': registerNameCase(request, event); break; - case "createPoll": + case 'createPoll': createPollCase(request, event); break; - case "voteOnPoll": + case 'voteOnPoll': voteOnPollCase(request, event); - break; - case "makeAdmin": + break; + case 'makeAdmin': makeAdminCase(request, event); break; - case "removeAdmin": + case 'removeAdmin': removeAdminCase(request, event); break; - case "notification": + case 'notification': notificationCase(request, event); break; - case "addTimestampEnterChat": + case 'addTimestampEnterChat': addTimestampEnterChatCase(request, event); break; - case "setApiKey": + case 'setApiKey': setApiKeyCase(request, event); break; - case "setCustomNodes": + case 'setCustomNodes': setCustomNodesCase(request, event); - case "getApiKey": + case 'getApiKey': getApiKeyCase(request, event); break; - case "getCustomNodesFromStorage": + case 'getCustomNodesFromStorage': getCustomNodesFromStorageCase(request, event); break; - case "notifyAdminRegenerateSecretKey": + case 'notifyAdminRegenerateSecretKey': notifyAdminRegenerateSecretKeyCase(request, event); break; - case "addGroupNotificationTimestamp": + case 'addGroupNotificationTimestamp': addGroupNotificationTimestampCase(request, event); break; - case "clearAllNotifications": + case 'clearAllNotifications': clearAllNotificationsCase(request, event); break; - case "setGroupData": + case 'setGroupData': setGroupDataCase(request, event); break; - case "getGroupDataSingle": + case 'getGroupDataSingle': getGroupDataSingleCase(request, event); break; - case "getTimestampEnterChat": + case 'getTimestampEnterChat': getTimestampEnterChatCase(request, event); break; - case "listActions": - listActionsCase(request, event); - break; - case "addTimestampMention": - addTimestampMentionCase(request, event); - break; - case "getTimestampMention": - getTimestampMentionCase(request, event); - break; - case "getGroupNotificationTimestamp": + case 'listActions': + listActionsCase(request, event); + break; + case 'addTimestampMention': + addTimestampMentionCase(request, event); + break; + case 'getTimestampMention': + getTimestampMentionCase(request, event); + break; + case 'getGroupNotificationTimestamp': getGroupNotificationTimestampCase(request, event); break; - case "encryptAndPublishSymmetricKeyGroupChat": + case 'encryptAndPublishSymmetricKeyGroupChat': encryptAndPublishSymmetricKeyGroupChatCase(request, event); break; - case "encryptAndPublishSymmetricKeyGroupChatForAdmins": + case 'encryptAndPublishSymmetricKeyGroupChatForAdmins': encryptAndPublishSymmetricKeyGroupChatForAdminsCase(request, event); break; - case "publishGroupEncryptedResource": + case 'publishGroupEncryptedResource': publishGroupEncryptedResourceCase(request, event); break; - case "publishOnQDN": + case 'publishOnQDN': publishOnQDNCase(request, event); break; - case "getUserSettings": + case 'getUserSettings': getUserSettingsCase(request, event); break; - case "handleActiveGroupDataFromSocket": + case 'handleActiveGroupDataFromSocket': handleActiveGroupDataFromSocketCase(request, event); break; - case "getThreadActivity": + case 'getThreadActivity': getThreadActivityCase(request, event); break; - case "updateThreadActivity": + case 'updateThreadActivity': updateThreadActivityCase(request, event); - case "decryptGroupEncryption": + case 'decryptGroupEncryption': decryptGroupEncryptionCase(request, event); break; - case "encryptSingle": + case 'encryptSingle': encryptSingleCase(request, event); break; - case "decryptSingle": + case 'decryptSingle': decryptSingleCase(request, event); break; - case "pauseAllQueues": + case 'pauseAllQueues': pauseAllQueuesCase(request, event); break; - case "resumeAllQueues": + case 'resumeAllQueues': resumeAllQueuesCase(request, event); break; - case "checkLocal": + case 'checkLocal': checkLocalCase(request, event); break; - case "decryptSingleForPublishes": + case 'decryptSingleForPublishes': decryptSingleForPublishesCase(request, event); break; - case "decryptDirect": + case 'decryptDirect': decryptDirectCase(request, event); break; - case "sendChatGroup": + case 'sendChatGroup': sendChatGroupCase(request, event); break; - case "sendChatDirect": + case 'sendChatDirect': sendChatDirectCase(request, event); break; - case "setupGroupWebsocket": + case 'setupGroupWebsocket': setupGroupWebsocketCase(request, event); break; - case "createRewardShare": + case 'createRewardShare': createRewardShareCase(request, event); break; - case "getRewardSharePrivateKey": - getRewardSharePrivateKeyCase(request, event); - break; - case "removeRewardShare" : + case 'getRewardSharePrivateKey': + getRewardSharePrivateKeyCase(request, event); + break; + case 'removeRewardShare': removeRewardShareCase(request, event); break; - case "addEnteredQmailTimestamp": + case 'addEnteredQmailTimestamp': addEnteredQmailTimestampCase(request, event); break; - case "getEnteredQmailTimestamp": + case 'getEnteredQmailTimestamp': getEnteredQmailTimestampCase(request, event); break; - case "logout": + case 'logout': { try { const logoutFunc = async () => { @@ -3288,16 +3311,16 @@ function setupMessageListener() { // for announcement notification clearInterval(interval); } - groupSecretkeys = {} + groupSecretkeys = {}; const wallet = await getSaveWallet(); const address = wallet.address0; const key1 = `tempPublish-${address}`; const key2 = `group-data-${address}`; const key3 = `${address}-publishData`; const keysToRemove = [ - "keyPair", - "walletInfo", - "active-groups-directs", + 'keyPair', + 'walletInfo', + 'active-groups-directs', key1, key2, key3, @@ -3306,7 +3329,9 @@ function setupMessageListener() { removeKeysAndLogout(keysToRemove, event, request); }; logoutFunc(); - } catch (error) {} + } catch (error) { + console.log(error); + } } break; @@ -3322,23 +3347,25 @@ const checkGroupList = async () => { try { const wallet = await getSaveWallet(); const address = wallet.address0; - const url = await createEndpoint(`/chat/active/${address}?encoding=BASE64&haschatreference=false`); + const url = await createEndpoint( + `/chat/active/${address}?encoding=BASE64&haschatreference=false` + ); const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const data = await response.json(); - const copyGroups = [...(data?.groups || [])] - const findIndex = copyGroups?.findIndex(item => item?.groupId === 0) - if(findIndex !== -1){ - copyGroups[findIndex] = { - ...(copyGroups[findIndex] || {}), - groupId: "0" - } - } - const filteredGroups = copyGroups + const copyGroups = [...(data?.groups || [])]; + const findIndex = copyGroups?.findIndex((item) => item?.groupId === 0); + if (findIndex !== -1) { + copyGroups[findIndex] = { + ...(copyGroups[findIndex] || {}), + groupId: '0', + }; + } + const filteredGroups = copyGroups; const sortedGroups = filteredGroups.sort( (a, b) => (b.timestamp || 0) - (a.timestamp || 0) @@ -3346,8 +3373,8 @@ const checkGroupList = async () => { const sortedDirects = (data?.direct || []) .filter( (item) => - item?.name !== "extension-proxy" && - item?.address !== "QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH" + item?.name !== 'extension-proxy' && + item?.address !== 'QSMMGSgysEuqDCuLw3S4cHrQkBrh3vP3VH' ) .sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0)); @@ -3363,17 +3390,17 @@ const checkGroupList = async () => { export const checkNewMessages = async () => { try { - let mutedGroups = (await getUserSettings({ key: "mutedGroups" })) || []; + let mutedGroups = (await getUserSettings({ key: 'mutedGroups' })) || []; if (!isArray(mutedGroups)) mutedGroups = []; - mutedGroups.push('0') - let myName = ""; + mutedGroups.push('0'); + let myName = ''; const userData = await getUserInfo(); if (userData?.name) { myName = userData.name; } let newAnnouncements = []; - const activeData = (await getStoredData("active-groups-directs")) || { + const activeData = (await getStoredData('active-groups-directs')) || { groups: [], directs: [], }; @@ -3391,9 +3418,9 @@ export const checkNewMessages = async () => { ); const response = await requestQueueAnnouncements.enqueue(() => { return fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); }); @@ -3425,7 +3452,7 @@ export const checkNewMessages = async () => { }) ); let isDisableNotifications = - (await getUserSettings({ key: "disable-push-notifications" })) || false; + (await getUserSettings({ key: 'disable-push-notifications' })) || false; if ( newAnnouncements.length > 0 && @@ -3433,19 +3460,20 @@ export const checkNewMessages = async () => { !isDisableNotifications ) { // Create a unique notification ID with type and group announcement details - const notificationId = - encodeURIComponent("chat_notification_" + - Date.now() + - "_type=group-announcement" + - `_from=${newAnnouncements[0]?.groupId}`); + const notificationId = encodeURIComponent( + 'chat_notification_' + + Date.now() + + '_type=group-announcement' + + `_from=${newAnnouncements[0]?.groupId}` + ); - const title = "New group announcement!"; + const title = 'New group announcement!'; const body = `You have received a new announcement from ${newAnnouncements[0]?.groupName}`; // Create and show the notification const notification = new window.Notification(title, { body, - icon: window.location.origin + "/qortal192.png", + icon: window.location.origin + '/qortal192.png', data: { id: notificationId }, }); @@ -3465,7 +3493,7 @@ export const checkNewMessages = async () => { window.postMessage( { - action: "SET_GROUP_ANNOUNCEMENTS", + action: 'SET_GROUP_ANNOUNCEMENTS', payload: savedtimestampAfter, }, targetOrigin @@ -3478,62 +3506,55 @@ export const checkNewMessages = async () => { export const checkPaymentsForNotifications = async (address) => { try { const isDisableNotifications = - (await getUserSettings({ key: "disable-push-notifications" })) || false; - if(isDisableNotifications) return - let latestPayment = null - const savedtimestamp = await getTimestampLatestPayment(); + (await getUserSettings({ key: 'disable-push-notifications' })) || false; + if (isDisableNotifications) return; + let latestPayment = null; + const savedtimestamp = await getTimestampLatestPayment(); - const url = await createEndpoint( - `/transactions/search?txType=PAYMENT&address=${address}&confirmationStatus=CONFIRMED&limit=5&reverse=true` - ); - - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - const responseData = await response.json(); + const url = await createEndpoint( + `/transactions/search?txType=PAYMENT&address=${address}&confirmationStatus=CONFIRMED&limit=5&reverse=true` + ); - const latestTx = responseData.filter( - (tx) => tx?.creatorAddress !== address && tx?.recipient === address - )[0]; - if (!latestTx) { - return; // continue to the next group - } - if ( - checkDifference(latestTx.timestamp) && - (!savedtimestamp || - latestTx.timestamp > - savedtimestamp) - ) { - if(latestTx.timestamp){ - latestPayment = latestTx - await addTimestampLatestPayment(latestTx.timestamp); - } - - // save new timestamp - } - - + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + const responseData = await response.json(); + + const latestTx = responseData.filter( + (tx) => tx?.creatorAddress !== address && tx?.recipient === address + )[0]; + if (!latestTx) { + return; // continue to the next group + } if ( - latestPayment + checkDifference(latestTx.timestamp) && + (!savedtimestamp || latestTx.timestamp > savedtimestamp) ) { - // Create a unique notification ID with type and group announcement details - const notificationId = - encodeURIComponent("payment_notification_" + - Date.now() + - "_type=payment-announcement"); + if (latestTx.timestamp) { + latestPayment = latestTx; + await addTimestampLatestPayment(latestTx.timestamp); + } - const title = "New payment!"; + // save new timestamp + } + + if (latestPayment) { + // Create a unique notification ID with type and group announcement details + const notificationId = encodeURIComponent( + 'payment_notification_' + Date.now() + '_type=payment-announcement' + ); + + const title = 'New payment!'; const body = `You have received a new payment of ${latestPayment?.amount} QORT`; // Create and show the notification const notification = new window.Notification(title, { body, - icon: window.location.origin + "/qortal192.png", + icon: window.location.origin + '/qortal192.png', data: { id: notificationId }, }); @@ -3552,27 +3573,28 @@ export const checkPaymentsForNotifications = async (address) => { window.postMessage( { - action: "SET_PAYMENT_ANNOUNCEMENT", + action: 'SET_PAYMENT_ANNOUNCEMENT', payload: latestPayment, }, targetOrigin ); } - } catch (error) { - console.error(error) - } + console.error(error); + } }; const checkActiveChatsForNotifications = async () => { try { checkGroupList(); - } catch (error) {} + } catch (error) { + console.log(error); + } }; export const checkThreads = async (bringBack) => { try { - let myName = ""; + let myName = ''; const userData = await getUserInfo(); if (userData?.name) { myName = userData.name; @@ -3599,9 +3621,9 @@ export const checkThreads = async (bringBack) => { `${endpoint}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=1&includemetadata=false&offset=${0}&reverse=true&prefix=true` ); const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); @@ -3677,29 +3699,28 @@ export const checkThreads = async (bringBack) => { chrome.storage.local.set({ [`threadactivity-${address}`]: dataString }); if (newAnnouncements.length > 0) { - const notificationId = - encodeURIComponent("chat_notification_" + - Date.now() + - "_type=thread-post" + - `_data=${JSON.stringify(newAnnouncements[0])}`); + const notificationId = encodeURIComponent( + 'chat_notification_' + + Date.now() + + '_type=thread-post' + + `_data=${JSON.stringify(newAnnouncements[0])}` + ); let isDisableNotifications = - (await getUserSettings({ key: "disable-push-notifications" })) || false; + (await getUserSettings({ key: 'disable-push-notifications' })) || false; if (!isDisableNotifications) { - - // Check user settings to see if notifications are disabled const isDisableNotifications = - (await getUserSettings({ key: "disable-push-notifications" })) || + (await getUserSettings({ key: 'disable-push-notifications' })) || false; if (!isDisableNotifications) { - const title = "New thread post!"; + const title = 'New thread post!'; const body = `New post in ${newAnnouncements[0]?.thread?.threadData?.title}`; // Create and show the notification const notification = new window.Notification(title, { body, - icon: window.location.origin + "/qortal192.png", + icon: window.location.origin + '/qortal192.png', data: { id: notificationId }, }); @@ -3721,7 +3742,7 @@ export const checkThreads = async (bringBack) => { window.postMessage( { - action: "SET_GROUP_ANNOUNCEMENTS", + action: 'SET_GROUP_ANNOUNCEMENTS', payload: savedtimestampAfter, }, targetOrigin @@ -3756,44 +3777,49 @@ export const checkThreads = async (bringBack) => { // BackgroundFetch.finish(taskId); // }); -let notificationCheckInterval -let paymentsCheckInterval +let notificationCheckInterval; +let paymentsCheckInterval; const createNotificationCheck = () => { // Check if an interval already exists before creating it if (!notificationCheckInterval) { - notificationCheckInterval = setInterval(async () => { - try { - // This would replace the Chrome alarm callback - const wallet = await getSaveWallet(); - const address = wallet?.address0; - if (!address) return; + notificationCheckInterval = setInterval( + async () => { + try { + // This would replace the Chrome alarm callback + const wallet = await getSaveWallet(); + const address = wallet?.address0; + if (!address) return; - checkActiveChatsForNotifications(); - checkNewMessages(); - checkThreads(); - } catch (error) { - console.error('Error checking notifications:', error); - } - }, 10 * 60 * 1000); // 10 minutes + checkActiveChatsForNotifications(); + checkNewMessages(); + checkThreads(); + } catch (error) { + console.error('Error checking notifications:', error); + } + }, + 10 * 60 * 1000 + ); // 10 minutes } if (!paymentsCheckInterval) { - paymentsCheckInterval = setInterval(async () => { - try { - // This would replace the Chrome alarm callback - const wallet = await getSaveWallet(); - const address = wallet?.address0; - if (!address) return; + paymentsCheckInterval = setInterval( + async () => { + try { + // This would replace the Chrome alarm callback + const wallet = await getSaveWallet(); + const address = wallet?.address0; + if (!address) return; - checkPaymentsForNotifications(address); - - } catch (error) { - console.error('Error checking payments:', error); - } - }, 3 * 60 * 1000); // 3 minutes + checkPaymentsForNotifications(address); + } catch (error) { + console.error('Error checking payments:', error); + } + }, + 3 * 60 * 1000 + ); // 3 minutes } }; // Call this function when initializing your app -createNotificationCheck(); \ No newline at end of file +createNotificationCheck(); diff --git a/src/components/Apps/AppsDevModeHome.tsx b/src/components/Apps/AppsDevModeHome.tsx index d9def9b..8c346c2 100644 --- a/src/components/Apps/AppsDevModeHome.tsx +++ b/src/components/Apps/AppsDevModeHome.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useMemo, useState } from "react"; +import React, { useContext, useMemo, useState } from 'react'; import { AppCircle, AppCircleContainer, @@ -6,8 +6,8 @@ import { AppLibrarySubTitle, AppsContainer, AppsParent, -} from "./Apps-styles"; -import { Buffer } from "buffer"; +} from './Apps-styles'; +import { Buffer } from 'buffer'; import { Avatar, @@ -20,17 +20,17 @@ import { DialogContentText, DialogTitle, Input, -} from "@mui/material"; -import { Add } from "@mui/icons-material"; -import { MyContext, getBaseApiReact, isMobile } from "../../App"; -import LogoSelected from "../../assets/svgs/LogoSelected.svg"; -import { executeEvent } from "../../utils/events"; -import { Spacer } from "../../common/Spacer"; -import { useModal } from "../../common/useModal"; -import { createEndpoint, isUsingLocal } from "../../background"; -import { Label } from "../Group/AddGroup"; -import ShortUniqueId from "short-unique-id"; -import swaggerSVG from '../../assets/svgs/swagger.svg' +} from '@mui/material'; +import { Add } from '@mui/icons-material'; +import { MyContext, getBaseApiReact, isMobile } from '../../App'; +import LogoSelected from '../../assets/svgs/LogoSelected.svg'; +import { executeEvent } from '../../utils/events'; +import { Spacer } from '../../common/Spacer'; +import { useModal } from '../../common/useModal'; +import { createEndpoint, isUsingLocal } from '../../background'; +import { Label } from '../Group/AddGroup'; +import ShortUniqueId from 'short-unique-id'; +import swaggerSVG from '../../assets/svgs/swagger.svg'; const uid = new ShortUniqueId({ length: 8 }); export const AppsDevModeHome = ({ @@ -40,8 +40,8 @@ export const AppsDevModeHome = ({ availableQapps, myName, }) => { - const [domain, setDomain] = useState("127.0.0.1"); - const [port, setPort] = useState(""); + const [domain, setDomain] = useState('127.0.0.1'); + const [port, setPort] = useState(''); const [selectedPreviewFile, setSelectedPreviewFile] = useState(null); const { isShow, onCancel, onOk, show, message } = useModal(); @@ -58,7 +58,7 @@ export const AppsDevModeHome = ({ const content = await window.electron.readFile(filePath); return { buffer: content, filePath }; } else { - console.log("No file selected."); + console.log('No file selected.'); } }; const handleSelectDirectry = async (existingDirectoryPath) => { @@ -67,7 +67,7 @@ export const AppsDevModeHome = ({ if (buffer) { return { buffer, directoryPath }; } else { - console.log("No file selected."); + console.log('No file selected.'); } }; @@ -78,34 +78,36 @@ export const AppsDevModeHome = ({ setOpenSnackGlobal(true); setInfoSnackCustom({ - type: "error", + type: 'error', message: - "Please use your local node for dev mode! Logout and use Local node.", + 'Please use your local node for dev mode! Logout and use Local node.', }); return; } const { portVal, domainVal } = await show({ - message: "", - publishFee: "", + message: '', + publishFee: '', }); - const framework = domainVal + ":" + portVal; + const framework = domainVal + ':' + portVal; const response = await fetch( `${getBaseApiReact()}/developer/proxy/start`, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "text/plain", + 'Content-Type': 'text/plain', }, body: framework, } ); const responseData = await response.text(); - executeEvent("appsDevModeAddTab", { + executeEvent('appsDevModeAddTab', { data: { - url: "http://127.0.0.1:" + responseData, + url: 'http://127.0.0.1:' + responseData, }, }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const addPreviewApp = async (isRefresh, existingFilePath, tabId) => { @@ -115,9 +117,9 @@ export const AppsDevModeHome = ({ setOpenSnackGlobal(true); setInfoSnackCustom({ - type: "error", + type: 'error', message: - "Please use your local node for dev mode! Logout and use Local node.", + 'Please use your local node for dev mode! Logout and use Local node.', }); return; } @@ -125,8 +127,8 @@ export const AppsDevModeHome = ({ setOpenSnackGlobal(true); setInfoSnackCustom({ - type: "error", - message: "You need a name to use preview", + type: 'error', + message: 'You need a name to use preview', }); return; } @@ -137,29 +139,29 @@ export const AppsDevModeHome = ({ setOpenSnackGlobal(true); setInfoSnackCustom({ - type: "error", - message: "Please select a file", + type: 'error', + message: 'Please select a file', }); return; } - const postBody = Buffer.from(buffer).toString("base64"); + const postBody = Buffer.from(buffer).toString('base64'); const endpoint = await createEndpoint( `/arbitrary/APP/${myName}/zip?preview=true` ); const response = await fetch(endpoint, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "text/plain", + 'Content-Type': 'text/plain', }, body: postBody, }); - if (!response?.ok) throw new Error("Invalid zip"); + if (!response?.ok) throw new Error('Invalid zip'); const previewPath = await response.text(); if (tabId) { - executeEvent("appsDevModeUpdateTab", { + executeEvent('appsDevModeUpdateTab', { data: { - url: "http://127.0.0.1:12391" + previewPath, + url: 'http://127.0.0.1:12391' + previewPath, isPreview: true, filePath, refreshFunc: (tabId) => { @@ -170,9 +172,9 @@ export const AppsDevModeHome = ({ }); return; } - executeEvent("appsDevModeAddTab", { + executeEvent('appsDevModeAddTab', { data: { - url: "http://127.0.0.1:12391" + previewPath, + url: 'http://127.0.0.1:12391' + previewPath, isPreview: true, filePath, refreshFunc: (tabId) => { @@ -192,9 +194,9 @@ export const AppsDevModeHome = ({ setOpenSnackGlobal(true); setInfoSnackCustom({ - type: "error", + type: 'error', message: - "Please use your local node for dev mode! Logout and use Local node.", + 'Please use your local node for dev mode! Logout and use Local node.', }); return; } @@ -202,8 +204,8 @@ export const AppsDevModeHome = ({ setOpenSnackGlobal(true); setInfoSnackCustom({ - type: "error", - message: "You need a name to use preview", + type: 'error', + message: 'You need a name to use preview', }); return; } @@ -214,29 +216,29 @@ export const AppsDevModeHome = ({ setOpenSnackGlobal(true); setInfoSnackCustom({ - type: "error", - message: "Please select a file", + type: 'error', + message: 'Please select a file', }); return; } - const postBody = Buffer.from(buffer).toString("base64"); + const postBody = Buffer.from(buffer).toString('base64'); const endpoint = await createEndpoint( `/arbitrary/APP/${myName}/zip?preview=true` ); const response = await fetch(endpoint, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "text/plain", + 'Content-Type': 'text/plain', }, body: postBody, }); - if (!response?.ok) throw new Error("Invalid zip"); + if (!response?.ok) throw new Error('Invalid zip'); const previewPath = await response.text(); if (tabId) { - executeEvent("appsDevModeUpdateTab", { + executeEvent('appsDevModeUpdateTab', { data: { - url: "http://127.0.0.1:12391" + previewPath, + url: 'http://127.0.0.1:12391' + previewPath, isPreview: true, directoryPath, refreshFunc: (tabId) => { @@ -247,9 +249,9 @@ export const AppsDevModeHome = ({ }); return; } - executeEvent("appsDevModeAddTab", { + executeEvent('appsDevModeAddTab', { data: { - url: "http://127.0.0.1:12391" + previewPath, + url: 'http://127.0.0.1:12391' + previewPath, isPreview: true, directoryPath, refreshFunc: (tabId) => { @@ -266,12 +268,12 @@ export const AppsDevModeHome = ({ <> Dev Mode Apps @@ -280,8 +282,8 @@ export const AppsDevModeHome = ({ @@ -307,7 +309,7 @@ export const AppsDevModeHome = ({ > @@ -323,7 +325,7 @@ export const AppsDevModeHome = ({ > @@ -334,10 +336,10 @@ export const AppsDevModeHome = ({ { - executeEvent("appsDevModeAddTab", { + executeEvent('appsDevModeAddTab', { data: { - service: "APP", - name: "Q-Sandbox", + service: 'APP', + name: 'Q-Sandbox', tabId: uid.rnd(), }, }); @@ -345,16 +347,16 @@ export const AppsDevModeHome = ({ > @@ -374,27 +376,27 @@ export const AppsDevModeHome = ({ { - executeEvent("appsDevModeAddTab", { + executeEvent('appsDevModeAddTab', { data: { - url: "http://127.0.0.1:12391", + url: 'http://127.0.0.1:12391', isPreview: false, - customIcon: swaggerSVG + customIcon: swaggerSVG, }, }); }} > @@ -419,20 +421,20 @@ export const AppsDevModeHome = ({ aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" onKeyDown={(e) => { - if (e.key === "Enter" && domain && port) { + if (e.key === 'Enter' && domain && port) { onOk({ portVal: port, domainVal: domain }); } }} > - {"Add custom framework"} + {'Add custom framework'} Domain @@ -444,10 +446,10 @@ export const AppsDevModeHome = ({ Port diff --git a/src/components/Chat/AdminSpace.tsx b/src/components/Chat/AdminSpace.tsx index 6eaf541..5d89ad3 100644 --- a/src/components/Chat/AdminSpace.tsx +++ b/src/components/Chat/AdminSpace.tsx @@ -1,21 +1,7 @@ -import React, { - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from "react"; -import { GroupMail } from "../Group/Forum/GroupMail"; -import { MyContext, isMobile } from "../../App"; -import { getRootHeight } from "../../utils/mobile/mobileUtils"; -import { Box, Typography } from "@mui/material"; -import { AdminSpaceInner } from "./AdminSpaceInner"; - - - - - +import { useContext, useEffect, useState } from 'react'; +import { MyContext, isMobile } from '../../App'; +import { Box, Typography } from '@mui/material'; +import { AdminSpaceInner } from './AdminSpaceInner'; export const AdminSpace = ({ selectedGroup, @@ -26,11 +12,11 @@ export const AdminSpace = ({ isAdmin, myAddress, hide, - defaultThread, + defaultThread, setDefaultThread, - setIsForceShowCreationKeyPopup + setIsForceShowCreationKeyPopup, }) => { - const { rootHeight } = useContext(MyContext); + const { rootHeight } = useContext(MyContext); const [isMoved, setIsMoved] = useState(false); useEffect(() => { if (hide) { @@ -42,26 +28,37 @@ export const AdminSpace = ({ return ( - {!isAdmin && Sorry, this space is only for Admins.} - {isAdmin && } - - + style={{ + // reference to change height + display: 'flex', + flexDirection: 'column', + height: isMobile ? `calc(${rootHeight} - 127px` : 'calc(100vh - 70px)', + left: hide && '-1000px', + opacity: hide ? 0 : 1, + position: hide ? 'fixed' : 'relative', + visibility: hide && 'hidden', + width: '100%', + }} + > + {!isAdmin && ( + + Sorry, this space is only for Admins. + + )} + {isAdmin && ( + + )} + ); }; diff --git a/src/components/Chat/AdminSpaceInner.tsx b/src/components/Chat/AdminSpaceInner.tsx index f469e9e..bd6af61 100644 --- a/src/components/Chat/AdminSpaceInner.tsx +++ b/src/components/Chat/AdminSpaceInner.tsx @@ -1,39 +1,42 @@ -import React, { useCallback, useContext, useEffect, useState } from "react"; +import { useCallback, useContext, useEffect, useState } from 'react'; import { MyContext, getArbitraryEndpointReact, getBaseApiReact, -} from "../../App"; -import { Box, Button, Typography } from "@mui/material"; +} from '../../App'; +import { Box, Button, Typography } from '@mui/material'; import { decryptResource, getPublishesFromAdmins, validateSecretKey, -} from "../Group/Group"; -import { getFee } from "../../background"; -import { base64ToUint8Array } from "../../qdn/encryption/group-encryption"; -import { uint8ArrayToObject } from "../../backgroundFunctions/encryption"; -import { formatTimestampForum } from "../../utils/time"; -import { Spacer } from "../../common/Spacer"; +} from '../Group/Group'; +import { getFee } from '../../background'; +import { base64ToUint8Array } from '../../qdn/encryption/group-encryption'; +import { uint8ArrayToObject } from '../../backgroundFunctions/encryption'; +import { formatTimestampForum } from '../../utils/time'; +import { Spacer } from '../../common/Spacer'; export const getPublishesFromAdminsAdminSpace = async ( admins: string[], groupId ) => { - const queryString = admins.map((name) => `name=${name}`).join("&"); + const queryString = admins.map((name) => `name=${name}`).join('&'); const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT_PRIVATE&identifier=admins-symmetric-qchat-group-${groupId}&exactmatchnames=true&limit=0&reverse=true&${queryString}&prefix=true`; const response = await fetch(url); + if (!response.ok) { - throw new Error("network error"); + throw new Error('network error'); } const adminData = await response.json(); const filterId = adminData.filter( (data: any) => data.identifier === `admins-symmetric-qchat-group-${groupId}` ); + if (filterId?.length === 0) { return false; } + const sortedData = filterId.sort((a: any, b: any) => { // Get the most recent date for both a and b const dateA = a.updated ? new Date(a.updated) : new Date(a.created); @@ -87,10 +90,11 @@ export const AdminSpaceInner = ({ const dataint8Array = base64ToUint8Array(decryptedKey.data); const decryptedKeyToObject = uint8ArrayToObject(dataint8Array); if (!validateSecretKey(decryptedKeyToObject)) - throw new Error("SecretKey is not valid"); + throw new Error('SecretKey is not valid'); setAdminGroupSecretKey(decryptedKeyToObject); setAdminGroupSecretKeyPublishDetails(getLatestPublish); } catch (error) { + console.log(error); } finally { setIsFetchingAdminGroupSecretKey(false); } @@ -106,6 +110,7 @@ export const AdminSpaceInner = ({ if (getLatestPublish === false) setGroupSecretKeyPublishDetails(false); setGroupSecretKeyPublishDetails(getLatestPublish); } catch (error) { + console.log(error); } finally { setIsFetchingGroupSecretKey(false); } @@ -113,15 +118,17 @@ export const AdminSpaceInner = ({ const createCommonSecretForAdmins = async () => { try { - const fee = await getFee("ARBITRARY"); + const fee = await getFee('ARBITRARY'); + await show({ - message: "Would you like to perform an ARBITRARY transaction?", - publishFee: fee.fee + " QORT", + message: 'Would you like to perform an ARBITRARY transaction?', + publishFee: fee.fee + ' QORT', }); + setIsLoadingPublishKey(true); window - .sendMessage("encryptAndPublishSymmetricKeyGroupChatForAdmins", { + .sendMessage('encryptAndPublishSymmetricKeyGroupChatForAdmins', { groupId: selectedGroup, previousData: adminGroupSecretKey, admins: adminsWithNames, @@ -129,27 +136,29 @@ export const AdminSpaceInner = ({ .then((response) => { if (!response?.error) { setInfoSnackCustom({ - type: "success", + type: 'success', message: - "Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.", + 'Successfully re-encrypted secret key. It may take a couple of minutes for the changes to propagate. Refresh the group in 5 mins.', }); setOpenSnackGlobal(true); return; } setInfoSnackCustom({ - type: "error", - message: response?.error || "unable to re-encrypt secret key", + type: 'error', + message: response?.error || 'unable to re-encrypt secret key', }); setOpenSnackGlobal(true); }) .catch((error) => { setInfoSnackCustom({ - type: "error", - message: error?.message || "unable to re-encrypt secret key", + type: 'error', + message: error?.message || 'unable to re-encrypt secret key', }); setOpenSnackGlobal(true); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; useEffect(() => { @@ -159,27 +168,32 @@ export const AdminSpaceInner = ({ return ( - Reminder: After publishing the key, it will take a couple of minutes for it to appear. Please just wait. + + Reminder: After publishing the key, it will take a couple of minutes for + it to appear. Please just wait. + {isFetchingGroupSecretKey && ( @@ -191,33 +205,47 @@ export const AdminSpaceInner = ({ )} {groupSecretKeyPublishDetails && ( - Last encryption date:{" "} + Last encryption date:{' '} {formatTimestampForum( groupSecretKeyPublishDetails?.updated || groupSecretKeyPublishDetails?.created - )}{" "} + )}{' '} {` by ${groupSecretKeyPublishDetails?.name}`} )} - setIsForceShowCreationKeyPopup(true)} variant="contained"> + setIsForceShowCreationKeyPopup(true)} + variant="contained" + > Publish group secret key + - This key is to encrypt GROUP related content. This is the only one used in this UI as of now. All group members will be able to see content encrypted with this key. + + + This key is to encrypt GROUP related content. This is the only one + used in this UI as of now. All group members will be able to see + content encrypted with this key. + + + {isFetchingAdminGroupSecretKey && ( @@ -228,20 +256,31 @@ export const AdminSpaceInner = ({ )} {adminGroupSecretKeyPublishDetails && ( - Last encryption date:{" "} + Last encryption date:{' '} {formatTimestampForum( adminGroupSecretKeyPublishDetails?.updated || adminGroupSecretKeyPublishDetails?.created )} )} - + Publish admin secret key + - This key is to encrypt ADMIN related content. Only admins would see content encrypted with it. + + + This key is to encrypt ADMIN related content. Only admins would see + content encrypted with it. + ); diff --git a/src/components/Chat/AnnouncementDiscussion.tsx b/src/components/Chat/AnnouncementDiscussion.tsx index 5ebe885..ed06a49 100644 --- a/src/components/Chat/AnnouncementDiscussion.tsx +++ b/src/components/Chat/AnnouncementDiscussion.tsx @@ -83,7 +83,9 @@ export const AnnouncementDiscussion = ({ [`${identifier}-${name}`]: messageData, }; }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const publishAnc = async ({ encryptedData, identifier }: any) => { @@ -107,7 +109,9 @@ export const AnnouncementDiscussion = ({ rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const setTempData = async () => { @@ -123,7 +127,9 @@ export const AnnouncementDiscussion = ({ }); setTempPublishedList(tempData); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const publishComment = async () => { @@ -235,7 +241,9 @@ export const AnnouncementDiscussion = ({ for (const data of responseData) { getData({ name: data.name, identifier: data.identifier }, isPrivate); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const combinedListTempAndReal = useMemo(() => { diff --git a/src/components/Chat/ChatDirect.tsx b/src/components/Chat/ChatDirect.tsx index 1af3f8e..49d7f94 100644 --- a/src/components/Chat/ChatDirect.tsx +++ b/src/components/Chat/ChatDirect.tsx @@ -7,13 +7,12 @@ import React, { useState, } from 'react'; -import { objectToBase64 } from '../../qdn/encryption/group-encryption'; import { ChatList } from './ChatList'; import '@chatscope/chat-ui-kit-styles/dist/default/styles.min.css'; import Tiptap from './TipTap'; import { CustomButton } from '../../styles/App-styles'; import CircularProgress from '@mui/material/CircularProgress'; -import { Box, ButtonBase, Input, Typography } from '@mui/material'; +import { Box, ButtonBase, Input, Typography, useTheme } from '@mui/material'; import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; import { getNameInfo } from '../Group/Group'; import { Spacer } from '../../common/Spacer'; @@ -36,7 +35,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'; +import { ReplyPreview } from './MessageItem'; const uid = new ShortUniqueId({ length: 5 }); @@ -52,6 +51,7 @@ export const ChatDirect = ({ close, setMobileViewModeKeepOpen, }) => { + const theme = useTheme(); const { queueChats, addToQueue, processWithNewMessages } = useMessageQueue(); const [isFocusedParent, setIsFocusedParent] = useState(false); const [onEditMessage, setOnEditMessage] = useState(null); @@ -87,7 +87,9 @@ export const ChatDirect = ({ const publicKey = await getPublicKey(address); if (publicKeyOfRecipientRef.current !== selectedDirect?.address) return; setPublicKeyOfRecipient(publicKey); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const tempMessages = useMemo(() => { @@ -167,6 +169,7 @@ export const ChatDirect = ({ text: item.message, unread: item?.sender === myAddress ? false : true, })); + setMessages((prev) => [...prev, ...formatted]); setChatReferences((prev) => { const organizedChatReferences = { ...prev }; @@ -183,7 +186,9 @@ export const ChatDirect = ({ {}), edit: item, }; - } catch (error) {} + } catch (error) { + console.log(error); + } }); return organizedChatReferences; }); @@ -214,7 +219,9 @@ export const ChatDirect = ({ {}), edit: item, }; - } catch (error) {} + } catch (error) { + console.log(error); + } }); return organizedChatReferences; }); @@ -227,7 +234,9 @@ export const ChatDirect = ({ rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const forceCloseWebSocket = () => { @@ -368,7 +377,6 @@ export const ChatDirect = ({ senderName: myName, }); setNewChat(null); - window .sendMessage('addTimestampEnterChat', { timestamp: Date.now(), @@ -396,7 +404,6 @@ export const ChatDirect = ({ }); } catch (error) { throw new Error(error); - } finally { } }; const clearEditorContent = () => { @@ -537,39 +544,39 @@ export const ChatDirect = ({ return ( {!isMobile && ( Close Direct Chat @@ -579,26 +586,26 @@ export const ChatDirect = ({ {isMobile && ( @@ -623,10 +630,10 @@ export const ChatDirect = ({ {replyMessage && ( @@ -723,9 +728,9 @@ export const ChatDirect = ({ {onEditMessage && ( @@ -735,7 +740,6 @@ export const ChatDirect = ({ onClick={() => { setReplyMessage(null); setOnEditMessage(null); - clearEditorContent(); }} > @@ -756,9 +760,9 @@ export const ChatDirect = ({ {isSending && ( )} @@ -815,12 +820,14 @@ export const ChatDirect = ({ + + { @@ -192,7 +194,9 @@ export const ChatGroup = ({ handleSecretKeyCreationInProgress(); return; } - } catch (error) {} + } catch (error) { + console.log(error); + } }); }; @@ -578,7 +582,9 @@ export const ChatGroup = ({ rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const forceCloseWebSocket = () => { @@ -621,7 +627,9 @@ export const ChatGroup = ({ middletierFunc(JSON.parse(e.data), selectedGroup); setIsLoading(false); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; socketRef.current.onclose = () => { clearTimeout(groupSocketTimeoutRef.current); @@ -700,7 +708,9 @@ export const ChatGroup = ({ rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const sendChatGroup = async ({ diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx index 98dd48b..87f7658 100644 --- a/src/components/Chat/GroupAnnouncements.tsx +++ b/src/components/Chat/GroupAnnouncements.tsx @@ -106,7 +106,9 @@ export const decryptPublishes = async (encryptedMessages: any[], secretKey) => { rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; export const handleUnencryptedPublishes = (publishes) => { let publishesData = []; @@ -117,7 +119,9 @@ export const handleUnencryptedPublishes = (publishes) => { if (decodedData) { publishesData.push({ decryptedData: decodedData }); } - } catch (error) {} + } catch (error) { + console.log(error); + } }); return publishesData; }; @@ -236,7 +240,9 @@ export const GroupAnnouncements = ({ rej(error.message || 'An error occurred'); }); }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const publishAnc = async ({ encryptedData, identifier }: any) => { @@ -286,7 +292,9 @@ export const GroupAnnouncements = ({ }); setTempPublishedList(tempData); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const publishAnnouncement = async () => { @@ -422,7 +430,9 @@ export const GroupAnnouncements = ({ isPrivate ); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const interval = useRef(null); @@ -449,7 +459,9 @@ export const GroupAnnouncements = ({ }, isPrivate ); - } catch (error) {} + } catch (error) { + console.log(error); + } } setAnnouncements(responseData); return; @@ -467,7 +479,9 @@ export const GroupAnnouncements = ({ { name: data.name, identifier: data.identifier }, isPrivate ); - } catch (error) {} + } catch (error) { + console.log(error); + } } setAnnouncements((prev) => [...newArray, ...prev]); } catch (error) { diff --git a/src/components/Embeds/Embed.tsx b/src/components/Embeds/Embed.tsx index 65b2cef..bef39c6 100644 --- a/src/components/Embeds/Embed.tsx +++ b/src/components/Embeds/Embed.tsx @@ -1,42 +1,46 @@ -import React, { useEffect, useMemo, useRef, useState } from "react"; -import { getBaseApiReact } from "../../App"; +import React, { useEffect, useMemo, useRef, useState } from 'react'; +import { getBaseApiReact } from '../../App'; +import { CustomizedSnackbars } from '../Snackbar/Snackbar'; -import { CustomizedSnackbars } from "../Snackbar/Snackbar"; +import { extractComponents } from '../Chat/MessageDisplay'; +import { executeEvent } from '../../utils/events'; -import { extractComponents } from "../Chat/MessageDisplay"; -import { executeEvent } from "../../utils/events"; - -import { base64ToBlobUrl } from "../../utils/fileReading"; -import { useRecoilState, useRecoilValue, useSetRecoilState } from "recoil"; -import { blobControllerAtom, blobKeySelector, resourceKeySelector, selectedGroupIdAtom } from "../../atoms/global"; -import { parseQortalLink } from "./embed-utils"; -import { PollCard } from "./PollEmbed"; -import { ImageCard } from "./ImageEmbed"; -import { AttachmentCard } from "./AttachmentEmbed"; -import { decodeIfEncoded } from "../../utils/decode"; +import { base64ToBlobUrl } from '../../utils/fileReading'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; +import { + blobControllerAtom, + blobKeySelector, + resourceKeySelector, + selectedGroupIdAtom, +} from '../../atoms/global'; +import { parseQortalLink } from './embed-utils'; +import { PollCard } from './PollEmbed'; +import { ImageCard } from './ImageEmbed'; +import { AttachmentCard } from './AttachmentEmbed'; +import { decodeIfEncoded } from '../../utils/decode'; const getPoll = async (name) => { const pollName = name; const url = `${getBaseApiReact()}/polls/${pollName}`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); - if (responseData?.message?.includes("POLL_NO_EXISTS")) { - throw new Error("POLL_NO_EXISTS"); + if (responseData?.message?.includes('POLL_NO_EXISTS')) { + throw new Error('POLL_NO_EXISTS'); } else if (responseData?.pollName) { const urlVotes = `${getBaseApiReact()}/polls/votes/${pollName}`; const responseVotes = await fetch(urlVotes, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); @@ -49,56 +53,64 @@ const getPoll = async (name) => { }; export const Embed = ({ embedLink }) => { - const [errorMsg, setErrorMsg] = useState(""); + const [errorMsg, setErrorMsg] = useState(''); const [isLoading, setIsLoading] = useState(false); const [poll, setPoll] = useState(null); - const [type, setType] = useState(""); + const [type, setType] = useState(''); const hasFetched = useRef(false); const [openSnack, setOpenSnack] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const [external, setExternal] = useState(null); - const [imageUrl, setImageUrl] = useState(""); + const [imageUrl, setImageUrl] = useState(''); const [parsedData, setParsedData] = useState(null); const setBlobs = useSetRecoilState(blobControllerAtom); - const [selectedGroupId] = useRecoilState(selectedGroupIdAtom) - const resourceData = useMemo(()=> { + const [selectedGroupId] = useRecoilState(selectedGroupIdAtom); + const resourceData = useMemo(() => { const parsedDataOnTheFly = parseQortalLink(embedLink); - if(parsedDataOnTheFly?.service && parsedDataOnTheFly?.name && parsedDataOnTheFly?.identifier){ + if ( + parsedDataOnTheFly?.service && + parsedDataOnTheFly?.name && + parsedDataOnTheFly?.identifier + ) { return { - service : parsedDataOnTheFly?.service, + service: parsedDataOnTheFly?.service, name: parsedDataOnTheFly?.name, identifier: parsedDataOnTheFly?.identifier, - fileName: parsedDataOnTheFly?.fileName ? decodeURIComponent(parsedDataOnTheFly?.fileName) : null, - mimeType: parsedDataOnTheFly?.mimeType ? decodeURIComponent(parsedDataOnTheFly?.mimeType) : null, - key: parsedDataOnTheFly?.key ? decodeURIComponent(parsedDataOnTheFly?.key) : null, - } + fileName: parsedDataOnTheFly?.fileName + ? decodeURIComponent(parsedDataOnTheFly?.fileName) + : null, + mimeType: parsedDataOnTheFly?.mimeType + ? decodeURIComponent(parsedDataOnTheFly?.mimeType) + : null, + key: parsedDataOnTheFly?.key + ? decodeURIComponent(parsedDataOnTheFly?.key) + : null, + }; } else { - return null + return null; } - }, [embedLink]) + }, [embedLink]); - const keyIdentifier = useMemo(()=> { - - if(resourceData){ - return `${resourceData.service}-${resourceData.name}-${resourceData.identifier}` + const keyIdentifier = useMemo(() => { + if (resourceData) { + return `${resourceData.service}-${resourceData.name}-${resourceData.identifier}`; } else { - return undefined + return undefined; } - }, [resourceData]) + }, [resourceData]); const blobUrl = useRecoilValue(blobKeySelector(keyIdentifier)); const handlePoll = async (parsedData) => { try { setIsLoading(true); - setErrorMsg(""); - setType("POLL"); + setErrorMsg(''); + setType('POLL'); if (!parsedData?.name) - throw new Error("Invalid poll embed link. Missing name."); + throw new Error('Invalid poll embed link. Missing name.'); const pollRes = await getPoll(parsedData.name); setPoll(pollRes); - } catch (error) { - setErrorMsg(error?.message || "Invalid embed link"); + setErrorMsg(error?.message || 'Invalid embed link'); } finally { setIsLoading(false); } @@ -106,8 +118,8 @@ export const Embed = ({ embedLink }) => { const getImage = async ({ identifier, name, service }, key, parsedData) => { try { - if(blobUrl?.blobUrl){ - return blobUrl?.blobUrl + if (blobUrl?.blobUrl) { + return blobUrl?.blobUrl; } let numberOfTries = 0; let imageFinalUrl = null; @@ -116,76 +128,76 @@ export const Embed = ({ embedLink }) => { const urlStatus = `${getBaseApiReact()}/arbitrary/resource/status/${service}/${name}/${identifier}?build=true`; const responseStatus = await fetch(urlStatus, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await responseStatus.json(); - if (responseData?.status === "READY") { + if (responseData?.status === 'READY') { if (parsedData?.encryptionType) { const urlData = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?encoding=base64`; const responseData = await fetch(urlData, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const data = await responseData.text(); if (data) { - let decryptedData + let decryptedData; try { - if(key && encryptionType === 'private'){ + if (key && encryptionType === 'private') { decryptedData = await window.sendMessage( - "DECRYPT_DATA_WITH_SHARING_KEY", - - { - encryptedData: data, + 'DECRYPT_DATA_WITH_SHARING_KEY', + + { + encryptedData: data, key: decodeURIComponent(key), - } - + } ); } - if(encryptionType === 'group'){ - + if (encryptionType === 'group') { decryptedData = await window.sendMessage( - "DECRYPT_QORTAL_GROUP_DATA", - - { - data64: data, - groupId: selectedGroupId, - } - - ); + 'DECRYPT_QORTAL_GROUP_DATA', - } + { + data64: data, + groupId: selectedGroupId, + } + ); + } } catch (error) { - throw new Error('Unable to decrypt') + throw new Error('Unable to decrypt'); } - - if (!decryptedData || decryptedData?.error) throw new Error("Could not decrypt data"); - imageFinalUrl = base64ToBlobUrl(decryptedData, parsedData?.mimeType ? decodeURIComponent(parsedData?.mimeType) : undefined) - setBlobs((prev=> { + + if (!decryptedData || decryptedData?.error) + throw new Error('Could not decrypt data'); + imageFinalUrl = base64ToBlobUrl( + decryptedData, + parsedData?.mimeType + ? decodeURIComponent(parsedData?.mimeType) + : undefined + ); + setBlobs((prev) => { return { ...prev, [`${service}-${name}-${identifier}`]: { blobUrl: imageFinalUrl, - timestamp: Date.now() - } - } - })) + timestamp: Date.now(), + }, + }; + }); } else { - throw new Error('No data for image') + throw new Error('No data for image'); } - } else { - imageFinalUrl = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?async=true`; - - // If parsedData is used here, it must be defined somewhere - - } + imageFinalUrl = `${getBaseApiReact()}/arbitrary/${service}/${name}/${identifier}?async=true`; + + // If parsedData is used here, it must be defined somewhere + } } }; @@ -203,18 +215,19 @@ export const Embed = ({ embedLink }) => { } if (imageFinalUrl) { - return imageFinalUrl; } else { setErrorMsg( - "Unable to download IMAGE. Please try again later by clicking the refresh button" + 'Unable to download IMAGE. Please try again later by clicking the refresh button' ); return null; } } catch (error) { - console.error("Error fetching image:", error); + console.error('Error fetching image:', error); setErrorMsg( - error?.error || error?.message || "An unexpected error occurred while trying to download the image" + error?.error || + error?.message || + 'An unexpected error occurred while trying to download the image' ); return null; } @@ -223,25 +236,27 @@ export const Embed = ({ embedLink }) => { const handleImage = async (parsedData) => { try { setIsLoading(true); - setErrorMsg(""); + setErrorMsg(''); if (!parsedData?.name || !parsedData?.service || !parsedData?.identifier) - throw new Error("Invalid image embed link. Missing param."); - let image = await getImage({ - name: parsedData.name, - service: parsedData.service, - identifier: parsedData?.identifier, - }, parsedData?.key, parsedData); - - setImageUrl(image); + throw new Error('Invalid image embed link. Missing param.'); + let image = await getImage( + { + name: parsedData.name, + service: parsedData.service, + identifier: parsedData?.identifier, + }, + parsedData?.key, + parsedData + ); + setImageUrl(image); } catch (error) { - setErrorMsg(error?.message || "Invalid embed link"); + setErrorMsg(error?.message || 'Invalid embed link'); } finally { setIsLoading(false); } }; - const handleLink = () => { try { const parsedData = parseQortalLink(embedLink); @@ -254,28 +269,26 @@ export const Embed = ({ embedLink }) => { setExternal(res); } } - } catch (error) { - - } + } catch (error) {} switch (type) { - case "POLL": + case 'POLL': { handlePoll(parsedData); } break; - case "IMAGE": - setType("IMAGE"); + case 'IMAGE': + setType('IMAGE'); + + break; + case 'ATTACHMENT': + setType('ATTACHMENT'); break; - case "ATTACHMENT": - setType("ATTACHMENT"); - - break; default: break; } } catch (error) { - setErrorMsg(error?.message || "Invalid embed link"); + setErrorMsg(error?.message || 'Invalid embed link'); } }; @@ -284,13 +297,13 @@ export const Embed = ({ embedLink }) => { const parsedData = parseQortalLink(embedLink); handleImage(parsedData); } catch (error) { - setErrorMsg(error?.message || "Invalid embed link"); + setErrorMsg(error?.message || 'Invalid embed link'); } }; const openExternal = () => { - executeEvent("addTab", { data: external }); - executeEvent("open-apps-mode", {}); + executeEvent('addTab', { data: external }); + executeEvent('open-apps-mode', {}); }; useEffect(() => { @@ -299,8 +312,6 @@ export const Embed = ({ embedLink }) => { hasFetched.current = true; }, [embedLink]); - - const resourceDetails = useRecoilValue(resourceKeySelector(keyIdentifier)); const { parsedType, encryptionType } = useMemo(() => { @@ -312,15 +323,17 @@ export const Embed = ({ embedLink }) => { parsedType = parsedDataOnTheFly.type; } if (parsedDataOnTheFly?.encryptionType) { - encryptionType = parsedDataOnTheFly?.encryptionType + encryptionType = parsedDataOnTheFly?.encryptionType; } - } catch (error) {} + } catch (error) { + console.log(error); + } return { parsedType, encryptionType }; }, [embedLink]); return ( - {parsedType === "POLL" && ( + {parsedType === 'POLL' && ( { errorMsg={errorMsg} /> )} - {parsedType === "IMAGE" && ( + {parsedType === 'IMAGE' && ( { )} {parsedType === 'ATTACHMENT' && ( { ); }; - - - - - - - - diff --git a/src/components/Embeds/PollEmbed.tsx b/src/components/Embeds/PollEmbed.tsx index 3da02c3..65e5983 100644 --- a/src/components/Embeds/PollEmbed.tsx +++ b/src/components/Embeds/PollEmbed.tsx @@ -1,5 +1,5 @@ -import React, { useContext, useEffect, useState } from "react"; -import { MyContext } from "../../App"; +import React, { useContext, useEffect, useState } from 'react'; +import { MyContext } from '../../App'; import { Card, CardContent, @@ -12,384 +12,389 @@ import { Box, ButtonBase, Divider, - -} from "@mui/material"; -import { getNameInfo } from "../Group/Group"; -import PollIcon from "@mui/icons-material/Poll"; -import { getFee } from "../../background"; -import RefreshIcon from "@mui/icons-material/Refresh"; -import { Spacer } from "../../common/Spacer"; -import OpenInNewIcon from "@mui/icons-material/OpenInNew"; -import { CustomLoader } from "../../common/CustomLoader"; - +} from '@mui/material'; +import { getNameInfo } from '../Group/Group'; +import PollIcon from '@mui/icons-material/Poll'; +import { getFee } from '../../background'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import { Spacer } from '../../common/Spacer'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import { CustomLoader } from '../../common/CustomLoader'; export const PollCard = ({ - poll, - setInfoSnack, - setOpenSnack, - refresh, - openExternal, - external, - isLoadingParent, - errorMsg, - }) => { - const [selectedOption, setSelectedOption] = useState(""); - const [ownerName, setOwnerName] = useState(""); - const [showResults, setShowResults] = useState(false); - const [isOpen, setIsOpen] = useState(false); - const { show, userInfo } = useContext(MyContext); - const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); - const handleVote = async () => { - const fee = await getFee("VOTE_ON_POLL"); - - await show({ - message: `Do you accept this VOTE_ON_POLL transaction? POLLS are public!`, - publishFee: fee.fee + " QORT", - }); - setIsLoadingSubmit(true); - - window - .sendMessage( - "voteOnPoll", - { - pollName: poll?.info?.pollName, - optionIndex: +selectedOption, - }, - 60000 - ) - .then((response) => { - setIsLoadingSubmit(false); - if (response.error) { - setInfoSnack({ - type: "error", - message: response?.error || "Unable to vote.", - }); - setOpenSnack(true); - return; - } else { - setInfoSnack({ - type: "success", - message: - "Successfully voted. Please wait a couple minutes for the network to propogate the changes.", - }); - setOpenSnack(true); - } - }) - .catch((error) => { - setIsLoadingSubmit(false); + poll, + setInfoSnack, + setOpenSnack, + refresh, + openExternal, + external, + isLoadingParent, + errorMsg, +}) => { + const [selectedOption, setSelectedOption] = useState(''); + const [ownerName, setOwnerName] = useState(''); + const [showResults, setShowResults] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const { show, userInfo } = useContext(MyContext); + const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); + const handleVote = async () => { + const fee = await getFee('VOTE_ON_POLL'); + + await show({ + message: `Do you accept this VOTE_ON_POLL transaction? POLLS are public!`, + publishFee: fee.fee + ' QORT', + }); + setIsLoadingSubmit(true); + + window + .sendMessage( + 'voteOnPoll', + { + pollName: poll?.info?.pollName, + optionIndex: +selectedOption, + }, + 60000 + ) + .then((response) => { + setIsLoadingSubmit(false); + if (response.error) { setInfoSnack({ - type: "error", - message: error?.message || "Unable to vote.", + type: 'error', + message: response?.error || 'Unable to vote.', + }); + setOpenSnack(true); + return; + } else { + setInfoSnack({ + type: 'success', + message: + 'Successfully voted. Please wait a couple minutes for the network to propogate the changes.', }); setOpenSnack(true); - }); - }; - - const getName = async (owner) => { - try { - const res = await getNameInfo(owner); - if (res) { - setOwnerName(res); } - } catch (error) {} - }; - - useEffect(() => { - if (poll?.info?.owner) { - getName(poll.info.owner); + }) + .catch((error) => { + setIsLoadingSubmit(false); + setInfoSnack({ + type: 'error', + message: error?.message || 'Unable to vote.', + }); + setOpenSnack(true); + }); + }; + + const getName = async (owner) => { + try { + const res = await getNameInfo(owner); + if (res) { + setOwnerName(res); } - }, [poll?.info?.owner]); - - return ( - { + if (poll?.info?.owner) { + getName(poll.info.owner); + } + }, [poll?.info?.owner]); + + return ( + + - - + POLL embed + + + + - POLL embed - - + + {external && ( - - {external && ( - - - - )} - + )} - + + - + + + + {!isOpen && !errorMsg && ( + <> + + { + setIsOpen(true); + }} + > + Show poll + + > + )} + {isLoadingParent && isOpen && ( + - Created by {ownerName || poll?.info?.owner} - - - - - {!isOpen && !errorMsg && ( - <> - - { - setIsOpen(true); - }} - > - Show poll - - > - )} - {isLoadingParent && isOpen && ( - - {" "} - {" "} - - )} - {errorMsg && ( - - {" "} - - {errorMsg} - {" "} - - )} - - - - {' '} + + )} + {errorMsg && ( + - + > + {' '} - Options - - setSelectedOption(e.target.value)} - > - {poll?.info?.pollOptions?.map((option, index) => ( - - } - label={option?.optionName} - sx={{ - "& .MuiFormControlLabel-label": { - fontSize: "14px", - - }, - }} - /> - ))} - - - - Vote - - {' '} + + )} + + + + + + + Options + + setSelectedOption(e.target.value)} + > + {poll?.info?.pollOptions?.map((option, index) => ( + + } + label={option?.optionName} sx={{ - fontSize: "14px", - fontStyle: "italic", + '& .MuiFormControlLabel-label': { + fontSize: '14px', + }, + }} + /> + ))} + + + + Vote + + + {' '} + {`${poll?.votes?.totalVotes} ${ + poll?.votes?.totalVotes === 1 ? ' vote' : ' votes' + }`} + + + + + item?.voterPublicKey === userInfo?.publicKey + ) + ? 'visible' + : 'hidden', + }} + > + You've already voted. + + + {isLoadingSubmit && ( + + Is processing transaction, please wait... + + )} + { + setShowResults((prev) => !prev); + }} + > + {showResults ? 'hide ' : 'show '} results + + + {showResults && } + + + ); +}; + +const PollResults = ({ votes }) => { + const maxVotes = Math.max( + ...votes?.voteCounts?.map((option) => option.voteCount) + ); + const options = votes?.voteCounts; + return ( + + {options + .sort((a, b) => b.voteCount - a.voteCount) // Sort options by votes (highest first) + .map((option, index) => ( + + + - {" "} - {`${poll?.votes?.totalVotes} ${ - poll?.votes?.totalVotes === 1 ? " vote" : " votes" - }`} + {`${index + 1}. ${option.optionName}`} + + + {option.voteCount} votes - - - item?.voterPublicKey === userInfo?.publicKey - ) - ? "visible" - : "hidden", + mt: 1, + height: 10, + backgroundColor: '#e0e0e0', + borderRadius: 5, + overflow: 'hidden', }} > - You've already voted. - - - {isLoadingSubmit && ( - - Is processing transaction, please wait... - - )} - { - setShowResults((prev) => !prev); - }} - > - {showResults ? "hide " : "show "} results - - - {showResults && } - - - ); - }; - - const PollResults = ({ votes }) => { - const maxVotes = Math.max( - ...votes?.voteCounts?.map((option) => option.voteCount) - ); - const options = votes?.voteCounts; - return ( - - {options - .sort((a, b) => b.voteCount - a.voteCount) // Sort options by votes (highest first) - .map((option, index) => ( - - - - {`${index + 1}. ${option.optionName}`} - - - {option.voteCount} votes - - - - + /> - ))} - - ); - }; \ No newline at end of file + + ))} + + ); +}; diff --git a/src/components/Group/AddGroupList.tsx b/src/components/Group/AddGroupList.tsx index ccd4850..433d0e4 100644 --- a/src/components/Group/AddGroupList.tsx +++ b/src/components/Group/AddGroupList.tsx @@ -7,7 +7,7 @@ import { Popover, TextField, Typography, -} from "@mui/material"; +} from '@mui/material'; import React, { useCallback, useContext, @@ -15,20 +15,20 @@ import React, { useMemo, useRef, useState, -} from "react"; +} from 'react'; import { AutoSizer, CellMeasurer, CellMeasurerCache, List, -} from "react-virtualized"; -import _ from "lodash"; -import { MyContext, getBaseApiReact } from "../../App"; -import { LoadingButton } from "@mui/lab"; -import { getBaseApi, getFee } from "../../background"; +} from 'react-virtualized'; +import _ from 'lodash'; +import { MyContext, getBaseApiReact } from '../../App'; +import { LoadingButton } from '@mui/lab'; +import { getBaseApi, getFee } from '../../background'; import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; -import { Spacer } from "../../common/Spacer"; +import { Spacer } from '../../common/Spacer'; const cache = new CellMeasurerCache({ fixedWidth: true, defaultHeight: 50, @@ -41,7 +41,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const [popoverAnchor, setPopoverAnchor] = useState(null); // Track which list item the popover is anchored to const [openPopoverIndex, setOpenPopoverIndex] = useState(null); // Track which list item has the popover open const listRef = useRef(); - const [inputValue, setInputValue] = useState(""); + const [inputValue, setInputValue] = useState(''); const [filteredItems, setFilteredItems] = useState(groups); const [isLoading, setIsLoading] = useState(false); @@ -72,9 +72,7 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const getGroups = async () => { try { - const response = await fetch( - `${getBaseApiReact()}/groups/?limit=0` - ); + const response = await fetch(`${getBaseApiReact()}/groups/?limit=0`); const groupData = await response.json(); const filteredGroup = groupData.filter( (item) => !memberGroups.find((group) => group.groupId === item.groupId) @@ -103,23 +101,25 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { const handleJoinGroup = async (group, isOpen) => { try { const groupId = group.groupId; - const fee = await getFee('JOIN_GROUP') - await show({ - message: "Would you like to perform an JOIN_GROUP transaction?" , - publishFee: fee.fee + ' QORT' - }) + const fee = await getFee('JOIN_GROUP'); + await show({ + message: 'Would you like to perform an JOIN_GROUP transaction?', + publishFee: fee.fee + ' QORT', + }); setIsLoading(true); await new Promise((res, rej) => { - window.sendMessage("joinGroup", { - groupId, - }) + window + .sendMessage('joinGroup', { + groupId, + }) .then((response) => { if (!response?.error) { setInfoSnack({ - type: "success", - message: "Successfully requested to join group. It may take a couple of minutes for the changes to propagate", + type: 'success', + message: + 'Successfully requested to join group. It may take a couple of minutes for the changes to propagate', }); - + if (isOpen) { setTxList((prev) => [ { @@ -145,14 +145,14 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { ...prev, ]); } - + setOpenSnack(true); handlePopoverClose(); res(response); return; } else { setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -161,18 +161,18 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { }) .catch((error) => { setInfoSnack({ - type: "error", - message: error.message || "An error occurred", + type: 'error', + message: error.message || 'An error occurred', }); setOpenSnack(true); rej(error); }); - }); setIsLoading(false); - } catch (error) {} finally { + } catch (error) { + console.log(error); + } finally { setIsLoading(false); - } }; @@ -195,30 +195,30 @@ export const AddGroupList = ({ setInfoSnack, setOpenSnack }) => { anchorEl={popoverAnchor} onClose={handlePopoverClose} anchorOrigin={{ - vertical: "bottom", - horizontal: "center", + vertical: 'bottom', + horizontal: 'center', }} transformOrigin={{ - vertical: "top", - horizontal: "center", + vertical: 'top', + horizontal: 'center', }} - style={{ marginTop: "8px" }} + style={{ marginTop: '8px' }} > Join {group?.groupName} {group?.isOpen === false && - "This is a closed/private group, so you will need to wait until an admin accepts your request"} + 'This is a closed/private group, so you will need to wait until an admin accepts your request'} { onClick={(event) => handlePopoverOpen(event, index)} > {group?.isOpen === false && ( - - )} - {group?.isOpen === true && ( - - )} - + + )} + {group?.isOpen === true && ( + + )} + { }; return ( - + Groups list { /> diff --git a/src/components/Group/Forum/GroupMail.tsx b/src/components/Group/Forum/GroupMail.tsx index 38d93e9..bbb1f8d 100644 --- a/src/components/Group/Forum/GroupMail.tsx +++ b/src/components/Group/Forum/GroupMail.tsx @@ -5,10 +5,10 @@ import React, { useMemo, useRef, useState, -} from "react"; -import { Avatar, Box, Popover, Typography } from "@mui/material"; +} from 'react'; +import { Avatar, Box, Popover, Typography } from '@mui/material'; // import { MAIL_SERVICE_TYPE, THREAD_SERVICE_TYPE } from "../../constants/mail"; -import { Thread } from "./Thread"; +import { Thread } from './Thread'; import { AllThreadP, ArrowDownIcon, @@ -38,61 +38,73 @@ import { ThreadSingleLastMessageP, ThreadSingleLastMessageSpanP, ThreadSingleTitle, -} from "./Mail-styles"; +} from './Mail-styles'; import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos'; -import { Spacer } from "../../../common/Spacer"; -import { formatDate, formatTimestamp } from "../../../utils/time"; -import LazyLoad from "../../../common/LazyLoad"; -import { delay } from "../../../utils/helpers"; -import { NewThread } from "./NewThread"; -import { getBaseApi } from "../../../background"; -import { decryptPublishes, getTempPublish, handleUnencryptedPublishes } from "../../Chat/GroupAnnouncements"; -import CheckSVG from "../../../assets/svgs/Check.svg"; -import SortSVG from "../../../assets/svgs/Sort.svg"; -import ArrowDownSVG from "../../../assets/svgs/ArrowDown.svg"; -import { LoadingSnackbar } from "../../Snackbar/LoadingSnackbar"; -import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../../utils/events"; +import { Spacer } from '../../../common/Spacer'; +import { formatDate, formatTimestamp } from '../../../utils/time'; +import LazyLoad from '../../../common/LazyLoad'; +import { delay } from '../../../utils/helpers'; +import { NewThread } from './NewThread'; +import { getBaseApi } from '../../../background'; +import { + decryptPublishes, + getTempPublish, + handleUnencryptedPublishes, +} from '../../Chat/GroupAnnouncements'; +import CheckSVG from '../../../assets/svgs/Check.svg'; +import SortSVG from '../../../assets/svgs/Sort.svg'; +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 { getArbitraryEndpointReact, getBaseApiReact, isMobile } from "../../../App"; -import { WrapperUserAction } from "../../WrapperUserAction"; -import { addDataPublishesFunc, getDataPublishesFunc } from "../Group"; -const filterOptions = ["Recently active", "Newest", "Oldest"]; +import { + getArbitraryEndpointReact, + getBaseApiReact, + isMobile, +} from '../../../App'; +import { WrapperUserAction } from '../../WrapperUserAction'; +import { addDataPublishesFunc, getDataPublishesFunc } from '../Group'; +const filterOptions = ['Recently active', 'Newest', 'Oldest']; -export const threadIdentifier = "DOCUMENT"; +export const threadIdentifier = 'DOCUMENT'; export const GroupMail = ({ selectedGroup, userInfo, getSecretKey, secretKey, - defaultThread, + defaultThread, setDefaultThread, hide, - isPrivate + isPrivate, }) => { const [viewedThreads, setViewedThreads] = React.useState({}); - const [filterMode, setFilterMode] = useState("Recently active"); + const [filterMode, setFilterMode] = useState('Recently active'); const [currentThread, setCurrentThread] = React.useState(null); const [recentThreads, setRecentThreads] = useState([]); const [allThreads, setAllThreads] = useState([]); const [members, setMembers] = useState(null); const [isOpenFilterList, setIsOpenFilterList] = useState(false); const anchorElInstanceFilter = useRef(null); - const [tempPublishedList, setTempPublishedList] = useState([]) - const dataPublishes = useRef({}) + const [tempPublishedList, setTempPublishedList] = useState([]); + const dataPublishes = useRef({}); - const [isLoading, setIsLoading] = useState(false) + const [isLoading, setIsLoading] = useState(false); const groupIdRef = useRef(null); const groupId = useMemo(() => { return selectedGroup?.groupId; }, [selectedGroup]); - useEffect(()=> { - if(!groupId) return - (async ()=> { - const res = await getDataPublishesFunc(groupId, 'thread') - dataPublishes.current = res || {} - })() - }, [groupId]) + useEffect(() => { + if (!groupId) return; + (async () => { + const res = await getDataPublishesFunc(groupId, 'thread'); + dataPublishes.current = res || {}; + })(); + }, [groupId]); useEffect(() => { if (groupId !== groupIdRef?.current) { @@ -103,55 +115,66 @@ export const GroupMail = ({ } }, [groupId]); - const setTempData = async ()=> { + const setTempData = async () => { try { - const getTempAnnouncements = await getTempPublish() - - if(getTempAnnouncements?.thread){ - let tempData = [] - Object.keys(getTempAnnouncements?.thread || {}).map((key)=> { - const value = getTempAnnouncements?.thread[key] - if(value?.data?.groupId === groupIdRef?.current){ - tempData.push(value.data) + const getTempAnnouncements = await getTempPublish(); + + if (getTempAnnouncements?.thread) { + let tempData = []; + Object.keys(getTempAnnouncements?.thread || {}).map((key) => { + const value = getTempAnnouncements?.thread[key]; + if (value?.data?.groupId === groupIdRef?.current) { + tempData.push(value.data); + } + }); + setTempPublishedList(tempData); } - - }) - setTempPublishedList(tempData) - } - } catch (error) { - - } - - } - - const getEncryptedResource = async ({ name, identifier, resource }, isPrivate) => { - let data = dataPublishes.current[`${name}-${identifier}`] - if(!data || (data?.update || data?.created !== (resource?.updated || resource?.created))){ - 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') + } catch (error) {} + }; + const getEncryptedResource = async ( + { name, identifier, resource }, + isPrivate + ) => { + let data = dataPublishes.current[`${name}-${identifier}`]; + if ( + !data || + data?.update || + data?.created !== (resource?.updated || resource?.created) + ) { + 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'); } else { - data = data.data + data = data.data; } - const response = isPrivate === false ? handleUnencryptedPublishes([data]) : await decryptPublishes([{ data }], secretKey); + const response = + isPrivate === false + ? handleUnencryptedPublishes([data]) + : await decryptPublishes([{ data }], secretKey); const messageData = response[0]; return messageData.decryptedData; }; - const updateThreadActivity = async ({threadId, qortalName, groupId, thread}) => { + const updateThreadActivity = async ({ + threadId, + qortalName, + groupId, + thread, + }) => { try { await new Promise((res, rej) => { - window.sendMessage("updateThreadActivity", { - threadId, - qortalName, - groupId, - thread, - }) + window + .sendMessage('updateThreadActivity', { + threadId, + qortalName, + groupId, + thread, + }) .then((response) => { if (!response?.error) { res(response); @@ -160,13 +183,10 @@ export const GroupMail = ({ rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); - } catch (error) { - } finally { } }; @@ -174,9 +194,9 @@ export const GroupMail = ({ const getAllThreads = React.useCallback( async (groupId: string, mode: string, isInitial?: boolean) => { try { - setIsLoading(true) + setIsLoading(true); const offset = isInitial ? 0 : allThreads.length; - const isReverse = mode === "Newest" ? true : false; + const isReverse = mode === 'Newest' ? true : false; if (isInitial) { // dispatch(setIsLoadingCustom("Loading threads")); } @@ -184,9 +204,9 @@ export const GroupMail = ({ 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", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); @@ -209,21 +229,26 @@ export const GroupMail = ({ let threadRes = null; try { threadRes = await Promise.race([ - getEncryptedResource({ - name: message.name, - identifier: message.identifier, - resource: message - }, isPrivate), + getEncryptedResource( + { + name: message.name, + identifier: message.identifier, + resource: message, + }, + isPrivate + ), delay(5000), ]); - } catch (error) {} + } catch (error) { + console.log(error); + } if (threadRes?.title) { fullObject = { ...message, threadData: threadRes, threadOwner: message?.name, - threadId: message.identifier + threadId: message.identifier, }; } } @@ -251,7 +276,7 @@ export const GroupMail = ({ console.log({ error }); } finally { if (isInitial) { - setIsLoading(false) + setIsLoading(false); // dispatch(setIsLoadingCustom(null)); } } @@ -261,21 +286,21 @@ export const GroupMail = ({ const getMailMessages = React.useCallback( async (groupId: string, members: any) => { try { - setIsLoading(true) + setIsLoading(true); const identifier = `thmsg-grp-${groupId}-thread-`; 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", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); const messagesForThread: any = {}; for (const message of responseData) { let str = message.identifier; - const parts = str.split("-"); + const parts = str.split('-'); // Get the second last element const secondLastId = parts[parts.length - 2]; @@ -295,16 +320,16 @@ export const GroupMail = ({ }) .sort((a, b) => b.created - a.created) .slice(0, 10); - + let fullThreadArray: any = []; const getMessageForThreads = newArray.map(async (message: any) => { try { const identifierQuery = message.threadId; 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", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); @@ -324,11 +349,14 @@ export const GroupMail = ({ fullThreadArray.push(fullObject); } else { let threadRes = await Promise.race([ - getEncryptedResource({ - name: thread.name, - identifier: message.threadId, - resource: thread - }, isPrivate), + getEncryptedResource( + { + name: thread.name, + identifier: message.threadId, + resource: thread, + }, + isPrivate + ), delay(10000), ]); if (threadRes?.title) { @@ -353,7 +381,7 @@ export const GroupMail = ({ setRecentThreads(sorted); } catch (error) { } finally { - setIsLoading(false) + setIsLoading(false); // dispatch(setIsLoadingCustom(null)); } }, @@ -361,7 +389,6 @@ export const GroupMail = ({ ); const getMessages = React.useCallback(async () => { - // if ( !groupId || members?.length === 0) return; if (!groupId || isPrivate === null) return; @@ -371,23 +398,23 @@ export const GroupMail = ({ const interval = useRef(null); const firstMount = useRef(false); - const filterModeRef = useRef(""); + const filterModeRef = useRef(''); useEffect(() => { - if(hide) return + if (hide) return; if (filterModeRef.current !== filterMode) { firstMount.current = false; } // if (groupId && !firstMount.current && members.length > 0) { if (groupId && !firstMount.current && isPrivate !== null) { - if (filterMode === "Recently active") { + if (filterMode === 'Recently active') { getMessages(); - } else if (filterMode === "Newest") { - getAllThreads(groupId, "Newest", true); - } else if (filterMode === "Oldest") { - getAllThreads(groupId, "Oldest", true); + } else if (filterMode === 'Newest') { + getAllThreads(groupId, 'Newest', true); + } else if (filterMode === 'Oldest') { + getAllThreads(groupId, 'Oldest', true); } - setTempData() + setTempData(); firstMount.current = true; } }, [groupId, members, filterMode, hide, isPrivate]); @@ -428,19 +455,16 @@ export const GroupMail = ({ } }, []); - - - let listOfThreadsToDisplay = recentThreads; - if (filterMode === "Newest" || filterMode === "Oldest") { + if (filterMode === 'Newest' || filterMode === 'Oldest') { listOfThreadsToDisplay = allThreads; } const onSubmitNewThread = useCallback( (val: any) => { - if (filterMode === "Recently active") { + if (filterMode === 'Recently active') { setRecentThreads((prev) => [val, ...prev]); - } else if (filterMode === "Newest") { + } else if (filterMode === 'Newest') { setAllThreads((prev) => [val, ...prev]); } }, @@ -461,72 +485,77 @@ export const GroupMail = ({ setIsOpenFilterList(false); }; - const refetchThreadsLists = useCallback(()=> { - if (filterMode === "Recently active") { + const refetchThreadsLists = useCallback(() => { + if (filterMode === 'Recently active') { getMessages(); - } else if (filterMode === "Newest") { - getAllThreads(groupId, "Newest", true); - } else if (filterMode === "Oldest") { - getAllThreads(groupId, "Oldest", true); + } else if (filterMode === 'Newest') { + getAllThreads(groupId, 'Newest', true); + } else if (filterMode === 'Oldest') { + getAllThreads(groupId, 'Oldest', true); } - }, [filterMode, isPrivate]) + }, [filterMode, isPrivate]); - const updateThreadActivityCurrentThread = ()=> { - if(!currentThread) return - const thread = currentThread + const updateThreadActivityCurrentThread = () => { + if (!currentThread) return; + const thread = currentThread; updateThreadActivity({ - threadId: thread?.threadId, qortalName: thread?.threadData?.name, groupId: groupId, thread: thread - }) - } + threadId: thread?.threadId, + qortalName: thread?.threadData?.name, + groupId: groupId, + thread: thread, + }); + }; - const setThreadFunc = (data)=> { - const thread = data + const setThreadFunc = (data) => { + const thread = data; setCurrentThread(thread); - if(thread?.threadId && thread?.threadData?.name){ - updateThreadActivity({ - threadId: thread?.threadId, qortalName: thread?.threadData?.name, groupId: groupId, thread: thread - }) - } + if (thread?.threadId && thread?.threadData?.name) { + updateThreadActivity({ + threadId: thread?.threadId, + qortalName: thread?.threadData?.name, + groupId: groupId, + thread: thread, + }); + } setTimeout(() => { - executeEvent("threadFetchMode", { - mode: "last-page" + executeEvent('threadFetchMode', { + mode: 'last-page', }); }, 300); - } + }; - - useEffect(()=> { - if(defaultThread){ - setThreadFunc(defaultThread) - setDefaultThread(null) + useEffect(() => { + if (defaultThread) { + setThreadFunc(defaultThread); + setDefaultThread(null); } - }, [defaultThread]) + }, [defaultThread]); const combinedListTempAndReal = useMemo(() => { // Combine the two lists - const transformTempPublishedList = tempPublishedList.map((item)=> { + const transformTempPublishedList = tempPublishedList.map((item) => { return { ...item, threadData: item.tempData, threadOwner: item?.name, - threadId: item.identifier - } - }) + threadId: item.identifier, + }; + }); const combined = [...transformTempPublishedList, ...listOfThreadsToDisplay]; - + // Remove duplicates based on the "identifier" const uniqueItems = new Map(); - combined.forEach(item => { - uniqueItems.set(item.threadId, item); // This will overwrite duplicates, keeping the last occurrence + combined.forEach((item) => { + uniqueItems.set(item.threadId, 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) => - filterMode === 'Oldest' - ? a.threadData?.createdAt - b.threadData?.createdAt - : b.threadData?.createdAt - a.threadData?.createdAt -); - + filterMode === 'Oldest' + ? a.threadData?.createdAt - b.threadData?.createdAt + : b.threadData?.createdAt - a.threadData?.createdAt + ); + return sortedList; }, [tempPublishedList, listOfThreadsToDisplay, filterMode]); @@ -548,9 +577,9 @@ export const GroupMail = ({ return ( @@ -583,7 +612,7 @@ export const GroupMail = ({ }} sx={{ backgroundColor: - filterMode === filter ? "rgba(74, 158, 244, 1)" : "unset", + filterMode === filter ? 'rgba(74, 158, 244, 1)' : 'unset', }} key={filter} > @@ -606,12 +635,11 @@ export const GroupMail = ({ - {selectedGroup && !currentThread && ( @@ -647,17 +675,22 @@ export const GroupMail = ({ - - {filterMode} + + {filterMode} - + @@ -668,112 +701,119 @@ export const GroupMail = ({ ]; const shouldAppearLighter = hasViewedRecent && - filterMode === "Recently active" && + filterMode === 'Recently active' && thread?.threadData?.createdAt < hasViewedRecent?.timestamp; return ( { setCurrentThread(thread); - if(thread?.threadId && thread?.threadData?.name){ + if (thread?.threadId && thread?.threadData?.name) { updateThreadActivity({ - threadId: thread?.threadId, qortalName: thread?.threadData?.name, groupId: groupId, thread: thread - }) + threadId: thread?.threadId, + qortalName: thread?.threadData?.name, + groupId: groupId, + thread: thread, + }); } }} > - {thread?.threadData?.name?.charAt(0)} - + - by {thread?.threadData?.name} - + {formatTimestamp(thread?.threadData?.createdAt)} {thread?.threadData?.title} - {filterMode === "Recently active" && ( + {filterMode === 'Recently active' && ( - last message:{" "} + last message:{' '} {formatDate(thread?.created)} )} - - { - setTimeout(() => { - executeEvent("threadFetchMode", { - mode: "last-page" - }); - }, 300); - - - }} sx={{ - position: 'absolute', - bottom: '2px', - right: '2px', - borderRadius: '5px', - backgroundColor: '#27282c', - display: 'flex', - gap: '10px', - alignItems: 'center', - padding: '5px', - cursor: 'pointer', - '&:hover': { - background: 'rgba(255, 255, 255, 0.60)' - } - }}> - Last page - + { + setTimeout(() => { + executeEvent('threadFetchMode', { + mode: 'last-page', + }); + }, 300); + }} + sx={{ + position: 'absolute', + bottom: '2px', + right: '2px', + borderRadius: '5px', + backgroundColor: '#27282c', + display: 'flex', + gap: '10px', + alignItems: 'center', + padding: '5px', + cursor: 'pointer', + '&:hover': { + background: 'rgba(255, 255, 255, 0.60)', + }, + }} + > + + Last page + + ); @@ -781,12 +821,12 @@ export const GroupMail = ({ {listOfThreadsToDisplay.length >= 20 && - filterMode !== "Recently active" && ( + filterMode !== 'Recently active' && ( getAllThreads(groupId, filterMode, false)} > @@ -797,7 +837,7 @@ export const GroupMail = ({ diff --git a/src/components/Group/Forum/NewThread.tsx b/src/components/Group/Forum/NewThread.tsx index 4c237ec..dd4c83a 100644 --- a/src/components/Group/Forum/NewThread.tsx +++ b/src/components/Group/Forum/NewThread.tsx @@ -1,11 +1,17 @@ -import React, { useEffect, useRef, useState } from "react"; -import { Box, Button, CircularProgress, Input, Typography } from "@mui/material"; -import ShortUniqueId from "short-unique-id"; -import CloseIcon from "@mui/icons-material/Close"; +import React, { useEffect, useRef, useState } from 'react'; +import { + Box, + Button, + CircularProgress, + Input, + Typography, +} from '@mui/material'; +import ShortUniqueId from 'short-unique-id'; +import CloseIcon from '@mui/icons-material/Close'; -import ModalCloseSVG from "../../../assets/svgs/ModalClose.svg"; +import ModalCloseSVG from '../../../assets/svgs/ModalClose.svg'; -import ComposeIconSVG from "../../../assets/svgs/ComposeIcon.svg"; +import ComposeIconSVG from '../../../assets/svgs/ComposeIcon.svg'; import { AttachmentContainer, @@ -22,20 +28,25 @@ import { NewMessageInputRow, NewMessageSendButton, NewMessageSendP, -} from "./Mail-styles"; +} from './Mail-styles'; -import { ReusableModal } from "./ReusableModal"; -import { Spacer } from "../../../common/Spacer"; -import { formatBytes } from "../../../utils/Size"; -import { CreateThreadIcon } from "../../../assets/svgs/CreateThreadIcon"; -import { SendNewMessage } from "../../../assets/svgs/SendNewMessage"; -import { TextEditor } from "./TextEditor"; -import { MyContext, isMobile, pauseAllQueues, resumeAllQueues } from "../../../App"; -import { getFee } from "../../../background"; -import TipTap from "../../Chat/TipTap"; -import { MessageDisplay } from "../../Chat/MessageDisplay"; -import { CustomizedSnackbars } from "../../Snackbar/Snackbar"; -import { saveTempPublish } from "../../Chat/GroupAnnouncements"; +import { ReusableModal } from './ReusableModal'; +import { Spacer } from '../../../common/Spacer'; +import { formatBytes } from '../../../utils/Size'; +import { CreateThreadIcon } from '../../../assets/svgs/CreateThreadIcon'; +import { SendNewMessage } from '../../../assets/svgs/SendNewMessage'; +import { TextEditor } from './TextEditor'; +import { + MyContext, + isMobile, + pauseAllQueues, + resumeAllQueues, +} from '../../../App'; +import { getFee } from '../../../background'; +import TipTap from '../../Chat/TipTap'; +import { MessageDisplay } from '../../Chat/MessageDisplay'; +import { CustomizedSnackbars } from '../../Snackbar/Snackbar'; +import { saveTempPublish } from '../../Chat/GroupAnnouncements'; const uid = new ShortUniqueId({ length: 8 }); @@ -54,21 +65,21 @@ export function objectToBase64(obj: any) { const jsonString = JSON.stringify(obj); // Step 2: Create a Blob from the JSON string - const blob = new Blob([jsonString], { type: "application/json" }); + const blob = new Blob([jsonString], { type: 'application/json' }); // Step 3: Create a FileReader to read the Blob as a base64-encoded string return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onloadend = () => { - if (typeof reader.result === "string") { + if (typeof reader.result === 'string') { // Remove 'data:application/json;base64,' prefix const base64 = reader.result.replace( - "data:application/json;base64,", - "" + 'data:application/json;base64,', + '' ); resolve(base64); } else { - reject(new Error("Failed to read the Blob as a base64-encoded string")); + reject(new Error('Failed to read the Blob as a base64-encoded string')); } }; reader.onerror = () => { @@ -94,10 +105,11 @@ export const publishGroupEncryptedResource = async ({ identifier, }) => { return new Promise((res, rej) => { - window.sendMessage("publishGroupEncryptedResource", { - encryptedData, - identifier, - }) + window + .sendMessage('publishGroupEncryptedResource', { + encryptedData, + identifier, + }) .then((response) => { if (!response?.error) { res(response); @@ -106,19 +118,19 @@ export const publishGroupEncryptedResource = async ({ rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); }; export const encryptSingleFunc = async (data: string, secretKeyObject: any) => { try { return new Promise((res, rej) => { - window.sendMessage("encryptSingle", { - data, - secretKeyObject, - }) + window + .sendMessage('encryptSingle', { + data, + secretKeyObject, + }) .then((response) => { if (!response?.error) { res(response); @@ -127,11 +139,12 @@ export const encryptSingleFunc = async (data: string, secretKeyObject: any) => { rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; export const NewThread = ({ groupInfo, @@ -145,14 +158,14 @@ export const NewThread = ({ postReply, myName, setPostReply, - isPrivate + isPrivate, }: NewMessageProps) => { const { show } = React.useContext(MyContext); const [isOpen, setIsOpen] = useState(false); - const [value, setValue] = useState(""); + const [value, setValue] = useState(''); const [isSending, setIsSending] = useState(false); - const [threadTitle, setThreadTitle] = useState(""); + const [threadTitle, setThreadTitle] = useState(''); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); const editorRef = useRef(null); @@ -168,44 +181,42 @@ export const NewThread = ({ const closeModal = () => { setIsOpen(false); - setValue(""); - if(setPostReply){ - setPostReply(null) + setValue(''); + if (setPostReply) { + setPostReply(null); } - }; async function publishQDNResource() { try { - pauseAllQueues() - if(isSending) return - setIsSending(true) - let name: string = ""; - let errorMsg = ""; + pauseAllQueues(); + if (isSending) return; + setIsSending(true); + let name: string = ''; + let errorMsg = ''; - name = userInfo?.name || ""; + name = userInfo?.name || ''; const missingFields: string[] = []; if (!isMessage && !threadTitle) { - errorMsg = "Please provide a thread title"; + errorMsg = 'Please provide a thread title'; } if (!name) { - errorMsg = "Cannot send a message without a access to your name"; + errorMsg = 'Cannot send a message without a access to your name'; } if (!groupInfo) { - errorMsg = "Cannot access group information"; + errorMsg = 'Cannot access group information'; } // if (!description) missingFields.push('subject') if (missingFields.length > 0) { - const missingFieldsString = missingFields.join(", "); + const missingFieldsString = missingFields.join(', '); const errMsg = `Missing: ${missingFieldsString}`; errorMsg = errMsg; } - if (errorMsg) { // dispatch( // setNotification({ @@ -217,17 +228,17 @@ export const NewThread = ({ } const htmlContent = editorRef.current.getHTML(); - - if (!htmlContent?.trim() || htmlContent?.trim() === "") - throw new Error("Please provide a first message to the thread"); - const fee = await getFee("ARBITRARY"); + + if (!htmlContent?.trim() || htmlContent?.trim() === '') + throw new Error('Please provide a first message to the thread'); + const fee = await getFee('ARBITRARY'); let feeToShow = fee.fee; if (!isMessage) { feeToShow = +feeToShow * 2; } await show({ - message: "Would you like to perform a ARBITRARY transaction?", - publishFee: feeToShow + " QORT", + message: 'Would you like to perform a ARBITRARY transaction?', + publishFee: feeToShow + ' QORT', }); let reply = null; @@ -245,20 +256,21 @@ export const NewThread = ({ threadOwner: currentThread?.threadData?.name || name, reply, }; - - const secretKey = isPrivate === false ? null : await getSecretKey(false, true); + + const secretKey = + isPrivate === false ? null : await getSecretKey(false, true); if (!secretKey && isPrivate) { - throw new Error("Cannot get group secret key"); + throw new Error('Cannot get group secret key'); } - + if (!isMessage) { const idThread = uid.rnd(); const idMsg = uid.rnd(); const messageToBase64 = await objectToBase64(mailObject); - const encryptSingleFirstPost = isPrivate === false ? messageToBase64 : await encryptSingleFunc( - messageToBase64, - secretKey - ); + const encryptSingleFirstPost = + isPrivate === false + ? messageToBase64 + : await encryptSingleFunc(messageToBase64, secretKey); const threadObject = { title: threadTitle, groupId: groupInfo.id, @@ -267,10 +279,10 @@ export const NewThread = ({ }; const threadToBase64 = await objectToBase64(threadObject); - const encryptSingleThread = isPrivate === false ? threadToBase64 : await encryptSingleFunc( - threadToBase64, - secretKey - ); + const encryptSingleThread = + isPrivate === false + ? threadToBase64 + : await encryptSingleFunc(threadToBase64, secretKey); let identifierThread = `grp-${groupInfo.groupId}-thread-${idThread}`; await publishGroupEncryptedResource({ identifier: identifierThread, @@ -288,23 +300,27 @@ export const NewThread = ({ service: 'DOCUMENT', tempData: threadObject, created: Date.now(), - groupId: groupInfo.groupId - } + groupId: groupInfo.groupId, + }; const dataToSaveToStoragePost = { name: myName, identifier: identifierPost, service: 'DOCUMENT', tempData: mailObject, created: Date.now(), - threadId: identifierThread - } - await saveTempPublish({data: dataToSaveToStorage, key: 'thread'}) - await saveTempPublish({data: dataToSaveToStoragePost, key: 'thread-post'}) - setInfoSnack({ - type: "success", - message: "Successfully created thread. It may take some time for the publish to propagate", + threadId: identifierThread, + }; + await saveTempPublish({ data: dataToSaveToStorage, key: 'thread' }); + await saveTempPublish({ + data: dataToSaveToStoragePost, + key: 'thread-post', }); - setOpenSnack(true) + setInfoSnack({ + type: 'success', + message: + 'Successfully created thread. It may take some time for the publish to propagate', + }); + setOpenSnack(true); // dispatch( // setNotification({ @@ -313,35 +329,36 @@ export const NewThread = ({ // }) // ); if (publishCallback) { - publishCallback() - + publishCallback(); } closeModal(); } else { - - if (!currentThread) throw new Error("unable to locate thread Id"); + if (!currentThread) throw new Error('unable to locate thread Id'); const idThread = currentThread.threadId; const messageToBase64 = await objectToBase64(mailObject); - const encryptSinglePost = isPrivate === false ? messageToBase64 : await encryptSingleFunc( - messageToBase64, - secretKey - ); + const encryptSinglePost = + isPrivate === false + ? messageToBase64 + : await encryptSingleFunc(messageToBase64, secretKey); const idMsg = uid.rnd(); let identifier = `thmsg-${idThread}-${idMsg}`; const res = await publishGroupEncryptedResource({ identifier: identifier, encryptedData: encryptSinglePost, }); - + const dataToSaveToStoragePost = { threadId: idThread, name: myName, identifier: identifier, service: 'DOCUMENT', tempData: mailObject, - created: Date.now() - } - await saveTempPublish({data: dataToSaveToStoragePost, key: 'thread-post'}) + created: Date.now(), + }; + await saveTempPublish({ + data: dataToSaveToStoragePost, + key: 'thread-post', + }); // await qortalRequest(multiplePublishMsg); // dispatch( // setNotification({ @@ -350,12 +367,13 @@ export const NewThread = ({ // }) // ); setInfoSnack({ - type: "success", - message: "Successfully created post. It may take some time for the publish to propagate", + type: 'success', + message: + 'Successfully created post. It may take some time for the publish to propagate', }); - setOpenSnack(true) - if(publishCallback){ - publishCallback() + setOpenSnack(true); + if (publishCallback) { + publishCallback(); } // messageCallback({ // identifier, @@ -369,17 +387,16 @@ export const NewThread = ({ closeModal(); } catch (error: any) { - if(error?.message){ + if (error?.message) { setInfoSnack({ - type: "error", + type: 'error', message: error?.message, }); - setOpenSnack(true) + setOpenSnack(true); } - } finally { setIsSending(false); - resumeAllQueues() + resumeAllQueues(); } } @@ -389,56 +406,59 @@ export const NewThread = ({ return ( setIsOpen(true)} > - {currentThread ? "New Post" : "New Thread"} + {currentThread ? 'New Post' : 'New Thread'} - {isMessage ? "Post Message" : "New Thread"} + {isMessage ? 'Post Message' : 'New Thread'} - + @@ -457,19 +477,19 @@ export const NewThread = ({ autoComplete="off" autoCorrect="off" sx={{ - width: "100%", - color: "white", - "& .MuiInput-input::placeholder": { - color: "rgba(255,255,255, 0.70) !important", - fontSize: isMobile ? '14px' : "20px", - fontStyle: "normal", + width: '100%', + color: 'white', + '& .MuiInput-input::placeholder': { + color: 'rgba(255,255,255, 0.70) !important', + fontSize: isMobile ? '14px' : '20px', + fontStyle: 'normal', fontWeight: 400, - lineHeight: "120%", // 24px - letterSpacing: "0.15px", + lineHeight: '120%', // 24px + letterSpacing: '0.15px', opacity: 1, }, - "&:focus": { - outline: "none", + '&:focus': { + outline: 'none', }, // Add any additional styles for the input here }} @@ -481,21 +501,18 @@ export const NewThread = ({ {postReply && postReply.textContentV2 && ( )} - {!isMobile && ( - - - )} + {!isMobile && } {isSending && ( - - + + )} - {isMessage ? "Post" : "Create Thread"} + {isMessage ? 'Post' : 'Create Thread'} {isMessage ? ( - + ) : ( - + )} - - + ); }; diff --git a/src/components/Group/Forum/Thread.tsx b/src/components/Group/Forum/Thread.tsx index a0940c8..c7a83a3 100644 --- a/src/components/Group/Forum/Thread.tsx +++ b/src/components/Group/Forum/Thread.tsx @@ -195,7 +195,9 @@ export const Thread = ({ [message.identifier]: fullObject, }; }); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const setTempData = async () => { @@ -216,7 +218,9 @@ export const Thread = ({ }); setTempPublishedList(tempData); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; const getMailMessages = React.useCallback( @@ -461,7 +465,9 @@ export const Thread = ({ } else { fullArrayMsg.unshift(fullObject); } - } catch (error) {} + } catch (error) { + console.log(error); + } } setMessages(fullArrayMsg); } catch (error) { diff --git a/src/components/Group/InviteMember.tsx b/src/components/Group/InviteMember.tsx index 147c9bb..40b44ac 100644 --- a/src/components/Group/InviteMember.tsx +++ b/src/components/Group/InviteMember.tsx @@ -1,4 +1,4 @@ -import { LoadingButton } from "@mui/lab"; +import { LoadingButton } from '@mui/lab'; import { Box, Button, @@ -6,45 +6,46 @@ import { MenuItem, Select, SelectChangeEvent, -} from "@mui/material"; -import React, { useState } from "react"; -import { Spacer } from "../../common/Spacer"; -import { Label } from "./AddGroup"; -import { getFee } from "../../background"; +} from '@mui/material'; +import React, { useState } from 'react'; +import { Spacer } from '../../common/Spacer'; +import { Label } from './AddGroup'; +import { getFee } from '../../background'; export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { - const [value, setValue] = useState(""); + const [value, setValue] = useState(''); const [expiryTime, setExpiryTime] = useState('259200'); - const [isLoadingInvite, setIsLoadingInvite] = useState(false) + const [isLoadingInvite, setIsLoadingInvite] = useState(false); const inviteMember = async () => { try { - const fee = await getFee('GROUP_INVITE') + const fee = await getFee('GROUP_INVITE'); await show({ - message: "Would you like to perform a GROUP_INVITE transaction?" , - publishFee: fee.fee + ' QORT' - }) - setIsLoadingInvite(true) + message: 'Would you like to perform a GROUP_INVITE transaction?', + publishFee: fee.fee + ' QORT', + }); + setIsLoadingInvite(true); if (!expiryTime || !value) return; new Promise((res, rej) => { - window.sendMessage("inviteToGroup", { - groupId, - qortalAddress: value, - inviteTime: +expiryTime, - }) + window + .sendMessage('inviteToGroup', { + groupId, + qortalAddress: value, + inviteTime: +expiryTime, + }) .then((response) => { if (!response?.error) { setInfoSnack({ - type: "success", + type: 'success', message: `Successfully invited ${value}. It may take a couple of minutes for the changes to propagate`, }); setOpenSnack(true); res(response); - - setValue(""); + + setValue(''); return; } setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -52,16 +53,17 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { }) .catch((error) => { setInfoSnack({ - type: "error", - message: error?.message || "An error occurred", + type: 'error', + message: error?.message || 'An error occurred', }); setOpenSnack(true); rej(error); }); - }); - } catch (error) {} finally { - setIsLoadingInvite(false) + } catch (error) { + console.log(error); + } finally { + setIsLoadingInvite(false); } }; @@ -72,8 +74,8 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { return ( Invite member @@ -83,8 +85,7 @@ export const InviteMember = ({ groupId, setInfoSnack, setOpenSnack, show }) => { placeholder="Name or address" onChange={(e) => setValue(e.target.value)} /> - - + Invitation Expiry Time { 30 days - Invite + + Invite + ); }; diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index afeff14..9f29a8a 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useRef, useState, -} from "react"; +} from 'react'; import { Avatar, Box, @@ -25,44 +25,44 @@ import { Select, TextField, Typography, -} from "@mui/material"; +} from '@mui/material'; -import { getNameInfo } from "./Group"; -import { getBaseApi, getFee } from "../../background"; -import { LoadingButton } from "@mui/lab"; -import LockIcon from "@mui/icons-material/Lock"; -import NoEncryptionGmailerrorredIcon from "@mui/icons-material/NoEncryptionGmailerrorred"; +import { getNameInfo } from './Group'; +import { getBaseApi, getFee } from '../../background'; +import { LoadingButton } from '@mui/lab'; +import LockIcon from '@mui/icons-material/Lock'; +import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { MyContext, getArbitraryEndpointReact, getBaseApiReact, isMobile, -} from "../../App"; -import { Spacer } from "../../common/Spacer"; -import { CustomLoader } from "../../common/CustomLoader"; -import { RequestQueueWithPromise } from "../../utils/queue/queue"; -import { useRecoilState } from "recoil"; +} from '../../App'; +import { Spacer } from '../../common/Spacer'; +import { CustomLoader } from '../../common/CustomLoader'; +import { RequestQueueWithPromise } from '../../utils/queue/queue'; +import { useRecoilState } from 'recoil'; import { myGroupsWhereIAmAdminAtom, promotionTimeIntervalAtom, promotionsAtom, -} from "../../atoms/global"; -import { Label } from "./AddGroup"; -import ShortUniqueId from "short-unique-id"; -import { CustomizedSnackbars } from "../Snackbar/Snackbar"; -import { getGroupNames } from "./UserListOfInvites"; -import { WrapperUserAction } from "../WrapperUserAction"; -import { useVirtualizer } from "@tanstack/react-virtual"; -import ErrorBoundary from "../../common/ErrorBoundary"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import ExpandLessIcon from "@mui/icons-material/ExpandLess"; +} from '../../atoms/global'; +import { Label } from './AddGroup'; +import ShortUniqueId from 'short-unique-id'; +import { CustomizedSnackbars } from '../Snackbar/Snackbar'; +import { getGroupNames } from './UserListOfInvites'; +import { WrapperUserAction } from '../WrapperUserAction'; +import { useVirtualizer } from '@tanstack/react-virtual'; +import ErrorBoundary from '../../common/ErrorBoundary'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; export const requestQueuePromos = new RequestQueueWithPromise(20); export function utf8ToBase64(inputString: string): string { // Encode the string as UTF-8 const utf8String = encodeURIComponent(inputString).replace( /%([0-9A-F]{2})/g, - (match, p1) => String.fromCharCode(Number("0x" + p1)) + (match, p1) => String.fromCharCode(Number('0x' + p1)) ); // Convert the UTF-8 encoded string to base64 @@ -83,7 +83,7 @@ export const ListOfGroupPromotions = () => { const [selectedGroup, setSelectedGroup] = useState(null); const [loading, setLoading] = useState(false); const [isShowModal, setIsShowModal] = useState(false); - const [text, setText] = useState(""); + const [text, setText] = useState(''); const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useRecoilState( myGroupsWhereIAmAdminAtom ); @@ -115,10 +115,12 @@ export const ListOfGroupPromotions = () => { useEffect(() => { try { (async () => { - const feeRes = await getFee("ARBITRARY"); + const feeRes = await getFee('ARBITRARY'); setFee(feeRes?.fee); })(); - } catch (error) {} + } catch (error) { + console.log(error); + } }, []); const getPromotions = useCallback(async () => { try { @@ -126,9 +128,9 @@ export const ListOfGroupPromotions = () => { const identifier = `group-promotions-ui24-`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=100&includemetadata=false&reverse=true&prefix=true`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); @@ -142,7 +144,7 @@ export const ListOfGroupPromotions = () => { promo.name }/${promo.identifier}`; const response = await fetch(url, { - method: "GET", + method: 'GET', }); try { @@ -164,7 +166,7 @@ export const ListOfGroupPromotions = () => { } } } catch (error) { - console.error("Error fetching promo:", error); + console.error('Error fetching promo:', error); } }); } @@ -222,10 +224,10 @@ export const ListOfGroupPromotions = () => { await new Promise((res, rej) => { window - .sendMessage("publishOnQDN", { + .sendMessage('publishOnQDN', { data: data, identifier: identifier, - service: "DOCUMENT", + service: 'DOCUMENT', }) .then((response) => { if (!response?.error) { @@ -235,23 +237,23 @@ export const ListOfGroupPromotions = () => { rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); }); setInfoSnack({ - type: "success", + type: 'success', message: - "Successfully published promotion. It may take a couple of minutes for the promotion to appear", + 'Successfully published promotion. It may take a couple of minutes for the promotion to appear', }); setOpenSnack(true); - setText(""); + setText(''); setSelectedGroup(null); setIsShowModal(false); } catch (error) { setInfoSnack({ - type: "error", + type: 'error', message: - error?.message || "Error publishing the promotion. Please try again", + error?.message || 'Error publishing the promotion. Please try again', }); setOpenSnack(true); } finally { @@ -262,30 +264,30 @@ export const ListOfGroupPromotions = () => { const handleJoinGroup = async (group, isOpen) => { try { const groupId = group.groupId; - const fee = await getFee("JOIN_GROUP"); + const fee = await getFee('JOIN_GROUP'); await show({ - message: "Would you like to perform an JOIN_GROUP transaction?", - publishFee: fee.fee + " QORT", + message: 'Would you like to perform an JOIN_GROUP transaction?', + publishFee: fee.fee + ' QORT', }); setIsLoadingJoinGroup(true); await new Promise((res, rej) => { window - .sendMessage("joinGroup", { + .sendMessage('joinGroup', { groupId, }) .then((response) => { if (!response?.error) { setInfoSnack({ - type: "success", + type: 'success', message: - "Successfully requested to join group. It may take a couple of minutes for the changes to propagate", + 'Successfully requested to join group. It may take a couple of minutes for the changes to propagate', }); if (isOpen) { setTxList((prev) => [ { ...response, - type: "joined-group", + type: 'joined-group', label: `Joined Group ${group?.groupName}: awaiting confirmation`, labelDone: `Joined Group ${group?.groupName}: success!`, done: false, @@ -297,7 +299,7 @@ export const ListOfGroupPromotions = () => { setTxList((prev) => [ { ...response, - type: "joined-group-request", + type: 'joined-group-request', label: `Requested to join Group ${group?.groupName}: awaiting confirmation`, labelDone: `Requested to join Group ${group?.groupName}: success!`, done: false, @@ -313,7 +315,7 @@ export const ListOfGroupPromotions = () => { return; } else { setInfoSnack({ - type: "error", + type: 'error', message: response?.error, }); setOpenSnack(true); @@ -322,8 +324,8 @@ export const ListOfGroupPromotions = () => { }) .catch((error) => { setInfoSnack({ - type: "error", - message: error.message || "An error occurred", + type: 'error', + message: error.message || 'An error occurred', }); setOpenSnack(true); rej(error); @@ -339,55 +341,58 @@ export const ListOfGroupPromotions = () => { return ( - + setIsExpanded((prev) => !prev)} > - Group promotions {promotions.length > 0 && ` (${promotions.length})`} + Group promotions{' '} + {promotions.length > 0 && ` (${promotions.length})`} {isExpanded ? ( ) : ( )} @@ -396,24 +401,24 @@ export const ListOfGroupPromotions = () => { <> @@ -421,7 +426,7 @@ export const ListOfGroupPromotions = () => { variant="contained" onClick={() => setIsShowModal(true)} sx={{ - fontSize: "12px", + fontSize: '12px', }} > Add Promotion @@ -431,22 +436,22 @@ export const ListOfGroupPromotions = () => { {loading && promotions.length === 0 && ( @@ -455,18 +460,18 @@ export const ListOfGroupPromotions = () => { {!loading && promotions.length === 0 && ( Nothing to display @@ -475,11 +480,11 @@ export const ListOfGroupPromotions = () => { )} { className="scrollable-container" style={{ flexGrow: 1, - overflow: "auto", - position: "relative", - display: "flex", - height: "0px", + overflow: 'auto', + position: 'relative', + display: 'flex', + height: '0px', }} > {rowVirtualizer.getVirtualItems().map((virtualRow) => { @@ -516,17 +521,17 @@ export const ListOfGroupPromotions = () => { ref={rowVirtualizer.measureElement} //measure dynamic row height key={promotion?.identifier} style={{ - position: "absolute", + position: 'absolute', top: 0, - left: "50%", // Move to the center horizontally + left: '50%', // Move to the center horizontally transform: `translateY(${virtualRow.start}px) translateX(-50%)`, // Adjust for centering - width: "100%", // Control width (90% of the parent) - padding: "10px 0", - display: "flex", - alignItems: "center", - overscrollBehavior: "none", - flexDirection: "column", - gap: "5px", + width: '100%', // Control width (90% of the parent) + padding: '10px 0', + display: 'flex', + alignItems: 'center', + overscrollBehavior: 'none', + flexDirection: 'column', + gap: '5px', }} > { > { - if (reason === "backdropClick") { + if (reason === 'backdropClick') { // Prevent closing on backdrop click return; } handlePopoverClose(); // Close only on other events like Esc key press }} anchorOrigin={{ - vertical: "top", - horizontal: "center", + vertical: 'top', + horizontal: 'center', }} transformOrigin={{ - vertical: "bottom", - horizontal: "center", + vertical: 'bottom', + horizontal: 'center', }} - style={{ marginTop: "8px" }} + style={{ marginTop: '8px' }} > @@ -586,17 +591,17 @@ export const ListOfGroupPromotions = () => { - Number of members:{" "} + Number of members:{' '} {` ${promotion?.memberCount}`} {promotion?.description && ( @@ -606,7 +611,7 @@ export const ListOfGroupPromotions = () => { {promotion?.isOpen === false && ( @@ -618,11 +623,11 @@ export const ListOfGroupPromotions = () => { { { {promotion?.name} @@ -690,8 +695,8 @@ export const ListOfGroupPromotions = () => { {promotion?.groupName} @@ -700,42 +705,42 @@ export const ListOfGroupPromotions = () => { {promotion?.isOpen === false && ( )} {promotion?.isOpen === true && ( )} {promotion?.isOpen - ? "Public group" - : "Private group"} + ? 'Public group' + : 'Private group'} {promotion?.data} @@ -743,9 +748,9 @@ export const ListOfGroupPromotions = () => { { handlePopoverOpen(event, promotion?.groupId) } sx={{ - fontSize: "12px", - color: "white", + fontSize: '12px', + color: 'white', }} > Join Group: {` ${promotion?.groupName}`} @@ -783,7 +788,7 @@ export const ListOfGroupPromotions = () => { aria-describedby="alert-dialog-description" > - {"Promote your group to non-members"} + {'Promote your group to non-members'} @@ -791,14 +796,14 @@ export const ListOfGroupPromotions = () => { group. - Max 200 characters. Publish Fee: {fee && fee} {" QORT"} + Max 200 characters. Publish Fee: {fee && fee} {' QORT'} Select a group @@ -832,11 +837,11 @@ export const ListOfGroupPromotions = () => { }} multiline={true} sx={{ - "& .MuiFormLabel-root": { - color: "white", + '& .MuiFormLabel-root': { + color: 'white', }, - "& .MuiFormLabel-root.Mui-focused": { - color: "white", + '& .MuiFormLabel-root.Mui-focused': { + color: 'white', }, }} /> diff --git a/src/components/Group/ManageMembers.tsx b/src/components/Group/ManageMembers.tsx index b1a35f0..de361fa 100644 --- a/src/components/Group/ManageMembers.tsx +++ b/src/components/Group/ManageMembers.tsx @@ -1,36 +1,36 @@ -import * as React from "react"; -import Button from "@mui/material/Button"; -import Dialog from "@mui/material/Dialog"; -import ListItemText from "@mui/material/ListItemText"; -import ListItemButton from "@mui/material/ListItemButton"; -import List from "@mui/material/List"; -import Divider from "@mui/material/Divider"; -import AppBar from "@mui/material/AppBar"; -import Toolbar from "@mui/material/Toolbar"; -import IconButton from "@mui/material/IconButton"; -import Typography from "@mui/material/Typography"; -import CloseIcon from "@mui/icons-material/Close"; -import Slide from "@mui/material/Slide"; -import { TransitionProps } from "@mui/material/transitions"; -import ListOfMembers from "./ListOfMembers"; -import { InviteMember } from "./InviteMember"; -import { ListOfInvites } from "./ListOfInvites"; -import { ListOfBans } from "./ListOfBans"; -import { ListOfJoinRequests } from "./ListOfJoinRequests"; -import { Box, ButtonBase, Card, Tab, Tabs } from "@mui/material"; -import { CustomizedSnackbars } from "../Snackbar/Snackbar"; -import { MyContext, getBaseApiReact, isMobile } from "../../App"; -import { getGroupMembers, getNames } from "./Group"; -import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar"; -import { getFee } from "../../background"; -import { LoadingButton } from "@mui/lab"; -import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; -import { Spacer } from "../../common/Spacer"; +import * as React from 'react'; +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import ListItemText from '@mui/material/ListItemText'; +import ListItemButton from '@mui/material/ListItemButton'; +import List from '@mui/material/List'; +import Divider from '@mui/material/Divider'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; +import CloseIcon from '@mui/icons-material/Close'; +import Slide from '@mui/material/Slide'; +import { TransitionProps } from '@mui/material/transitions'; +import ListOfMembers from './ListOfMembers'; +import { InviteMember } from './InviteMember'; +import { ListOfInvites } from './ListOfInvites'; +import { ListOfBans } from './ListOfBans'; +import { ListOfJoinRequests } from './ListOfJoinRequests'; +import { Box, ButtonBase, Card, Tab, Tabs } from '@mui/material'; +import { CustomizedSnackbars } from '../Snackbar/Snackbar'; +import { MyContext, getBaseApiReact, isMobile } from '../../App'; +import { getGroupMembers, getNames } from './Group'; +import { LoadingSnackbar } from '../Snackbar/LoadingSnackbar'; +import { getFee } from '../../background'; +import { LoadingButton } from '@mui/lab'; +import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; +import { Spacer } from '../../common/Spacer'; import InsertLinkIcon from '@mui/icons-material/InsertLink'; function a11yProps(index: number) { return { id: `simple-tab-${index}`, - "aria-controls": `simple-tabpanel-${index}`, + 'aria-controls': `simple-tabpanel-${index}`, }; } @@ -50,16 +50,16 @@ export const ManageMembers = ({ selectedGroup, isAdmin, - isOwner + isOwner, }) => { const [membersWithNames, setMembersWithNames] = React.useState([]); - const [tab, setTab] = React.useState("create"); + const [tab, setTab] = React.useState('create'); const [value, setValue] = React.useState(0); const [openSnack, setOpenSnack] = React.useState(false); const [infoSnack, setInfoSnack] = React.useState(null); - const [isLoadingMembers, setIsLoadingMembers] = React.useState(false) - const [isLoadingLeave, setIsLoadingLeave] = React.useState(false) - const [groupInfo, setGroupInfo] = React.useState(null) + const [isLoadingMembers, setIsLoadingMembers] = React.useState(false); + const [isLoadingLeave, setIsLoadingLeave] = React.useState(false); + const [groupInfo, setGroupInfo] = React.useState(null); const handleChange = (event: React.SyntheticEvent, newValue: number) => { setValue(newValue); }; @@ -69,20 +69,20 @@ export const ManageMembers = ({ setOpen(false); }; - const handleLeaveGroup = async () => { try { - setIsLoadingLeave(true) - const fee = await getFee('LEAVE_GROUP') + setIsLoadingLeave(true); + const fee = await getFee('LEAVE_GROUP'); await show({ - message: "Would you like to perform an LEAVE_GROUP transaction?" , - publishFee: fee.fee + ' QORT' - }) + message: 'Would you like to perform an LEAVE_GROUP transaction?', + publishFee: fee.fee + ' QORT', + }); await new Promise((res, rej) => { - window.sendMessage("leaveGroup", { - groupId: selectedGroup?.groupId, - }) + window + .sendMessage('leaveGroup', { + groupId: selectedGroup?.groupId, + }) .then((response) => { if (!response?.error) { setTxList((prev) => [ @@ -98,8 +98,9 @@ export const ManageMembers = ({ ]); res(response); setInfoSnack({ - type: "success", - message: "Successfully requested to leave group. It may take a couple of minutes for the changes to propagate", + type: 'success', + message: + 'Successfully requested to leave group. It may take a couple of minutes for the changes to propagate', }); setOpenSnack(true); return; @@ -107,57 +108,62 @@ export const ManageMembers = ({ rej(response.error); }) .catch((error) => { - rej(error.message || "An error occurred"); + rej(error.message || 'An error occurred'); }); - }); - } catch (error) {} finally { - setIsLoadingLeave(false) + } catch (error) { + console.log(error); + } finally { + setIsLoadingLeave(false); } }; - const getMembersWithNames = React.useCallback(async (groupId) => { + const getMembersWithNames = React.useCallback(async (groupId) => { try { - setIsLoadingMembers(true) + setIsLoadingMembers(true); const res = await getGroupMembers(groupId); const resWithNames = await getNames(res.members); setMembersWithNames(resWithNames); - setIsLoadingMembers(false) - } catch (error) {} + setIsLoadingMembers(false); + } catch (error) { + console.log(error); + } }, []); const getMembers = async (groupId) => { try { const res = await getGroupMembers(groupId); setMembersWithNames(res?.members || []); - } catch (error) {} + } catch (error) { + console.log(error); + } }; const getGroupInfo = async (groupId) => { try { - const response = await fetch( - `${getBaseApiReact()}/groups/${groupId}` - ); - const groupData = await response.json(); - setGroupInfo(groupData) - } catch (error) {} + const response = await fetch(`${getBaseApiReact()}/groups/${groupId}`); + const groupData = await response.json(); + setGroupInfo(groupData); + } catch (error) { + console.log(error); + } }; - React.useEffect(()=> { - if(selectedGroup?.groupId){ - getMembers(selectedGroup?.groupId) - getGroupInfo(selectedGroup?.groupId) + React.useEffect(() => { + if (selectedGroup?.groupId) { + getMembers(selectedGroup?.groupId); + getGroupInfo(selectedGroup?.groupId); } - }, [selectedGroup?.groupId]) + }, [selectedGroup?.groupId]); - const openGroupJoinRequestFunc = ()=> { - setValue(4) - } + const openGroupJoinRequestFunc = () => { + setValue(4); + }; React.useEffect(() => { - subscribeToEvent("openGroupJoinRequest", openGroupJoinRequestFunc); + subscribeToEvent('openGroupJoinRequest', openGroupJoinRequestFunc); return () => { - unsubscribeFromEvent("openGroupJoinRequest", openGroupJoinRequestFunc); + unsubscribeFromEvent('openGroupJoinRequest', openGroupJoinRequestFunc); }; }, []); @@ -169,7 +175,7 @@ export const ManageMembers = ({ onClose={handleClose} TransitionComponent={Transition} > - + Manage Members @@ -186,117 +192,136 @@ export const ManageMembers = ({ - - - - - - - - + + + + + + + + - + - GroupId: {groupInfo?.groupId} - GroupName: {groupInfo?.groupName} - Number of members: {groupInfo?.memberCount} - { - const link = `qortal://use-group/action-join/groupid-${groupInfo?.groupId}` - await navigator.clipboard.writeText(link); - }}> Join Group Link + GroupId: {groupInfo?.groupId} + GroupName: {groupInfo?.groupName} + + Number of members: {groupInfo?.memberCount} + + { + const link = `qortal://use-group/action-join/groupid-${groupInfo?.groupId}`; + await navigator.clipboard.writeText(link); + }} + > + Join Group Link + - - {selectedGroup?.groupId && !isOwner && ( - - Leave Group - - )} + + {selectedGroup?.groupId && !isOwner && ( + + Leave Group + + )} {value === 0 && ( - getMembersWithNames(selectedGroup?.groupId)}>Load members with names + getMembersWithNames(selectedGroup?.groupId)} + > + Load members with names + )} - {value === 1 && ( + {value === 1 && ( - + )} {value === 2 && ( - - + )} {value === 3 && ( - + )} - + {value === 4 && ( - + )} - + - ); }; diff --git a/src/components/MainAvatar.tsx b/src/components/MainAvatar.tsx index faca10a..ed0a119 100644 --- a/src/components/MainAvatar.tsx +++ b/src/components/MainAvatar.tsx @@ -1,22 +1,29 @@ -import React, { useContext, useEffect, useState } from "react"; -import Logo2 from "../assets/svgs/Logo2.svg"; -import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from "../App"; -import { Avatar, Box, Button, ButtonBase, Popover, Typography } from "@mui/material"; -import { Spacer } from "../common/Spacer"; -import ImageUploader from "../common/ImageUploader"; -import { getFee } from "../background"; -import { fileToBase64 } from "../utils/fileReading"; -import { LoadingButton } from "@mui/lab"; +import React, { useContext, useEffect, useState } from 'react'; +import Logo2 from '../assets/svgs/Logo2.svg'; +import { MyContext, getArbitraryEndpointReact, getBaseApiReact } from '../App'; +import { + Avatar, + Box, + Button, + ButtonBase, + Popover, + Typography, +} from '@mui/material'; +import { Spacer } from '../common/Spacer'; +import ImageUploader from '../common/ImageUploader'; +import { getFee } from '../background'; +import { fileToBase64 } from '../utils/fileReading'; +import { LoadingButton } from '@mui/lab'; import ErrorIcon from '@mui/icons-material/Error'; export const MainAvatar = ({ myName, balance, setOpenSnack, setInfoSnack }) => { const [hasAvatar, setHasAvatar] = useState(false); const [avatarFile, setAvatarFile] = useState(null); - const [tempAvatar, setTempAvatar] = useState(null) + const [tempAvatar, setTempAvatar] = useState(null); const { show } = useContext(MyContext); const [anchorEl, setAnchorEl] = useState(null); -const [isLoading, setIsLoading] = useState(false) + const [isLoading, setIsLoading] = useState(false); // Handle child element click to open Popover const handleChildClick = (event) => { event.stopPropagation(); // Prevent parent onClick from firing @@ -37,93 +44,105 @@ const [isLoading, setIsLoading] = useState(false) const identifier = `qortal_avatar`; const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=THUMBNAIL&identifier=${identifier}&limit=1&name=${myName}&includemetadata=false&prefix=true`; const response = await fetch(url, { - method: "GET", + method: 'GET', headers: { - "Content-Type": "application/json", + 'Content-Type': 'application/json', }, }); const responseData = await response.json(); if (responseData?.length > 0) { setHasAvatar(true); } - } catch (error) {} + } catch (error) { + console.log(error); + } }; useEffect(() => { if (!myName) return; checkIfAvatarExists(); }, [myName]); - - const publishAvatar = async ()=> { + const publishAvatar = async () => { try { - const fee = await getFee('ARBITRARY') - if(+balance < +fee.fee) throw new Error(`Publishing an Avatar requires ${fee.fee}`) - await show({ - message: "Would you like to publish an avatar?" , - publishFee: fee.fee + ' QORT' - }) - setIsLoading(true); - const avatarBase64 = await fileToBase64(avatarFile) - await new Promise((res, rej) => { - window.sendMessage("publishOnQDN", { - data: avatarBase64, - identifier: "qortal_avatar", - service: "THUMBNAIL", - }) - .then((response) => { - if (!response?.error) { - res(response); - return; - } - rej(response.error); - }) - .catch((error) => { - rej(error.message || "An error occurred"); - }); - - }); - setAvatarFile(null); - setTempAvatar(`data:image/webp;base64,${avatarBase64}`) - handleClose() + const fee = await getFee('ARBITRARY'); + if (+balance < +fee.fee) + throw new Error(`Publishing an Avatar requires ${fee.fee}`); + await show({ + message: 'Would you like to publish an avatar?', + publishFee: fee.fee + ' QORT', + }); + setIsLoading(true); + const avatarBase64 = await fileToBase64(avatarFile); + await new Promise((res, rej) => { + window + .sendMessage('publishOnQDN', { + data: avatarBase64, + identifier: 'qortal_avatar', + service: 'THUMBNAIL', + }) + .then((response) => { + if (!response?.error) { + res(response); + return; + } + rej(response.error); + }) + .catch((error) => { + rej(error.message || 'An error occurred'); + }); + }); + setAvatarFile(null); + setTempAvatar(`data:image/webp;base64,${avatarBase64}`); + handleClose(); } catch (error) { if (error?.message) { - setOpenSnack(true) - setInfoSnack({ - type: "error", - message: error?.message, - }); - } + setOpenSnack(true); + setInfoSnack({ + type: 'error', + message: error?.message, + }); + } } finally { - setIsLoading(false); + setIsLoading(false); } - } + }; - if(tempAvatar){ + if (tempAvatar) { return ( - <> - + + {myName?.charAt(0)} + + + - {myName?.charAt(0)} - - - - change avatar - - - - > - ); + change avatar + + + + > + ); } if (hasAvatar) { @@ -131,8 +150,8 @@ const [isLoading, setIsLoading] = useState(false) <> change avatar - + > ); } @@ -160,42 +189,61 @@ const [isLoading, setIsLoading] = useState(false) set avatar - + > ); }; - -const PopoverComp = ({avatarFile, setAvatarFile, id, open, anchorEl, handleClose, publishAvatar, isLoading, myName}) => { - return ( - { + return ( + - + - (500 KB max. for GIFS){" "} + (500 KB max. for GIFS){' '} setAvatarFile(file)}> Choose Image @@ -203,23 +251,34 @@ const PopoverComp = ({avatarFile, setAvatarFile, id, open, anchorEl, handleClose {avatarFile?.name} {!myName && ( - - - A registered name is required to set an avatar + alignItems: 'center', + }} + > + + + A registered name is required to set an avatar + )} - - - + + + Publish avatar - ) - }; \ No newline at end of file + ); +}; diff --git a/src/components/Minting/Minting.tsx b/src/components/Minting/Minting.tsx index 4a9eeb3..de16e09 100644 --- a/src/components/Minting/Minting.tsx +++ b/src/components/Minting/Minting.tsx @@ -74,7 +74,9 @@ export const Minting = ({ } const data = await response.json(); setMintingAccounts(data); - } catch (error) {} + } catch (error) { + console.log(error); + } }, []); const accountIsMinting = useMemo(() => { @@ -199,7 +201,9 @@ export const Minting = ({ const data = await response.json(); setRewardShares(data); return data; - } catch (error) {} + } catch (error) { + console.log(error); + } }, []); const addMintingAccount = useCallback(async (val) => { diff --git a/src/components/TaskManager/TaskManager.tsx b/src/components/TaskManager/TaskManager.tsx index f9f728e..f1f075b 100644 --- a/src/components/TaskManager/TaskManager.tsx +++ b/src/components/TaskManager/TaskManager.tsx @@ -60,7 +60,9 @@ export const TaskManager = ({ getUserInfo }) => { } clearInterval(intervals.current[signature]); } - } catch (error) {} + } catch (error) { + console.log(error); + } stop = false; } }; diff --git a/src/styles/App-styles.ts b/src/styles/App-styles.ts index eba266f..9b385bc 100644 --- a/src/styles/App-styles.ts +++ b/src/styles/App-styles.ts @@ -187,7 +187,7 @@ export const CustomInput = styled(TextField)(({ theme }) => ({ outline: 'none', width: '183px', // Adjust the width as needed input: { - fontSize: 10, + fontSize: '12px', fontFamily: 'Inter', fontWeight: 400, color: theme.palette.text.primary, @@ -230,7 +230,7 @@ export const CustomLabel = styled(InputLabel)(({ theme }) => ({ backgroundColor: theme.palette.background.default, color: theme.palette.text.primary, fontFamily: 'Inter', - fontSize: '10px', + fontSize: '15px', fontWeight: 400, lineHeight: '12px', })); diff --git a/src/useQortalGetSaveSettings.tsx b/src/useQortalGetSaveSettings.tsx index e4e2945..9e31853 100644 --- a/src/useQortalGetSaveSettings.tsx +++ b/src/useQortalGetSaveSettings.tsx @@ -106,7 +106,9 @@ export const useQortalGetSaveSettings = (myName, isAuthenticated) => { setSettingsQDNLastUpdated(0); } setCanSave(true); - } catch (error) {} + } catch (error) { + console.log(error); + } }, [] );
Groups list