diff --git a/src/assets/Icons/q-trade-logo.webp b/src/assets/Icons/q-trade-logo.webp new file mode 100644 index 0000000..77ba972 Binary files /dev/null and b/src/assets/Icons/q-trade-logo.webp differ diff --git a/src/common/Spinners/BarSpinner/BarSpinner.tsx b/src/common/Spinners/BarSpinner/BarSpinner.tsx new file mode 100644 index 0000000..a51a512 --- /dev/null +++ b/src/common/Spinners/BarSpinner/BarSpinner.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import './barSpinner.css' +export const BarSpinner = ({width = '20px', color}) => { + return ( +
+ ) +} diff --git a/src/common/Spinners/BarSpinner/barSpinner.css b/src/common/Spinners/BarSpinner/barSpinner.css new file mode 100644 index 0000000..529bc50 --- /dev/null +++ b/src/common/Spinners/BarSpinner/barSpinner.css @@ -0,0 +1,19 @@ +/* HTML:
*/ +.loader-bar { + width: 45px; + aspect-ratio: .75; + --c:no-repeat linear-gradient(currentColor 0 0); + background: + var(--c) 0% 100%, + var(--c) 50% 100%, + var(--c) 100% 100%; + background-size: 20% 65%; + animation: l8 1s infinite linear; + } + @keyframes l8 { + 16.67% {background-position: 0% 0% ,50% 100%,100% 100%} + 33.33% {background-position: 0% 0% ,50% 0% ,100% 100%} + 50% {background-position: 0% 0% ,50% 0% ,100% 0% } + 66.67% {background-position: 0% 100%,50% 0% ,100% 0% } + 83.33% {background-position: 0% 100%,50% 100%,100% 0% } + } \ No newline at end of file diff --git a/src/components/Explore/Explore.tsx b/src/components/Explore/Explore.tsx new file mode 100644 index 0000000..7cc96a0 --- /dev/null +++ b/src/components/Explore/Explore.tsx @@ -0,0 +1,101 @@ +import { Box, ButtonBase, Typography } from "@mui/material"; +import React from "react"; +import ChatIcon from "@mui/icons-material/Chat"; +import qTradeLogo from "../../assets/Icons/q-trade-logo.webp"; +import AppsIcon from "@mui/icons-material/Apps"; +import { executeEvent } from "../../utils/events"; +export const Explore = ({setDesktopViewMode}) => { + return ( + + { + executeEvent("addTab", { + data: { service: "APP", name: "q-trade" }, + }); + executeEvent("open-apps-mode", {}); + }} + > + + + Trade QORT + + + { + setDesktopViewMode('apps') + + }} + > + + + See Apps + + + { + executeEvent("openGroupMessage", { + from: "0" , + }); + }} + > + + + General Chat + + + + ); +}; diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx index 48ef02c..08f75e9 100644 --- a/src/components/Group/Group.tsx +++ b/src/components/Group/Group.tsx @@ -1299,11 +1299,11 @@ export const Group = ({ if (isLoadingOpenSectionFromNotification.current) return; const groupId = e.detail?.from; - const findGroup = groups?.find((group) => +group?.groupId === +groupId); if (findGroup?.groupId === selectedGroup?.groupId) { isLoadingOpenSectionFromNotification.current = false; - + setChatMode("groups"); + setDesktopViewMode('chat') return; } if (findGroup) { diff --git a/src/components/Group/GroupInvites.tsx b/src/components/Group/GroupInvites.tsx index 0eda0f6..13e5850 100644 --- a/src/components/Group/GroupInvites.tsx +++ b/src/components/Group/GroupInvites.tsx @@ -10,16 +10,20 @@ import CommentIcon from "@mui/icons-material/Comment"; import InfoIcon from "@mui/icons-material/Info"; import GroupAddIcon from "@mui/icons-material/GroupAdd"; import { executeEvent } from "../../utils/events"; -import { Box, Typography } from "@mui/material"; +import { Box, ButtonBase, Collapse, Typography } from "@mui/material"; import { Spacer } from "../../common/Spacer"; import { getGroupNames } from "./UserListOfInvites"; import { CustomLoader } from "../../common/CustomLoader"; import { getBaseApiReact, isMobile } from "../../App"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import ExpandLessIcon from "@mui/icons-material/ExpandLess"; export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState( [] ); + const [isExpanded, setIsExpanded] = React.useState(false); + const [loading, setLoading] = React.useState(true); const getJoinRequests = async () => { @@ -53,121 +57,129 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => { alignItems: "center", }} > - setIsExpanded((prev)=> !prev)} > - Group Invites: + Group Invites {groupsWithJoinRequests?.length > 0 && ` (${groupsWithJoinRequests?.length})`} - - + {isExpanded ? : ( + + )} + + + - {loading && groupsWithJoinRequests.length === 0 && ( - + {loading && groupsWithJoinRequests.length === 0 && ( + + + + )} + {!loading && groupsWithJoinRequests.length === 0 && ( + + + Nothing to display + + + )} + - - - )} - {!loading && groupsWithJoinRequests.length === 0 && ( - - - Nothing to display - - - )} - - {groupsWithJoinRequests?.map((group) => { - return ( - { - setOpenAddGroup(true); - setTimeout(() => { - executeEvent("openGroupInvitesRequest", {}); - }, 300); - }} - disablePadding - secondaryAction={ - - { + return ( + { + setOpenAddGroup(true); + setTimeout(() => { + executeEvent("openGroupInvitesRequest", {}); + }, 300); + }} + disablePadding + secondaryAction={ + + + + } + > + + - - } - > - - - - - ); - })} - - + + + ); + })} + + + ); }; diff --git a/src/components/Group/GroupJoinRequests.tsx b/src/components/Group/GroupJoinRequests.tsx index 03c2258..76f958a 100644 --- a/src/components/Group/GroupJoinRequests.tsx +++ b/src/components/Group/GroupJoinRequests.tsx @@ -11,16 +11,20 @@ import InfoIcon from "@mui/icons-material/Info"; import { RequestQueueWithPromise } from "../../utils/queue/queue"; import GroupAddIcon from '@mui/icons-material/GroupAdd'; import { executeEvent } from "../../utils/events"; -import { Box, Typography } from "@mui/material"; +import { Box, ButtonBase, Collapse, Typography } from "@mui/material"; import { Spacer } from "../../common/Spacer"; import { CustomLoader } from "../../common/CustomLoader"; import { getBaseApi } from "../../background"; import { MyContext, getBaseApiReact, isMobile } from "../../App"; import { myGroupsWhereIAmAdminAtom } from "../../atoms/global"; import { useSetRecoilState } from "recoil"; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; export const requestQueueGroupJoinRequests = new RequestQueueWithPromise(2) export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, getTimestampEnterChat, setSelectedGroup, setGroupSection, setMobileViewMode, setDesktopViewMode }) => { + const [isExpanded, setIsExpanded] = React.useState(false) + const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([]) const [loading, setLoading] = React.useState(true) const {txList, setTxList} = React.useContext(MyContext) @@ -109,26 +113,33 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get flexDirection: "column", alignItems: 'center' }}> - setIsExpanded((prev)=> !prev)} > - Join Requests: + Join Requests {filteredJoinRequests?.filter((group)=> group?.data?.length > 0)?.length > 0 && ` (${filteredJoinRequests?.filter((group)=> group?.data?.length > 0)?.length})`} - - - + {isExpanded ? : ( + + )} + + + ); }; diff --git a/src/components/Group/HomeDesktop.tsx b/src/components/Group/HomeDesktop.tsx index e3b847d..26cf612 100644 --- a/src/components/Group/HomeDesktop.tsx +++ b/src/components/Group/HomeDesktop.tsx @@ -1,4 +1,4 @@ -import { Box, Button, Typography } from "@mui/material"; +import { Box, Button, Divider, Typography } from "@mui/material"; import React from "react"; import { Spacer } from "../../common/Spacer"; import { ListOfThreadPostsWatched } from "./ListOfThreadPostsWatched"; @@ -7,7 +7,10 @@ import { GroupJoinRequests } from "./GroupJoinRequests"; import { GroupInvites } from "./GroupInvites"; import RefreshIcon from "@mui/icons-material/Refresh"; import { ListOfGroupPromotions } from "./ListOfGroupPromotions"; - +import { QortPrice } from "../Home/QortPrice"; +import ExploreIcon from "@mui/icons-material/Explore"; +import { Explore } from "../Explore/Explore"; +import { NewUsersCTA } from "../Home/NewUsersCTA"; export const HomeDesktop = ({ refreshHomeDataFunc, myAddress, @@ -22,12 +25,12 @@ export const HomeDesktop = ({ setOpenAddGroup, setMobileViewMode, setDesktopViewMode, - desktopViewMode + desktopViewMode, }) => { return ( - - 15 ? "16px" : "20px", - padding: '10px' + display: "flex", + width: "100%", + flexDirection: "column", + height: "100%", + alignItems: "flex-start", + maxWidth: "1036px", }} > - Welcome - {userInfo?.name ? ( - {`, ${userInfo?.name}`} - ) : null} - - - {!isLoadingGroups && ( - 15 ? "16px" : "20px", + padding: "10px", }} > - - - - {desktopViewMode === 'home' && ( - <> - {`, ${userInfo?.name}`} + ) : null} + + + {!isLoadingGroups && ( + + + + item?.groupId !== "0").length !== 0 + } + /> + + + {desktopViewMode === "home" && ( + <> + {/* + */} + + + + + + + + )} + + - - + )} + + {!isLoadingGroups && ( + <> + + + + {" "} + + Explore + {" "} - - + + + + - - )} - - - )} - {!isLoadingGroups && ( - - )} + + + + )} - {/* */} - + ); diff --git a/src/components/Group/ListOfGroupPromotions.tsx b/src/components/Group/ListOfGroupPromotions.tsx index 2f474d3..a983f56 100644 --- a/src/components/Group/ListOfGroupPromotions.tsx +++ b/src/components/Group/ListOfGroupPromotions.tsx @@ -9,6 +9,8 @@ import { Avatar, Box, Button, + ButtonBase, + Collapse, Dialog, DialogActions, DialogContent, @@ -28,8 +30,8 @@ import { 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 LockIcon from "@mui/icons-material/Lock"; +import NoEncryptionGmailerrorredIcon from "@mui/icons-material/NoEncryptionGmailerrorred"; import { MyContext, getArbitraryEndpointReact, @@ -40,7 +42,11 @@ 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 { + myGroupsWhereIAmAdminAtom, + promotionTimeIntervalAtom, + promotionsAtom, +} from "../../atoms/global"; import { Label } from "./AddGroup"; import ShortUniqueId from "short-unique-id"; import { CustomizedSnackbars } from "../Snackbar/Snackbar"; @@ -48,7 +54,8 @@ 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 { @@ -65,8 +72,6 @@ export function utf8ToBase64(inputString: string): string { const uid = new ShortUniqueId({ length: 8 }); - - export function getGroupId(str) { const match = str.match(/group-(\d+)-/); return match ? match[1] : null; @@ -82,12 +87,12 @@ export const ListOfGroupPromotions = () => { const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useRecoilState( myGroupsWhereIAmAdminAtom ); - const [promotions, setPromotions] = useRecoilState( - promotionsAtom - ); + const [promotions, setPromotions] = useRecoilState(promotionsAtom); const [promotionTimeInterval, setPromotionTimeInterval] = useRecoilState( promotionTimeIntervalAtom ); + const [isExpanded, setIsExpanded] = React.useState(false); + const [openSnack, setOpenSnack] = useState(false); const [infoSnack, setInfoSnack] = useState(null); const [fee, setFee] = useState(null); @@ -96,16 +101,16 @@ export const ListOfGroupPromotions = () => { const { show, setTxList } = useContext(MyContext); const listRef = useRef(); - const rowVirtualizer = useVirtualizer({ - count: promotions.length, - getItemKey: React.useCallback( - (index) => promotions[index]?.identifier, - [promotions] - ), - getScrollElement: () => listRef.current, - estimateSize: () => 80, // Provide an estimated height of items, adjust this as needed - overscan: 10, // Number of items to render outside the visible area to improve smoothness - }); + const rowVirtualizer = useVirtualizer({ + count: promotions.length, + getItemKey: React.useCallback( + (index) => promotions[index]?.identifier, + [promotions] + ), + getScrollElement: () => listRef.current, + estimateSize: () => 80, // Provide an estimated height of items, adjust this as needed + overscan: 10, // Number of items to render outside the visible area to improve smoothness + }); useEffect(() => { try { @@ -117,7 +122,7 @@ export const ListOfGroupPromotions = () => { }, []); const getPromotions = useCallback(async () => { try { - setPromotionTimeInterval(Date.now()) + setPromotionTimeInterval(Date.now()); 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, { @@ -168,7 +173,9 @@ export const ListOfGroupPromotions = () => { }); await Promise.all(getPromos); - const groupWithInfo = await getGroupNames(data.sort((a, b) => b.created - a.created)); + const groupWithInfo = await getGroupNames( + data.sort((a, b) => b.created - a.created) + ); setPromotions(groupWithInfo); } catch (error) { console.error(error); @@ -177,22 +184,23 @@ export const ListOfGroupPromotions = () => { useEffect(() => { const now = Date.now(); - + const timeSinceLastFetch = now - promotionTimeInterval; - const initialDelay = timeSinceLastFetch >= THIRTY_MINUTES - ? 0 - : THIRTY_MINUTES - timeSinceLastFetch; + const initialDelay = + timeSinceLastFetch >= THIRTY_MINUTES + ? 0 + : THIRTY_MINUTES - timeSinceLastFetch; const initialTimeout = setTimeout(() => { getPromotions(); - + // Start a 30-minute interval const interval = setInterval(() => { getPromotions(); }, THIRTY_MINUTES); - + return () => clearInterval(interval); }, initialDelay); - + return () => clearTimeout(initialTimeout); }, [getPromotions, promotionTimeInterval]); @@ -328,100 +336,143 @@ export const ListOfGroupPromotions = () => { } }; - - return ( - - + setIsExpanded((prev) => !prev)} > - Group Promotions + Group promotions {promotions.length > 0 && ` (${promotions.length})`} - - - + {isExpanded ? ( + + ) : ( + + )} + + - - {loading && promotions.length === 0 && ( + + <> - - - )} - {!loading && promotions.length === 0 && ( - - - Nothing to display - + + + + - )} + + {loading && promotions.length === 0 && ( + + + + )} + {!loading && promotions.length === 0 && ( + + + Nothing to display + + + )}
{ const index = virtualRow.index; const promotion = promotions[index]; return ( -
{ gap: "5px", }} > - - Error loading content: Invalid Data - - } - > - - { - if (reason === "backdropClick") { - // Prevent closing on backdrop click - return; - } - handlePopoverClose(); // Close only on other events like Esc key press - }} - anchorOrigin={{ - vertical: "top", - horizontal: "center", - }} - transformOrigin={{ - vertical: "bottom", - horizontal: "center", - }} - style={{ marginTop: "8px" }} - > - - - Group name: {` ${promotion?.groupName}`} - - - Number of members: {` ${promotion?.memberCount}`} - - {promotion?.description && ( - - {promotion?.description} - - )} - {promotion?.isOpen === false && ( - - *This is a closed/private group, so you will need to wait - until an admin accepts your request - - )} - - - - Close - - - handleJoinGroup(promotion, promotion?.isOpen) - } - > - Join - - - - + + Error loading content: Invalid Data + + } + > + + { + if (reason === "backdropClick") { + // Prevent closing on backdrop click + return; + } + handlePopoverClose(); // Close only on other events like Esc key press + }} + anchorOrigin={{ + vertical: "top", + horizontal: "center", + }} + transformOrigin={{ + vertical: "bottom", + horizontal: "center", + }} + style={{ marginTop: "8px" }} + > + + + Group name: {` ${promotion?.groupName}`} + + + Number of members:{" "} + {` ${promotion?.memberCount}`} + + {promotion?.description && ( + + {promotion?.description} + + )} + {promotion?.isOpen === false && ( + + *This is a closed/private group, so you + will need to wait until an admin accepts + your request + + )} + + + + Close + + + handleJoinGroup( + promotion, + promotion?.isOpen + ) + } + > + Join + + + + - - - - {promotion?.name?.charAt(0)} - - - {promotion?.name} - - - - {promotion?.groupName} - - - - - {promotion?.isOpen === false && ( - - )} - {promotion?.isOpen === true && ( - - )} - - {promotion?.isOpen ? 'Public group' : 'Private group' } - - - - - {promotion?.data} - - - - - - - + + + + {promotion?.name?.charAt(0)} + + + {promotion?.name} + + + + {promotion?.groupName} + + + + + {promotion?.isOpen === false && ( + + )} + {promotion?.isOpen === true && ( + + )} + + {promotion?.isOpen + ? "Public group" + : "Private group"} + + + + + {promotion?.data} + + + + + + +
- ); })}
- -
+
+ + {isShowModal && ( diff --git a/src/components/Group/QMailMessages.tsx b/src/components/Group/QMailMessages.tsx index ccce59b..ee62a4c 100644 --- a/src/components/Group/QMailMessages.tsx +++ b/src/components/Group/QMailMessages.tsx @@ -1,11 +1,11 @@ -import React, { useCallback, useEffect, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import List from "@mui/material/List"; import ListItem from "@mui/material/ListItem"; import ListItemButton from "@mui/material/ListItemButton"; import ListItemIcon from "@mui/material/ListItemIcon"; import ListItemText from "@mui/material/ListItemText"; import moment from 'moment' -import { Box, Typography } from "@mui/material"; +import { Box, ButtonBase, Collapse, Typography } from "@mui/material"; import { Spacer } from "../../common/Spacer"; import { getBaseApiReact, isMobile } from "../../App"; import { MessagingIcon } from '../../assets/Icons/MessagingIcon'; @@ -15,6 +15,10 @@ import { executeEvent } from '../../utils/events'; import { CustomLoader } from '../../common/CustomLoader'; import { useRecoilState } from 'recoil'; import { mailsAtom, qMailLastEnteredTimestampAtom } from '../../atoms/global'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import ExpandLessIcon from '@mui/icons-material/ExpandLess'; +import MarkEmailUnreadIcon from '@mui/icons-material/MarkEmailUnread'; +import { last } from 'slate'; export const isLessThanOneWeekOld = (timestamp) => { // Current time in milliseconds const now = Date.now(); @@ -41,6 +45,7 @@ export function formatEmailDate(timestamp: number) { } } export const QMailMessages = ({userName, userAddress}) => { + const [isExpanded, setIsExpanded] = useState(false) const [mails, setMails] = useRecoilState(mailsAtom) const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState(qMailLastEnteredTimestampAtom) const [loading, setLoading] = useState(true) @@ -99,7 +104,16 @@ export const QMailMessages = ({userName, userAddress}) => { }, [getMails, userName, userAddress]); - + const anyUnread = useMemo(()=> { + let unread = false + + mails.forEach((mail)=> { + if(lastEnteredTimestamp && isLessThanOneWeekOld(mail?.created)){ + unread = true + } + }) + return unread + }, [mails, lastEnteredTimestamp]) return ( { }} > - setIsExpanded((prev)=> !prev)} > Latest Q-Mails - - - + + {isExpanded ? : ( + + )} + + { + ) } diff --git a/src/components/Group/ThingsToDoInitial.tsx b/src/components/Group/ThingsToDoInitial.tsx index 6825947..0a02f36 100644 --- a/src/components/Group/ThingsToDoInitial.tsx +++ b/src/components/Group/ThingsToDoInitial.tsx @@ -64,6 +64,7 @@ if(hasDoneNameAndBalanceAndIsLoaded){ ); } +if(!isLoaded) return null return ( @@ -96,7 +97,6 @@ if(hasDoneNameAndBalanceAndIsLoaded){ { + if (balance === undefined || +balance > 0) return null; + return ( + + + + + + Are you a new user? + + + + Please message us on Telegram or Discord if you need 4 QORT to start + chatting without any limitations + + + + { + if (window?.electronAPI?.openExternal) { + window.electronAPI.openExternal( + "https://link.qortal.dev/telegram-invite" + ); + } else { + window.open( + "https://link.qortal.dev/telegram-invite", + "_blank" + ); + } + }} + > + Telegram + + { + if (window?.electronAPI?.openExternal) { + window.electronAPI.openExternal( + "https://link.qortal.dev/discord-invite" + ); + } else { + window.open("https://link.qortal.dev/discord-invite", "_blank"); + } + }} + > + Discord + + + + + ); +}; diff --git a/src/components/Home/QortPrice.tsx b/src/components/Home/QortPrice.tsx new file mode 100644 index 0000000..3d54a04 --- /dev/null +++ b/src/components/Home/QortPrice.tsx @@ -0,0 +1,209 @@ +import React, { useCallback, useEffect, useState } from 'react' +import { getBaseApiReact } from '../../App'; +import { Box, Tooltip, Typography } from '@mui/material'; +import { BarSpinner } from '../../common/Spinners/BarSpinner/BarSpinner'; + +function getAverageLtcPerQort(trades) { + let totalQort = 0; + let totalLtc = 0; + + trades.forEach((trade) => { + const qort = parseFloat(trade.qortAmount); + const ltc = parseFloat(trade.foreignAmount); + + totalQort += qort; + totalLtc += ltc; + }); + + // Avoid division by zero + if (totalQort === 0) return 0; + + // Weighted average price + return parseFloat((totalLtc / totalQort).toFixed(8)); + } + + function getTwoWeeksAgoTimestamp() { + const now = new Date(); + now.setDate(now.getDate() - 14); // Subtract 14 days + return now.getTime(); // Get timestamp in milliseconds + } + + function formatWithCommasAndDecimals(number) { + + return Number(number).toLocaleString(); + } + + +export const QortPrice = () => { + const [ltcPerQort, setLtcPerQort] = useState(null) + const [supply, setSupply] = useState(null) + const [lastBlock, setLastBlock] = useState(null) + const [loading, setLoading] = useState(true) + + const getPrice = useCallback(async () => { + try { + setLoading(true) + + const response = await fetch(`${getBaseApiReact()}/crosschain/trades?foreignBlockchain=LITECOIN&minimumTimestamp=${getTwoWeeksAgoTimestamp()}&limit=20&reverse=true`); + const data = await response.json(); + + + setLtcPerQort(getAverageLtcPerQort(data)); + } catch (error) { + console.error(error); + } finally { + setLoading(false) + + } + }, []) + + const getLastBlock = useCallback(async () => { + try { + setLoading(true) + + const response = await fetch(`${getBaseApiReact()}/blocks/last`); + const data = await response.json(); + + setLastBlock(data); + } catch (error) { + console.error(error); + } finally { + setLoading(false) + + } + }, []) + + const getSupplyInCirculation = useCallback(async () => { + try { + setLoading(true) + + const response = await fetch(`${getBaseApiReact()}/stats/supply/circulating`); + const data = await response.text(); + formatWithCommasAndDecimals(data) + setSupply(formatWithCommasAndDecimals(data)); + } catch (error) { + console.error(error); + } finally { + setLoading(false) + + } + }, []) + + + + + useEffect(() => { + + getPrice(); + getSupplyInCirculation() + getLastBlock() + const interval = setInterval(() => { + getPrice(); + getSupplyInCirculation() + getLastBlock() + }, 900000); + + return () => clearInterval(interval); + + }, [getPrice]); + + console.log('supply', supply) + + return ( + + Based on the latest 20 trades} + placement="bottom" + arrow + sx={{ fontSize: "24" }} + slotProps={{ + tooltip: { + sx: { + color: "#ffffff", + backgroundColor: "#444444", + }, + }, + arrow: { + sx: { + color: "#444444", + }, + }, + }} + > + + Price + {!ltcPerQort ? ( + + ): ( + {ltcPerQort} LTC/QORT + )} + + + + + + Supply + {!supply ? ( + + ): ( + {supply} QORT + )} + + + + Last height + {!lastBlock?.height ? ( + + ): ( + {lastBlock?.height} + + )} + + + + ) +}