From d1be4944208129e44c27733a8e30ed321142edfe Mon Sep 17 00:00:00 2001 From: PhilReact Date: Sat, 28 Dec 2024 21:05:07 +0200 Subject: [PATCH] batch of updates 3 --- package-lock.json | 12 + package.json | 1 + src/App.tsx | 12 +- src/components/Apps/AppRating.tsx | 9 +- src/components/Apps/AppsDesktop.tsx | 2 +- src/components/Apps/AppsNavBarDesktop.tsx | 5 +- src/components/Chat/ChatGroup.tsx | 6 +- src/components/Embeds/PollEmbed.tsx | 23 +- src/components/Group/Group.tsx | 7 +- src/components/Group/ListOfBans.tsx | 11 +- src/components/Group/ListOfInvites.tsx | 10 +- src/components/Group/ListOfJoinRequests.tsx | 13 +- src/components/Group/ListOfMembers.tsx | 2 +- src/components/Group/ManageMembers.tsx | 12 +- src/components/TaskManager/TaskManger.tsx | 282 ++++++++++---------- src/qortalRequests/get.ts | 6 +- src/utils/time.ts | 18 ++ 17 files changed, 246 insertions(+), 185 deletions(-) diff --git a/package-lock.json b/package-lock.json index b2cd9a0..2d3d276 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,6 +53,7 @@ "react-frame-component": "^5.2.7", "react-infinite-scroller": "^1.2.6", "react-intersection-observer": "^9.13.0", + "react-json-view-lite": "^2.0.1", "react-qr-code": "^2.0.15", "react-quill": "^2.0.0", "react-redux": "^9.1.2", @@ -9573,6 +9574,17 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-json-view-lite": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-2.0.1.tgz", + "integrity": "sha512-yElNMSzL7UJ9rMDQIbTiBemXbvfAoqpxM/0IQd3nr52CLLBC0HxOSKcta/bayct2QCq7ZVzLzI8CGfuf387hHw==", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, "node_modules/react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", diff --git a/package.json b/package.json index 1ba83b0..17410d6 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "react-frame-component": "^5.2.7", "react-infinite-scroller": "^1.2.6", "react-intersection-observer": "^9.13.0", + "react-json-view-lite": "^2.0.1", "react-qr-code": "^2.0.15", "react-quill": "^2.0.0", "react-redux": "^9.1.2", diff --git a/src/App.tsx b/src/App.tsx index b127506..1b80e97 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -43,7 +43,8 @@ import Return from "./assets/svgs/Return.svg"; import Success from "./assets/svgs/Success.svg"; import Info from "./assets/svgs/Info.svg"; import CloseIcon from "@mui/icons-material/Close"; - +import { JsonView, allExpanded, darkStyles } from 'react-json-view-lite'; +import 'react-json-view-lite/dist/index.css'; import { createAccount, generateRandomSentence, @@ -2797,6 +2798,15 @@ function App() { {messageQortalRequestExtension?.highlightedText} + {messageQortalRequestExtension?.json && ( + <> + + + + + + + )} {messageQortalRequestExtension?.fee && ( <> diff --git a/src/components/Apps/AppRating.tsx b/src/components/Apps/AppRating.tsx index a498779..aab1a87 100644 --- a/src/components/Apps/AppRating.tsx +++ b/src/components/Apps/AppRating.tsx @@ -103,14 +103,15 @@ export const AppRating = ({ app, myName, ratingCountPosition = "right" }) => { getRating(app?.name, app?.service); }, [getRating, app?.name]); - const rateFunc = async (event, newValue) => { + const rateFunc = async (event, chosenValue, currentValue) => { try { + const newValue = chosenValue || currentValue if (!myName) throw new Error("You need a name to rate."); if (!app?.name) return; - const fee = await getFee("ARBITRARY"); + const fee = await getFee("CREATE_POLL"); await show({ - message: `Would you like to rate this app a rating of ${newValue}?`, + message: `Would you like to rate this app a rating of ${newValue}?. It will create a POLL tx.`, publishFee: fee.fee + " QORT", }); @@ -212,7 +213,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = "right" }) => { rateFunc(event, rating, value)} precision={1} readOnly={hasPublishedRating === null} size="small" diff --git a/src/components/Apps/AppsDesktop.tsx b/src/components/Apps/AppsDesktop.tsx index 6d84ba7..8d01024 100644 --- a/src/components/Apps/AppsDesktop.tsx +++ b/src/components/Apps/AppsDesktop.tsx @@ -368,7 +368,7 @@ export const AppsDesktop = ({ mode, setMode, show , myName, goToHome, setDesktop {mode !== 'home' && ( - + )} diff --git a/src/components/Apps/AppsNavBarDesktop.tsx b/src/components/Apps/AppsNavBarDesktop.tsx index 9ba6540..d33ba09 100644 --- a/src/components/Apps/AppsNavBarDesktop.tsx +++ b/src/components/Apps/AppsNavBarDesktop.tsx @@ -64,7 +64,7 @@ export function saveToLocalStorage(key, subKey, newValue) { } } -export const AppsNavBarDesktop = () => { +export const AppsNavBarDesktop = ({disableBack}) => { const [tabs, setTabs] = useState([]); const [selectedTab, setSelectedTab] = useState(null); const [navigationController, setNavigationController] = useRecoilState(navigationControllerAtom) @@ -108,10 +108,11 @@ export const AppsNavBarDesktop = () => { const isDisableBackButton = useMemo(()=> { + if(disableBack) return true if(selectedTab && navigationController[selectedTab?.tabId]?.hasBack) return false if(selectedTab && !navigationController[selectedTab?.tabId]?.hasBack) return true return false - }, [navigationController, selectedTab]) + }, [navigationController, selectedTab, disableBack]) diff --git a/src/components/Chat/ChatGroup.tsx b/src/components/Chat/ChatGroup.tsx index 26833fa..98b73e5 100644 --- a/src/components/Chat/ChatGroup.tsx +++ b/src/components/Chat/ChatGroup.tsx @@ -252,7 +252,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, const content = item?.content || item.decryptedData?.content; const sender = item.sender; const newTimestamp = item.timestamp; - const contentState = item?.contentState || item.decryptedData?.contentState; + const contentState = item?.contentState !== undefined ? item?.contentState : item.decryptedData?.contentState; if (!content || typeof content !== "string" || !sender || typeof sender !== "string" || !newTimestamp) { console.warn("Invalid content, sender, or timestamp in reaction data", item); @@ -344,7 +344,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey, const content = item?.content || item.decryptedData?.content; const sender = item.sender; const newTimestamp = item.timestamp; - const contentState = item?.contentState || item.decryptedData?.contentState; + const contentState = item?.contentState !== undefined ? item?.contentState : item.decryptedData?.contentState; if (!content || typeof content !== "string" || !sender || typeof sender !== "string" || !newTimestamp) { console.warn("Invalid content, sender, or timestamp in reaction data", item); @@ -755,7 +755,7 @@ useEffect(() => { setIsSending(false) resumeAllQueues() } - }, []) + }, [isPrivate]) console.log('isPrivate', isPrivate) diff --git a/src/components/Embeds/PollEmbed.tsx b/src/components/Embeds/PollEmbed.tsx index 3da02c3..6b104c4 100644 --- a/src/components/Embeds/PollEmbed.tsx +++ b/src/components/Embeds/PollEmbed.tsx @@ -48,16 +48,17 @@ export const PollCard = ({ }); setIsLoadingSubmit(true); - window - .sendMessage( - "voteOnPoll", + chrome?.runtime?.sendMessage( { - pollName: poll?.info?.pollName, + action: "VOTE_ON_POLL", + type: "qortalRequest", + payload: { + pollName: poll?.info?.pollName, optionIndex: +selectedOption, + } + }, - 60000 - ) - .then((response) => { + (response) => { setIsLoadingSubmit(false); if (response.error) { setInfoSnack({ @@ -75,14 +76,6 @@ export const PollCard = ({ setOpenSnack(true); } }) - .catch((error) => { - setIsLoadingSubmit(false); - setInfoSnack({ - type: "error", - message: error?.message || "Unable to vote.", - }); - setOpenSnack(true); - }); }; const getName = async (owner) => { diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 2859d51..595f2c8 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -96,6 +96,7 @@ import LockIcon from '@mui/icons-material/Lock'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import { useSetRecoilState } from "recoil"; import { selectedGroupIdAtom } from "../../atoms/global"; +import { sortArrayByTimestampAndGroupName } from "../../utils/time"; // let touchStartY = 0; // let disablePullToRefresh = false; @@ -976,7 +977,7 @@ export const Group = ({ chrome?.runtime?.onMessage.addListener((message, sender, sendResponse) => { if (message.action === "SET_GROUPS") { // Update the component state with the received 'sendqort' state - setGroups(message.payload); + setGroups(sortArrayByTimestampAndGroupName(message.payload)); getLatestRegularChat(message.payload) setMemberGroups(message.payload); @@ -2632,6 +2633,10 @@ export const Group = ({ Wait until an admin re-encrypts the keys. + + Only unencrypted messages will be displayed. + + Try notifying an admin from the list of admins below: diff --git a/src/components/Group/ListOfBans.tsx b/src/components/Group/ListOfBans.tsx index 41ef9f4..e1d7698 100644 --- a/src/components/Group/ListOfBans.tsx +++ b/src/components/Group/ListOfBans.tsx @@ -12,7 +12,7 @@ export const getMemberInvites = async (groupNumber) => { return groupData; } -const getNames = async (listOfMembers) => { +const getNames = async (listOfMembers, includeNoNames) => { let members = []; if (listOfMembers && Array.isArray(listOfMembers)) { for (const member of listOfMembers) { @@ -20,6 +20,8 @@ const getNames = async (listOfMembers) => { const name = await getNameInfo(member.offender); if (name) { members.push({ ...member, name }); + } else if(includeNoNames){ + members.push({ ...member, name: name || "" }); } } } @@ -42,7 +44,8 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { const getInvites = async (groupId) => { try { const res = await getMemberInvites(groupId); - const resWithNames = await getNames(res); + const resWithNames = await getNames(res, true); + setBans(resWithNames); } catch (error) { console.error(error); @@ -151,9 +154,9 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => { handlePopoverOpen(event, index)}> - diff --git a/src/components/Group/ListOfInvites.tsx b/src/components/Group/ListOfInvites.tsx index 9bdf124..8dfc599 100644 --- a/src/components/Group/ListOfInvites.tsx +++ b/src/components/Group/ListOfInvites.tsx @@ -12,7 +12,7 @@ export const getMemberInvites = async (groupNumber) => { return groupData; } -const getNames = async (listOfMembers) => { +const getNames = async (listOfMembers, includeNoNames) => { let members = []; if (listOfMembers && Array.isArray(listOfMembers)) { for (const member of listOfMembers) { @@ -20,6 +20,8 @@ const getNames = async (listOfMembers) => { const name = await getNameInfo(member.invitee); if (name) { members.push({ ...member, name }); + } else if(includeNoNames){ + members.push({ ...member, name: name || "" }); } } } @@ -43,7 +45,7 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => const getInvites = async (groupId) => { try { const res = await getMemberInvites(groupId); - const resWithNames = await getNames(res); + const resWithNames = await getNames(res, true); setInvites(resWithNames); } catch (error) { console.error(error); @@ -152,9 +154,9 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) => handlePopoverOpen(event, index)}> - diff --git a/src/components/Group/ListOfJoinRequests.tsx b/src/components/Group/ListOfJoinRequests.tsx index b3422da..026cfee 100644 --- a/src/components/Group/ListOfJoinRequests.tsx +++ b/src/components/Group/ListOfJoinRequests.tsx @@ -12,14 +12,16 @@ export const getMemberInvites = async (groupNumber) => { return groupData; } -const getNames = async (listOfMembers) => { +const getNames = async (listOfMembers, includeNoNames) => { let members = []; if (listOfMembers && Array.isArray(listOfMembers)) { for (const member of listOfMembers) { if (member.joiner) { const name = await getNameInfo(member.joiner); if (name) { - members.push({ ...member, name }); + members.push({ ...member, name: name || "" }); + } else if(includeNoNames){ + members.push({ ...member, name: name || "" }); } } } @@ -27,6 +29,7 @@ const getNames = async (listOfMembers) => { return members; } + const cache = new CellMeasurerCache({ fixedWidth: true, defaultHeight: 50, @@ -44,7 +47,7 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show } const getInvites = async (groupId) => { try { const res = await getMemberInvites(groupId); - const resWithNames = await getNames(res); + const resWithNames = await getNames(res, true); setInvites(resWithNames); } catch (error) { console.error(error); @@ -165,9 +168,9 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show } handlePopoverOpen(event, index)}> - diff --git a/src/components/Group/ListOfMembers.tsx b/src/components/Group/ListOfMembers.tsx index 54950f6..515686c 100644 --- a/src/components/Group/ListOfMembers.tsx +++ b/src/components/Group/ListOfMembers.tsx @@ -328,7 +328,7 @@ const ListOfMembers = ({ { + const getMembersWithNames = React.useCallback(async (groupId) => { try { setIsLoadingMembers(true) const res = await getGroupMembers(groupId); @@ -121,6 +122,13 @@ export const ManageMembers = ({ setMembersWithNames(resWithNames); setIsLoadingMembers(false) } catch (error) {} + }, []); + + const getMembers = async (groupId) => { + try { + const res = await getGroupMembers(groupId); + setMembersWithNames(res?.members || []); + } catch (error) {} }; React.useEffect(()=> { @@ -256,6 +264,8 @@ export const ManageMembers = ({ maxWidth: '750px' }} > + + { +export const TaskManger = ({ getUserInfo }) => { const { txList, setTxList, memberGroups } = useContext(MyContext); - const [open, setOpen] = React.useState(true); - + const [open, setOpen] = React.useState(false); + const intervals = useRef({}); + const handleClick = () => { - setOpen(!open); + setOpen((prev) => !prev); }; - const intervals = useRef({}) + const getStatus = ({ signature }, callback) => { + let stop = false; + const getAnswer = async () => { + const getTx = async () => { + const url = `${getBaseApiReact()}/transactions/signature/${signature}`; + const res = await fetch(url); + return await res.json(); + }; - const getStatus = ({signature}, callback?: any) =>{ - - let stop = false - - const getAnswer = async () => { - const getTx = async () => { - const url = `${getBaseApiReact()}/transactions/signature/${signature}` - const res = await fetch(url) - - return await res.json() - } - - if (!stop) { - stop = true - - try { - const txTransaction = await getTx() - - if (!txTransaction.error && txTransaction.signature) { - await new Promise((res)=> { + if (!stop) { + stop = true; + try { + const txTransaction = await getTx(); + if (!txTransaction.error && txTransaction.signature) { + await new Promise((res) => setTimeout(() => { - res(null) - }, 300000); - }) - setTxList((prev)=> { + res(null); + }, 300000) + ); + setTxList((prev) => { let previousData = [...prev]; - const findTxWithSignature = previousData.findIndex((tx)=> tx.signature === signature) - if(findTxWithSignature !== -1){ + const findTxWithSignature = previousData.findIndex( + (tx) => tx.signature === signature + ); + if (findTxWithSignature !== -1) { previousData[findTxWithSignature].done = true; - return previousData + return previousData; } - return previousData - }) - if(callback){ - callback(true) + return previousData; + }); + if (callback) { + callback(true); } - clearInterval(intervals.current[signature]) + clearInterval(intervals.current[signature]); + } + } catch (error) {} + stop = false; + } + }; - } - } catch (error) { } - - stop = false - } - } - - intervals.current[signature] = setInterval(getAnswer, 120000) - } + intervals.current[signature] = setInterval(getAnswer, 120000); + }; useEffect(() => { setTxList((prev) => { @@ -80,111 +72,117 @@ export const TaskManger = ({getUserInfo}) => { const findGroup = txList.findIndex( (tx) => tx?.type === "joined-group" && tx?.groupId === group.groupId ); - if (findGroup !== -1 && !previousData[findGroup]?.done ) { - // add notification + if (findGroup !== -1 && !previousData[findGroup]?.done) { previousData[findGroup].done = true; } - }); + memberGroups.forEach((group) => { const findGroup = txList.findIndex( - (tx) => tx?.type === "created-group" && tx?.groupName === group.groupName + (tx) => + tx?.type === "created-group" && tx?.groupName === group.groupName ); - if (findGroup !== -1 && !previousData[findGroup]?.done ) { - // add notification + if (findGroup !== -1 && !previousData[findGroup]?.done) { previousData[findGroup].done = true; } - }); - prev.forEach((tx, index)=> { - if(tx?.type === "leave-group" && memberGroups.findIndex( - (group) => tx?.groupId === group.groupId - ) === -1){ + + prev.forEach((tx, index) => { + if ( + tx?.type === "leave-group" && + memberGroups.findIndex((group) => tx?.groupId === group.groupId) === -1 + ) { previousData[index].done = true; } + }); - }) - prev.forEach((tx, index)=> { - - if(tx?.type === "created-common-secret" && tx?.signature && !tx.done){ - if(intervals.current[tx.signature]) return - - getStatus({signature: tx.signature}) + prev.forEach((tx) => { + if ( + ["created-common-secret", "joined-group-request", "join-request-accept"].includes( + tx?.type + ) && + tx?.signature && + !tx.done + ) { + if (!intervals.current[tx.signature]) { + getStatus({ signature: tx.signature }); + } } - - }) - prev.forEach((tx, index)=> { - - if(tx?.type === "joined-group-request" && tx?.signature && !tx.done){ - if(intervals.current[tx.signature]) return - - getStatus({signature: tx.signature}) + if (tx?.type === "register-name" && tx?.signature && !tx.done) { + if (!intervals.current[tx.signature]) { + getStatus({ signature: tx.signature }, getUserInfo); + } } + }); - }) - prev.forEach((tx, index)=> { - - if(tx?.type === "join-request-accept" && tx?.signature && !tx.done){ - if(intervals.current[tx.signature]) return - - getStatus({signature: tx.signature}) - } - - }) - - prev.forEach((tx, index)=> { - - if(tx?.type === "register-name" && tx?.signature && !tx.done){ - if(intervals.current[tx.signature]) return - - getStatus({signature: tx.signature}, getUserInfo) - } - - }) - return previousData; }); }, [memberGroups, getUserInfo]); - if(isMobile) return null + if (isMobile || txList?.length === 0 || txList.every((item) => item?.done)) + return null; - if (txList?.length === 0 || txList.filter((item) => !item?.done).length === 0) return null; return ( - - - - {txList.find((item) => !item.done) ? ( - - ) : ( - - )} - - - {open ? : } - - - - {txList.map((item) => { - return ( - - - - - ); - })} + <> + {!open && ( + + {txList.some((item) => !item.done) ? : } + + )} + {open && ( + + + + {txList.some((item) => !item.done) ? ( + + ) : ( + + )} + + + {open ? : } + + + + {txList.map((item) => ( + + + + ))} + + - - + )} + ); }; diff --git a/src/qortalRequests/get.ts b/src/qortalRequests/get.ts index 63f6b72..c824cfe 100644 --- a/src/qortalRequests/get.ts +++ b/src/qortalRequests/get.ts @@ -1638,6 +1638,10 @@ export const getWalletBalance = async (data, bypassPermission?: boolean, isFromE throw new Error(errorMsg); } + const isGateway = await isRunningGateway() + + if(data?.coin === 'ARRR' && isGateway) throw new Error('Cannot view ARRR balance through the gateway. Please use your local node.') + const value = (await getPermission(`qAPPAutoWalletBalance-${appInfo?.name}-${data.coin}`)) || false; let skip = false; if (value) { @@ -1691,7 +1695,7 @@ export const getWalletBalance = async (data, bypassPermission?: boolean, isFromE case "BTC": _url = await createEndpoint(`/crosschain/btc/walletbalance`); - _body = parsedData.derivedMasterPublicKey; + _body = parsedData.btcPublicKey; break; case "LTC": _url = await createEndpoint(`/crosschain/ltc/walletbalance`); diff --git a/src/utils/time.ts b/src/utils/time.ts index 77a0daf..b0a27cf 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -35,4 +35,22 @@ export function formatTimestamp(timestamp: number): string { const date = moment(unixTimestamp, 'x').fromNow() return date + } + + export function sortArrayByTimestampAndGroupName(array) { + return array.sort((a, b) => { + if (a.timestamp && b.timestamp) { + // Both have timestamp, sort by timestamp descending + return b.timestamp - a.timestamp; + } else if (a.timestamp) { + // Only `a` has timestamp, it comes first + return -1; + } else if (b.timestamp) { + // Only `b` has timestamp, it comes first + return 1; + } else { + // Neither has timestamp, sort alphabetically by groupName + return a.groupName.localeCompare(b.groupName); + } + }); } \ No newline at end of file