Merge pull request #324 from bugout-dev/CreateNew-ABIDashboard

"Create new" Frontend Expreience
pull/351/head
Yhtyyar Sahatov 2021-10-27 16:32:52 +03:00 zatwierdzone przez GitHub
commit cdc6f7231a
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
26 zmienionych plików z 878 dodań i 535 usunięć

Wyświetl plik

@ -16,6 +16,8 @@
"@emotion/styled": "^11.3.0",
"@stripe/stripe-js": "^1.16.0",
"axios": "^0.21.1",
"color": "^4.0.1",
"downshift": "^6.1.7",
"focus-visible": "^5.2.0",
"framer-motion": "^4.1.17",
"mixpanel-browser": "^2.41.0",

Wyświetl plik

@ -25,6 +25,7 @@ import {
} from "../src/core/providers/AnalyticsProvider/constants";
import { AWS_ASSETS_PATH } from "../src/core/constants";
import mixpanel from "mixpanel-browser";
import { MODAL_TYPES } from "../src/core/providers/OverlayProvider/constants";
const ConnectedButtons = dynamic(
() => import("../src/components/ConnectedButtons"),
@ -398,7 +399,7 @@ const Homepage = () => {
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Early access CTA: developer want to find more button`,
}
);
toggleModal("hubspot-developer");
toggleModal(MODAL_TYPES.HUBSPOT);
}}
>
request early access

Wyświetl plik

@ -1,5 +1,5 @@
import { getLayout } from "../src/layouts/AppLayout";
import React, { useState } from "react";
import React, { useContext } from "react";
import SubscriptionsList from "../src/components/SubscriptionsList";
import { useSubscriptions } from "../src/core/hooks";
import {
@ -10,21 +10,14 @@ import {
Heading,
Flex,
Button,
Modal,
useDisclosure,
ModalHeader,
ModalCloseButton,
ModalBody,
ModalOverlay,
ModalContent,
} from "@chakra-ui/react";
import NewSubscription from "../src/components/NewSubscription";
import { AiOutlinePlusCircle } from "react-icons/ai";
import OverlayContext from "../src/core/providers/OverlayProvider/context";
import { MODAL_TYPES } from "../src/core/providers/OverlayProvider/constants";
const Subscriptions = () => {
const { subscriptionsCache } = useSubscriptions();
const { isOpen, onOpen, onClose } = useDisclosure();
const [isAddingFreeSubscription, setIsAddingFreeSubscription] = useState();
const modal = useContext(OverlayContext);
document.title = `My Subscriptions`;
@ -40,32 +33,11 @@ const Subscriptions = () => {
borderBottomWidth: "2px",
};
const newSubscriptionClicked = (isForFree) => {
setIsAddingFreeSubscription(isForFree);
onOpen();
const newSubscriptionClicked = () => {
modal.toggleModal(MODAL_TYPES.NEW_SUBSCRIPTON);
};
return (
<Box w="100%" px="7%" pt={2}>
<Modal
isOpen={isOpen}
onClose={onClose}
size="2xl"
scrollBehavior="outside"
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Subscribe to a new address</ModalHeader>
<ModalCloseButton />
<ModalBody>
<NewSubscription
isFreeOption={isAddingFreeSubscription}
onClose={onClose}
isModal={true}
/>
</ModalBody>
</ModalContent>
</Modal>
{subscriptionsCache.isLoading ? (
<Center>
<Spinner

Wyświetl plik

@ -1,72 +1,32 @@
import React, { useContext, useEffect, useRef } from "react";
import React, { useEffect, useRef } from "react";
import { getLayout } from "../src/layouts/AppLayout";
import UIContext from "../src/core/providers/UIProvider/context";
import {
Heading,
Text,
Button,
Stack,
ButtonGroup,
Spacer,
UnorderedList,
ListItem,
Fade,
chakra,
useBoolean,
Flex,
IconButton,
Tooltip,
Accordion,
AccordionItem,
AccordionButton,
AccordionPanel,
AccordionIcon,
Divider,
} from "@chakra-ui/react";
import StepProgress from "../src/components/StepProgress";
import { ArrowLeftIcon, ArrowRightIcon } from "@chakra-ui/icons";
import Scrollable from "../src/components/Scrollable";
import NewSubscription from "../src/components/NewSubscription";
import StreamEntry from "../src/components/StreamEntry";
import SubscriptionsList from "../src/components/SubscriptionsList";
import { useSubscriptions } from "../src/core/hooks";
import router from "next/router";
import { FaFilter } from "react-icons/fa";
const Welcome = () => {
const { subscriptionsCache } = useSubscriptions();
const ui = useContext(UIContext);
const [showSubscriptionForm, setShowSubscriptionForm] = useBoolean(true);
useEffect(() => {
if (typeof window !== "undefined") {
document.title = `Welcome to moonstream.to!`;
}
}, []);
const progressButtonCallback = (index) => {
ui.setOnboardingStep(index);
};
const SubscriptonCreatedCallback = () => {
setShowSubscriptionForm.off();
};
const scrollRef = useRef();
const handleNextClick = () => {
if (ui.onboardingStep < ui.onboardingSteps.length - 1) {
ui.setOnboardingStep(ui.onboardingStep + 1);
scrollRef?.current?.scrollIntoView();
} else {
ui.setOnboardingComplete(true);
router.push("/stream");
}
};
return (
<Scrollable>
<Stack px="7%" pt={4} w="100%" spacing={4} ref={scrollRef}>
<StepProgress
{/* <StepProgress
numSteps={ui.onboardingSteps.length}
currentStep={ui.onboardingStep}
colorScheme="blue"
@ -77,11 +37,11 @@ const Welcome = () => {
"How to read stream",
]}
style="arrows"
/>
/> */}
{ui.onboardingStep === 0 && (
{true && (
<Fade in>
<Stack spacing={4}>
<Stack spacing={4} pb={14}>
<Stack
px={[0, 12, null]}
// mt={24}
@ -240,192 +200,8 @@ const Welcome = () => {
</Stack>
</Fade>
)}
{ui.onboardingStep === 1 && (
<Fade in>
<Stack px="7%">
<Stack
px={[0, 12, null]}
// mt={24}
bgColor="gray.50"
borderRadius="xl"
boxShadow="xl"
py={4}
my={2}
>
<Heading as="h4" size="md">
Subscriptions
</Heading>
<chakra.span fontWeight="semibold" pl={2}>
Subscriptions are an essential tool of Moonstream. We gather
data for you based on addresses you have subscribed to.
<br />
Subscribe to any address you are interested in and it will
become part of your stream.
<br />
Name of subscription (you can change it later).
<UnorderedList>
<ListItem>
Color - you can set colors to easily identify a
subscription in your stream
</ListItem>
<ListItem>Address - the address you subscribe to</ListItem>
<ListItem>
Label - we recommend using a human-readable name that
represents the subscription
</ListItem>
<ListItem>
Source - In Alpha were only supporting Ethereum
blockchain, but more sources are coming soon!
</ListItem>
</UnorderedList>
</chakra.span>
</Stack>
{subscriptionsCache.data.subscriptions.length > 0 &&
!subscriptionsCache.isLoading && (
<>
<Heading>
{" "}
You already have some subscriptions set up
</Heading>
</>
)}
<SubscriptionsList />
{showSubscriptionForm && (
<Flex direction="column" pt={6}>
<Divider bgColor="gray.500" borderWidth="2px" />
<Heading
size="md"
pt={2}
>{`Let's add new subscription!`}</Heading>
<NewSubscription
isFreeOption={false}
onClose={SubscriptonCreatedCallback}
/>
</Flex>
)}
{!showSubscriptionForm && (
<Button
colorScheme="green"
variant="solid"
onClick={() => setShowSubscriptionForm.on()}
>
Add another subscription
</Button>
)}
</Stack>
</Fade>
)}
{ui.onboardingStep === 2 && (
<Fade in>
<Stack>
<Stack
px={[0, 12, null]}
// mt={24}
bgColor="gray.50"
borderRadius="xl"
boxShadow="xl"
py={4}
my={2}
>
<Heading as="h4" size="md">
Stream
</Heading>
<chakra.span fontWeight="semibold" pl={2}>
We are almost done!
<br />
{`Stream is where you can read data you've subscribed to. There are different cards for different subscription types.`}
<br />
If the card has some extra details, there will be an orange
button on the right hand side inviting you to see more!
<br />
Below is a typical card for an Ethereum blockchain event.
Useful information right on the card:
<UnorderedList py={2}>
<ListItem>Hash - Unique ID of the event</ListItem>
<ListItem>
From - Sender address. If it is one of your subscription
addresses, it will appear in color with a label
</ListItem>
<ListItem>
To - Receiver address. If it is one of your subscription
addresses, it will appear in color with a label
</ListItem>
<ListItem>
Nonce - Counter how many transaction addresses have been
sent. It also determines the sequence of transactions!
</ListItem>
<ListItem>
Gas Price - This is how much ether is being paid per gas
unit
</ListItem>
<ListItem>Gas - Amount of gas this event consumes</ListItem>
</UnorderedList>
</chakra.span>
</Stack>
<Stack
pb={ui.isMobileView ? 24 : 8}
w={ui.isMobileView ? "100%" : "calc(100% - 300px)"}
alignSelf="center"
>
<Flex h="3rem" w="100%" bgColor="gray.100" alignItems="center">
<Flex maxW="90%"></Flex>
<Spacer />
<Tooltip
variant="onboarding"
placement={ui.isMobileView ? "bottom" : "right"}
label="Filtering menu"
isOpen={true}
maxW="150px"
hasArrow
>
<IconButton
mr={4}
// onClick={onOpen}
colorScheme="blue"
variant="ghost"
icon={<FaFilter />}
/>
</Tooltip>
</Flex>
<StreamEntry
mt={20}
entry={{
event_type: "ethereum_blockchain",
event_data: {
from: "this is address from",
to: "this is to address",
hash: "this is hash",
},
}}
showOnboardingTooltips={true}
/>
</Stack>
<Stack
px={12}
// mt={24}
bgColor="gray.50"
borderRadius="xl"
boxShadow="xl"
py={4}
my={2}
>
<Heading as="h4" size="md">
Applying filters
</Heading>
<chakra.span fontWeight="semibold" pl={2}>
You can apply various filters by clicking the filter menu
button.
<br />
{`Right now you can use it to select addresses from and to, and we are adding more complex queries soon — stay tuned!`}
<br />
</chakra.span>
</Stack>
</Stack>
</Fade>
)}
<ButtonGroup>
{/* <ButtonGroup>
<Button
colorScheme="orange"
leftIcon={<ArrowLeftIcon />}
@ -462,7 +238,7 @@ const Welcome = () => {
? `Next`
: `Finish and move to stream`}
</Button>
</ButtonGroup>
</ButtonGroup> */}
</Stack>
</Scrollable>
);

