batch of updates 3

This commit is contained in:
PhilReact 2024-12-28 21:05:07 +02:00
parent 97a0dd7557
commit d1be494420
17 changed files with 246 additions and 185 deletions

12
package-lock.json generated
View File

@ -53,6 +53,7 @@
"react-frame-component": "^5.2.7", "react-frame-component": "^5.2.7",
"react-infinite-scroller": "^1.2.6", "react-infinite-scroller": "^1.2.6",
"react-intersection-observer": "^9.13.0", "react-intersection-observer": "^9.13.0",
"react-json-view-lite": "^2.0.1",
"react-qr-code": "^2.0.15", "react-qr-code": "^2.0.15",
"react-quill": "^2.0.0", "react-quill": "^2.0.0",
"react-redux": "^9.1.2", "react-redux": "^9.1.2",
@ -9573,6 +9574,17 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "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": { "node_modules/react-lifecycles-compat": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",

View File

@ -57,6 +57,7 @@
"react-frame-component": "^5.2.7", "react-frame-component": "^5.2.7",
"react-infinite-scroller": "^1.2.6", "react-infinite-scroller": "^1.2.6",
"react-intersection-observer": "^9.13.0", "react-intersection-observer": "^9.13.0",
"react-json-view-lite": "^2.0.1",
"react-qr-code": "^2.0.15", "react-qr-code": "^2.0.15",
"react-quill": "^2.0.0", "react-quill": "^2.0.0",
"react-redux": "^9.1.2", "react-redux": "^9.1.2",

View File

@ -43,7 +43,8 @@ import Return from "./assets/svgs/Return.svg";
import Success from "./assets/svgs/Success.svg"; import Success from "./assets/svgs/Success.svg";
import Info from "./assets/svgs/Info.svg"; import Info from "./assets/svgs/Info.svg";
import CloseIcon from "@mui/icons-material/Close"; 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 { import {
createAccount, createAccount,
generateRandomSentence, generateRandomSentence,
@ -2797,6 +2798,15 @@ function App() {
{messageQortalRequestExtension?.highlightedText} {messageQortalRequestExtension?.highlightedText}
</TextP> </TextP>
{messageQortalRequestExtension?.json && (
<>
<Spacer height="15px" />
<JsonView data={messageQortalRequestExtension?.json} shouldExpandNode={allExpanded} style={darkStyles} />
<Spacer height="15px" />
</>
)}
{messageQortalRequestExtension?.fee && ( {messageQortalRequestExtension?.fee && (
<> <>
<Spacer height="15px" /> <Spacer height="15px" />

View File

@ -103,14 +103,15 @@ export const AppRating = ({ app, myName, ratingCountPosition = "right" }) => {
getRating(app?.name, app?.service); getRating(app?.name, app?.service);
}, [getRating, app?.name]); }, [getRating, app?.name]);
const rateFunc = async (event, newValue) => { const rateFunc = async (event, chosenValue, currentValue) => {
try { try {
const newValue = chosenValue || currentValue
if (!myName) throw new Error("You need a name to rate."); if (!myName) throw new Error("You need a name to rate.");
if (!app?.name) return; if (!app?.name) return;
const fee = await getFee("ARBITRARY"); const fee = await getFee("CREATE_POLL");
await show({ 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", publishFee: fee.fee + " QORT",
}); });
@ -212,7 +213,7 @@ export const AppRating = ({ app, myName, ratingCountPosition = "right" }) => {
<Rating <Rating
value={value} value={value}
onChange={rateFunc} onChange={(event, rating)=> rateFunc(event, rating, value)}
precision={1} precision={1}
readOnly={hasPublishedRating === null} readOnly={hasPublishedRating === null}
size="small" size="small"

View File

@ -368,7 +368,7 @@ export const AppsDesktop = ({ mode, setMode, show , myName, goToHome, setDesktop
</ButtonBase> </ButtonBase>
<Save isDesktop myName={myName} /> <Save isDesktop myName={myName} />
{mode !== 'home' && ( {mode !== 'home' && (
<AppsNavBarDesktop /> <AppsNavBarDesktop disableBack={isNewTabWindow && mode === 'viewer'} />
)} )}

View File

@ -64,7 +64,7 @@ export function saveToLocalStorage(key, subKey, newValue) {
} }
} }
export const AppsNavBarDesktop = () => { export const AppsNavBarDesktop = ({disableBack}) => {
const [tabs, setTabs] = useState([]); const [tabs, setTabs] = useState([]);
const [selectedTab, setSelectedTab] = useState(null); const [selectedTab, setSelectedTab] = useState(null);
const [navigationController, setNavigationController] = useRecoilState(navigationControllerAtom) const [navigationController, setNavigationController] = useRecoilState(navigationControllerAtom)
@ -108,10 +108,11 @@ export const AppsNavBarDesktop = () => {
const isDisableBackButton = useMemo(()=> { const isDisableBackButton = useMemo(()=> {
if(disableBack) return true
if(selectedTab && navigationController[selectedTab?.tabId]?.hasBack) return false if(selectedTab && navigationController[selectedTab?.tabId]?.hasBack) return false
if(selectedTab && !navigationController[selectedTab?.tabId]?.hasBack) return true if(selectedTab && !navigationController[selectedTab?.tabId]?.hasBack) return true
return false return false
}, [navigationController, selectedTab]) }, [navigationController, selectedTab, disableBack])

View File

@ -252,7 +252,7 @@ export const ChatGroup = ({selectedGroup, secretKey, setSecretKey, getSecretKey,
const content = item?.content || item.decryptedData?.content; const content = item?.content || item.decryptedData?.content;
const sender = item.sender; const sender = item.sender;
const newTimestamp = item.timestamp; 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) { if (!content || typeof content !== "string" || !sender || typeof sender !== "string" || !newTimestamp) {
console.warn("Invalid content, sender, or timestamp in reaction data", item); 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 content = item?.content || item.decryptedData?.content;
const sender = item.sender; const sender = item.sender;
const newTimestamp = item.timestamp; 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) { if (!content || typeof content !== "string" || !sender || typeof sender !== "string" || !newTimestamp) {
console.warn("Invalid content, sender, or timestamp in reaction data", item); console.warn("Invalid content, sender, or timestamp in reaction data", item);
@ -755,7 +755,7 @@ useEffect(() => {
setIsSending(false) setIsSending(false)
resumeAllQueues() resumeAllQueues()
} }
}, []) }, [isPrivate])
console.log('isPrivate', isPrivate) console.log('isPrivate', isPrivate)

View File

@ -48,16 +48,17 @@ export const PollCard = ({
}); });
setIsLoadingSubmit(true); setIsLoadingSubmit(true);
window chrome?.runtime?.sendMessage(
.sendMessage(
"voteOnPoll",
{ {
pollName: poll?.info?.pollName, action: "VOTE_ON_POLL",
type: "qortalRequest",
payload: {
pollName: poll?.info?.pollName,
optionIndex: +selectedOption, optionIndex: +selectedOption,
}
}, },
60000 (response) => {
)
.then((response) => {
setIsLoadingSubmit(false); setIsLoadingSubmit(false);
if (response.error) { if (response.error) {
setInfoSnack({ setInfoSnack({
@ -75,14 +76,6 @@ export const PollCard = ({
setOpenSnack(true); setOpenSnack(true);
} }
}) })
.catch((error) => {
setIsLoadingSubmit(false);
setInfoSnack({
type: "error",
message: error?.message || "Unable to vote.",
});
setOpenSnack(true);
});
}; };
const getName = async (owner) => { const getName = async (owner) => {

View File

@ -96,6 +96,7 @@ import LockIcon from '@mui/icons-material/Lock';
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred'; import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
import { useSetRecoilState } from "recoil"; import { useSetRecoilState } from "recoil";
import { selectedGroupIdAtom } from "../../atoms/global"; import { selectedGroupIdAtom } from "../../atoms/global";
import { sortArrayByTimestampAndGroupName } from "../../utils/time";
// let touchStartY = 0; // let touchStartY = 0;
// let disablePullToRefresh = false; // let disablePullToRefresh = false;
@ -976,7 +977,7 @@ export const Group = ({
chrome?.runtime?.onMessage.addListener((message, sender, sendResponse) => { chrome?.runtime?.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === "SET_GROUPS") { if (message.action === "SET_GROUPS") {
// Update the component state with the received 'sendqort' state // Update the component state with the received 'sendqort' state
setGroups(message.payload); setGroups(sortArrayByTimestampAndGroupName(message.payload));
getLatestRegularChat(message.payload) getLatestRegularChat(message.payload)
setMemberGroups(message.payload); setMemberGroups(message.payload);
@ -2632,6 +2633,10 @@ export const Group = ({
Wait until an admin re-encrypts the keys. Wait until an admin re-encrypts the keys.
</Typography> </Typography>
<Spacer height="25px" /> <Spacer height="25px" />
<Typography>
<strong>Only unencrypted messages will be displayed.</strong>
</Typography>
<Spacer height="25px" />
<Typography> <Typography>
Try notifying an admin from the list of admins below: Try notifying an admin from the list of admins below:
</Typography> </Typography>

View File

@ -12,7 +12,7 @@ export const getMemberInvites = async (groupNumber) => {
return groupData; return groupData;
} }
const getNames = async (listOfMembers) => { const getNames = async (listOfMembers, includeNoNames) => {
let members = []; let members = [];
if (listOfMembers && Array.isArray(listOfMembers)) { if (listOfMembers && Array.isArray(listOfMembers)) {
for (const member of listOfMembers) { for (const member of listOfMembers) {
@ -20,6 +20,8 @@ const getNames = async (listOfMembers) => {
const name = await getNameInfo(member.offender); const name = await getNameInfo(member.offender);
if (name) { if (name) {
members.push({ ...member, 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) => { const getInvites = async (groupId) => {
try { try {
const res = await getMemberInvites(groupId); const res = await getMemberInvites(groupId);
const resWithNames = await getNames(res); const resWithNames = await getNames(res, true);
setBans(resWithNames); setBans(resWithNames);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -151,9 +154,9 @@ export const ListOfBans = ({ groupId, setInfoSnack, setOpenSnack, show }) => {
</Popover> </Popover>
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}> <ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
<ListItemAvatar> <ListItemAvatar>
<Avatar <Avatar
alt={member?.name} alt={member?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`} src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
/> />
</ListItemAvatar> </ListItemAvatar>
<ListItemText primary={member?.name || member?.offender} /> <ListItemText primary={member?.name || member?.offender} />

View File

@ -12,7 +12,7 @@ export const getMemberInvites = async (groupNumber) => {
return groupData; return groupData;
} }
const getNames = async (listOfMembers) => { const getNames = async (listOfMembers, includeNoNames) => {
let members = []; let members = [];
if (listOfMembers && Array.isArray(listOfMembers)) { if (listOfMembers && Array.isArray(listOfMembers)) {
for (const member of listOfMembers) { for (const member of listOfMembers) {
@ -20,6 +20,8 @@ const getNames = async (listOfMembers) => {
const name = await getNameInfo(member.invitee); const name = await getNameInfo(member.invitee);
if (name) { if (name) {
members.push({ ...member, 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) => { const getInvites = async (groupId) => {
try { try {
const res = await getMemberInvites(groupId); const res = await getMemberInvites(groupId);
const resWithNames = await getNames(res); const resWithNames = await getNames(res, true);
setInvites(resWithNames); setInvites(resWithNames);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -152,9 +154,9 @@ export const ListOfInvites = ({ groupId, setInfoSnack, setOpenSnack, show }) =>
</Popover> </Popover>
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}> <ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
<ListItemAvatar> <ListItemAvatar>
<Avatar <Avatar
alt={member?.name} alt={member?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`} src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
/> />
</ListItemAvatar> </ListItemAvatar>
<ListItemText primary={member?.name || member?.invitee} /> <ListItemText primary={member?.name || member?.invitee} />

View File

@ -12,14 +12,16 @@ export const getMemberInvites = async (groupNumber) => {
return groupData; return groupData;
} }
const getNames = async (listOfMembers) => { const getNames = async (listOfMembers, includeNoNames) => {
let members = []; let members = [];
if (listOfMembers && Array.isArray(listOfMembers)) { if (listOfMembers && Array.isArray(listOfMembers)) {
for (const member of listOfMembers) { for (const member of listOfMembers) {
if (member.joiner) { if (member.joiner) {
const name = await getNameInfo(member.joiner); const name = await getNameInfo(member.joiner);
if (name) { 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; return members;
} }
const cache = new CellMeasurerCache({ const cache = new CellMeasurerCache({
fixedWidth: true, fixedWidth: true,
defaultHeight: 50, defaultHeight: 50,
@ -44,7 +47,7 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
const getInvites = async (groupId) => { const getInvites = async (groupId) => {
try { try {
const res = await getMemberInvites(groupId); const res = await getMemberInvites(groupId);
const resWithNames = await getNames(res); const resWithNames = await getNames(res, true);
setInvites(resWithNames); setInvites(resWithNames);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
@ -165,9 +168,9 @@ export const ListOfJoinRequests = ({ groupId, setInfoSnack, setOpenSnack, show }
</Popover> </Popover>
<ListItemButton onClick={(event) => handlePopoverOpen(event, index)}> <ListItemButton onClick={(event) => handlePopoverOpen(event, index)}>
<ListItemAvatar> <ListItemAvatar>
<Avatar <Avatar
alt={member?.name} alt={member?.name}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`} src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
/> />
</ListItemAvatar> </ListItemAvatar>
<ListItemText primary={member?.name || member?.joiner} /> <ListItemText primary={member?.name || member?.joiner} />

View File

@ -328,7 +328,7 @@ const ListOfMembers = ({
<ListItemAvatar> <ListItemAvatar>
<Avatar <Avatar
alt={member?.name || member?.member} alt={member?.name || member?.member}
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true`} src={member?.name ? `${getBaseApiReact()}/arbitrary/THUMBNAIL/${member?.name}/qortal_avatar?async=true` : ''}
/> />
</ListItemAvatar> </ListItemAvatar>
<ListItemText <ListItemText

View File

@ -25,6 +25,7 @@ import { LoadingSnackbar } from "../Snackbar/LoadingSnackbar";
import { getFee } from "../../background"; import { getFee } from "../../background";
import { LoadingButton } from "@mui/lab"; import { LoadingButton } from "@mui/lab";
import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
import { Spacer } from "../../common/Spacer";
function a11yProps(index: number) { function a11yProps(index: number) {
return { return {
@ -113,7 +114,7 @@ export const ManageMembers = ({
} }
}; };
const getMembers = async (groupId) => { const getMembersWithNames = React.useCallback(async (groupId) => {
try { try {
setIsLoadingMembers(true) setIsLoadingMembers(true)
const res = await getGroupMembers(groupId); const res = await getGroupMembers(groupId);
@ -121,6 +122,13 @@ export const ManageMembers = ({
setMembersWithNames(resWithNames); setMembersWithNames(resWithNames);
setIsLoadingMembers(false) setIsLoadingMembers(false)
} catch (error) {} } catch (error) {}
}, []);
const getMembers = async (groupId) => {
try {
const res = await getGroupMembers(groupId);
setMembersWithNames(res?.members || []);
} catch (error) {}
}; };
React.useEffect(()=> { React.useEffect(()=> {
@ -256,6 +264,8 @@ export const ManageMembers = ({
maxWidth: '750px' maxWidth: '750px'
}} }}
> >
<Button variant="contained" onClick={()=> getMembersWithNames(selectedGroup?.groupId)}>Load members with names</Button>
<Spacer height="10px" />
<ListOfMembers <ListOfMembers
members={membersWithNames || []} members={membersWithNames || []}
groupId={selectedGroup?.groupId} groupId={selectedGroup?.groupId}

View File

@ -1,77 +1,69 @@
import { List, ListItemButton, ListItemIcon } from "@mui/material"; import {
List,
ListItemButton,
ListItemIcon,
ListItemText,
Collapse,
IconButton,
} from "@mui/material";
import React, { useContext, useEffect, useRef } from "react"; import React, { useContext, useEffect, useRef } from "react";
import ListItemText from "@mui/material/ListItemText";
import Collapse from "@mui/material/Collapse";
import InboxIcon from "@mui/icons-material/MoveToInbox";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import StarBorder from "@mui/icons-material/StarBorder";
import PendingIcon from "@mui/icons-material/Pending"; import PendingIcon from "@mui/icons-material/Pending";
import TaskAltIcon from "@mui/icons-material/TaskAlt"; import TaskAltIcon from "@mui/icons-material/TaskAlt";
import ExpandLess from "@mui/icons-material/ExpandLess";
import ExpandMore from "@mui/icons-material/ExpandMore";
import { MyContext, getBaseApiReact, isMobile } from "../../App"; import { MyContext, getBaseApiReact, isMobile } from "../../App";
import { getBaseApi } from "../../background";
export const TaskManger = ({ getUserInfo }) => {
export const TaskManger = ({getUserInfo}) => {
const { txList, setTxList, memberGroups } = useContext(MyContext); const { txList, setTxList, memberGroups } = useContext(MyContext);
const [open, setOpen] = React.useState(true); const [open, setOpen] = React.useState(false);
const intervals = useRef({});
const handleClick = () => { 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) =>{ if (!stop) {
stop = true;
let stop = false try {
const txTransaction = await getTx();
const getAnswer = async () => { if (!txTransaction.error && txTransaction.signature) {
const getTx = async () => { await new Promise((res) =>
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)=> {
setTimeout(() => { setTimeout(() => {
res(null) res(null);
}, 300000); }, 300000)
}) );
setTxList((prev)=> { setTxList((prev) => {
let previousData = [...prev]; let previousData = [...prev];
const findTxWithSignature = previousData.findIndex((tx)=> tx.signature === signature) const findTxWithSignature = previousData.findIndex(
if(findTxWithSignature !== -1){ (tx) => tx.signature === signature
);
if (findTxWithSignature !== -1) {
previousData[findTxWithSignature].done = true; previousData[findTxWithSignature].done = true;
return previousData return previousData;
} }
return previousData return previousData;
}) });
if(callback){ if (callback) {
callback(true) callback(true);
} }
clearInterval(intervals.current[signature]) clearInterval(intervals.current[signature]);
}
} catch (error) {}
stop = false;
}
};
} intervals.current[signature] = setInterval(getAnswer, 120000);
} catch (error) { } };
stop = false
}
}
intervals.current[signature] = setInterval(getAnswer, 120000)
}
useEffect(() => { useEffect(() => {
setTxList((prev) => { setTxList((prev) => {
@ -80,111 +72,117 @@ export const TaskManger = ({getUserInfo}) => {
const findGroup = txList.findIndex( const findGroup = txList.findIndex(
(tx) => tx?.type === "joined-group" && tx?.groupId === group.groupId (tx) => tx?.type === "joined-group" && tx?.groupId === group.groupId
); );
if (findGroup !== -1 && !previousData[findGroup]?.done ) { if (findGroup !== -1 && !previousData[findGroup]?.done) {
// add notification
previousData[findGroup].done = true; previousData[findGroup].done = true;
} }
}); });
memberGroups.forEach((group) => { memberGroups.forEach((group) => {
const findGroup = txList.findIndex( 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 ) { if (findGroup !== -1 && !previousData[findGroup]?.done) {
// add notification
previousData[findGroup].done = true; previousData[findGroup].done = true;
} }
}); });
prev.forEach((tx, index)=> {
if(tx?.type === "leave-group" && memberGroups.findIndex( prev.forEach((tx, index) => {
(group) => tx?.groupId === group.groupId if (
) === -1){ tx?.type === "leave-group" &&
memberGroups.findIndex((group) => tx?.groupId === group.groupId) === -1
) {
previousData[index].done = true; previousData[index].done = true;
} }
});
}) prev.forEach((tx) => {
prev.forEach((tx, index)=> { if (
["created-common-secret", "joined-group-request", "join-request-accept"].includes(
if(tx?.type === "created-common-secret" && tx?.signature && !tx.done){ tx?.type
if(intervals.current[tx.signature]) return ) &&
tx?.signature &&
getStatus({signature: tx.signature}) !tx.done
) {
if (!intervals.current[tx.signature]) {
getStatus({ signature: tx.signature });
}
} }
if (tx?.type === "register-name" && tx?.signature && !tx.done) {
}) if (!intervals.current[tx.signature]) {
prev.forEach((tx, index)=> { getStatus({ signature: tx.signature }, getUserInfo);
}
if(tx?.type === "joined-group-request" && tx?.signature && !tx.done){
if(intervals.current[tx.signature]) return
getStatus({signature: tx.signature})
} }
});
})
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; return previousData;
}); });
}, [memberGroups, getUserInfo]); }, [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 ( return (
<List <>
sx={{ width: "100%", maxWidth: 360, bgcolor: "background.paper" }} {!open && (
component="nav" <IconButton
aria-labelledby="nested-list-subheader" onClick={handleClick}
> sx={{
<ListItemButton onClick={handleClick}> position: "fixed",
<ListItemIcon> bottom: 16,
{txList.find((item) => !item.done) ? ( right: 16,
<PendingIcon sx={{ bgcolor: "primary.main",
color: 'white' color: "white",
}} /> ":hover": { bgcolor: "primary.dark" },
) : ( }}
<TaskAltIcon sx={{ >
color: 'white' {txList.some((item) => !item.done) ? <PendingIcon /> : <TaskAltIcon />}
}} /> </IconButton>
)} )}
</ListItemIcon> {open && (
<ListItemText primary="Ongoing Transactions" /> <List
{open ? <ExpandLess /> : <ExpandMore />} sx={{
</ListItemButton> position: "fixed",
<Collapse in={open} timeout="auto" unmountOnExit> bottom: 16,
<List component="div" disablePadding sx={{ right: 16,
maxHeight: '400px', width: "300px",
overflow: 'auto' maxHeight: "400px",
}}> bgcolor: "background.paper",
{txList.map((item) => { boxShadow: 4,
return ( overflow: "auto",
<ListItemButton key={item?.signature} sx={{ pl: 4 }}> zIndex: 10,
padding: '0px'
<ListItemText primary={item?.done ? item.labelDone : item.label} /> }}
</ListItemButton> component="nav"
); >
})} <ListItemButton onClick={handleClick}>
<ListItemIcon>
{txList.some((item) => !item.done) ? (
<PendingIcon sx={{
color:'white'
}} />
) : (
<TaskAltIcon sx={{
color:'white'
}} />
)}
</ListItemIcon>
<ListItemText primary="Ongoing Transactions" />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
{txList.map((item) => (
<ListItemButton key={item?.signature} sx={{ pl: 4 }}>
<ListItemText
primary={item?.done ? item.labelDone : item.label}
/>
</ListItemButton>
))}
</List>
</Collapse>
</List> </List>
</Collapse> )}
</List> </>
); );
}; };

View File

@ -1638,6 +1638,10 @@ export const getWalletBalance = async (data, bypassPermission?: boolean, isFromE
throw new Error(errorMsg); 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; const value = (await getPermission(`qAPPAutoWalletBalance-${appInfo?.name}-${data.coin}`)) || false;
let skip = false; let skip = false;
if (value) { if (value) {
@ -1691,7 +1695,7 @@ export const getWalletBalance = async (data, bypassPermission?: boolean, isFromE
case "BTC": case "BTC":
_url = await createEndpoint(`/crosschain/btc/walletbalance`); _url = await createEndpoint(`/crosschain/btc/walletbalance`);
_body = parsedData.derivedMasterPublicKey; _body = parsedData.btcPublicKey;
break; break;
case "LTC": case "LTC":
_url = await createEndpoint(`/crosschain/ltc/walletbalance`); _url = await createEndpoint(`/crosschain/ltc/walletbalance`);

View File

@ -35,4 +35,22 @@ export function formatTimestamp(timestamp: number): string {
const date = moment(unixTimestamp, 'x').fromNow() const date = moment(unixTimestamp, 'x').fromNow()
return date 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);
}
});
} }