pull/237/head
Tim Pechersky 2021-09-07 11:39:43 +02:00
rodzic 6e9d5ef6ec
commit 9e9d787d9b
7 zmienionych plików z 171 dodań i 44 usunięć

Wyświetl plik

@ -1,10 +1,17 @@
import React, { useEffect, useState } from "react";
import React, {
useCallback,
useEffect,
useLayoutEffect,
useRef,
useState,
} from "react";
import { getLayout } from "../src/layouts/AppLayout";
import { Spinner, Flex, Heading, Stack } from "@chakra-ui/react";
import Scrollable from "../src/components/Scrollable";
import useNFTs from "../src/core/hooks/useNFTs";
import RangeSelector from "../src/components/RangeSelector";
import StatsCard from "../src/components/StatsCard";
import useWindowSize from "../src/core/hooks/useWindowSize";
const HOUR_KEY = "Hourly";
const DAY_KEY = "Daily";
@ -15,17 +22,84 @@ timeMap[DAY_KEY] = "day";
timeMap[WEEK_KEY] = "week";
const Analytics = () => {
const windowSize = useWindowSize();
useEffect(() => {
if (typeof window !== "undefined") {
document.title = `Analytics: Page under construction`;
}
}, []);
const [nodesReady, setNodeReady] = useState({
ntx: false,
values: false,
mints: false,
});
const nTxRef_ = useRef();
const valueRef_ = useRef();
const mintsRef_ = useRef();
const nTxRef = useCallback(
(node) => {
if (node !== null && !nodesReady.ntx) {
setNodeReady({ ...nodesReady, ntx: true });
nTxRef_.current = node;
}
},
[nodesReady]
);
const valueRef = useCallback(
(node) => {
if (node !== null && !nodesReady.values) {
setNodeReady({ ...nodesReady, values: true });
valueRef_.current = node;
}
},
[nodesReady]
);
const mintsRef = useCallback(
(node) => {
if (node !== null && !nodesReady.mints) {
setNodeReady({ ...nodesReady, mints: true });
mintsRef_.current = node;
}
},
[nodesReady]
);
const [timeRange, setTimeRange] = useState(HOUR_KEY);
const { nftCache } = useNFTs();
if (nftCache.isLoading) return <Spinner />;
useLayoutEffect(() => {
const items = [nTxRef_, valueRef_, mintsRef_];
console.log("useeffect fired");
if (nTxRef_.current) {
var firstItemInCurrentRow = items[0];
items.forEach((item) => {
if (item.current) {
if (item !== firstItemInCurrentRow) {
// Check if the current item is at the same
// height as the first item in the current row.
if (
item.current.offsetTop === firstItemInCurrentRow.current.offsetTop
) {
item.current.style.borderLeft =
"3px dashed var(--chakra-colors-gray-600)";
} else {
// This item was lower, it must be
// the first in a new row.
firstItemInCurrentRow = item;
item.current.style.borderLeft = "0px dashed black";
}
}
} else {
firstItemInCurrentRow = item;
}
});
}
}, [nodesReady, windowSize]);
if (nftCache.isLoading) return <Spinner />;
return (
<Scrollable>
<Flex
@ -37,13 +111,14 @@ const Analytics = () => {
alignItems="center"
minH="100vh"
>
<Heading as="h1" py={4}>
<Heading as="h1" py={4} fontSize={["md", "xl"]}>
NFT market analysis
</Heading>
<RangeSelector
placeSelf="flex-start"
initialRange={timeRange}
ranges={Object.keys(timeMap)}
size={["sm", "md", null]}
onChange={(e) => setTimeRange(e)}
/>
<Stack
@ -53,24 +128,31 @@ const Analytics = () => {
h="auto"
direction="row"
minW="240px"
spacing={[2, 0, null]}
spacing={[0, 0, null]}
boxShadow="md"
borderRadius="lg"
bgColor="gray.100"
// divider={<StackDivider borderColor="gray.800" />}
>
<StatsCard
ref={(node) => nTxRef(node)}
// borderTopLeftRadius="inherit"
labelKey="transactions"
timeRange={timeMap[timeRange]}
netLabel="Ethereum mainnet"
label="Number of transactions"
/>
<StatsCard
ref={(node) => valueRef(node)}
labelKey="values"
timeRange={timeMap[timeRange]}
netLabel="Ethereum mainnet"
label="Value of transactions"
/>
<StatsCard
ref={(node) => mintsRef(node)}
// borderTopRightRadius="inherit"
// borderRightWidth="0"
labelKey="mints"
timeRange={timeMap[timeRange]}
netLabel="Ethereum mainnet"

Wyświetl plik

@ -17,13 +17,13 @@ const AccountIconButton = (props) => {
const { logout } = useLogout();
return (
<Menu>
<Menu {...props}>
<MenuButton
{...props}
variant="inherit"
colorScheme="inherit"
as={IconButton}
aria-label="Account menu"
icon={<RiAccountCircleLine size="26px" />}
// variant="outline"
icon={<RiAccountCircleLine m={0} size="26px" />}
color="gray.100"
/>
<MenuList

Wyświetl plik

@ -1,7 +1,13 @@
import React, { useEffect, useState, useRef } from "react";
import { Stack, Container, chakra } from "@chakra-ui/react";
const RangeSelector_ = ({ className, ranges, onChange, initialRange }) => {
const RangeSelector_ = ({
className,
ranges,
onChange,
initialRange,
size,
}) => {
const [range, setRange] = useState(initialRange ?? ranges[0]);
const isFirstRun = useRef(true);
@ -20,11 +26,11 @@ const RangeSelector_ = ({ className, ranges, onChange, initialRange }) => {
return (
<Container
key={`date-range-${className}-${idx}`}
size="xs"
bgColor={isActive ? "secondary.900" : "primary.50"}
color={!isActive ? "primary.900" : "primary.50"}
boxShadow="sm"
borderRadius="md"
fontSize={size}
fontWeight="600"
onClick={() => setRange(item)}
_hover={{

Wyświetl plik

@ -20,7 +20,7 @@ const Scrollable = (props) => {
const currentScroll = Math.ceil(getScrollPrecent(e) / 10);
if (currentScroll > scrollDepth) {
setScrollDepth(currentScroll);
mixpanel.get_distinct_id() &&
mixpanel?.get_distinct_id() &&
mixpanel.people.increment({
[`Scroll depth at: ${router.nextRouter.pathname}`]: currentScroll,
});

Wyświetl plik

@ -9,17 +9,13 @@ const TIME_PERIOD = {
previous: 1,
};
const isNumberNotZero = (str) => {
if (isNaN(Number(str) || Number(str) == 0)) {
return false;
} else {
return true;
}
const isNumberNonzeroAndFinite = (str) => {
return !(isNaN(Number(str)) || Number(str) === 0);
};
const getEthValue = (string) => {
const ether = web3.utils.fromWei(string, "ether");
return nFormatter(ether, 3);
return nFormatter(ether, 2);
};
const nFormatter = (num, digits) => {
@ -32,23 +28,22 @@ const nFormatter = (num, digits) => {
{ value: 1e15, symbol: "P" },
{ value: 1e18, symbol: "E" },
];
const rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
var item = lookup
let item = lookup
.slice()
.reverse()
.find(function (item) {
return num >= item.value;
.find(function (element) {
return num >= element.value;
});
return item
? (num / item.value).toFixed(digits).replace(rx, "$1") + item.symbol
: "0";
return item ? (num / item.value).toFixed(digits) + item.symbol : "0";
};
const getChange = (a, b) => {
if (isNumberNotZero(a) && isNumberNotZero(b)) {
if (isNumberNonzeroAndFinite(a) && isNumberNonzeroAndFinite(b)) {
let retval = (Math.abs(Number(a) - Number(b)) * 100) / Number(b);
retval = retval > 9999 ? nFormatter(retval, 3) : retval;
return retval.toFixed(2);
retval =
Math.abs(retval) > 9999 ? nFormatter(retval, 2) : retval.toFixed(2);
return retval;
} else {
return "-";
}
@ -62,11 +57,19 @@ const getDiff = (a, b) => {
}
};
const getSign = (a) => {
const isZeroOrPositive = (a) => {
if (isNaN(a)) return "-";
return Number(a) >= 0 ? true : false;
};
const StatsCard_ = ({ className, label, netLabel, labelKey, timeRange }) => {
const StatsCard_ = ({
className,
label,
netLabel,
labelKey,
timeRange,
innerRef,
}) => {
const { nftCache } = useNFTs();
const [nftData, setData] = useState();
@ -87,29 +90,30 @@ const StatsCard_ = ({ className, label, netLabel, labelKey, timeRange }) => {
setData({
dimension: labelKey === "values" ? "Eth" : "#",
isValueIncrease: getSign(valueChange),
isShareIncrease: getSign(shareChange),
isValueIncrease: isZeroOrPositive(valueChange),
isShareIncrease: isZeroOrPositive(shareChange),
valueChange,
shareChange,
share,
value:
labelKey === "values"
? getEthValue(cacheData.amount)
: nFormatter(cacheData.amount, 3),
: nFormatter(cacheData.amount, 2),
});
}
}, [nftCache?.data, nftCache.isLoading, labelKey, timeRange]);
if (nftCache.isLoading || !nftData) return "";
return (
<Stack className={className}>
<Stack className={className} ref={innerRef}>
<Box
id="nft-card-title"
w="full"
borderTopRadius="inherit"
borderRadius="base"
fontWeight="600"
bgColor="gray.200"
px={4}
textAlign="center"
fontSize={["sm", "md", null]}
>
{label}
</Box>
@ -122,12 +126,13 @@ const StatsCard_ = ({ className, label, netLabel, labelKey, timeRange }) => {
>
<Box
w="100%"
fontSize="1.125rem"
fontSize={["1rem", "1.125rem", null]}
borderStyle="dashed"
borderRightWidth="3px"
borderRightColor="gray.300"
// alignItems="center"
h="100%"
id="nft-card-value"
>
<Link
textDecorationLine="underline"
@ -145,9 +150,10 @@ const StatsCard_ = ({ className, label, netLabel, labelKey, timeRange }) => {
<Stack
w="100%"
direction="row"
fontSize="1.125rem"
fontSize={["1rem", "1.125rem", null]}
placeContent="center"
alignItems="center"
id="nft-card-value-change"
>
{nftData.isValueIncrease && <TriangleUpIcon color="suggested.900" />}
{!nftData.isValueIncrease && <TriangleDownIcon color="unsafe.900" />}
@ -165,12 +171,18 @@ const StatsCard_ = ({ className, label, netLabel, labelKey, timeRange }) => {
borderTopStyle="dashed"
borderTopColor="gray.300"
gridColumn="span 2"
fontSize="0.825rem"
fontSize={["0.625rem", "0.825rem", null]}
id="nft-card-share-label"
>
Total share in {netLabel}
</Text>
<Text>{nftData.share}%</Text>
<Stack direction="row" placeContent="center" alignItems="center">
<Text id="nft-card-share-value">{nftData.share}%</Text>
<Stack
direction="row"
placeContent="center"
alignItems="center"
id="nft-card-share-change"
>
{nftData.isShareIncrease && (
<TriangleUpIcon color="suggested.900" />
)}
@ -195,7 +207,7 @@ const StatsCard_ = ({ className, label, netLabel, labelKey, timeRange }) => {
const StatsCard = chakra(StatsCard_, {
baseStyle: {
borderStyle: "solid",
borderRightWidth: "1px",
// borderRightWidth: "1px",
borderRightColor: "gray.600",
w: "240px",
minW: "240px",
@ -204,4 +216,6 @@ const StatsCard = chakra(StatsCard_, {
},
});
export default StatsCard;
export default React.forwardRef((props, ref) => (
<StatsCard innerRef={ref} {...props} />
));

Wyświetl plik

@ -0,0 +1,26 @@
import { useState, useEffect } from "react";
const useWindowSize = () => {
const [windowSize, setWindowSize] = useState({
width: undefined,
height: undefined,
});
useEffect(() => {
// Handler to call on window resize
function handleResize() {
// Set window width/height to state
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
// Add event listener
window.addEventListener("resize", handleResize);
// Call handler right away so state gets updated with initial window size
handleResize();
// Remove event listener on cleanup
return () => window.removeEventListener("resize", handleResize);
}, []);
return windowSize;
};
export default useWindowSize;

Wyświetl plik

@ -63,7 +63,6 @@ const UIProvider = ({ children }) => {
}
}, [user]);
// *********** Sidebar states **********************
// Whether sidebar should be visible at all or hidden