Wyświetl plik

@ -4,9 +4,9 @@ import theme from "./Theme/theme";
import {
AnalyticsProvider,
UserProvider,
ModalProvider,
UIProvider,
DataProvider,
OverlayProvider,
} from "./core/providers";
import { StripeProvider } from "./core/providers/StripeProvider";
@ -18,17 +18,17 @@ const AppContext = (props) => {
}, []);
return (
<UserProvider>
<ModalProvider>
<StripeProvider>
<ChakraProvider theme={theme}>
<DataProvider>
<UIProvider>
<StripeProvider>
<ChakraProvider theme={theme}>
<DataProvider>
<UIProvider>
<OverlayProvider>
<AnalyticsProvider>{props.children}</AnalyticsProvider>
</UIProvider>
</DataProvider>
</ChakraProvider>
</StripeProvider>
</ModalProvider>
</OverlayProvider>
</UIProvider>
</DataProvider>
</ChakraProvider>
</StripeProvider>
</UserProvider>
);
};

Wyświetl plik

@ -0,0 +1,63 @@
import React, { useContext } from "react";
import {
Menu,
MenuButton,
MenuList,
MenuItem,
MenuGroup,
MenuDivider,
IconButton,
chakra,
} from "@chakra-ui/react";
import { PlusSquareIcon } from "@chakra-ui/icons";
import UIContext from "../core/providers/UIProvider/context";
import OverlayContext from "../core/providers/OverlayProvider/context";
import {
DRAWER_TYPES,
MODAL_TYPES,
} from "../core/providers/OverlayProvider/constants";
const AddNewIconButton = (props) => {
const ui = useContext(UIContext);
const modal = useContext(OverlayContext);
return (
<Menu>
<MenuButton
{...props}
as={IconButton}
// onClick={ui.addNewDrawerState.onOpen}
aria-label="Account menu"
icon={<PlusSquareIcon />}
// variant="outline"
color="gray.100"
/>
<MenuList
zIndex="dropdown"
width={["100vw", "100vw", "18rem", "20rem", "22rem", "24rem"]}
borderRadius={0}
>
<MenuGroup>
<MenuItem
onClick={() => modal.toggleDrawer(DRAWER_TYPES.NEW_DASHBOARD)}
>
New Dashboard...
</MenuItem>
<MenuItem
onClick={() => modal.toggleModal(MODAL_TYPES.NEW_SUBSCRIPTON)}
>
New Subscription...
</MenuItem>
{ui.isInDashboard && <MenuItem>New report...</MenuItem>}
</MenuGroup>
<MenuDivider />
</MenuList>
</Menu>
);
};
const ChakraAddNewIconButton = chakra(AddNewIconButton);
export default ChakraAddNewIconButton;

