diff --git a/src/App.tsx b/src/App.tsx
index 9e14601..7df8043 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -35,6 +35,7 @@ import RefreshIcon from "@mui/icons-material/Refresh";
import Logo2 from "./assets/svgs/Logo2.svg";
import Copy from "./assets/svgs/Copy.svg";
import ltcLogo from "./assets/ltc.png";
+import PersonSearchIcon from '@mui/icons-material/PersonSearch';
import qortLogo from "./assets/qort.png";
import { CopyToClipboard } from "react-copy-to-clipboard";
import Download from "./assets/svgs/Download.svg";
@@ -113,6 +114,7 @@ import {
canSaveSettingToQdnAtom,
enabledDevModeAtom,
fullScreenAtom,
+ groupsPropertiesAtom,
hasSettingsChangedAtom,
isDisabledEditorEnterAtom,
isUsingImportExportSettingsAtom,
@@ -145,6 +147,8 @@ import { QMailStatus } from "./components/QMailStatus";
import { GlobalActions } from "./components/GlobalActions/GlobalActions";
import { useBlockedAddresses } from "./components/Group/useBlockUsers";
import { WalletIcon } from "./assets/Icons/WalletIcon";
+import { DrawerUserLookup } from "./components/Drawer/DrawerUserLookup";
+import { UserLookup } from "./components/UserLookup.tsx/UserLookup";
type extStates =
| "not-authenticated"
@@ -400,6 +404,7 @@ function App() {
const [openSnack, setOpenSnack] = useState(false);
const [hasLocalNode, setHasLocalNode] = useState(false);
const [isOpenDrawerProfile, setIsOpenDrawerProfile] = useState(false);
+ const [isOpenDrawerLookup, setIsOpenDrawerLookup] = useState(false)
const [apiKey, setApiKey] = useState("");
const [isOpenSendQort, setIsOpenSendQort] = useState(false);
const [isOpenSendQortSuccess, setIsOpenSendQortSuccess] = useState(false);
@@ -488,7 +493,7 @@ function App() {
const resetAtomOldPinnedAppsAtom = useResetRecoilState(oldPinnedAppsAtom);
const resetAtomQMailLastEnteredTimestampAtom = useResetRecoilState(qMailLastEnteredTimestampAtom)
const resetAtomMailsAtom = useResetRecoilState(mailsAtom)
-
+ const resetGroupPropertiesAtom = useResetRecoilState(groupsPropertiesAtom)
const resetAllRecoil = () => {
resetAtomSortablePinnedAppsAtom();
resetAtomCanSaveSettingToQdnAtom();
@@ -498,6 +503,8 @@ function App() {
resetAtomIsUsingImportExportSettingsAtom()
resetAtomQMailLastEnteredTimestampAtom()
resetAtomMailsAtom()
+ resetGroupPropertiesAtom()
+
};
useEffect(() => {
if (!isMobile) return;
@@ -1696,6 +1703,38 @@ function App() {
/>
+
+ {
+ setIsOpenDrawerLookup(true);
+ }}
+ >
+ USER LOOKUP}
+ placement="left"
+ arrow
+ sx={{ fontSize: "24" }}
+ slotProps={{
+ tooltip: {
+ sx: {
+ color: "#ffffff",
+ backgroundColor: "#444444",
+ },
+ },
+ arrow: {
+ sx: {
+ color: "#444444",
+ },
+ },
+ }}
+ >
+
+
+
{desktopViewMode !== 'home' && (
<>
@@ -2055,7 +2094,7 @@ function App() {
display: "flex",
flexDirection: "column",
alignItems: "center",
- zIndex: 6,
+ zIndex: 10000,
}}
>
@@ -3110,7 +3149,7 @@ function App() {
display: "flex",
flexDirection: "column",
alignItems: "center",
- zIndex: 6,
+ zIndex: 10000,
}}
>
@@ -3210,6 +3249,9 @@ function App() {
open={isShow}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
+ sx={{
+ zIndex: 10001
+ }}
>
{message.paymentFee ? "Payment" : "Publish"}
@@ -3635,7 +3677,7 @@ function App() {
>
{renderProfileLeft()}
-
+
{extState === "create-wallet" && walletToBeDownloaded && (
{
diff --git a/src/atoms/global.ts b/src/atoms/global.ts
index b385f00..cb49bf7 100644
--- a/src/atoms/global.ts
+++ b/src/atoms/global.ts
@@ -169,4 +169,9 @@ export const qMailLastEnteredTimestampAtom = atom({
export const mailsAtom = atom({
key: 'mailsAtom',
default: [],
+});
+
+export const groupsPropertiesAtom = atom({
+ key: 'groupsPropertiesAtom',
+ default: {},
});
\ No newline at end of file
diff --git a/src/background.ts b/src/background.ts
index 91dc597..ad8ddf3 100644
--- a/src/background.ts
+++ b/src/background.ts
@@ -845,7 +845,7 @@ export async function getNameInfoForOthers(address) {
return "";
}
}
-async function getAddressInfo(address) {
+export async function getAddressInfo(address) {
const validApi = await getBaseApi();
const response = await fetch(validApi + "/addresses/" + address);
const data = await response.json();
diff --git a/src/components/Apps/AppsPrivate.tsx b/src/components/Apps/AppsPrivate.tsx
index b014cd2..d18fcad 100644
--- a/src/components/Apps/AppsPrivate.tsx
+++ b/src/components/Apps/AppsPrivate.tsx
@@ -1,4 +1,4 @@
-import React, { useContext, useState } from "react";
+import React, { useContext, useMemo, useState } from "react";
import {
Avatar,
Box,
@@ -18,7 +18,7 @@ import {
import { useDropzone } from "react-dropzone";
import { useHandlePrivateApps } from "./useHandlePrivateApps";
import { useRecoilState, useSetRecoilState } from "recoil";
-import { myGroupsWhereIAmAdminAtom } from "../../atoms/global";
+import { groupsPropertiesAtom, myGroupsWhereIAmAdminAtom } from "../../atoms/global";
import { Label } from "../Group/AddGroup";
import { Spacer } from "../../common/Spacer";
import {
@@ -43,14 +43,22 @@ export const AppsPrivate = ({myName}) => {
const [logo, setLogo] = useState(null);
const [qortalUrl, setQortalUrl] = useState("");
const [selectedGroup, setSelectedGroup] = useState(0);
-
+ const [groupsProperties] = useRecoilState(groupsPropertiesAtom)
const [valueTabPrivateApp, setValueTabPrivateApp] = useState(0);
- const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useRecoilState(
+ const [myGroupsWhereIAmAdminFromGlobal] = useRecoilState(
myGroupsWhereIAmAdminAtom
);
+
+ const myGroupsWhereIAmAdmin = useMemo(()=> {
+ return myGroupsWhereIAmAdminFromGlobal?.filter((group)=> groupsProperties[group?.groupId]?.isOpen === false)
+ }, [myGroupsWhereIAmAdminFromGlobal, groupsProperties])
const [isOpenPrivateModal, setIsOpenPrivateModal] = useState(false);
const { show, setInfoSnackCustom, setOpenSnackGlobal, memberGroups } = useContext(MyContext);
+
+ const myGroupsPrivate = useMemo(()=> {
+ return memberGroups?.filter((group)=> groupsProperties[group?.groupId]?.isOpen === false)
+ }, [memberGroups, groupsProperties])
const [privateAppValues, setPrivateAppValues] = useState({
name: "",
service: "DOCUMENT",
@@ -95,7 +103,7 @@ export const AppsPrivate = ({myName}) => {
await openApp(privateAppValues, true);
} catch (error) {
- console.log('error', error?.message)
+ console.error(error)
}
};
@@ -311,7 +319,7 @@ export const AppsPrivate = ({myName}) => {
>
- {memberGroups
+ {myGroupsPrivate
?.filter((item) => !item?.isOpen)
.map((group) => {
return (
diff --git a/src/components/Chat/GroupAnnouncements.tsx b/src/components/Chat/GroupAnnouncements.tsx
index cdcd5db..9ba7668 100644
--- a/src/components/Chat/GroupAnnouncements.tsx
+++ b/src/components/Chat/GroupAnnouncements.tsx
@@ -194,7 +194,7 @@ export const GroupAnnouncements = ({
};
});
} catch (error) {
- console.log("error", error);
+ console.error("error", error);
}
};
diff --git a/src/components/Drawer/DrawerUserLookup.tsx b/src/components/Drawer/DrawerUserLookup.tsx
new file mode 100644
index 0000000..f64e096
--- /dev/null
+++ b/src/components/Drawer/DrawerUserLookup.tsx
@@ -0,0 +1,22 @@
+import * as React from 'react';
+import Box from '@mui/material/Box';
+import Drawer from '@mui/material/Drawer';
+
+export const DrawerUserLookup = ({open, setOpen, children}) => {
+
+ const toggleDrawer = (newOpen: boolean) => () => {
+ setOpen(newOpen);
+ };
+
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/src/components/Group/Group.tsx b/src/components/Group/Group.tsx
index 08f75e9..c4c7622 100644
--- a/src/components/Group/Group.tsx
+++ b/src/components/Group/Group.tsx
@@ -74,8 +74,8 @@ import { HubsIcon } from "../../assets/Icons/HubsIcon";
import { MessagingIcon } from "../../assets/Icons/MessagingIcon";
import { formatEmailDate } from "./QMailMessages";
import { AdminSpace } from "../Chat/AdminSpace";
-import { useSetRecoilState } from "recoil";
-import { addressInfoControllerAtom, selectedGroupIdAtom } from "../../atoms/global";
+import { useRecoilState, useSetRecoilState } from "recoil";
+import { addressInfoControllerAtom, groupsPropertiesAtom, selectedGroupIdAtom } from "../../atoms/global";
import { sortArrayByTimestampAndGroupName } from "../../utils/time";
import BlockIcon from '@mui/icons-material/Block';
import LockIcon from '@mui/icons-material/Lock';
@@ -446,7 +446,7 @@ export const Group = ({
const [isOpenSideViewGroups, setIsOpenSideViewGroups] = useState(false)
const [isForceShowCreationKeyPopup, setIsForceShowCreationKeyPopup] = useState(false)
- const [groupsProperties, setGroupsProperties] = useState({})
+ const [groupsProperties, setGroupsProperties] = useRecoilState(groupsPropertiesAtom)
const setUserInfoForLevels = useSetRecoilState(addressInfoControllerAtom);
const isPrivate = useMemo(()=> {
diff --git a/src/components/Home/QortPrice.tsx b/src/components/Home/QortPrice.tsx
index 3d54a04..f4586fb 100644
--- a/src/components/Home/QortPrice.tsx
+++ b/src/components/Home/QortPrice.tsx
@@ -1,209 +1,257 @@
-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';
+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";
+import { formatDate } from "../../utils/time";
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));
- }
+ let totalQort = 0;
+ let totalLtc = 0;
- function getTwoWeeksAgoTimestamp() {
- const now = new Date();
- now.setDate(now.getDate() - 14); // Subtract 14 days
- return now.getTime(); // Get timestamp in milliseconds
- }
-
- function formatWithCommasAndDecimals(number) {
+ trades.forEach((trade) => {
+ const qort = parseFloat(trade.qortAmount);
+ const ltc = parseFloat(trade.foreignAmount);
- return Number(number).toLocaleString();
- }
+ 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 [ltcPerQort, setLtcPerQort] = useState(null);
+ const [supply, setSupply] = useState(null);
+ const [lastBlock, setLastBlock] = useState(null);
+ const [loading, setLoading] = useState(true);
- const getLastBlock = useCallback(async () => {
- try {
- setLoading(true)
-
- const response = await fetch(`${getBaseApiReact()}/blocks/last`);
- const data = await response.json();
+ const getPrice = useCallback(async () => {
+ try {
+ setLoading(true);
- setLastBlock(data);
- } catch (error) {
- console.error(error);
- } finally {
- setLoading(false)
-
- }
- }, [])
+ const response = await fetch(
+ `${getBaseApiReact()}/crosschain/trades?foreignBlockchain=LITECOIN&minimumTimestamp=${getTwoWeeksAgoTimestamp()}&limit=20&reverse=true`
+ );
+ const data = await response.json();
- 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)
-
- }
- }, [])
-
-
-
+ 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]);
- 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",
- },
- },
- }}
- >
-
+
+ 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
+ fontWeight: "bold",
+ }}
+ >
+ Price
+
+ {!ltcPerQort ? (
+
+ ) : (
+
+ {ltcPerQort} LTC/QORT
+
)}
+
+
+
+
+ Supply
+
+ {!supply ? (
+
+ ) : (
+
+ {supply} QORT
+
+ )}
+
+
+ {lastBlock?.timestamp && formatDate(lastBlock?.timestamp)}
+
+ }
+ placement="bottom"
+ arrow
+ sx={{ fontSize: "24" }}
+ slotProps={{
+ tooltip: {
+ sx: {
+ color: "#ffffff",
+ backgroundColor: "#444444",
+ },
+ },
+ arrow: {
+ sx: {
+ color: "#444444",
+ },
+ },
+ }}
+ >
+
-
-
-
-
- Supply
- {!supply ? (
-
- ): (
- {supply} QORT
- )}
-
-
-
- Last height
- {!lastBlock?.height ? (
+ fontWeight: "bold",
+ }}
+ >
+ Last height
+
+ {!lastBlock?.height ? (
- ): (
- {lastBlock?.height}
-
+ ) : (
+
+ {lastBlock?.height}
+
)}
-
+
+
- )
-}
+ );
+};
diff --git a/src/components/UserLookup.tsx/UserLookup.tsx b/src/components/UserLookup.tsx/UserLookup.tsx
new file mode 100644
index 0000000..d8bb0ea
--- /dev/null
+++ b/src/components/UserLookup.tsx/UserLookup.tsx
@@ -0,0 +1,495 @@
+import React, { useCallback, useEffect, useState } from "react";
+import { DrawerUserLookup } from "../Drawer/DrawerUserLookup";
+import {
+ Avatar,
+ Box,
+ Button,
+ ButtonBase,
+ Card,
+ Divider,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableRow,
+ TextField,
+ Tooltip,
+ Typography,
+ Table,
+ CircularProgress,
+} from "@mui/material";
+import { getAddressInfo, getNameOrAddress } from "../../background";
+import { getBaseApiReact } from "../../App";
+import { getNameInfo } from "../Group/Group";
+import AccountCircleIcon from "@mui/icons-material/AccountCircle";
+import { Spacer } from "../../common/Spacer";
+import { formatTimestamp } from "../../utils/time";
+import CloseFullscreenIcon from '@mui/icons-material/CloseFullscreen';
+import SearchIcon from '@mui/icons-material/Search';
+import { executeEvent, subscribeToEvent, unsubscribeFromEvent } from "../../utils/events";
+
+function formatAddress(str) {
+ if (str.length <= 12) return str;
+
+ const first6 = str.slice(0, 6);
+ const last6 = str.slice(-6);
+
+ return `${first6}....${last6}`;
+}
+
+export const UserLookup = ({ isOpenDrawerLookup, setIsOpenDrawerLookup }) => {
+ const [nameOrAddress, setNameOrAddress] = useState("");
+ const [errorMessage, setErrorMessage] = useState("");
+ const [addressInfo, setAddressInfo] = useState(null);
+ const [isLoadingUser, setIsLoadingUser] = useState(false);
+ const [isLoadingPayments, setIsLoadingPayments] = useState(false);
+ const [payments, setPayments] = useState([]);
+ const lookupFunc = useCallback(async (messageAddressOrName) => {
+ try {
+ setErrorMessage('')
+ setIsLoadingUser(true)
+ setPayments([])
+ setAddressInfo(null)
+ const inputAddressOrName = messageAddressOrName || nameOrAddress
+ if (!inputAddressOrName?.trim())
+ throw new Error("Please insert a name or address");
+ const owner = await getNameOrAddress(inputAddressOrName);
+ if (!owner) throw new Error("Name does not exist");
+ const addressInfoRes = await getAddressInfo(owner);
+ if (!addressInfoRes?.publicKey) {
+ throw new Error("Address does not exist on blockchain");
+ }
+ const name = await getNameInfo(owner);
+ const balanceRes = await fetch(
+ `${getBaseApiReact()}/addresses/balance/${owner}`
+ );
+ const balanceData = await balanceRes.json();
+ setAddressInfo({
+ ...addressInfoRes,
+ balance: balanceData,
+ name,
+ });
+ setIsLoadingUser(false)
+ setIsLoadingPayments(true)
+
+ const getPayments = await fetch(
+ `${getBaseApiReact()}/transactions/search?txType=PAYMENT&address=${owner}&confirmationStatus=CONFIRMED&limit=20&reverse=true`
+ );
+ const paymentsData = await getPayments.json();
+ setPayments(paymentsData);
+
+ } catch (error) {
+ setErrorMessage(error?.message)
+ console.error(error);
+ } finally {
+ setIsLoadingUser(false)
+ setIsLoadingPayments(false)
+ }
+ }, [nameOrAddress]);
+
+ const openUserLookupDrawerFunc = useCallback((e) => {
+ setIsOpenDrawerLookup(true)
+ const message = e.detail?.addressOrName;
+ if(message){
+ lookupFunc(message)
+ }
+ }, [lookupFunc, setIsOpenDrawerLookup]);
+
+ useEffect(() => {
+ subscribeToEvent("openUserLookupDrawer", openUserLookupDrawerFunc);
+
+ return () => {
+ unsubscribeFromEvent("openUserLookupDrawer", openUserLookupDrawerFunc);
+ };
+ }, [openUserLookupDrawerFunc]);
+
+
+
+ return (
+
+
+
+
+
+ setNameOrAddress(e.target.value)}
+ size="small"
+ placeholder="Address or Name"
+ autoComplete="off"
+ onKeyDown={(e) => {
+ if (e.key === "Enter" && nameOrAddress) {
+ lookupFunc();
+ }
+ }}
+ />
+
+
+
+ {
+ setIsOpenDrawerLookup(false)
+ }}>
+
+
+
+
+ {!isLoadingUser && errorMessage && (
+
+ {errorMessage}
+
+ )}
+ {isLoadingUser && (
+
+
+
+ )}
+ {!isLoadingUser && addressInfo && (
+ <>
+
+
+
+
+
+ {addressInfo?.name ?? "Name not registered"}
+
+
+
+ {addressInfo?.name ? (
+
+
+
+ ) : (
+
+ )}
+
+
+
+ Level {addressInfo?.level}
+
+
+
+
+
+ Address
+
+
+ copy address
+
+ }
+ placement="bottom"
+ arrow
+ sx={{ fontSize: "24" }}
+ slotProps={{
+ tooltip: {
+ sx: {
+ color: "#ffffff",
+ backgroundColor: "#444444",
+ },
+ },
+ arrow: {
+ sx: {
+ color: "#444444",
+ },
+ },
+ }}
+ >
+ {
+ navigator.clipboard.writeText(addressInfo?.address);
+ }}
+ >
+
+ {addressInfo?.address}
+
+
+
+
+
+ Balance
+ {addressInfo?.balance}
+
+
+
+
+
+
+
+ >
+ )}
+
+ {isLoadingPayments && (
+
+
+
+ )}
+ {!isLoadingPayments && addressInfo && (
+
+ 20 most recent payments
+
+ {!isLoadingPayments && payments?.length === 0 && (
+
+ No payments
+
+ )}
+
+
+
+ Sender
+ Reciver
+ Amount
+ Time
+
+
+
+ {payments.map((payment, index) => (
+
+
+
+ copy address
+
+ }
+ placement="bottom"
+ arrow
+ sx={{ fontSize: "24" }}
+ slotProps={{
+ tooltip: {
+ sx: {
+ color: "#ffffff",
+ backgroundColor: "#444444",
+ },
+ },
+ arrow: {
+ sx: {
+ color: "#444444",
+ },
+ },
+ }}
+ >
+ {
+ navigator.clipboard.writeText(
+ payment?.creatorAddress
+ );
+ }}
+ >
+ {formatAddress(payment?.creatorAddress)}
+
+
+
+
+
+ copy address
+
+ }
+ placement="bottom"
+ arrow
+ sx={{ fontSize: "24" }}
+ slotProps={{
+ tooltip: {
+ sx: {
+ color: "#ffffff",
+ backgroundColor: "#444444",
+ },
+ },
+ arrow: {
+ sx: {
+ color: "#444444",
+ },
+ },
+ }}
+ >
+ {
+ navigator.clipboard.writeText(payment?.recipient);
+ }}
+ >
+ {formatAddress(payment?.recipient)}
+
+
+
+
+
+ {payment?.amount}
+
+ {formatTimestamp(payment?.timestamp)}
+
+ ))}
+
+
+
+ )}
+
+
+
+
+ );
+};
diff --git a/src/components/WrapperUserAction.tsx b/src/components/WrapperUserAction.tsx
index 8bcc03a..3bcb143 100644
--- a/src/components/WrapperUserAction.tsx
+++ b/src/components/WrapperUserAction.tsx
@@ -121,6 +121,42 @@ export const WrapperUserAction = ({ children, address, name, disabled }) => {
>
Copy address
+
+
+
+
+
diff --git a/src/utils/time.ts b/src/utils/time.ts
index b0a27cf..c89c1cd 100644
--- a/src/utils/time.ts
+++ b/src/utils/time.ts
@@ -12,7 +12,7 @@ export function formatTimestamp(timestamp: number): string {
} else if (elapsedTime < 1440) {
return `${Math.floor(elapsedTime / 60)}h ago`
} else {
- return timestampMoment.format('MMM D')
+ return timestampMoment.format('MMM D, YYYY')
}
}
export function formatTimestampForum(timestamp: number): string {