mirror of
https://github.com/Qortal/Qortal-Hub.git
synced 2025-04-23 19:37:52 +00:00
reworked homepage
This commit is contained in:
parent
519a0bb652
commit
ae5ca83963
BIN
src/assets/Icons/q-trade-logo.webp
Normal file
BIN
src/assets/Icons/q-trade-logo.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
10
src/common/Spinners/BarSpinner/BarSpinner.tsx
Normal file
10
src/common/Spinners/BarSpinner/BarSpinner.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import './barSpinner.css'
|
||||||
|
export const BarSpinner = ({width = '20px', color}) => {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
width,
|
||||||
|
color: color || 'green'
|
||||||
|
}} className="loader-bar"></div>
|
||||||
|
)
|
||||||
|
}
|
19
src/common/Spinners/BarSpinner/barSpinner.css
Normal file
19
src/common/Spinners/BarSpinner/barSpinner.css
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* HTML: <div class="loader"></div> */
|
||||||
|
.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% }
|
||||||
|
}
|
101
src/components/Explore/Explore.tsx
Normal file
101
src/components/Explore/Explore.tsx
Normal file
@ -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 (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "20px",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ButtonBase
|
||||||
|
sx={{
|
||||||
|
"&:hover": { backgroundColor: "secondary.main" },
|
||||||
|
transition: "all 0.1s ease-in-out",
|
||||||
|
padding: "5px",
|
||||||
|
borderRadius: "5px",
|
||||||
|
gap: "5px",
|
||||||
|
}}
|
||||||
|
onClick={async () => {
|
||||||
|
executeEvent("addTab", {
|
||||||
|
data: { service: "APP", name: "q-trade" },
|
||||||
|
});
|
||||||
|
executeEvent("open-apps-mode", {});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
style={{
|
||||||
|
borderRadius: "50%",
|
||||||
|
height: '30px'
|
||||||
|
}}
|
||||||
|
src={qTradeLogo}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Trade QORT
|
||||||
|
</Typography>
|
||||||
|
</ButtonBase>
|
||||||
|
<ButtonBase
|
||||||
|
sx={{
|
||||||
|
"&:hover": { backgroundColor: "secondary.main" },
|
||||||
|
transition: "all 0.1s ease-in-out",
|
||||||
|
padding: "5px",
|
||||||
|
borderRadius: "5px",
|
||||||
|
gap: "5px",
|
||||||
|
}}
|
||||||
|
onClick={()=> {
|
||||||
|
setDesktopViewMode('apps')
|
||||||
|
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AppsIcon
|
||||||
|
sx={{
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
See Apps
|
||||||
|
</Typography>
|
||||||
|
</ButtonBase>
|
||||||
|
<ButtonBase
|
||||||
|
sx={{
|
||||||
|
"&:hover": { backgroundColor: "secondary.main" },
|
||||||
|
transition: "all 0.1s ease-in-out",
|
||||||
|
padding: "5px",
|
||||||
|
borderRadius: "5px",
|
||||||
|
gap: "5px",
|
||||||
|
}}
|
||||||
|
onClick={async () => {
|
||||||
|
executeEvent("openGroupMessage", {
|
||||||
|
from: "0" ,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ChatIcon
|
||||||
|
sx={{
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
General Chat
|
||||||
|
</Typography>
|
||||||
|
</ButtonBase>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
@ -1299,11 +1299,11 @@ export const Group = ({
|
|||||||
if (isLoadingOpenSectionFromNotification.current) return;
|
if (isLoadingOpenSectionFromNotification.current) return;
|
||||||
|
|
||||||
const groupId = e.detail?.from;
|
const groupId = e.detail?.from;
|
||||||
|
|
||||||
const findGroup = groups?.find((group) => +group?.groupId === +groupId);
|
const findGroup = groups?.find((group) => +group?.groupId === +groupId);
|
||||||
if (findGroup?.groupId === selectedGroup?.groupId) {
|
if (findGroup?.groupId === selectedGroup?.groupId) {
|
||||||
isLoadingOpenSectionFromNotification.current = false;
|
isLoadingOpenSectionFromNotification.current = false;
|
||||||
|
setChatMode("groups");
|
||||||
|
setDesktopViewMode('chat')
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (findGroup) {
|
if (findGroup) {
|
||||||
|
@ -10,16 +10,20 @@ import CommentIcon from "@mui/icons-material/Comment";
|
|||||||
import InfoIcon from "@mui/icons-material/Info";
|
import InfoIcon from "@mui/icons-material/Info";
|
||||||
import GroupAddIcon from "@mui/icons-material/GroupAdd";
|
import GroupAddIcon from "@mui/icons-material/GroupAdd";
|
||||||
import { executeEvent } from "../../utils/events";
|
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 { Spacer } from "../../common/Spacer";
|
||||||
import { getGroupNames } from "./UserListOfInvites";
|
import { getGroupNames } from "./UserListOfInvites";
|
||||||
import { CustomLoader } from "../../common/CustomLoader";
|
import { CustomLoader } from "../../common/CustomLoader";
|
||||||
import { getBaseApiReact, isMobile } from "../../App";
|
import { getBaseApiReact, isMobile } from "../../App";
|
||||||
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
|
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||||
|
|
||||||
export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
||||||
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState(
|
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState(
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||||
|
|
||||||
const [loading, setLoading] = React.useState(true);
|
const [loading, setLoading] = React.useState(true);
|
||||||
|
|
||||||
const getJoinRequests = async () => {
|
const getJoinRequests = async () => {
|
||||||
@ -53,121 +57,129 @@ export const GroupInvites = ({ myAddress, setOpenAddGroup }) => {
|
|||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<ButtonBase
|
||||||
sx={{
|
sx={{
|
||||||
width: "322px",
|
width: "322px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "row",
|
||||||
padding: "0px 20px",
|
padding: "0px 20px",
|
||||||
|
gap: '10px',
|
||||||
|
justifyContent: 'flex-start'
|
||||||
}}
|
}}
|
||||||
|
onClick={()=> setIsExpanded((prev)=> !prev)}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: "1rem",
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Group Invites:
|
Group Invites {groupsWithJoinRequests?.length > 0 && ` (${groupsWithJoinRequests?.length})`}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Spacer height="10px" />
|
{isExpanded ? <ExpandLessIcon sx={{
|
||||||
</Box>
|
marginLeft: 'auto'
|
||||||
|
}} /> : (
|
||||||
|
<ExpandMoreIcon sx={{
|
||||||
|
marginLeft: 'auto'
|
||||||
|
}}/>
|
||||||
|
)}
|
||||||
|
</ButtonBase>
|
||||||
|
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "322px",
|
||||||
|
height: isMobile ? "165px" : "250px",
|
||||||
|
|
||||||
<Box
|
display: "flex",
|
||||||
sx={{
|
flexDirection: "column",
|
||||||
width: "322px",
|
bgcolor: "background.paper",
|
||||||
height: isMobile ? "165px" : "250px",
|
padding: "20px",
|
||||||
|
borderRadius: "19px",
|
||||||
display: "flex",
|
}}
|
||||||
flexDirection: "column",
|
>
|
||||||
bgcolor: "background.paper",
|
{loading && groupsWithJoinRequests.length === 0 && (
|
||||||
padding: "20px",
|
<Box
|
||||||
borderRadius: "19px",
|
sx={{
|
||||||
}}
|
width: "100%",
|
||||||
>
|
display: "flex",
|
||||||
{loading && groupsWithJoinRequests.length === 0 && (
|
justifyContent: "center",
|
||||||
<Box
|
}}
|
||||||
|
>
|
||||||
|
<CustomLoader />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{!loading && groupsWithJoinRequests.length === 0 && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "11px",
|
||||||
|
fontWeight: 400,
|
||||||
|
color: "rgba(255, 255, 255, 0.2)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Nothing to display
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<List
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
display: "flex",
|
maxWidth: 360,
|
||||||
justifyContent: "center",
|
bgcolor: "background.paper",
|
||||||
|
maxHeight: "300px",
|
||||||
|
overflow: "auto",
|
||||||
}}
|
}}
|
||||||
|
className="scrollable-container"
|
||||||
>
|
>
|
||||||
<CustomLoader />
|
{groupsWithJoinRequests?.map((group) => {
|
||||||
</Box>
|
return (
|
||||||
)}
|
<ListItem
|
||||||
{!loading && groupsWithJoinRequests.length === 0 && (
|
sx={{
|
||||||
<Box
|
marginBottom: "20px",
|
||||||
sx={{
|
}}
|
||||||
width: "100%",
|
key={group?.groupId}
|
||||||
display: "flex",
|
onClick={() => {
|
||||||
justifyContent: "center",
|
setOpenAddGroup(true);
|
||||||
alignItems: 'center',
|
setTimeout(() => {
|
||||||
height: '100%',
|
executeEvent("openGroupInvitesRequest", {});
|
||||||
|
}, 300);
|
||||||
}}
|
}}
|
||||||
>
|
disablePadding
|
||||||
<Typography
|
secondaryAction={
|
||||||
sx={{
|
<IconButton edge="end" aria-label="comments">
|
||||||
fontSize: "11px",
|
<GroupAddIcon
|
||||||
fontWeight: 400,
|
sx={{
|
||||||
color: 'rgba(255, 255, 255, 0.2)'
|
color: "white",
|
||||||
}}
|
fontSize: "18px",
|
||||||
>
|
}}
|
||||||
Nothing to display
|
/>
|
||||||
</Typography>
|
</IconButton>
|
||||||
</Box>
|
}
|
||||||
)}
|
>
|
||||||
<List
|
<ListItemButton disableRipple role={undefined} dense>
|
||||||
sx={{
|
<ListItemText
|
||||||
width: "100%",
|
|
||||||
maxWidth: 360,
|
|
||||||
bgcolor: "background.paper",
|
|
||||||
maxHeight: "300px",
|
|
||||||
overflow: "auto",
|
|
||||||
}}
|
|
||||||
className="scrollable-container"
|
|
||||||
>
|
|
||||||
{groupsWithJoinRequests?.map((group) => {
|
|
||||||
return (
|
|
||||||
<ListItem
|
|
||||||
sx={{
|
|
||||||
marginBottom: "20px",
|
|
||||||
}}
|
|
||||||
key={group?.groupId}
|
|
||||||
onClick={() => {
|
|
||||||
setOpenAddGroup(true);
|
|
||||||
setTimeout(() => {
|
|
||||||
executeEvent("openGroupInvitesRequest", {});
|
|
||||||
}, 300);
|
|
||||||
}}
|
|
||||||
disablePadding
|
|
||||||
secondaryAction={
|
|
||||||
<IconButton edge="end" aria-label="comments">
|
|
||||||
<GroupAddIcon
|
|
||||||
sx={{
|
sx={{
|
||||||
color: "white",
|
"& .MuiTypography-root": {
|
||||||
fontSize: "18px",
|
fontSize: "13px",
|
||||||
|
fontWeight: 400,
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
|
primary={`${group?.groupName} has invited you`}
|
||||||
/>
|
/>
|
||||||
</IconButton>
|
</ListItemButton>
|
||||||
}
|
</ListItem>
|
||||||
>
|
);
|
||||||
<ListItemButton disableRipple role={undefined} dense>
|
})}
|
||||||
<ListItemText
|
</List>
|
||||||
sx={{
|
</Box>
|
||||||
"& .MuiTypography-root": {
|
</Collapse>
|
||||||
fontSize: "13px",
|
|
||||||
fontWeight: 400,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
primary={`${group?.groupName} has invited you`}
|
|
||||||
/>
|
|
||||||
</ListItemButton>
|
|
||||||
</ListItem>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</List>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -11,16 +11,20 @@ import InfoIcon from "@mui/icons-material/Info";
|
|||||||
import { RequestQueueWithPromise } from "../../utils/queue/queue";
|
import { RequestQueueWithPromise } from "../../utils/queue/queue";
|
||||||
import GroupAddIcon from '@mui/icons-material/GroupAdd';
|
import GroupAddIcon from '@mui/icons-material/GroupAdd';
|
||||||
import { executeEvent } from "../../utils/events";
|
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 { Spacer } from "../../common/Spacer";
|
||||||
import { CustomLoader } from "../../common/CustomLoader";
|
import { CustomLoader } from "../../common/CustomLoader";
|
||||||
import { getBaseApi } from "../../background";
|
import { getBaseApi } from "../../background";
|
||||||
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
import { MyContext, getBaseApiReact, isMobile } from "../../App";
|
||||||
import { myGroupsWhereIAmAdminAtom } from "../../atoms/global";
|
import { myGroupsWhereIAmAdminAtom } from "../../atoms/global";
|
||||||
import { useSetRecoilState } from "recoil";
|
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 requestQueueGroupJoinRequests = new RequestQueueWithPromise(2)
|
||||||
|
|
||||||
export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, getTimestampEnterChat, setSelectedGroup, setGroupSection, setMobileViewMode, setDesktopViewMode }) => {
|
export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, getTimestampEnterChat, setSelectedGroup, setGroupSection, setMobileViewMode, setDesktopViewMode }) => {
|
||||||
|
const [isExpanded, setIsExpanded] = React.useState(false)
|
||||||
|
|
||||||
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([])
|
const [groupsWithJoinRequests, setGroupsWithJoinRequests] = React.useState([])
|
||||||
const [loading, setLoading] = React.useState(true)
|
const [loading, setLoading] = React.useState(true)
|
||||||
const {txList, setTxList} = React.useContext(MyContext)
|
const {txList, setTxList} = React.useContext(MyContext)
|
||||||
@ -109,26 +113,33 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get
|
|||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
alignItems: 'center'
|
alignItems: 'center'
|
||||||
}}>
|
}}>
|
||||||
<Box
|
<ButtonBase
|
||||||
sx={{
|
sx={{
|
||||||
width: "322px",
|
width: "322px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "row",
|
||||||
padding: '0px 20px',
|
padding: '0px 20px',
|
||||||
|
gap: '10px',
|
||||||
|
justifyContent: 'flex-start'
|
||||||
}}
|
}}
|
||||||
|
onClick={()=> setIsExpanded((prev)=> !prev)}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: "1rem",
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Join Requests:
|
Join Requests {filteredJoinRequests?.filter((group)=> group?.data?.length > 0)?.length > 0 && ` (${filteredJoinRequests?.filter((group)=> group?.data?.length > 0)?.length})`}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Spacer height="10px" />
|
{isExpanded ? <ExpandLessIcon sx={{
|
||||||
</Box>
|
marginLeft: 'auto'
|
||||||
|
}} /> : (
|
||||||
|
<ExpandMoreIcon sx={{
|
||||||
|
marginLeft: 'auto'
|
||||||
|
}}/>
|
||||||
|
)}
|
||||||
|
</ButtonBase>
|
||||||
|
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "322px",
|
width: "322px",
|
||||||
@ -227,6 +238,7 @@ export const GroupJoinRequests = ({ myAddress, groups, setOpenManageMembers, get
|
|||||||
|
|
||||||
</List>
|
</List>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Collapse>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box, Button, Typography } from "@mui/material";
|
import { Box, Button, Divider, Typography } from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from "../../common/Spacer";
|
||||||
import { ListOfThreadPostsWatched } from "./ListOfThreadPostsWatched";
|
import { ListOfThreadPostsWatched } from "./ListOfThreadPostsWatched";
|
||||||
@ -7,7 +7,10 @@ import { GroupJoinRequests } from "./GroupJoinRequests";
|
|||||||
import { GroupInvites } from "./GroupInvites";
|
import { GroupInvites } from "./GroupInvites";
|
||||||
import RefreshIcon from "@mui/icons-material/Refresh";
|
import RefreshIcon from "@mui/icons-material/Refresh";
|
||||||
import { ListOfGroupPromotions } from "./ListOfGroupPromotions";
|
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 = ({
|
export const HomeDesktop = ({
|
||||||
refreshHomeDataFunc,
|
refreshHomeDataFunc,
|
||||||
myAddress,
|
myAddress,
|
||||||
@ -22,12 +25,12 @@ export const HomeDesktop = ({
|
|||||||
setOpenAddGroup,
|
setOpenAddGroup,
|
||||||
setMobileViewMode,
|
setMobileViewMode,
|
||||||
setDesktopViewMode,
|
setDesktopViewMode,
|
||||||
desktopViewMode
|
desktopViewMode,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: desktopViewMode === 'home' ? "flex" : "none",
|
display: desktopViewMode === "home" ? "flex" : "none",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
@ -36,105 +39,169 @@ export const HomeDesktop = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Box sx={{
|
<Box
|
||||||
display: "flex",
|
|
||||||
width: "100%",
|
|
||||||
flexDirection: "column",
|
|
||||||
height: "100%",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
maxWidth: '1036px'
|
|
||||||
}}>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
sx={{
|
||||||
color: "rgba(255, 255, 255, 1)",
|
display: "flex",
|
||||||
fontWeight: 400,
|
width: "100%",
|
||||||
fontSize: userInfo?.name?.length > 15 ? "16px" : "20px",
|
flexDirection: "column",
|
||||||
padding: '10px'
|
height: "100%",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
maxWidth: "1036px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Welcome
|
<Typography
|
||||||
{userInfo?.name ? (
|
|
||||||
<span
|
|
||||||
style={{
|
|
||||||
fontStyle: "italic",
|
|
||||||
}}
|
|
||||||
>{`, ${userInfo?.name}`}</span>
|
|
||||||
) : null}
|
|
||||||
</Typography>
|
|
||||||
<Spacer height="30px" />
|
|
||||||
{!isLoadingGroups && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
color: "rgba(255, 255, 255, 1)",
|
||||||
gap: "15px",
|
fontWeight: 400,
|
||||||
flexWrap: "wrap",
|
fontSize: userInfo?.name?.length > 15 ? "16px" : "20px",
|
||||||
justifyContent: "center",
|
padding: "10px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box sx={{
|
Welcome
|
||||||
width: '330px',
|
{userInfo?.name ? (
|
||||||
display: 'flex',
|
<span
|
||||||
alignItems: 'center',
|
style={{
|
||||||
justifyContent: 'center'
|
fontStyle: "italic",
|
||||||
}}>
|
}}
|
||||||
<ThingsToDoInitial
|
>{`, ${userInfo?.name}`}</span>
|
||||||
balance={balance}
|
) : null}
|
||||||
myAddress={myAddress}
|
</Typography>
|
||||||
name={userInfo?.name}
|
<Spacer height="30px" />
|
||||||
userInfo={userInfo}
|
{!isLoadingGroups && (
|
||||||
hasGroups={groups?.length !== 0}
|
<Box
|
||||||
/>
|
sx={{
|
||||||
</Box>
|
display: "flex",
|
||||||
{desktopViewMode === 'home' && (
|
gap: "20px",
|
||||||
<>
|
flexWrap: "wrap",
|
||||||
<Box sx={{
|
width: "100%",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "20px",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
flexDirection: "column",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "330px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ThingsToDoInitial
|
||||||
|
balance={balance}
|
||||||
|
myAddress={myAddress}
|
||||||
|
name={userInfo?.name}
|
||||||
|
userInfo={userInfo}
|
||||||
|
hasGroups={
|
||||||
|
groups?.filter((item) => item?.groupId !== "0").length !== 0
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{desktopViewMode === "home" && (
|
||||||
|
<>
|
||||||
|
{/* <Box sx={{
|
||||||
width: '330px',
|
width: '330px',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center'
|
justifyContent: 'center'
|
||||||
}}>
|
}}>
|
||||||
<ListOfThreadPostsWatched />
|
<ListOfThreadPostsWatched />
|
||||||
|
</Box> */}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "330px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GroupJoinRequests
|
||||||
|
setGroupSection={setGroupSection}
|
||||||
|
setSelectedGroup={setSelectedGroup}
|
||||||
|
getTimestampEnterChat={getTimestampEnterChat}
|
||||||
|
setOpenManageMembers={setOpenManageMembers}
|
||||||
|
myAddress={myAddress}
|
||||||
|
groups={groups}
|
||||||
|
setMobileViewMode={setMobileViewMode}
|
||||||
|
setDesktopViewMode={setDesktopViewMode}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "330px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GroupInvites
|
||||||
|
setOpenAddGroup={setOpenAddGroup}
|
||||||
|
myAddress={myAddress}
|
||||||
|
groups={groups}
|
||||||
|
setMobileViewMode={setMobileViewMode}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
<QortPrice />
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{
|
)}
|
||||||
width: '330px',
|
|
||||||
display: 'flex',
|
{!isLoadingGroups && (
|
||||||
alignItems: 'center',
|
<>
|
||||||
justifyContent: 'center'
|
<Spacer height="60px" />
|
||||||
}}>
|
<Divider
|
||||||
<GroupJoinRequests
|
color="secondary"
|
||||||
setGroupSection={setGroupSection}
|
sx={{
|
||||||
setSelectedGroup={setSelectedGroup}
|
width: "100%",
|
||||||
getTimestampEnterChat={getTimestampEnterChat}
|
}}
|
||||||
setOpenManageMembers={setOpenManageMembers}
|
>
|
||||||
myAddress={myAddress}
|
<Box
|
||||||
groups={groups}
|
sx={{
|
||||||
setMobileViewMode={setMobileViewMode}
|
display: "flex",
|
||||||
setDesktopViewMode={setDesktopViewMode}
|
gap: "10px",
|
||||||
/>
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ExploreIcon
|
||||||
|
sx={{
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
/>{" "}
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Explore
|
||||||
|
</Typography>{" "}
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{
|
</Divider>
|
||||||
width: '330px',
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
alignItems: 'center',
|
display: "flex",
|
||||||
justifyContent: 'center'
|
gap: "20px",
|
||||||
}}>
|
flexWrap: "wrap",
|
||||||
<GroupInvites
|
width: "100%",
|
||||||
setOpenAddGroup={setOpenAddGroup}
|
justifyContent: "center",
|
||||||
myAddress={myAddress}
|
}}
|
||||||
groups={groups}
|
>
|
||||||
setMobileViewMode={setMobileViewMode}
|
<ListOfGroupPromotions />
|
||||||
/>
|
<Explore setDesktopViewMode={setDesktopViewMode} />
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
<NewUsersCTA balance={balance} />
|
||||||
)}
|
</>
|
||||||
|
|
||||||
</Box>
|
)}
|
||||||
)}
|
|
||||||
{!isLoadingGroups && (
|
|
||||||
<ListOfGroupPromotions />
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Spacer height="26px" />
|
<Spacer height="26px" />
|
||||||
|
|
||||||
{/* <Box
|
{/* <Box
|
||||||
@ -155,7 +222,7 @@ export const HomeDesktop = ({
|
|||||||
Refresh home data
|
Refresh home data
|
||||||
</Button>
|
</Button>
|
||||||
</Box> */}
|
</Box> */}
|
||||||
|
|
||||||
<Spacer height="180px" />
|
<Spacer height="180px" />
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,8 @@ import {
|
|||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
|
ButtonBase,
|
||||||
|
Collapse,
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogActions,
|
DialogActions,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@ -28,8 +30,8 @@ import {
|
|||||||
import { getNameInfo } from "./Group";
|
import { getNameInfo } from "./Group";
|
||||||
import { getBaseApi, getFee } from "../../background";
|
import { getBaseApi, getFee } from "../../background";
|
||||||
import { LoadingButton } from "@mui/lab";
|
import { LoadingButton } from "@mui/lab";
|
||||||
import LockIcon from '@mui/icons-material/Lock';
|
import LockIcon from "@mui/icons-material/Lock";
|
||||||
import NoEncryptionGmailerrorredIcon from '@mui/icons-material/NoEncryptionGmailerrorred';
|
import NoEncryptionGmailerrorredIcon from "@mui/icons-material/NoEncryptionGmailerrorred";
|
||||||
import {
|
import {
|
||||||
MyContext,
|
MyContext,
|
||||||
getArbitraryEndpointReact,
|
getArbitraryEndpointReact,
|
||||||
@ -40,7 +42,11 @@ import { Spacer } from "../../common/Spacer";
|
|||||||
import { CustomLoader } from "../../common/CustomLoader";
|
import { CustomLoader } from "../../common/CustomLoader";
|
||||||
import { RequestQueueWithPromise } from "../../utils/queue/queue";
|
import { RequestQueueWithPromise } from "../../utils/queue/queue";
|
||||||
import { useRecoilState } from "recoil";
|
import { useRecoilState } from "recoil";
|
||||||
import { myGroupsWhereIAmAdminAtom, promotionTimeIntervalAtom, promotionsAtom } from "../../atoms/global";
|
import {
|
||||||
|
myGroupsWhereIAmAdminAtom,
|
||||||
|
promotionTimeIntervalAtom,
|
||||||
|
promotionsAtom,
|
||||||
|
} from "../../atoms/global";
|
||||||
import { Label } from "./AddGroup";
|
import { Label } from "./AddGroup";
|
||||||
import ShortUniqueId from "short-unique-id";
|
import ShortUniqueId from "short-unique-id";
|
||||||
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
import { CustomizedSnackbars } from "../Snackbar/Snackbar";
|
||||||
@ -48,7 +54,8 @@ import { getGroupNames } from "./UserListOfInvites";
|
|||||||
import { WrapperUserAction } from "../WrapperUserAction";
|
import { WrapperUserAction } from "../WrapperUserAction";
|
||||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||||
import ErrorBoundary from "../../common/ErrorBoundary";
|
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 const requestQueuePromos = new RequestQueueWithPromise(20);
|
||||||
|
|
||||||
export function utf8ToBase64(inputString: string): string {
|
export function utf8ToBase64(inputString: string): string {
|
||||||
@ -65,8 +72,6 @@ export function utf8ToBase64(inputString: string): string {
|
|||||||
|
|
||||||
const uid = new ShortUniqueId({ length: 8 });
|
const uid = new ShortUniqueId({ length: 8 });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export function getGroupId(str) {
|
export function getGroupId(str) {
|
||||||
const match = str.match(/group-(\d+)-/);
|
const match = str.match(/group-(\d+)-/);
|
||||||
return match ? match[1] : null;
|
return match ? match[1] : null;
|
||||||
@ -82,12 +87,12 @@ export const ListOfGroupPromotions = () => {
|
|||||||
const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useRecoilState(
|
const [myGroupsWhereIAmAdmin, setMyGroupsWhereIAmAdmin] = useRecoilState(
|
||||||
myGroupsWhereIAmAdminAtom
|
myGroupsWhereIAmAdminAtom
|
||||||
);
|
);
|
||||||
const [promotions, setPromotions] = useRecoilState(
|
const [promotions, setPromotions] = useRecoilState(promotionsAtom);
|
||||||
promotionsAtom
|
|
||||||
);
|
|
||||||
const [promotionTimeInterval, setPromotionTimeInterval] = useRecoilState(
|
const [promotionTimeInterval, setPromotionTimeInterval] = useRecoilState(
|
||||||
promotionTimeIntervalAtom
|
promotionTimeIntervalAtom
|
||||||
);
|
);
|
||||||
|
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||||
|
|
||||||
const [openSnack, setOpenSnack] = useState(false);
|
const [openSnack, setOpenSnack] = useState(false);
|
||||||
const [infoSnack, setInfoSnack] = useState(null);
|
const [infoSnack, setInfoSnack] = useState(null);
|
||||||
const [fee, setFee] = useState(null);
|
const [fee, setFee] = useState(null);
|
||||||
@ -96,16 +101,16 @@ export const ListOfGroupPromotions = () => {
|
|||||||
const { show, setTxList } = useContext(MyContext);
|
const { show, setTxList } = useContext(MyContext);
|
||||||
|
|
||||||
const listRef = useRef();
|
const listRef = useRef();
|
||||||
const rowVirtualizer = useVirtualizer({
|
const rowVirtualizer = useVirtualizer({
|
||||||
count: promotions.length,
|
count: promotions.length,
|
||||||
getItemKey: React.useCallback(
|
getItemKey: React.useCallback(
|
||||||
(index) => promotions[index]?.identifier,
|
(index) => promotions[index]?.identifier,
|
||||||
[promotions]
|
[promotions]
|
||||||
),
|
),
|
||||||
getScrollElement: () => listRef.current,
|
getScrollElement: () => listRef.current,
|
||||||
estimateSize: () => 80, // Provide an estimated height of items, adjust this as needed
|
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
|
overscan: 10, // Number of items to render outside the visible area to improve smoothness
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
@ -117,7 +122,7 @@ export const ListOfGroupPromotions = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
const getPromotions = useCallback(async () => {
|
const getPromotions = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
setPromotionTimeInterval(Date.now())
|
setPromotionTimeInterval(Date.now());
|
||||||
const identifier = `group-promotions-ui24-`;
|
const identifier = `group-promotions-ui24-`;
|
||||||
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=100&includemetadata=false&reverse=true&prefix=true`;
|
const url = `${getBaseApiReact()}${getArbitraryEndpointReact()}?mode=ALL&service=DOCUMENT&identifier=${identifier}&limit=100&includemetadata=false&reverse=true&prefix=true`;
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
@ -168,7 +173,9 @@ export const ListOfGroupPromotions = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(getPromos);
|
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);
|
setPromotions(groupWithInfo);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -177,22 +184,23 @@ export const ListOfGroupPromotions = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
const timeSinceLastFetch = now - promotionTimeInterval;
|
const timeSinceLastFetch = now - promotionTimeInterval;
|
||||||
const initialDelay = timeSinceLastFetch >= THIRTY_MINUTES
|
const initialDelay =
|
||||||
? 0
|
timeSinceLastFetch >= THIRTY_MINUTES
|
||||||
: THIRTY_MINUTES - timeSinceLastFetch;
|
? 0
|
||||||
|
: THIRTY_MINUTES - timeSinceLastFetch;
|
||||||
const initialTimeout = setTimeout(() => {
|
const initialTimeout = setTimeout(() => {
|
||||||
getPromotions();
|
getPromotions();
|
||||||
|
|
||||||
// Start a 30-minute interval
|
// Start a 30-minute interval
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
getPromotions();
|
getPromotions();
|
||||||
}, THIRTY_MINUTES);
|
}, THIRTY_MINUTES);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, initialDelay);
|
}, initialDelay);
|
||||||
|
|
||||||
return () => clearTimeout(initialTimeout);
|
return () => clearTimeout(initialTimeout);
|
||||||
}, [getPromotions, promotionTimeInterval]);
|
}, [getPromotions, promotionTimeInterval]);
|
||||||
|
|
||||||
@ -328,100 +336,143 @@ export const ListOfGroupPromotions = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
marginTop: "20px",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
marginTop: "25px",
|
justifyContent: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box sx={{
|
||||||
sx={{
|
display: 'flex',
|
||||||
width: isMobile ? "320px" : "750px",
|
gap: '20px',
|
||||||
maxWidth: "90%",
|
width: '100%',
|
||||||
display: "flex",
|
justifyContent: 'space-between'
|
||||||
flexDirection: "column",
|
}}>
|
||||||
padding: "0px 20px",
|
<ButtonBase
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "space-between",
|
flexDirection: "row",
|
||||||
alignItems: "center",
|
padding: `0px ${isExpanded ? "24px" : "20px"}`,
|
||||||
|
gap: "10px",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
alignSelf: isExpanded && "flex-start",
|
||||||
}}
|
}}
|
||||||
|
onClick={() => setIsExpanded((prev) => !prev)}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: "1rem",
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Group Promotions
|
Group promotions {promotions.length > 0 && ` (${promotions.length})`}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button
|
{isExpanded ? (
|
||||||
variant="contained"
|
<ExpandLessIcon
|
||||||
onClick={() => setIsShowModal(true)}
|
sx={{
|
||||||
sx={{
|
marginLeft: "auto",
|
||||||
fontSize: "12px",
|
}}
|
||||||
}}
|
/>
|
||||||
>
|
) : (
|
||||||
Add Promotion
|
<ExpandMoreIcon
|
||||||
</Button>
|
sx={{
|
||||||
</Box>
|
marginLeft: "auto",
|
||||||
<Spacer height="10px" />
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</ButtonBase>
|
||||||
|
<Box
|
||||||
|
style={{
|
||||||
|
width: "330px",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
|
||||||
sx={{
|
<>
|
||||||
width: isMobile ? "320px" : "750px",
|
|
||||||
maxWidth: "90%",
|
|
||||||
maxHeight: "700px",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
bgcolor: "background.paper",
|
|
||||||
padding: "20px 0px",
|
|
||||||
borderRadius: "19px",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{loading && promotions.length === 0 && (
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "100%",
|
width: isMobile ? "320px" : "750px",
|
||||||
|
maxWidth: "90%",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
justifyContent: "center",
|
flexDirection: "column",
|
||||||
|
padding: "0px 20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CustomLoader />
|
<Box
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
{!loading && promotions.length === 0 && (
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
height: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Typography
|
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "11px",
|
width: "100%",
|
||||||
fontWeight: 400,
|
display: "flex",
|
||||||
color: "rgba(255, 255, 255, 0.2)",
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Nothing to display
|
<Typography
|
||||||
</Typography>
|
sx={{
|
||||||
|
fontSize: "13px",
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
></Typography>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
onClick={() => setIsShowModal(true)}
|
||||||
|
sx={{
|
||||||
|
fontSize: "12px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Add Promotion
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
<Spacer height="10px" />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: isMobile ? "320px" : "750px",
|
||||||
|
maxWidth: "90%",
|
||||||
|
maxHeight: "700px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
bgcolor: "background.paper",
|
||||||
|
padding: "20px 0px",
|
||||||
|
borderRadius: "19px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{loading && promotions.length === 0 && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CustomLoader />
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{!loading && promotions.length === 0 && (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
fontSize: "11px",
|
||||||
|
fontWeight: 400,
|
||||||
|
color: "rgba(255, 255, 255, 0.2)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Nothing to display
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: "600px",
|
height: "600px",
|
||||||
@ -460,7 +511,6 @@ export const ListOfGroupPromotions = () => {
|
|||||||
const index = virtualRow.index;
|
const index = virtualRow.index;
|
||||||
const promotion = promotions[index];
|
const promotion = promotions[index];
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div
|
<div
|
||||||
data-index={virtualRow.index} //needed for dynamic row height measurement
|
data-index={virtualRow.index} //needed for dynamic row height measurement
|
||||||
ref={rowVirtualizer.measureElement} //measure dynamic row height
|
ref={rowVirtualizer.measureElement} //measure dynamic row height
|
||||||
@ -479,236 +529,251 @@ export const ListOfGroupPromotions = () => {
|
|||||||
gap: "5px",
|
gap: "5px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ErrorBoundary
|
<ErrorBoundary
|
||||||
fallback={
|
fallback={
|
||||||
<Typography>
|
<Typography>
|
||||||
Error loading content: Invalid Data
|
Error loading content: Invalid Data
|
||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
padding: "0px 20px",
|
padding: "0px 20px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Popover
|
<Popover
|
||||||
open={openPopoverIndex === promotion?.groupId}
|
open={openPopoverIndex === promotion?.groupId}
|
||||||
anchorEl={popoverAnchor}
|
anchorEl={popoverAnchor}
|
||||||
onClose={(event, reason) => {
|
onClose={(event, reason) => {
|
||||||
if (reason === "backdropClick") {
|
if (reason === "backdropClick") {
|
||||||
// Prevent closing on backdrop click
|
// Prevent closing on backdrop click
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handlePopoverClose(); // Close only on other events like Esc key press
|
handlePopoverClose(); // Close only on other events like Esc key press
|
||||||
}}
|
}}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: "top",
|
vertical: "top",
|
||||||
horizontal: "center",
|
horizontal: "center",
|
||||||
}}
|
}}
|
||||||
transformOrigin={{
|
transformOrigin={{
|
||||||
vertical: "bottom",
|
vertical: "bottom",
|
||||||
horizontal: "center",
|
horizontal: "center",
|
||||||
}}
|
}}
|
||||||
style={{ marginTop: "8px" }}
|
style={{ marginTop: "8px" }}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "325px",
|
width: "325px",
|
||||||
height: "auto",
|
height: "auto",
|
||||||
maxHeight: "400px",
|
maxHeight: "400px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "10px",
|
gap: "10px",
|
||||||
padding: "10px",
|
padding: "10px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: "13px",
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Group name: {` ${promotion?.groupName}`}
|
Group name: {` ${promotion?.groupName}`}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: "13px",
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Number of members: {` ${promotion?.memberCount}`}
|
Number of members:{" "}
|
||||||
</Typography>
|
{` ${promotion?.memberCount}`}
|
||||||
{promotion?.description && (
|
</Typography>
|
||||||
<Typography
|
{promotion?.description && (
|
||||||
sx={{
|
<Typography
|
||||||
fontSize: "13px",
|
sx={{
|
||||||
fontWeight: 600,
|
fontSize: "13px",
|
||||||
}}
|
fontWeight: 600,
|
||||||
>
|
}}
|
||||||
{promotion?.description}
|
>
|
||||||
</Typography>
|
{promotion?.description}
|
||||||
)}
|
</Typography>
|
||||||
{promotion?.isOpen === false && (
|
)}
|
||||||
<Typography
|
{promotion?.isOpen === false && (
|
||||||
sx={{
|
<Typography
|
||||||
fontSize: "13px",
|
sx={{
|
||||||
fontWeight: 600,
|
fontSize: "13px",
|
||||||
}}
|
fontWeight: 600,
|
||||||
>
|
}}
|
||||||
*This is a closed/private group, so you will need to wait
|
>
|
||||||
until an admin accepts your request
|
*This is a closed/private group, so you
|
||||||
</Typography>
|
will need to wait until an admin accepts
|
||||||
)}
|
your request
|
||||||
<Spacer height="5px" />
|
</Typography>
|
||||||
<Box
|
)}
|
||||||
sx={{
|
<Spacer height="5px" />
|
||||||
display: "flex",
|
<Box
|
||||||
gap: "20px",
|
sx={{
|
||||||
alignItems: "center",
|
display: "flex",
|
||||||
width: "100%",
|
gap: "20px",
|
||||||
justifyContent: "center",
|
alignItems: "center",
|
||||||
}}
|
width: "100%",
|
||||||
>
|
justifyContent: "center",
|
||||||
<LoadingButton
|
}}
|
||||||
loading={isLoadingJoinGroup}
|
>
|
||||||
loadingPosition="start"
|
<LoadingButton
|
||||||
variant="contained"
|
loading={isLoadingJoinGroup}
|
||||||
onClick={handlePopoverClose}
|
loadingPosition="start"
|
||||||
>
|
variant="contained"
|
||||||
Close
|
onClick={handlePopoverClose}
|
||||||
</LoadingButton>
|
>
|
||||||
<LoadingButton
|
Close
|
||||||
loading={isLoadingJoinGroup}
|
</LoadingButton>
|
||||||
loadingPosition="start"
|
<LoadingButton
|
||||||
variant="contained"
|
loading={isLoadingJoinGroup}
|
||||||
onClick={() =>
|
loadingPosition="start"
|
||||||
handleJoinGroup(promotion, promotion?.isOpen)
|
variant="contained"
|
||||||
}
|
onClick={() =>
|
||||||
>
|
handleJoinGroup(
|
||||||
Join
|
promotion,
|
||||||
</LoadingButton>
|
promotion?.isOpen
|
||||||
</Box>
|
)
|
||||||
</Box>
|
}
|
||||||
</Popover>
|
>
|
||||||
|
Join
|
||||||
|
</LoadingButton>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gap: "15px",
|
gap: "15px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Avatar
|
<Avatar
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: "#27282c",
|
backgroundColor: "#27282c",
|
||||||
color: "white",
|
color: "white",
|
||||||
}}
|
}}
|
||||||
alt={promotion?.name}
|
alt={promotion?.name}
|
||||||
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
src={`${getBaseApiReact()}/arbitrary/THUMBNAIL/${
|
||||||
promotion?.name
|
promotion?.name
|
||||||
}/qortal_avatar?async=true`}
|
}/qortal_avatar?async=true`}
|
||||||
>
|
>
|
||||||
{promotion?.name?.charAt(0)}
|
{promotion?.name?.charAt(0)}
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontWight: 600,
|
fontWight: 600,
|
||||||
fontFamily: "Inter",
|
fontFamily: "Inter",
|
||||||
color: "cadetBlue",
|
color: "cadetBlue",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{promotion?.name}
|
{promotion?.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontWight: 600,
|
fontWight: 600,
|
||||||
fontFamily: "Inter",
|
fontFamily: "Inter",
|
||||||
color: "cadetBlue",
|
color: "cadetBlue",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{promotion?.groupName}
|
{promotion?.groupName}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
<Box sx={{
|
<Box
|
||||||
display: 'flex',
|
sx={{
|
||||||
gap: '20px',
|
display: "flex",
|
||||||
alignItems: 'center'
|
gap: "20px",
|
||||||
}}>
|
alignItems: "center",
|
||||||
{promotion?.isOpen === false && (
|
}}
|
||||||
<LockIcon sx={{
|
>
|
||||||
color: 'var(--green)'
|
{promotion?.isOpen === false && (
|
||||||
}} />
|
<LockIcon
|
||||||
)}
|
sx={{
|
||||||
{promotion?.isOpen === true && (
|
color: "var(--green)",
|
||||||
<NoEncryptionGmailerrorredIcon sx={{
|
}}
|
||||||
color: 'var(--danger)'
|
/>
|
||||||
}} />
|
)}
|
||||||
)}
|
{promotion?.isOpen === true && (
|
||||||
<Typography
|
<NoEncryptionGmailerrorredIcon
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "15px",
|
color: "var(--danger)",
|
||||||
fontWeight: 600,
|
}}
|
||||||
}}
|
/>
|
||||||
>
|
)}
|
||||||
{promotion?.isOpen ? 'Public group' : 'Private group' }
|
<Typography
|
||||||
</Typography>
|
sx={{
|
||||||
</Box>
|
fontSize: "15px",
|
||||||
<Spacer height="20px" />
|
fontWeight: 600,
|
||||||
<Typography
|
}}
|
||||||
sx={{
|
>
|
||||||
fontWight: 600,
|
{promotion?.isOpen
|
||||||
fontFamily: "Inter",
|
? "Public group"
|
||||||
color: "cadetBlue",
|
: "Private group"}
|
||||||
}}
|
</Typography>
|
||||||
>
|
</Box>
|
||||||
{promotion?.data}
|
<Spacer height="20px" />
|
||||||
</Typography>
|
<Typography
|
||||||
<Spacer height="20px" />
|
sx={{
|
||||||
<Box
|
fontWight: 600,
|
||||||
sx={{
|
fontFamily: "Inter",
|
||||||
display: "flex",
|
color: "cadetBlue",
|
||||||
justifyContent: "center",
|
}}
|
||||||
width: "100%",
|
>
|
||||||
}}
|
{promotion?.data}
|
||||||
>
|
</Typography>
|
||||||
<Button
|
<Spacer height="20px" />
|
||||||
// variant="contained"
|
<Box
|
||||||
onClick={(event) => handlePopoverOpen(event, promotion?.groupId)}
|
sx={{
|
||||||
sx={{
|
display: "flex",
|
||||||
fontSize: "12px",
|
justifyContent: "center",
|
||||||
color: 'white'
|
width: "100%",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Join Group: {` ${promotion?.groupName}`}
|
<Button
|
||||||
</Button>
|
// variant="contained"
|
||||||
</Box>
|
onClick={(event) =>
|
||||||
</Box>
|
handlePopoverOpen(event, promotion?.groupId)
|
||||||
<Spacer height="50px" />
|
}
|
||||||
|
sx={{
|
||||||
|
fontSize: "12px",
|
||||||
|
color: "white",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Join Group: {` ${promotion?.groupName}`}
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<Spacer height="50px" />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Box>
|
||||||
</Box>
|
</>
|
||||||
|
</Collapse>
|
||||||
<Spacer height="20px" />
|
<Spacer height="20px" />
|
||||||
|
|
||||||
{isShowModal && (
|
{isShowModal && (
|
||||||
|
@ -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 List from "@mui/material/List";
|
||||||
import ListItem from "@mui/material/ListItem";
|
import ListItem from "@mui/material/ListItem";
|
||||||
import ListItemButton from "@mui/material/ListItemButton";
|
import ListItemButton from "@mui/material/ListItemButton";
|
||||||
import ListItemIcon from "@mui/material/ListItemIcon";
|
import ListItemIcon from "@mui/material/ListItemIcon";
|
||||||
import ListItemText from "@mui/material/ListItemText";
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { Box, Typography } from "@mui/material";
|
import { Box, ButtonBase, Collapse, Typography } from "@mui/material";
|
||||||
import { Spacer } from "../../common/Spacer";
|
import { Spacer } from "../../common/Spacer";
|
||||||
import { getBaseApiReact, isMobile } from "../../App";
|
import { getBaseApiReact, isMobile } from "../../App";
|
||||||
import { MessagingIcon } from '../../assets/Icons/MessagingIcon';
|
import { MessagingIcon } from '../../assets/Icons/MessagingIcon';
|
||||||
@ -15,6 +15,10 @@ import { executeEvent } from '../../utils/events';
|
|||||||
import { CustomLoader } from '../../common/CustomLoader';
|
import { CustomLoader } from '../../common/CustomLoader';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
import { mailsAtom, qMailLastEnteredTimestampAtom } from '../../atoms/global';
|
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) => {
|
export const isLessThanOneWeekOld = (timestamp) => {
|
||||||
// Current time in milliseconds
|
// Current time in milliseconds
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
@ -41,6 +45,7 @@ export function formatEmailDate(timestamp: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const QMailMessages = ({userName, userAddress}) => {
|
export const QMailMessages = ({userName, userAddress}) => {
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false)
|
||||||
const [mails, setMails] = useRecoilState(mailsAtom)
|
const [mails, setMails] = useRecoilState(mailsAtom)
|
||||||
const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState(qMailLastEnteredTimestampAtom)
|
const [lastEnteredTimestamp, setLastEnteredTimestamp] = useRecoilState(qMailLastEnteredTimestampAtom)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
@ -99,7 +104,16 @@ export const QMailMessages = ({userName, userAddress}) => {
|
|||||||
|
|
||||||
}, [getMails, 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 (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -111,25 +125,37 @@ export const QMailMessages = ({userName, userAddress}) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Box
|
<ButtonBase
|
||||||
sx={{
|
sx={{
|
||||||
width: "322px",
|
width: "322px",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "row",
|
||||||
|
gap: '10px',
|
||||||
padding: "0px 20px",
|
padding: "0px 20px",
|
||||||
|
justifyContent: 'flex-start'
|
||||||
}}
|
}}
|
||||||
|
onClick={()=> setIsExpanded((prev)=> !prev)}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: "1rem",
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Latest Q-Mails
|
Latest Q-Mails
|
||||||
</Typography>
|
</Typography>
|
||||||
<Spacer height="10px" />
|
<MarkEmailUnreadIcon sx={{
|
||||||
</Box>
|
color: anyUnread ? '--unread' : 'white'
|
||||||
|
}}/>
|
||||||
|
{isExpanded ? <ExpandLessIcon sx={{
|
||||||
|
marginLeft: 'auto'
|
||||||
|
}} /> : (
|
||||||
|
<ExpandMoreIcon sx={{
|
||||||
|
color: anyUnread ? '--unread' : 'white',
|
||||||
|
marginLeft: 'auto'
|
||||||
|
}} />
|
||||||
|
)}
|
||||||
|
</ButtonBase>
|
||||||
|
<Collapse in={isExpanded} timeout="auto" unmountOnExit>
|
||||||
<Box
|
<Box
|
||||||
className="scrollable-container"
|
className="scrollable-container"
|
||||||
sx={{
|
sx={{
|
||||||
@ -248,6 +274,7 @@ export const QMailMessages = ({userName, userAddress}) => {
|
|||||||
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
</Collapse>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,7 @@ if(hasDoneNameAndBalanceAndIsLoaded){
|
|||||||
<QMailMessages userAddress={userInfo?.address} userName={userInfo?.name} />
|
<QMailMessages userAddress={userInfo?.address} userName={userInfo?.name} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if(!isLoaded) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@ -84,7 +85,7 @@ if(hasDoneNameAndBalanceAndIsLoaded){
|
|||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
sx={{
|
sx={{
|
||||||
fontSize: "13px",
|
fontSize: "1rem",
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -96,7 +97,6 @@ if(hasDoneNameAndBalanceAndIsLoaded){
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
width: "322px",
|
width: "322px",
|
||||||
height: isMobile ? "165px" : "250px",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
bgcolor: "background.paper",
|
bgcolor: "background.paper",
|
||||||
|
91
src/components/Home/NewUsersCTA.tsx
Normal file
91
src/components/Home/NewUsersCTA.tsx
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { Box, ButtonBase, Typography } from "@mui/material";
|
||||||
|
import React from "react";
|
||||||
|
import { Spacer } from "../../common/Spacer";
|
||||||
|
|
||||||
|
export const NewUsersCTA = ({ balance }) => {
|
||||||
|
if (balance === undefined || +balance > 0) return null;
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Spacer height="40px" />
|
||||||
|
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "320px",
|
||||||
|
justifyContent: "center",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
padding: "15px",
|
||||||
|
outline: "1px solid gray",
|
||||||
|
borderRadius: "4px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
sx={{
|
||||||
|
textAlign: "center",
|
||||||
|
fontSize: "1.2rem",
|
||||||
|
fontWeight: "bold",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Are you a new user?
|
||||||
|
</Typography>
|
||||||
|
<Spacer height="20px" />
|
||||||
|
<Typography>
|
||||||
|
Please message us on Telegram or Discord if you need 4 QORT to start
|
||||||
|
chatting without any limitations
|
||||||
|
</Typography>
|
||||||
|
<Spacer height="20px" />
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
gap: "10px",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ButtonBase
|
||||||
|
sx={{
|
||||||
|
textDecoration: "underline",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (window?.electronAPI?.openExternal) {
|
||||||
|
window.electronAPI.openExternal(
|
||||||
|
"https://link.qortal.dev/telegram-invite"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
window.open(
|
||||||
|
"https://link.qortal.dev/telegram-invite",
|
||||||
|
"_blank"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Telegram
|
||||||
|
</ButtonBase>
|
||||||
|
<ButtonBase
|
||||||
|
sx={{
|
||||||
|
textDecoration: "underline",
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (window?.electronAPI?.openExternal) {
|
||||||
|
window.electronAPI.openExternal(
|
||||||
|
"https://link.qortal.dev/discord-invite"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
window.open("https://link.qortal.dev/discord-invite", "_blank");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Discord
|
||||||
|
</ButtonBase>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
209
src/components/Home/QortPrice.tsx
Normal file
209
src/components/Home/QortPrice.tsx
Normal file
@ -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 (
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
gap: "20px",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
flexDirection: 'column',
|
||||||
|
width: "322px"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
title={<span style={{ color: "white", fontSize: "14px", fontWeight: 700 }}>Based on the latest 20 trades</span>}
|
||||||
|
placement="bottom"
|
||||||
|
arrow
|
||||||
|
sx={{ fontSize: "24" }}
|
||||||
|
slotProps={{
|
||||||
|
tooltip: {
|
||||||
|
sx: {
|
||||||
|
color: "#ffffff",
|
||||||
|
backgroundColor: "#444444",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
arrow: {
|
||||||
|
sx: {
|
||||||
|
color: "#444444",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{
|
||||||
|
width: "322px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: '10px',
|
||||||
|
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
}}>
|
||||||
|
<Typography sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}>Price</Typography>
|
||||||
|
{!ltcPerQort ? (
|
||||||
|
<BarSpinner width="16px" color="white" />
|
||||||
|
): (
|
||||||
|
<Typography sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
}}>{ltcPerQort} LTC/QORT</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
</Tooltip>
|
||||||
|
<Box sx={{
|
||||||
|
width: "322px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: '10px',
|
||||||
|
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
}}>
|
||||||
|
<Typography sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}>Supply</Typography>
|
||||||
|
{!supply ? (
|
||||||
|
<BarSpinner width="16px" color="white" />
|
||||||
|
): (
|
||||||
|
<Typography sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
}}>{supply} QORT</Typography>
|
||||||
|
)}
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
<Box sx={{
|
||||||
|
width: "322px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: '10px',
|
||||||
|
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
}}>
|
||||||
|
<Typography sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}}>Last height</Typography>
|
||||||
|
{!lastBlock?.height ? (
|
||||||
|
<BarSpinner width="16px" color="white" />
|
||||||
|
): (
|
||||||
|
<Typography sx={{
|
||||||
|
fontSize: "1rem",
|
||||||
|
}}>{lastBlock?.height}</Typography>
|
||||||
|
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user