Wyświetl plik

@ -1,5 +1,4 @@
import React, { useState, useContext, useEffect } from "react";
import RouterLink from "next/link";
import {
Flex,
Image,
@ -23,11 +22,11 @@ import {
ArrowLeftIcon,
ArrowRightIcon,
} from "@chakra-ui/icons";
import { MdTimeline } from "react-icons/md";
import useRouter from "../core/hooks/useRouter";
import UIContext from "../core/providers/UIProvider/context";
import AccountIconButton from "./AccountIconButton";
import RouteButton from "./RouteButton";
import AddNewIconButton from "./AddNewIconButton";
import {
USER_NAV_PATHES,
ALL_NAV_PATHES,
@ -129,6 +128,13 @@ const AppNavbar = () => {
})}
</ButtonGroup>
<SupportPopover />
<AddNewIconButton
colorScheme="blue"
variant="link"
color="gray.100"
size="lg"
h="32px"
/>
<AccountIconButton
colorScheme="blue"
variant="link"
@ -161,19 +167,14 @@ const AppNavbar = () => {
}}
/>
)}
<RouterLink href="/stream" passHref>
<IconButton
m={0}
variant="link"
justifyContent="space-evenly"
alignContent="center"
h="32px"
size={iconSize}
colorScheme="gray"
aria-label="go to ticker"
icon={<MdTimeline />}
/>
</RouterLink>
<AddNewIconButton
variant="link"
justifyContent="space-evenly"
alignContent="center"
h="32px"
size={iconSize}
colorScheme="blue"
/>
{!isSearchBarActive && (
<IconButton
m={0}

Wyświetl plik

@ -54,8 +54,6 @@ const ArrowCTA = (props) => {
null,
];
const speedConst = -0.05;
return (
<SimpleGrid
columns={props.button4 ? [1, 2, 4, null, 4] : [1, 2, 3, null, 3]}

Wyświetl plik

@ -1,11 +1,7 @@
/** @jsxRuntime classic */
/** @jsx jsx */
import { jsx } from "@emotion/react";
import { useEffect } from "react";
import React, { useEffect } from "react";
import { useForm } from "react-hook-form";
import { useToast, useForgotPassword } from "../core/hooks";
import {
Heading,
FormControl,
InputGroup,
FormErrorMessage,
@ -14,7 +10,7 @@ import {
InputRightElement,
} from "@chakra-ui/react";
import CustomIcon from "./CustomIcon";
import Modal from "./Modal";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
const ForgotPassword = ({ toggleModal }) => {
const toast = useToast();
@ -24,43 +20,38 @@ const ForgotPassword = ({ toggleModal }) => {
useEffect(() => {
if (!data) return;
toggleModal(null);
toggleModal(MODAL_TYPES.OFF);
}, [data, toggleModal, toast]);
return (
<Modal onClose={() => toggleModal(null)}>
<Heading mt={2} size="md">
Forgot Password
</Heading>
<form onSubmit={handleSubmit(forgotPassword)}>
<FormControl isInvalid={errors.email} my={4}>
<InputGroup>
<Input
colorScheme="blue"
variant="filled"
placeholder="Your email here"
name="email"
ref={register({ required: "Email is required!" })}
/>
<InputRightElement>
<CustomIcon icon="name" />
</InputRightElement>
</InputGroup>
<FormErrorMessage color="red.400" pl="1">
{errors.email && errors.email.message}
</FormErrorMessage>
</FormControl>
<Button
type="submit"
variant="solid"
colorScheme="blue"
width="100%"
isLoading={isLoading}
>
Send
</Button>
</form>
</Modal>
<form onSubmit={handleSubmit(forgotPassword)}>
<FormControl isInvalid={errors.email} my={4}>
<InputGroup>
<Input
colorScheme="blue"
variant="filled"
placeholder="Your email here"
name="email"
ref={register({ required: "Email is required!" })}
/>
<InputRightElement>
<CustomIcon icon="name" />
</InputRightElement>
</InputGroup>
<FormErrorMessage color="red.400" pl="1">
{errors.email && errors.email.message}
</FormErrorMessage>
</FormControl>
<Button
type="submit"
variant="solid"
colorScheme="blue"
width="100%"
isLoading={isLoading}
>
Send
</Button>
</form>
);
};

Wyświetl plik

@ -1,10 +1,10 @@
import React, { useEffect } from "react";
import { Heading, Spinner } from "@chakra-ui/react";
import Modal from "./Modal";
import { Spinner } from "@chakra-ui/react";
import { useToast } from "../core/hooks";
import HubspotForm from "react-hubspot-form";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
const RequestIntegration = ({ toggleModal, title, formId }) => {
const RequestIntegration = ({ toggleModal, formId }) => {
const toast = useToast();
useEffect(() => {
@ -14,7 +14,7 @@ const RequestIntegration = ({ toggleModal, title, formId }) => {
event.data.eventName === "onFormSubmitted"
) {
if (event.data.id === formId) {
toggleModal(null);
toggleModal(MODAL_TYPES.OFF);
toast("Request sent", "success");
}
}
@ -28,17 +28,12 @@ const RequestIntegration = ({ toggleModal, title, formId }) => {
}, [toast, toggleModal]);
return (
<Modal onClose={() => toggleModal(null)}>
<Heading my={2} as="h2" fontSize={["xl", "3xl"]}>
{title}
</Heading>
<HubspotForm
region="na1"
portalId="8018701"
formId={formId}
loading={<Spinner colorScheme="blue" speed="1s" />}
/>
</Modal>
<HubspotForm
region="na1"
portalId="8018701"
formId={formId}
loading={<Spinner colorScheme="blue" speed="1s" />}
/>
);
};

Wyświetl plik

@ -16,6 +16,7 @@ import ChakraAccountIconButton from "./AccountIconButton";
import RouteButton from "./RouteButton";
import { ALL_NAV_PATHES, WHITE_LOGO_W_TEXT_URL } from "../core/constants";
import router from "next/router";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
const LandingNavbar = () => {
const ui = useContext(UIContext);
@ -71,7 +72,7 @@ const LandingNavbar = () => {
))}
{ui.isLoggedIn && (
<RouterLink href="/stream" passHref>
<RouterLink href="/welcome" passHref>
<Button
as={Link}
colorScheme="orange"
@ -99,7 +100,7 @@ const LandingNavbar = () => {
{!ui.isLoggedIn && (
<Button
color="white"
onClick={() => toggleModal("login")}
onClick={() => toggleModal(MODAL_TYPES.LOGIN)}
fontWeight="400"
>
Log in

Wyświetl plik

@ -1,29 +0,0 @@
/** @jsxRuntime classic */
/** @jsx jsx */
import { jsx } from "@emotion/react";
import { Flex, Image } from "@chakra-ui/react";
import CustomIcon from "../CustomIcon";
import styles from "./styles";
const Modal = ({ children, onClose }) => (
<Flex onClick={onClose} css={styles.modal} zIndex={100002}>
<Flex onClick={(e) => e.stopPropagation()} css={styles.flex}>
<Image
color="blue.900"
height="24px"
width="22px"
sx={{ filter: "grayscale: 50%" }}
fill="blue.800"
src={
"https://s3.amazonaws.com/static.simiotics.com/moonstream/assets/logo-black.svg"
}
/>
<Flex cursor="pointer" onClick={onClose} css={styles.close}>
<CustomIcon height="13px" width="13px" icon="close" />
</Flex>
{children}
</Flex>
</Flex>
);
export default Modal;

Wyświetl plik

@ -1,38 +0,0 @@
import { css } from "@emotion/react";
const styles = {
modal: css`
position: fixed;
align-items: center;
justify-content: center;
display: flex;
left: 0;
top: 0;
height: 100%;
width: 100%;
background: rgba(0, 0, 0, 0.5);
`,
flex: css`
position: relative;
background: #ffffff;
flex-direction: column;
width: 31.25rem;
padding: 2.5rem;
border-radius: 0.375rem;
@media (max-width: 576px) {
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
border-radius: 0;
}
box-shadow: 0 11px 34px 0 rgba(0, 0, 0, 0.15);
`,
close: css`
position: absolute;
right: 38px;
top: 38px;
`,
};
export default styles;

Wyświetl plik

@ -1,17 +1,12 @@
/** @jsxRuntime classic */
/** @jsx jsx */
import { jsx } from "@emotion/react";
import React, { Suspense, useContext } from "react";
import { Flex } from "@chakra-ui/react";
import UIContext from "../core/providers/UIProvider/context";
const ForgotPassword = React.lazy(() => import("./ForgotPassword"));
const SignIn = React.lazy(() => import("./SignIn"));
const LandingNavbar = React.lazy(() => import("./LandingNavbar"));
const AppNavbar = React.lazy(() => import("./AppNavbar"));
const HubspotForm = React.lazy(() => import("./HubspotForm"));
const Navbar = () => {
const { modal, toggleModal, isAppView, isLoggedIn } = useContext(UIContext);
const { isAppView, isLoggedIn } = useContext(UIContext);
return (
<Flex
@ -26,30 +21,6 @@ const Navbar = () => {
overflow="hidden"
>
<Suspense fallback={""}>
{/* {modal === "register" && <SignUp toggleModal={toggleModal} />} */}
{modal === "login" && <SignIn toggleModal={toggleModal} />}
{modal === "forgot" && <ForgotPassword toggleModal={toggleModal} />}
{modal === "hubspot-trader" && (
<HubspotForm
toggleModal={toggleModal}
title={"Join the waitlist"}
formId={"29a17405-819b-405d-9563-f75bfb3774e0"}
/>
)}
{modal === "hubspot-fund" && (
<HubspotForm
toggleModal={toggleModal}
title={"Join the waitlist"}
formId={"04f0b8df-6b8f-4cd0-871f-4e872523b6f5"}
/>
)}
{modal === "hubspot-developer" && (
<HubspotForm
toggleModal={toggleModal}
title={"Join the waitlist"}
formId={"1897f4a1-3a00-475b-9bd5-5ca2725bd720"}
/>
)}
{(!isAppView || !isLoggedIn) && <LandingNavbar />}
{isAppView && isLoggedIn && <AppNavbar />}
</Suspense>

Wyświetl plik

@ -0,0 +1,433 @@
import React, { useEffect } from "react";
import {
chakra,
FormLabel,
Input,
Stack,
InputGroup,
Box,
Button,
Table,
Th,
Td,
Tr,
Thead,
Tbody,
Center,
Checkbox,
CloseButton,
InputRightAddon,
Badge,
InputLeftAddon,
} from "@chakra-ui/react";
import { CheckCircleIcon } from "@chakra-ui/icons";
import { useStorage, useSubscriptions } from "../core/hooks";
import Downshift from "downshift";
import color from "color";
const NewDashboard = (props) => {
const [newDashboardForm, setNewDashboardForm] = useStorage(
sessionStorage,
"new_dashboard",
{
name: "",
subscriptions: [
{
label: "",
id: null,
isMethods: false,
},
],
}
);
const subscriptions = useSubscriptions();
const [pickerItems, setPickerItems] = React.useState(
subscriptions.subscriptionsCache.data?.subscriptions
);
useEffect(() => {
if (!subscriptions.subscriptionsCache.isLoading) {
const massaged = subscriptions.subscriptionsCache.data?.subscriptions.map(
(item) => {
return { value: item.address, ...item };
}
);
setPickerItems(massaged);
}
}, [
subscriptions.subscriptionsCache.data,
subscriptions.subscriptionsCache.isLoading,
]);
const filterFn = (item, inputValue) =>
item.subscription_type_id === "ethereum_blockchain" &&
(!inputValue ||
item.address.toUpperCase().includes(inputValue.toUpperCase()) ||
item.label.toUpperCase().includes(inputValue.toUpperCase()));
return (
<>
<Stack spacing="24px">
<Box>
<FormLabel htmlFor="name">Name dashboard</FormLabel>
<Input
ref={props?.firstField}
id="name"
type="search"
placeholder="How new board should be named?"
value={newDashboardForm?.name}
onChange={(e) =>
setNewDashboardForm((prevState) => {
return { ...prevState, name: e.target.value };
})
}
/>
</Box>
<Box>
<FormLabel htmlFor="Addresses">Address list</FormLabel>
<Table
borderColor="gray.200"
// borderWidth="1px"
variant="simple"
colorScheme="blue"
justifyContent="center"
borderBottomRadius="xl"
alignItems="baseline"
h="auto"
size="sm"
mt={0}
>
<Thead>
<Tr>
<Th>Address</Th>
<Th w="90px">ABI State</Th>
<Th w="90px">Track ABI</Th>
<Th w="60px"></Th>
</Tr>
</Thead>
<Tbody>
{newDashboardForm?.subscriptions.map((subscibedItem, idx) => {
return (
<Tr key={`form-address-row-${idx}`}>
<Td>
{!subscriptions.subscriptionsCache.isLoading &&
subscriptions.subscriptionsCache.data &&
pickerItems && (
<>
<Downshift
onSelect={(selectedItem) => {
const newState = { ...newDashboardForm };
newState.subscriptions[idx] = selectedItem;
setNewDashboardForm(newState);
}}
// isOpen={showSuggestions}
itemToString={(item) => (item ? item.label : "")}
initialSelectedItem={subscibedItem ?? undefined}
>
{({
getInputProps,
getItemProps,
getLabelProps,
getMenuProps,
getToggleButtonProps,
isOpen,
inputValue,
highlightedIndex,
// selectedItem,
getRootProps,
}) => {
const labelColor =
subscibedItem.color &&
color(`${subscibedItem.color}`);
return (
<Box pos="relative">
<Box
// style={comboboxStyles}
{...getRootProps(
{},
{ suppressRefError: true }
)}
>
<InputGroup>
<InputLeftAddon
isTruncated
maxW="60px"
fontSize="sm"
bgColor={
subscibedItem?.color ?? "gray.100"
}
>
<FormLabel
alignContent="center"
my={2}
{...getLabelProps()}
color={
labelColor
? labelColor?.isDark()
? "white"
: labelColor.darken(0.6).hex()
: "inherit"
}
>{`#${idx}:`}</FormLabel>
</InputLeftAddon>
<Input
placeholder="Subscription to use in dashboard"
isTruncated
fontSize="sm"
defaultValue={
subscibedItem?.label ?? "yoyoy"
}
{...getInputProps({
defaultValue:
subscibedItem?.label ?? "iha",
})}
></Input>
<InputRightAddon>
{" "}
<button
{...getToggleButtonProps()}
aria-label={"toggle menu"}
>
&#8595;
</button>
</InputRightAddon>
</InputGroup>
</Box>
{/* <Menu
isOpen={isOpen}
// style={menuStyles}
// position="absolute"
colorScheme="blue"
bgColor="gray.300"
inset="unset"
// spacing={2}
// p={2}
> */}
{isOpen ? (
<Stack
// display="flex"
direction="column"
className="menuListTim"
{...getMenuProps()}
bgColor="gray.300"
borderRadius="md"
boxShadow="lg"
pos="absolute"
left={0}
right={0}
spacing={2}
zIndex={1000}
py={2}
>
{pickerItems &&
pickerItems.filter((item) =>
filterFn(item, inputValue)
).length === 0 && (
<Button
colorScheme="orange"
variant="outline"
size="sm"
>
Set new subscription to:{" "}
{inputValue}{" "}
</Button>
)}
{pickerItems &&
pickerItems
.filter((item) =>
filterFn(item, inputValue)
)
.map((item, index) => {
const badgeColor = color(
`${item.color}`
);
return (
<Stack
px={4}
py={1}
alignItems="center"
key={item.value}
{...getItemProps({
key: item.value,
index,
item,
})}
direction="row"
w="100%"
bgColor={
index === highlightedIndex
? "orange.900"
: "inherit"
}
color={
index === highlightedIndex
? "gray.100"
: "inherit"
}
>
<chakra.span whiteSpace="nowrap">
{item.label}
</chakra.span>
<Badge
size="sm"
placeSelf="self-end"
colorScheme={
item.hasABI
? "green"
: "gray"
}
>
ABI
</Badge>
<Badge
isTruncated
size="sm"
placeSelf="self-end"
bgColor={item.color}
color={
badgeColor.isDark()
? badgeColor
.lighten(100)
.hex()
: badgeColor
.darken(0.6)
.hex()
}
>
{item.address}
</Badge>
</Stack>
);
})}
</Stack>
) : null}
{/* </Menu> */}
</Box>
);
}}
</Downshift>
</>
// <AutoComplete
// openOnFocus
// creatable
// suggestWhenEmpty
// >
// <AutoCompleteInput variant="filled" />
// <AutoCompleteList>
// {pickerItems?.map((subscription, oid) => (
// <AutoCompleteItem
// key={`row-${idx}-option-${oid}`}
// value={subscription.address}
// textTransform="capitalize"
// align="center"
// >
// {/* <Avatar
// size="sm"
// // name={person.name}
// // src={person.image}
// /> */}
// <Text ml="4">{subscription.label}</Text>
// </AutoCompleteItem>
// ))}
// {/* <AutoCompleteCreatable>
// {({ value }) => (
// <span>New Subscription: {value}</span>
// )}
// </AutoCompleteCreatable> */}
// </AutoCompleteList>
// </AutoComplete>
)}
</Td>
<Td w="90px">
{subscibedItem.hasABI && subscibedItem.address && (
<CheckCircleIcon color="green" />
)}
{!subscibedItem.hasABI && (
<Button
colorScheme="orange"
size="xs"
py={2}
disabled={!subscibedItem.address}
>
Upload
</Button>
)}
</Td>
<Td w="60px">
<Checkbox
isDisabled={
!subscibedItem.address || !subscibedItem.hasABI
}
isChecked={subscibedItem.isMethods}
></Checkbox>
</Td>
<Td>
{idx > 0 && (
<CloseButton
onClick={() => {
const hardcopy = [
...newDashboardForm?.subscriptions,
];
hardcopy.splice(idx, 1);
setNewDashboardForm((prevState) => {
return {
...prevState,
subscriptions: [...hardcopy],
};
});
}}
/>
)}
</Td>
</Tr>
);
})}
</Tbody>
</Table>
<Center>
<Button
w="100px"
colorScheme="gray"
variant="solid"
p={0}
maxH="1.25rem"
_active={{ textDecor: "none" }}
onClick={() => {
const newState = { ...newDashboardForm };
newState.subscriptions.push({
label: "",
id: null,
isMethods: false,
});
setNewDashboardForm(newState);
}}
>
+
</Button>
</Center>
</Box>
{/* <Box>
<FormLabel htmlFor="desc">ABI</FormLabel>
<Textarea
id="desc"
placeholder="ABI Upload element should be here instead"
/>
</Box> */}
</Stack>
</>
);
};
const ChakraNewDashboard = chakra(NewDashboard);
export default ChakraNewDashboard;

Wyświetl plik

@ -17,10 +17,10 @@ import {
ArrowRightIcon,
LockIcon,
} from "@chakra-ui/icons";
import { MdTimeline, MdSettings } from "react-icons/md";
import { ImStatsBars } from "react-icons/im";
import { MdSettings } from "react-icons/md";
import { HiAcademicCap } from "react-icons/hi";
import { WHITE_LOGO_W_TEXT_URL } from "../core/constants";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
const Sidebar = () => {
const ui = useContext(UIContext);
@ -68,24 +68,8 @@ const Sidebar = () => {
</SidebarHeader>
{ui.isLoggedIn && (
<SidebarContent>
<Menu iconShape="square">
<MenuItem icon={<MdTimeline />}>
{" "}
<RouterLink href="/stream">Stream</RouterLink>
</MenuItem>
</Menu>
<Menu iconShape="square">
<MenuItem icon={<ImStatsBars />}>
{" "}
<RouterLink href="/analytics">Analytics </RouterLink>
</MenuItem>
</Menu>
<Menu iconShape="square">
<MenuItem icon={<MdSettings />}>
{" "}
<RouterLink href="/subscriptions">Subscriptions </RouterLink>
</MenuItem>
</Menu>
<Menu iconShape="square"></Menu>
<Menu iconShape="square"></Menu>
{ui.isMobileView && (
<Menu iconShape="square">
<MenuItem icon={<HiAcademicCap />}>
@ -112,7 +96,7 @@ const Sidebar = () => {
<Menu iconShape="square">
<MenuItem
onClick={() => {
ui.toggleModal("login");
ui.toggleModal(MODAL_TYPES.LOGIN);
ui.setSidebarToggled(false);
}}
>
@ -137,6 +121,10 @@ const Sidebar = () => {
<MenuItem icon={<LockIcon />}>
<RouterLink href="/account/tokens">API Tokens</RouterLink>
</MenuItem>
<MenuItem icon={<MdSettings />}>
{" "}
<RouterLink href="/subscriptions">Subscriptions </RouterLink>
</MenuItem>
</Menu>
)}
</SidebarFooter>

Wyświetl plik

@ -1,10 +1,6 @@
/** @jsxRuntime classic */
/** @jsx jsx */
import { jsx } from "@emotion/react";
import { useEffect } from "react";
import React, { useEffect } from "react";
import { useForm } from "react-hook-form";
import {
Heading,
Text,
Stack,
Box,
@ -19,7 +15,7 @@ import {
import CustomIcon from "./CustomIcon";
import { useLogin } from "../core/hooks";
import PasswordInput from "./PasswordInput";
import Modal from "./Modal";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
const SignIn = ({ toggleModal }) => {
const { handleSubmit, errors, register } = useForm();
@ -30,14 +26,11 @@ const SignIn = ({ toggleModal }) => {
return;
}
toggleModal(null);
toggleModal(MODAL_TYPES.OFF);
}, [data, toggleModal]);
return (
<Modal onClose={() => toggleModal(null)}>
<Heading mt={2} size="md">
Login now
</Heading>
<>
<Text color="gray.1200" fontSize="md">
To your Moonstream account
</Text>
@ -91,7 +84,7 @@ const SignIn = ({ toggleModal }) => {
cursor="pointer"
color="blue.800"
as="span"
onClick={() => toggleModal("forgot")}
onClick={() => toggleModal(MODAL_TYPES.FORGOT)}
>
Forgot your password?
</Box>
@ -112,7 +105,7 @@ const SignIn = ({ toggleModal }) => {
Register
</Box> */}
</Text>
</Modal>
</>
);
};

Wyświetl plik

@ -4,7 +4,6 @@ import { jsx } from "@emotion/react";
import { useContext, useEffect } from "react";
import { useForm } from "react-hook-form";
import {
Heading,
Text,
Stack,
Box,
@ -17,9 +16,9 @@ import {
} from "@chakra-ui/react";
import CustomIcon from "./CustomIcon";
import { useSignUp } from "../core/hooks";
import Modal from "./Modal";
import PasswordInput from "./PasswordInput";
import UIContext from "../core/providers/UIProvider/context";
import { MODAL_TYPES } from "../core/providers/OverlayProvider/constants";
const SignUp = ({ toggleModal }) => {
const { handleSubmit, errors, register } = useForm();
@ -28,15 +27,12 @@ const SignUp = ({ toggleModal }) => {
useEffect(() => {
if (isSuccess) {
ui.toggleModal(null);
ui.toggleModal(MODAL_TYPES.OFF);
}
}, [isSuccess, toggleModal, ui]);
return (
<Modal onClose={() => ui.toggleModal(null)}>
<Heading mt={2} size="md">
Create an account
</Heading>
<>
<Text color="gray.1200" fontSize="md">
Sign up for free
</Text>
@ -105,12 +101,12 @@ const SignUp = ({ toggleModal }) => {
cursor="pointer"
color="blue.400"
as="span"
onClick={() => toggleModal("login")}
onClick={() => toggleModal(MODAL_TYPES.LOGIN)}
>
Login
</Box>
</Text>
</Modal>
</>
);
};

Wyświetl plik

@ -1,8 +1,8 @@
import ModalContext from "../providers/ModalProvider/context";
import OverlayContext from "../providers/OverlayProvider/context";
import { useContext } from "react";
const useModals = () => {
const modals = useContext(ModalContext);
const modals = useContext(OverlayContext);
return modals;
};

Wyświetl plik

@ -1,5 +0,0 @@
import { createContext } from "react";
const ModalContext = createContext();
export default ModalContext;

Wyświetl plik

@ -1,13 +0,0 @@
import React, { useState } from "react";
import ModalContext from "./context";
const ModalProvider = ({ children }) => {
const [modal, toggleModal] = useState();
return (
<ModalContext.Provider value={{ modal, toggleModal }}>
{children}
</ModalContext.Provider>
);
};
export default ModalProvider;

Wyświetl plik

@ -0,0 +1,20 @@
export const MODAL_TYPES = {
OFF: 0,
LOGIN: 1,
SIGNUP: 2,
FORGOT: 3,
VERIFY: 4,
CONFIRM: 5,
HUBSPOT: 6,
NEW_SUBSCRIPTON: 7,
};
export const DRAWER_TYPES = {
OFF: 0,
NEW_DASHBOARD: 1,
};
export const ALERT_TYPES = {
OFF: 0,
CLOSE_DRAWER: 1,
};

Wyświetl plik

@ -0,0 +1,5 @@
import { createContext } from "react";
const OverlayContext = createContext();
export default OverlayContext;

Wyświetl plik

@ -0,0 +1,234 @@
import React, { useState, useLayoutEffect, useContext, Suspense } from "react";
import OverlayContext from "./context";
import { MODAL_TYPES, DRAWER_TYPES } from "./constants";
import {
Modal,
ModalOverlay,
ModalCloseButton,
ModalBody,
ModalContent,
useDisclosure,
ModalHeader,
Drawer,
DrawerBody,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
DrawerContent,
DrawerCloseButton,
AlertDialog,
AlertDialogBody,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogContent,
AlertDialogOverlay,
Button,
Spinner,
} from "@chakra-ui/react";
import UserContext from "../UserProvider/context";
import UIContext from "../UIProvider/context";
const ForgotPassword = React.lazy(() =>
import("../../../components/ForgotPassword")
);
const SignIn = React.lazy(() => import("../../../components/SignIn"));
const HubspotForm = React.lazy(() => import("../../../components/HubspotForm"));
const NewDashboard = React.lazy(() =>
import("../../../components/NewDashboard")
);
const NewSubscription = React.lazy(() =>
import("../../../components/NewSubscription")
);
const OverlayProvider = ({ children }) => {
const ui = useContext(UIContext);
const { user } = useContext(UserContext);
const [modal, toggleModal] = useState(MODAL_TYPES.OFF);
const [drawer, toggleDrawer] = useState(DRAWER_TYPES.OFF);
const [alertCallback, setAlertCallback] = useState(null);
const drawerDisclosure = useDisclosure();
const modalDisclosure = useDisclosure();
const alertDisclosure = useDisclosure();
useLayoutEffect(() => {
if (modal === MODAL_TYPES.OFF && modalDisclosure.isOpen) {
modalDisclosure.onClose();
} else if (modal !== MODAL_TYPES.OFF && !modalDisclosure.isOpen) {
modalDisclosure.onOpen();
}
}, [modal, modalDisclosure]);
useLayoutEffect(() => {
if (drawer === DRAWER_TYPES.OFF && drawerDisclosure.isOpen) {
drawerDisclosure.onClose();
} else if (drawer !== DRAWER_TYPES.OFF && !drawerDisclosure.isOpen) {
drawerDisclosure.onOpen();
}
}, [drawer, drawerDisclosure]);
const handleAlertConfirm = () => {
alertCallback && alertCallback();
alertDisclosure.onClose();
};
const toggleAlert = (callback) => {
setAlertCallback(() => callback);
alertDisclosure.onOpen();
};
console.assert(
Object.values(DRAWER_TYPES).some((element) => element === drawer)
);
console.assert(
Object.values(MODAL_TYPES).some((element) => element === modal)
);
const cancelRef = React.useRef();
const firstField = React.useRef();
useLayoutEffect(() => {
if (
ui.isAppView &&
ui.isInit &&
!user?.username &&
!ui.isLoggingOut &&
!ui.isLoggingIn &&
!modal
) {
toggleModal(MODAL_TYPES.LOGIN);
} else if (user && ui.isLoggingOut) {
toggleModal(MODAL_TYPES.OFF);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ui.isAppView, ui.isAppReady, user, ui.isLoggingOut, modal]);
return (
<OverlayContext.Provider
value={{ modal, toggleModal, drawer, toggleDrawer }}
>
<AlertDialog
isOpen={alertDisclosure.isOpen}
leastDestructiveRef={cancelRef}
// onClose={onClose}
>
<AlertDialogOverlay>
<AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold">
Cancel
</AlertDialogHeader>
<AlertDialogBody>Are you sure you want to cancel?</AlertDialogBody>
<AlertDialogFooter>
<Button ref={cancelRef} onClick={() => alertDisclosure.onClose()}>
Cancel
</Button>
<Button
colorScheme="red"
ml={3}
onClick={() => {
handleAlertConfirm();
}}
>
Confirm
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
<Modal
isOpen={modalDisclosure.isOpen}
onClose={() => toggleModal(MODAL_TYPES.OFF)}
size="2xl"
scrollBehavior="outside"
trapFocus={false}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>
{modal === MODAL_TYPES.NEW_SUBSCRIPTON &&
"Subscribe to a new address"}
{modal === MODAL_TYPES.FORGOT && "Forgot Password"}
{modal === MODAL_TYPES.HUBSPOT && "Join the waitlist"}
{modal === MODAL_TYPES.LOGIN && "Login now"}
{modal === MODAL_TYPES.SIGNUP && "Create an account"}
</ModalHeader>
<ModalCloseButton />
<ModalBody zIndex={100002}>
<Suspense fallback={<Spinner />}>
{modal === MODAL_TYPES.NEW_SUBSCRIPTON && (
<NewSubscription
onClose={() => toggleModal(MODAL_TYPES.OFF)}
isModal={true}
/>
)}
{modal === MODAL_TYPES.FORGOT && <ForgotPassword />}
{modal === MODAL_TYPES.HUBSPOT && (
<HubspotForm
toggleModal={toggleModal}
title={"Join the waitlist"}
formId={"1897f4a1-3a00-475b-9bd5-5ca2725bd720"}
/>
)}
{modal === MODAL_TYPES.LOGIN && (
<SignIn toggleModal={toggleModal} />
)}
{
modal === MODAL_TYPES.SIGNUP && ""
// <SignUp toggleModal={toggleModal} />
}
</Suspense>
</ModalBody>
</ModalContent>
</Modal>
{/* )} */}
<Drawer
isOpen={drawerDisclosure.isOpen}
placement="right"
size="xl"
// w="80%"
initialFocusRef={firstField}
onClose={() => toggleAlert(() => toggleDrawer(DRAWER_TYPES.OFF))}
>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader borderBottomWidth="1px">
{DRAWER_TYPES.NEW_DASHBOARD && "New dashboard"}
</DrawerHeader>
<DrawerBody>
{DRAWER_TYPES.NEW_DASHBOARD && (
<Suspense fallback={<Spinner />}>
<NewDashboard firstField={firstField} />
</Suspense>
)}
</DrawerBody>
<DrawerFooter borderTopWidth="1px">
<Button
variant="outline"
mr={3}
onClick={() => toggleAlert(() => toggleDrawer(DRAWER_TYPES.OFF))}
>
Cancel
</Button>
<Button
colorScheme="blue"
onClick={() => {
//TODO: @Peersky Implement logic part
console.log("submit clicked");
}}
>
Submit
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
{children}
</OverlayContext.Provider>
);
};
export default OverlayProvider;

Wyświetl plik

@ -9,7 +9,6 @@ import { useBreakpointValue } from "@chakra-ui/react";
import { useStorage, useQuery, useRouter } from "../../hooks";
import UIContext from "./context";
import UserContext from "../UserProvider/context";
import ModalContext from "../ModalProvider/context";
import { v4 as uuid4 } from "uuid";
import { PreferencesService } from "../../services";
@ -41,7 +40,6 @@ const UIProvider = ({ children }) => {
});
// const isMobileView = true;
const { modal, toggleModal } = useContext(ModalContext);
const [searchTerm, setSearchTerm] = useQuery("q", "", true, false);
const [searchBarActive, setSearchBarActive] = useState(false);
@ -57,22 +55,6 @@ const UIProvider = ({ children }) => {
const [isAppReady, setAppReady] = useState(false);
const [isAppView, setAppView] = useState(false);
useLayoutEffect(() => {
if (
isAppView &&
isInit &&
!user?.username &&
!isLoggingOut &&
!isLoggingIn &&
!modal
) {
toggleModal("login");
} else if (user || isLoggingOut) {
toggleModal(false);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isAppView, isAppReady, user, isLoggingOut, modal]);
useEffect(() => {
if (isLoggingOut && !isAppView && user) {
setLoggingOut(false);
@ -269,8 +251,6 @@ const UIProvider = ({ children }) => {
// eslint-disable-next-line
}, [onboardingStep, router.nextRouter.pathname, user, isAppReady]);
// ********************************************************
useEffect(() => {
if (
isInit &&
@ -293,6 +273,14 @@ const UIProvider = ({ children }) => {
onboardingRedirectCheckPassed,
]);
//***************Overlay's states ************************/
// const [newDashboardForm, setNewDashboardForm] = useStorage(
// window.sessionStorage,
// "newDashboardForm",
// null
// );
const [newDashboardForm, setNewDashboardForm] = useState();
return (
<UIContext.Provider
value={{
@ -314,8 +302,6 @@ const UIProvider = ({ children }) => {
isAppReady,
entriesViewMode,
setEntryDetailView,
modal,
toggleModal,
sessionId,
currentTransaction,
setCurrentTransaction,
@ -329,6 +315,8 @@ const UIProvider = ({ children }) => {
isLoggingOut,
isLoggingIn,
setLoggingIn,
newDashboardForm,
setNewDashboardForm,
}}
>
{children}

Wyświetl plik

@ -1,6 +1,6 @@
export { default as AnalyticsProvider } from "./AnalyticsProvider";
export { default as JournalsProvider } from "./DataProvider";
export { default as ModalProvider } from "./ModalProvider";
export { default as OverlayProvider } from "./OverlayProvider";
export { default as StripeProvider } from "./StripeProvider";
export { default as UserProvider } from "./UserProvider";
export { default as UIProvider } from "./UIProvider";