Merge branch 'main' into etherscan-nft

etherscan-nft
yhtiyar 2021-08-19 19:37:45 +03:00
commit 457858c23e
24 zmienionych plików z 1208 dodań i 207 usunięć

Wyświetl plik

@ -0,0 +1,32 @@
"""Unique const for addr
Revision ID: ea8185bd24c7
Revises: 40871a7807f6
Create Date: 2021-08-18 09:41:00.512462
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'ea8185bd24c7'
down_revision = '40871a7807f6'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index('ix_ethereum_addresses_address', table_name='ethereum_addresses')
op.create_index(op.f('ix_ethereum_addresses_address'), 'ethereum_addresses', ['address'], unique=True)
op.create_unique_constraint(op.f('uq_ethereum_labels_id'), 'ethereum_labels', ['id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(op.f('uq_ethereum_labels_id'), 'ethereum_labels', type_='unique')
op.drop_index(op.f('ix_ethereum_addresses_address'), table_name='ethereum_addresses')
op.create_index('ix_ethereum_addresses_address', 'ethereum_addresses', ['address'], unique=False)
# ### end Alembic commands ###

Wyświetl plik

@ -113,7 +113,7 @@ class EthereumAddress(Base): # type: ignore
nullable=True,
index=True,
)
address = Column(VARCHAR(256), nullable=False, index=True)
address = Column(VARCHAR(256), nullable=False, unique=True, index=True)
created_at = Column(
DateTime(timezone=True), server_default=utcnow(), nullable=False
)

Wyświetl plik

@ -39,7 +39,6 @@ export default function CachingApp({ Component, pageProps }) {
router.events.on("routeChangeComplete", handleStop);
router.events.on("routeChangeError", handleStop);
console.log("_app", router.asPath);
return () => {
router.events.off("routeChangeStart", handleStart);
router.events.off("routeChangeComplete", handleStop);
@ -49,8 +48,6 @@ export default function CachingApp({ Component, pageProps }) {
const getLayout =
Component.getLayout || ((page) => <DefaultLayout>{page}</DefaultLayout>);
console.log("_app loaded", router.asPath);
return (
<>
<style global jsx>{`

Wyświetl plik

@ -10,7 +10,9 @@ import {
UnorderedList,
ListItem,
} from "@chakra-ui/react";
import RouteButton from "../../src/components/RouteButton";
const Entry = () => {
console.count("render stream!");
const ui = useContext(UIContext);
useEffect(() => {
@ -49,6 +51,14 @@ const Entry = () => {
subscription screen
</ListItem>
</UnorderedList>
<RouteButton
variant="solid"
size="md"
colorScheme="suggested"
href="/welcome"
>
Learn how to use moonstream
</RouteButton>
</Stack>
</>
</Box>

Wyświetl plik

@ -15,7 +15,7 @@ import {
ModalOverlay,
ModalContent,
} from "@chakra-ui/react";
import NewSubscription from "../src/components/NewSubscription";
import NewSubscription from "../src/components/NewModalSubscripton";
import { AiOutlinePlusCircle } from "react-icons/ai";
const Subscriptions = () => {

Wyświetl plik

@ -0,0 +1,468 @@
import React, { useContext, 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,
Radio,
RadioGroup,
UnorderedList,
ListItem,
Fade,
chakra,
useBoolean,
Flex,
IconButton,
Tooltip,
} from "@chakra-ui/react";
import StepProgress from "../src/components/StepProgress";
import { ArrowLeftIcon, ArrowRightIcon } from "@chakra-ui/icons";
import Scrollable from "../src/components/Scrollable";
import AnalyticsContext from "../src/core/providers/AnalyticsProvider/context";
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 = () => {
console.count("render welcome!");
const { subscriptionsCache } = useSubscriptions();
const ui = useContext(UIContext);
const { mixpanel, isLoaded, MIXPANEL_PROPS } = useContext(AnalyticsContext);
const [profile, setProfile] = React.useState();
const [showSubscriptionForm, setShowSubscriptionForm] = useBoolean(true);
useEffect(() => {
if (typeof window !== "undefined") {
document.title = `Welcome to moonstream.to!`;
}
}, []);
const progressButtonCallback = (index) => {
ui.setOnboardingStep(index);
};
useEffect(() => {
if (profile && isLoaded) {
mixpanel.people.set({
[`${MIXPANEL_PROPS.USER_SPECIALITY}`]: profile,
});
}
}, [profile, MIXPANEL_PROPS, isLoaded, mixpanel]);
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.setisOnboardingComplete(true);
router.push("/stream");
}
};
return (
<Scrollable>
<Stack px="7%" pt={4} w="100%" spacing={4} ref={scrollRef}>
<StepProgress
numSteps={ui.onboardingSteps.length}
currentStep={ui.onboardingStep}
colorScheme="primary"
buttonCallback={progressButtonCallback}
buttonTitles={[
"Moonstream basics",
"Setup subscriptions",
"How to read stream",
]}
style="arrows"
/>
{ui.onboardingStep === 0 && (
<Fade in>
<Stack spacing={4}>
<Stack
px={12}
// mt={24}
bgColor="gray.50"
borderRadius="xl"
boxShadow="xl"
py={4}
>
<Heading as="h4" size="md">
Greetings traveller!
</Heading>
<Text fontWeight="semibold" pl={2}>
{" "}
We are very excited to see you onboard!
</Text>
<Text fontWeight="semibold" pl={2}>
Moonstream is a product which helps anyone participate in
decentralized finance. From the most sophisticated flash
arbitrageurs to people looking for yield from currency that
would otherwise lie dormant in their exchange accounts.
</Text>
<Text fontWeight="semibold" pl={2}>
Moonstream is ment to give you critical insights needed to
succeed in your crypto quest!
</Text>
</Stack>
<Stack
px={12}
// mt={24}
bgColor="gray.50"
borderRadius="xl"
boxShadow="xl"
py={4}
>
<Heading as="h4" size="md">
How does Moonstream work?
</Heading>
<chakra.span fontWeight="semibold" pl={2}>
<Text fontWeight="bold" display="inline">
We run nodes
</Text>{" "}
- Now get most precise and accurate data you can just query
our database. You {`don't`} need to maintain your node, and
still have data that miners have access to!
</chakra.span>
<chakra.span fontWeight="semibold" pl={2}>
<Text fontWeight="bold" display="inline">
We crawl data
</Text>{" "}
We analyze millions of transactions, data, smart contract code
to link all them together
</chakra.span>
<chakra.span fontWeight="semibold" pl={2}>
<Text fontWeight="bold" display="inline">
We provide data
</Text>{" "}
We allow you to fetch data trough the website or trough API
</chakra.span>
<chakra.span fontWeight="semibold" pl={2}>
<Text fontWeight="bold" display="inline">
We analyze data
</Text>{" "}
We find most interesting stuff and show it to you!
</chakra.span>
</Stack>
<Stack
px={12}
// mt={24}
bgColor="gray.50"
borderRadius="xl"
boxShadow="xl"
py={4}
>
<Heading as="h4" size="md">
UI 101?
</Heading>
<Text fontWeight="semibold" pl={2}>
On the left side corner there is sidebar that you can use to
navigate across the website. There are following views you can
navigate to:
</Text>
<chakra.span fontWeight="semibold" pl={2}>
<Text fontWeight="bold" display="inline">
Subscriptions
</Text>{" "}
- Use this screen to set up addresses you would like to
monitor to.{" "}
<i>
NB: Without setting up subscriptions moonstream will have
quite empty feel!{" "}
</i>{" "}
No worries, we will help you to set up your subscriptions in
the next steps!
</chakra.span>
<chakra.span fontWeight="semibold" pl={2}>
<Text fontWeight="bold" display="inline">
Stream
</Text>{" "}
This view is somewhat similar to a bank statement where you
can define time range and see what happened in that time over
your subscriptions. In next steps we will show how you can
apply filters to it!
</chakra.span>
<chakra.span fontWeight="semibold" pl={2}>
<Text fontWeight="bold" display="inline">
Stream Entry
</Text>{" "}
You can see detailed view of stream cards with very specific
and essential data, like methods called in smart contracts
etc!!
</chakra.span>
<chakra.span fontWeight="semibold" pl={2}>
<Text fontWeight="bold" display="inline">
Analytics
</Text>{" "}
This section is under construction yet. Soon you will be able
to create your dashboard with data of your interest. We will
really appretiate if you visit that section, and fill up form
describing what analytical tools you would love to see there!
</chakra.span>
</Stack>
<Stack
px={12}
// mt={24}
bgColor="gray.50"
borderRadius="xl"
boxShadow="xl"
py={4}
>
<Heading as="h4" size="md">
Tell us more about your needs?
</Heading>
<Text fontWeight="semibold" pl={2}>
In order to fetch best possible experience, we would like to
know some details about you
</Text>
<Text fontWeight="semibold" pl={2}>
Please tell us what profile describes you best?{" "}
<i>
This is purely analytical data, you can change it anytime
later
</i>
</Text>
<RadioGroup
onChange={setProfile}
value={profile}
fontWeight="bold"
>
<Stack direction="row" justifyContent="space-evenly">
<Radio value="trader">I am trading crypto currency</Radio>
<Radio value="fund">I represent investment fund</Radio>
<Radio value="developer">I am developer</Radio>
</Stack>
</RadioGroup>
</Stack>
</Stack>
</Fade>
)}
{ui.onboardingStep === 1 && (
<Fade in>
<Stack px="7%">
<Stack
px={12}
// 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 is essential tool of Moonstream. We gather data
for you based on addresses you have subscribed for.
<br />
Subscribe to any addres which you are interested in and they
will become part of your stream!
<UnorderedList>
<ListItem>
Color - you can define color to easily identify this
subscription in your stream
</ListItem>
<ListItem>Address - thing you subscribe to</ListItem>
<ListItem>
Label - Its good idea to use human readible name that
represents address
</ListItem>
<ListItem>
Source - In Alpha we support only Ethereum blockchain,
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 && (
<>
<Heading pt={12}>{`Let's add new subscription!`}</Heading>
<NewSubscription
isFreeOption={true}
onClose={SubscriptonCreatedCallback}
/>
</>
)}
{!showSubscriptionForm && (
<Button
colorScheme="suggested"
variant="solid"
onClick={() => setShowSubscriptionForm.on()}
>
Add another subscription
</Button>
)}
</Stack>
</Fade>
)}
{ui.onboardingStep === 2 && (
<Fade in>
<Stack>
<Stack
px={12}
// 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 for. Here
you have different cards for different subscription types.`}
<br />
If card has some extra details - there will be orange button
on right hand side inviting you to see more!
<br />
Below is typical card for 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 - will appear in color and with label{" "}
</ListItem>
<ListItem>
To - receiver address. If it is one of your subscription
addresses - will appear in color and with label{" "}
</ListItem>
<ListItem>
Nonce - Counter how many transactions address has sent. It
also determines sequence of transaction!{" "}
</ListItem>
<ListItem>
Gas Price - this is how much ether is being paid per gas
unit
</ListItem>
<ListItem>
Gas - Ammount 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="primary"
variant="ghost"
icon={<FaFilter />}
/>
</Tooltip>
</Flex>
<StreamEntry
mt={20}
entry={{
from_address: "this is address from",
to_address: "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 filter menu button
<br />
{`Right now you can use it to select address from and to, we are adding more complex queries soon, stay tuna! `}
<br />
</chakra.span>
</Stack>
</Stack>
</Fade>
)}
<ButtonGroup>
<Button
colorScheme="secondary"
leftIcon={<ArrowLeftIcon />}
variant="outline"
hidden={ui.onboardingStep === 0}
disabled={ui.onboardingStep === 0}
onClick={() => {
ui.setOnboardingStep(ui.onboardingStep - 1);
scrollRef?.current?.scrollIntoView();
}}
>
Previous
</Button>
<Spacer />
<Button
colorScheme="secondary"
variant="solid"
rightIcon={<ArrowRightIcon />}
// hidden={!(ui.onboardingStep < ui.onboardingSteps.length - 1)}
// disabled={!(ui.onboardingStep < ui.onboardingSteps.length - 1)}
onClick={() => handleNextClick()}
>
{ui.onboardingStep < ui.onboardingSteps.length - 1
? `Next`
: `Finish `}
</Button>
</ButtonGroup>
</Stack>
</Scrollable>
);
};
Welcome.getLayout = getLayout;
export default Welcome;

Wyświetl plik

@ -1,13 +1,38 @@
const Spinner = {
baseStyle: {
color: "primary.400",
thickness: "4px",
speed: "1.5s",
my: 8,
const baseStyle = {
color: "primary.400",
thickness: "4px",
speed: "1.5s",
my: 8,
};
const variants = {
basic: { thickness: "4px", speed: "1.5s" },
};
const sizes = {
xs: {
"--spinner-size": "0.75rem",
},
variants: {
basic: { thickness: "4px", speed: "1.5s" },
sm: {
"--spinner-size": "1rem",
},
md: {
"--spinner-size": "1.5rem",
},
lg: {
"--spinner-size": "2rem",
},
xl: {
"--spinner-size": "3rem",
},
};
export default Spinner;
const defaultProps = {
size: "md",
};
export default {
baseStyle,
sizes,
defaultProps,
variants,
};

Wyświetl plik

@ -0,0 +1,46 @@
import { mode } from "@chakra-ui/theme-tools";
const baseStyle = (props) => {
const bg = mode("gray.700", "gray.300")(props);
return {
"--tooltip-bg": `colors.${bg}`,
px: "8px",
py: "2px",
bg: "var(--tooltip-bg)",
"--popper-arrow-bg": "var(--tooltip-bg)",
color: mode("whiteAlpha.900", "gray.900")(props),
borderRadius: "sm",
fontWeight: "medium",
fontSize: "sm",
boxShadow: "md",
maxW: "320px",
zIndex: "tooltip",
};
};
const variantOnboarding = (props) => {
const bg = mode("secondary.700", "secondary.300")(props);
return {
"--tooltip-bg": `colors.${bg}`,
px: "8px",
py: "2px",
bg: "var(--tooltip-bg)",
"--popper-arrow-bg": "var(--tooltip-bg)",
color: mode("whiteAlpha.900", "gray.900")(props),
borderRadius: "md",
fontWeight: "medium",
fontSize: "sm",
boxShadow: "md",
maxW: "320px",
zIndex: "tooltip",
};
};
const variants = {
onboarding: variantOnboarding,
};
export default {
baseStyle,
variants,
};

Wyświetl plik

@ -8,6 +8,8 @@ import NumberInput from "./NumberInput";
import Badge from "./Badge";
import Checkbox from "./Checkbox";
import Table from "./Table";
import Tooltip from "./Tooltip";
import Spinner from "./Spinner";
import { createBreakpoints } from "@chakra-ui/theme-tools";
const breakpointsCustom = createBreakpoints({
@ -53,6 +55,8 @@ const theme = extendTheme({
Badge,
Checkbox,
Table,
Spinner,
Tooltip,
},
fonts: {

Wyświetl plik

@ -15,6 +15,7 @@ import {
PopoverCloseButton,
useBreakpointValue,
Spacer,
ButtonGroup,
} from "@chakra-ui/react";
import {
HamburgerIcon,
@ -26,6 +27,7 @@ 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";
const AppNavbar = () => {
const ui = useContext(UIContext);
@ -95,6 +97,14 @@ const AppNavbar = () => {
<Flex width="100%" px={2}>
<Spacer />
<Flex placeSelf="flex-end">
<ButtonGroup spacing={4}>
{/* <RouteButton variant="link" href="/docs">
Docs
</RouteButton> */}
<RouteButton variant="link" href="/welcome">
Learn how to
</RouteButton>
</ButtonGroup>
<SupportPopover />
<AccountIconButton
colorScheme="primary"

Wyświetl plik

@ -204,7 +204,6 @@ const EntriesNavigation = () => {
};
const dropFilterArrayItem = (idx) => {
console.log("dropFilterArrayItem", idx, filterState);
const oldArray = [...filterState];
const newArray = oldArray.filter(function (ele) {
return ele != oldArray[idx];
@ -242,7 +241,6 @@ const EntriesNavigation = () => {
};
const handleFilterStateCallback = (props) => {
console.log("handleFilterStateCallback", props);
const currentFilterState = [...filterState];
currentFilterState.push({ ...props });
@ -529,6 +527,7 @@ const EntriesNavigation = () => {
?.sort((a, b) => b.timestamp - a.timestamp) // TODO(Andrey) improve that for bi chunks of data sorting can take time
.map((entry, idx) => (
<StreamEntry
showOnboardingTooltips={false}
key={`entry-list-${idx}`}
entry={entry}
disableDelete={!canDelete}

Wyświetl plik

@ -13,6 +13,7 @@ import { HamburgerIcon } from "@chakra-ui/icons";
import useModals from "../core/hooks/useModals";
import UIContext from "../core/providers/UIProvider/context";
import ChakraAccountIconButton from "./AccountIconButton";
import RouteButton from "./RouteButton";
const LandingNavbar = () => {
const ui = useContext(UIContext);
@ -52,6 +53,17 @@ const LandingNavbar = () => {
spacing={4}
pr={16}
>
{ui.isLoggedIn && (
<ButtonGroup spacing={4}>
{/* <RouteButton variant="link" href="/docs">
Docs
</RouteButton> */}
<RouteButton variant="link" href="/welcome">
Learn how to
</RouteButton>
</ButtonGroup>
)}
{ui.isLoggedIn && (
<RouterLink href="/stream" passHref>
<Button

Wyświetl plik

@ -0,0 +1,177 @@
import React, { useState, useEffect } from "react";
import { useSubscriptions } from "../core/hooks";
import {
Input,
Stack,
Text,
HStack,
useRadioGroup,
FormControl,
FormErrorMessage,
ModalBody,
ModalCloseButton,
ModalHeader,
Button,
ModalFooter,
Spinner,
IconButton,
} from "@chakra-ui/react";
import RadioCard from "./RadioCard";
import { useForm } from "react-hook-form";
import { GithubPicker } from "react-color";
import { BiRefresh } from "react-icons/bi";
import { makeColor } from "../core/utils/makeColor";
const NewSubscription = ({ isFreeOption, onClose }) => {
const [color, setColor] = useState(makeColor());
const { typesCache, createSubscription } = useSubscriptions();
const { handleSubmit, errors, register } = useForm({});
const [radioState, setRadioState] = useState("ethereum_blockchain");
let { getRootProps, getRadioProps } = useRadioGroup({
name: "type",
defaultValue: radioState,
onChange: setRadioState,
});
const group = getRootProps();
useEffect(() => {
if (createSubscription.isSuccess) {
onClose();
}
}, [createSubscription.isSuccess, onClose]);
if (typesCache.isLoading) return <Spinner />;
const createSubscriptionWrap = (props) => {
createSubscription.mutate({
...props,
color: color,
type: isFreeOption ? "free" : radioState,
});
};
const handleChangeColorComplete = (color) => {
setColor(color.hex);
};
return (
<form onSubmit={handleSubmit(createSubscriptionWrap)}>
<ModalHeader>Subscribe to a new address</ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl isInvalid={errors.label}>
<Input
my={2}
type="text"
autoComplete="off"
placeholder="Enter label"
name="label"
ref={register({ required: "label is required!" })}
></Input>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.label && errors.label.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.address}>
<Input
type="text"
autoComplete="off"
my={2}
placeholder="Enter address"
name="address"
ref={register({ required: "address is required!" })}
></Input>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.address && errors.address.message}
</FormErrorMessage>
</FormControl>
<Stack my={16} direction="column">
<Text fontWeight="600">
{isFreeOption
? `Free subscription is only availible Ethereum blockchain source`
: `On which source?`}
</Text>
<FormControl isInvalid={errors.subscription_type}>
<HStack {...group} alignItems="stretch">
{typesCache.data.subscriptions.map((type) => {
const radio = getRadioProps({
value: type.id,
isDisabled:
!type.active ||
(isFreeOption &&
type.subscription_type !== "ethereum_blockchain"),
});
return (
<RadioCard key={`subscription_type_${type.id}`} {...radio}>
{type.name}
</RadioCard>
);
})}
</HStack>
<Input
type="hidden"
placeholder="subscription_type"
name="subscription_type"
ref={register({ required: "select type" })}
value={radioState}
onChange={() => null}
></Input>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.subscription_type && errors.subscription_type.message}
</FormErrorMessage>
</FormControl>
</Stack>
<FormControl isInvalid={errors.color}>
<Stack direction="row" pb={2}>
<Text fontWeight="600" alignSelf="center">
Label color
</Text>{" "}
<IconButton
size="md"
// colorScheme="primary"
color={"white.100"}
_hover={{ bgColor: { color } }}
bgColor={color}
variant="outline"
onClick={() => setColor(makeColor())}
icon={<BiRefresh />}
/>
<Input
type="input"
placeholder="color"
name="color"
ref={register({ required: "color is required!" })}
value={color}
onChange={() => null}
w="200px"
></Input>
</Stack>
<GithubPicker
// color={this.state.background}
onChangeComplete={handleChangeColorComplete}
/>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.color && errors.color.message}
</FormErrorMessage>
</FormControl>
</ModalBody>
<ModalFooter>
<Button
type="submit"
colorScheme="suggested"
isLoading={createSubscription.isLoading}
>
Confirm
</Button>
<Button colorScheme="gray" onClick={onClose}>
Cancel
</Button>
</ModalFooter>
</form>
);
};
export default NewSubscription;

Wyświetl plik

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react";
import React, { useState, useEffect, useCallback } from "react";
import { useSubscriptions } from "../core/hooks";
import {
Input,
@ -8,23 +8,24 @@ import {
useRadioGroup,
FormControl,
FormErrorMessage,
ModalBody,
ModalCloseButton,
ModalHeader,
Button,
ModalFooter,
Spinner,
IconButton,
ButtonGroup,
Spacer,
Flex,
} from "@chakra-ui/react";
import RadioCard from "./RadioCard";
import { useForm } from "react-hook-form";
import { GithubPicker } from "react-color";
// import { useForm } from "react-hook-form";
import { CirclePicker } from "react-color";
import { BiRefresh } from "react-icons/bi";
import { makeColor } from "../core/utils/makeColor";
const NewSubscription = ({ isFreeOption, onClose }) => {
import { useForm } from "react-hook-form";
const _NewSubscription = ({ isFreeOption, onClose, setIsLoading }) => {
const [color, setColor] = useState(makeColor());
const { typesCache, createSubscription } = useSubscriptions();
const { handleSubmit, errors, register } = useForm({});
const { typesCache, createSubscription } = useSubscriptions();
// const { handleSubmit, errors, register } = useForm({});
const [radioState, setRadioState] = useState("ethereum_blockchain");
let { getRootProps, getRadioProps } = useRadioGroup({
name: "type",
@ -34,96 +35,105 @@ const NewSubscription = ({ isFreeOption, onClose }) => {
const group = getRootProps();
useEffect(() => {
if (setIsLoading) {
setIsLoading(createSubscription.isLoading);
}
}, [createSubscription.isLoading, setIsLoading]);
useEffect(() => {
if (createSubscription.isSuccess) {
onClose();
}
}, [createSubscription.isSuccess, onClose]);
if (typesCache.isLoading) return <Spinner />;
const createSubscriptionWrapper = useCallback(
(props) => {
createSubscription.mutate({
...props,
color: color,
type: isFreeOption ? 0 : radioState,
});
},
[createSubscription, isFreeOption, color, radioState]
);
const createSubscriptionWrap = (props) => {
createSubscription.mutate({
...props,
color: color,
type: isFreeOption ? "free" : radioState,
});
};
if (typesCache.isLoading) return <Spinner />;
const handleChangeColorComplete = (color) => {
setColor(color.hex);
};
return (
<form onSubmit={handleSubmit(createSubscriptionWrap)}>
<ModalHeader>Subscribe to a new address</ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl isInvalid={errors.label}>
<Input
my={2}
type="text"
autoComplete="off"
placeholder="Enter label"
name="label"
ref={register({ required: "label is required!" })}
></Input>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.label && errors.label.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors.address}>
<Input
type="text"
autoComplete="off"
my={2}
placeholder="Enter address"
name="address"
ref={register({ required: "address is required!" })}
></Input>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.address && errors.address.message}
</FormErrorMessage>
</FormControl>
<Stack my={16} direction="column">
<Text fontWeight="600">
{isFreeOption
? `Free subscription is only availible Ethereum blockchain source`
: `On which source?`}
</Text>
if (!errors) return "";
<FormControl isInvalid={errors.subscription_type}>
<HStack {...group} alignItems="stretch">
{typesCache.data.subscriptions.map((type) => {
const radio = getRadioProps({
value: type.id,
isDisabled:
!type.active ||
(isFreeOption &&
type.subscription_type !== "ethereum_blockchain"),
});
return (
<RadioCard key={`subscription_type_${type.id}`} {...radio}>
{type.name}
</RadioCard>
);
})}
</HStack>
<Input
type="hidden"
placeholder="subscription_type"
name="subscription_type"
ref={register({ required: "select type" })}
value={radioState}
onChange={() => null}
></Input>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.subscription_type && errors.subscription_type.message}
</FormErrorMessage>
</FormControl>
</Stack>
<FormControl isInvalid={errors.color}>
<Stack direction="row" pb={2}>
return (
<form onSubmit={handleSubmit(createSubscriptionWrapper)}>
<FormControl isInvalid={errors?.label}>
<Input
my={2}
type="text"
autoComplete="off"
placeholder="Meaningful name of your subscription"
name="label"
ref={register({ required: "label is required!" })}
></Input>
<FormErrorMessage color="unsafe.400" pl="1">
{errors?.label && errors?.label.message}
</FormErrorMessage>
</FormControl>
<FormControl isInvalid={errors?.address}>
<Input
type="text"
autoComplete="off"
my={2}
placeholder="Address to subscribe to"
name="address"
ref={register({ required: "address is required!" })}
></Input>
<FormErrorMessage color="unsafe.400" pl="1">
{errors?.address && errors?.address.message}
</FormErrorMessage>
</FormControl>
<Stack my={4} direction="column">
{/* <Text fontWeight="600">
{isFreeOption
? `Right now you can subscribe only to ethereum blockchain`
: `On which source?`}
</Text> */}
<FormControl isInvalid={errors?.subscription_type}>
<HStack {...group} alignItems="stretch">
{typesCache.data.subscriptions.map((type) => {
const radio = getRadioProps({
value: type.id,
isDisabled:
!type.active ||
(isFreeOption &&
type.subscription_type !== "ethereum_blockchain"),
});
return (
<RadioCard key={`subscription_type_${type.id}`} {...radio}>
{type.name}
</RadioCard>
);
})}
</HStack>
<Input
type="hidden"
placeholder="subscription_type"
name="subscription_type"
ref={register({ required: "select type" })}
value={radioState}
onChange={() => null}
></Input>
<FormErrorMessage color="unsafe.400" pl="1">
{errors?.subscription_type && errors?.subscription_type.message}
</FormErrorMessage>
</FormControl>
</Stack>
<FormControl isInvalid={errors?.color}>
<Flex direction="row" pb={2} flexWrap="wrap">
<Stack pt={2} direction="row" h="min-content">
<Text fontWeight="600" alignSelf="center">
Label color
</Text>{" "}
@ -147,18 +157,21 @@ const NewSubscription = ({ isFreeOption, onClose }) => {
w="200px"
></Input>
</Stack>
<Flex p={2}>
<CirclePicker
onChangeComplete={handleChangeColorComplete}
circleSpacing={1}
circleSize={24}
/>
</Flex>
</Flex>
<GithubPicker
// color={this.state.background}
onChangeComplete={handleChangeColorComplete}
/>
<FormErrorMessage color="unsafe.400" pl="1">
{errors?.color && errors?.color.message}
</FormErrorMessage>
</FormControl>
<FormErrorMessage color="unsafe.400" pl="1">
{errors.color && errors.color.message}
</FormErrorMessage>
</FormControl>
</ModalBody>
<ModalFooter>
<ButtonGroup direction="row" justifyContent="space-evenly">
<Button
type="submit"
colorScheme="suggested"
@ -166,12 +179,13 @@ const NewSubscription = ({ isFreeOption, onClose }) => {
>
Confirm
</Button>
<Spacer />
<Button colorScheme="gray" onClick={onClose}>
Cancel
</Button>
</ModalFooter>
</ButtonGroup>
</form>
);
};
export default NewSubscription;
export default _NewSubscription;

Wyświetl plik

@ -0,0 +1,17 @@
import React from "react";
import { chakra, Button, Link } from "@chakra-ui/react";
import NextLink from "next/link";
const _RouteButton = (props) => {
return (
<NextLink href={props.href} passHref>
<Button as={Link} {...props}>
{props.children}
</Button>
</NextLink>
);
};
const RouteButton = chakra(_RouteButton, "button");
export default RouteButton;

Wyświetl plik

@ -14,6 +14,7 @@ import React from "react";
import { HamburgerIcon, ArrowLeftIcon, ArrowRightIcon } from "@chakra-ui/icons";
import { MdTimeline, MdSettings } from "react-icons/md";
import { ImStatsBars } from "react-icons/im";
import { HiAcademicCap } from "react-icons/hi";
const Sidebar = () => {
const ui = useContext(UIContext);
@ -79,6 +80,13 @@ const Sidebar = () => {
<RouterLink href="/subscriptions">Subscriptions </RouterLink>
</MenuItem>
</Menu>
{ui.isMobileView && (
<Menu iconShape="square">
<MenuItem icon={<HiAcademicCap />}>
<RouterLink href="/welcome">Learn how to</RouterLink>
</MenuItem>
</Menu>
)}
</SidebarContent>
)}
{!ui.isLoggedIn && (

Wyświetl plik

@ -36,17 +36,16 @@ const SteamEntryDetails = () => {
>
<HStack id="EntryHeader" width="100%" m={0}>
<Heading
overflow="hidden"
width={entry?.context_url ? "calc(100% - 28px)" : "100%"}
minH="36px"
style={{ marginLeft: "0" }}
m={0}
p={0}
fontWeight="600"
fontSize="1.5rem"
fontSize="md"
textAlign="left"
>
{entry && entry.tx.hash}
{entry && `Hash: ${entry.tx.hash}`}
</Heading>
</HStack>
</Skeleton>

Wyświetl plik

@ -0,0 +1,68 @@
import React, { useContext } from "react";
import { Box, Button, Progress, ButtonGroup } from "@chakra-ui/react";
import _ from "lodash";
import UIContext from "../core/providers/UIProvider/context";
const StepProgress = ({
numSteps,
currentStep,
colorScheme,
buttonCallback,
buttonTitles,
}) => {
const ui = useContext(UIContext);
return (
<Box w="100%" h="auto" pos="relative">
<ButtonGroup
display="inline-flex"
flexDirection="row"
justifyContent="space-between"
w="100%"
m={0}
p={0}
spacing={0}
>
{_.times(numSteps, (i) => {
const setActive = i === parseInt(currentStep) ? true : false;
return (
<Button
key={`${i}-progress-steps`}
size={ui.isMobileView ? "md" : "sm"}
borderRadius={ui.isMobileView ? "full" : "md"}
// size="sm"
// bgColor={`${colorScheme}.200`}
_active={{ bgColor: `${colorScheme}.1200` }}
zIndex={1}
m={0}
colorScheme={colorScheme}
isActive={setActive}
onClick={() => buttonCallback(i)}
>
{ui.isMobileView && i + 1}
{!ui.isMobileView && buttonTitles[i]}
</Button>
);
})}
</ButtonGroup>
<Progress
position="absolute"
top="50%"
transform="translateY(-50%)"
h={2}
w="full"
// hasStripe
// isAnimated
max={numSteps - 1}
min={0}
value={currentStep}
/>
{/* <Flex
h="1rem"
flexGrow={1}
flexBasis="10px"
backgroundColor={`${colorScheme}.300`}
/> */}
</Box>
);
};
export default StepProgress;

Wyświetl plik

@ -11,6 +11,7 @@ import {
useMediaQuery,
Spacer,
Spinner,
chakra,
} from "@chakra-ui/react";
import moment from "moment";
import { ArrowRightIcon } from "@chakra-ui/icons";
@ -18,7 +19,7 @@ import UIContext from "../core/providers/UIProvider/context";
import { useToast } from "../core/hooks";
import { useSubscriptions } from "../core/hooks";
const StreamEntry = ({ entry }) => {
const StreamEntry = ({ entry, showOnboardingTooltips, className }) => {
const { subscriptionsCache } = useSubscriptions();
const ui = useContext(UIContext);
const [copyString, setCopyString] = useState(false);
@ -49,6 +50,7 @@ const StreamEntry = ({ entry }) => {
return (
<Flex
className={className}
p={0}
m={1}
mr={2}
@ -89,35 +91,44 @@ const StreamEntry = ({ entry }) => {
overflowX="hidden"
overflowY="visible"
>
<Stack
className="title"
direction="row"
w="100%"
h="1.6rem"
minH="1.6rem"
textAlign="center"
spacing={0}
alignItems="center"
bgColor="gray.300"
<Tooltip
hasArrow
isOpen={showOnboardingTooltips}
// shouldWrapChildren
label="Top of card describes type of event. Ethereum blockchain in this case. It as unique hash shown here"
variant="onboarding"
placement="top"
>
<Image
boxSize="16px"
src={
"https://upload.wikimedia.org/wikipedia/commons/0/05/Ethereum_logo_2014.svg"
}
/>
<Heading px={1} size="xs">
Hash
</Heading>
<Spacer />
<Text
isTruncated
onClick={() => setCopyString(entry.hash)}
pr={12}
<Stack
className="title"
direction="row"
w="100%"
h="1.6rem"
minH="1.6rem"
textAlign="center"
spacing={0}
alignItems="center"
bgColor="gray.300"
>
{entry.hash}
</Text>
</Stack>
<Image
boxSize="16px"
src={
"https://upload.wikimedia.org/wikipedia/commons/0/05/Ethereum_logo_2014.svg"
}
/>
<Heading px={1} size="xs">
Hash
</Heading>
<Spacer />
<Text
isTruncated
onClick={() => setCopyString(entry.hash)}
pr={12}
>
{entry.hash}
</Text>
</Stack>
</Tooltip>
<Stack
className="CardAddressesRow"
direction={showFullView ? "row" : "column"}
@ -143,16 +154,25 @@ const StreamEntry = ({ entry }) => {
placeContent="center"
spacing={0}
>
<Text
bgColor="gray.600"
h="100%"
fontSize="sm"
py="2px"
px={2}
w={showFullView ? null : "120px"}
<Tooltip
hasArrow
isOpen={showOnboardingTooltips && !ui.isMobileView}
label="From and to addresses, clicking names will copy address to clipboard!"
variant="onboarding"
placement={ui.isMobileView ? "bottom" : "left"}
maxW="150px"
>
From:
</Text>
<Text
bgColor="gray.600"
h="100%"
fontSize="sm"
py="2px"
px={2}
w={showFullView ? null : "120px"}
>
From:
</Text>
</Tooltip>
<Tooltip label={entry.from_address} aria-label="From:">
<Text
mx={0}
@ -168,6 +188,7 @@ const StreamEntry = ({ entry }) => {
</Text>
</Tooltip>
</Stack>
<Stack
overflow="hidden"
textOverflow="ellipsis"
@ -322,22 +343,33 @@ const StreamEntry = ({ entry }) => {
</Stack>
)}
<Flex>
<IconButton
m={0}
onClick={() => ui.setCurrentTransaction(entry)}
h="full"
// minH="24px"
borderLeftRadius={0}
variant="solid"
px={0}
minW="24px"
colorScheme="secondary"
icon={<ArrowRightIcon w="24px" />}
/>
<Tooltip
hasArrow
isOpen={showOnboardingTooltips}
placement={ui.isMobileView ? "bottom" : "right"}
label="Clicking side arrow will bring up detailed view"
variant="onboarding"
maxW="150px"
>
<IconButton
m={0}
onClick={() => ui.setCurrentTransaction(entry)}
h="full"
// minH="24px"
borderLeftRadius={0}
variant="solid"
px={0}
minW="24px"
colorScheme="secondary"
icon={<ArrowRightIcon w="24px" />}
/>
</Tooltip>
</Flex>
</Stack>
</Flex>
);
};
export default StreamEntry;
const StreamChakraEntry = chakra(StreamEntry);
export default StreamChakraEntry;

Wyświetl plik

@ -1,5 +1,5 @@
import React from "react";
import { Skeleton, IconButton } from "@chakra-ui/react";
import { Skeleton, IconButton, Container } from "@chakra-ui/react";
import {
Table,
Th,
@ -12,6 +12,7 @@ import {
EditableInput,
Image,
EditablePreview,
Button,
} from "@chakra-ui/react";
import { DeleteIcon } from "@chakra-ui/icons";
import moment from "moment";
@ -20,7 +21,7 @@ import { useSubscriptions } from "../core/hooks";
import ConfirmationRequest from "./ConfirmationRequest";
import ColorSelector from "./ColorSelector";
const SubscriptionsList = () => {
const SubscriptionsList = ({ emptyCTA }) => {
const { subscriptionsCache, updateSubscription, deleteSubscription } =
useSubscriptions();
@ -31,7 +32,10 @@ const SubscriptionsList = () => {
updateSubscription.mutate(data);
};
if (subscriptionsCache.data) {
if (
subscriptionsCache.data &&
subscriptionsCache.data.subscriptions.length > 0
) {
return (
<Table
borderColor="gray.200"
@ -138,6 +142,16 @@ const SubscriptionsList = () => {
</Tbody>
</Table>
);
} else if (
subscriptionsCache.data &&
subscriptionsCache.data.subscriptions.length === 0
) {
return (
<Container>
{` You don't have any subscriptions at the moment.`}
{emptyCTA && <Button variant="suggested">Create one</Button>}
</Container>
);
} else if (subscriptionsCache.isLoading) {
return <Skeleton />;
} else {

Wyświetl plik

@ -16,7 +16,6 @@ const toEth = (wei) => {
const TxABI = (props) => {
const byteCode = props.byteCode;
const abi = props.abi;
console.log(abi.functions);
return (
<VStack spacing={3}>
<br />
@ -58,25 +57,29 @@ const TxInfo = (props) => {
return (
<Box boxShadow="xs" p="6" rounded="md">
<StatGroup>
<Stat>
<Stat px={2}>
<StatLabel>Value</StatLabel>
<StatNumber>{toEth(transaction.tx.value)} eth</StatNumber>
<StatNumber fontSize="md">
{toEth(transaction.tx.value)} eth
</StatNumber>
<StatHelpText>amount of ETH to transfer</StatHelpText>
</Stat>
<Stat>
<StatLabel>Gas limit</StatLabel>
<StatNumber>{transaction.tx.gas}</StatNumber>
<StatNumber fontSize="md">{transaction.tx.gas}</StatNumber>
<StatHelpText>Maximum amount of gas </StatHelpText>
<StatHelpText>provided for the transaction</StatHelpText>
</Stat>
<Stat>
<StatLabel>Gas price</StatLabel>
<StatNumber>{toEth(transaction.tx.gasPrice)} eth</StatNumber>
<StatNumber fontSize="md">
{toEth(transaction.tx.gasPrice)} eth
</StatNumber>
<StatHelpText>the fee the sender pays per unit of gas</StatHelpText>
</Stat>
</StatGroup>
<Table variant="simple">
<Table variant="simple" size="sm">
<Tbody>
{Object.keys(transaction.tx)
.filter(dont_display)
@ -85,7 +88,7 @@ const TxInfo = (props) => {
transaction.tx[key] != undefined && (
<Tr key={key}>
<Td>{key}</Td>
<Td>{transaction.tx[key]}</Td>
<Td wordBreak="break-all">{transaction.tx[key]}</Td>
</Tr>
)
)}

Wyświetl plik

@ -1,9 +1,11 @@
import { useEffect } from "react";
import { useContext } from "react";
import { useMutation } from "react-query";
import { AuthService } from "../services";
import { useUser, useToast, useInviteAccept, useRouter, useAnalytics } from ".";
import UIContext from "../providers/UIProvider/context";
const useSignUp = (source) => {
const ui = useContext(UIContext);
const router = useRouter();
const { getUser } = useUser();
const toast = useToast();
@ -30,28 +32,16 @@ const useSignUp = (source) => {
{ full_url: router.nextRouter.asPath, code: source }
);
}
getUser();
ui.setisOnboardingComplete(false);
ui.setOnboardingState({ welcome: 0, subscriptions: 0, stream: 0 });
router.push("/welcome", undefined, { shallow: false });
},
onError: (error) => {
toast(error, "error");
},
});
useEffect(() => {
if (!data) {
return;
}
getUser();
const requested_pricing = window.sessionStorage.getItem(
"requested_pricing_plan"
);
const redirectURL = requested_pricing ? "/subscriptions" : "/stream";
router.push(redirectURL);
window.sessionStorage.clear("requested_pricing");
}, [data, getUser, router]);
return {
signUp,
isLoading,

Wyświetl plik

@ -5,6 +5,7 @@ export const MIXPANEL_PROPS = {
PRICING_PLAN: "Pricing Plan",
TOGGLE_LEFT_SIDEBAR: "Toggle left sidebar",
BUTTON_NAME: "Button",
USER_SPECIALITY: "user speciality",
};
export const MIXPANEL_EVENTS = {

Wyświetl plik

@ -17,15 +17,7 @@ const UIProvider = ({ children }) => {
xl: false,
"2xl": false,
});
const currentBreakpoint = useBreakpointValue({
base: 0,
sm: 1,
md: 2,
lg: 3,
xl: 4,
"2xl": 5,
});
// const isMobileView = true;
const { modal, toggleModal } = useContext(ModalContext);
const [searchTerm, setSearchTerm] = useQuery("q", "", true, false);
@ -44,7 +36,8 @@ const UIProvider = ({ children }) => {
router.nextRouter.asPath.includes("/stream") ||
router.nextRouter.asPath.includes("/account") ||
router.nextRouter.asPath.includes("/subscriptions") ||
router.nextRouter.asPath.includes("/analytics")
router.nextRouter.asPath.includes("/analytics") ||
router.nextRouter.asPath.includes("/welcome")
);
useEffect(() => {
@ -81,7 +74,8 @@ const UIProvider = ({ children }) => {
router.nextRouter.asPath.includes("/stream") ||
router.nextRouter.asPath.includes("/account") ||
router.nextRouter.asPath.includes("/subscriptions") ||
router.nextRouter.asPath.includes("/analytics")
router.nextRouter.asPath.includes("/analytics") ||
router.nextRouter.asPath.includes("/welcome")
);
}, [router.nextRouter.asPath, user]);
@ -119,7 +113,7 @@ const UIProvider = ({ children }) => {
//Sidebar is visible at at breakpoint value less then 2
//Sidebar is visible always in appView
useEffect(() => {
if (currentBreakpoint < 2) {
if (isMobileView) {
setSidebarVisible(true);
setSidebarCollapsed(false);
} else {
@ -130,7 +124,7 @@ const UIProvider = ({ children }) => {
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentBreakpoint, isAppView]);
}, [isMobileView, isAppView]);
// *********** Entries layout states **********************
@ -159,6 +153,81 @@ const UIProvider = ({ children }) => {
);
}, [isEntryDetailView, isMobileView]);
// *********** Onboarding state **********************
const onboardingSteps = [
{
step: "welcome",
description: "Basics of how Moonstream works",
},
{
step: "subscriptions",
description: "Learn how to subscribe to blockchain events",
},
{
step: "stream",
description: "Learn how to use your Moonstream to analyze blah blah blah",
},
];
const [onboardingState, setOnboardingState] = useStorage(
window.localStorage,
"onboardingState",
{
welcome: 0,
subscriptions: 0,
stream: 0,
}
);
const [onboardingStep, setOnboardingStep] = useState(() => {
const step = onboardingSteps.findIndex(
(event) => onboardingState[event.step] === 0
);
if (step === -1 && isOnboardingComplete) return onboardingSteps.length - 1;
else if (step === -1 && !isOnboardingComplete) return 0;
else return step;
});
const [isOnboardingComplete, setisOnboardingComplete] = useStorage(
window.localStorage,
"isOnboardingComplete",
isLoggedIn ? true : false
);
useEffect(() => {
if (isLoggedIn && !isOnboardingComplete) {
router.replace("/welcome");
}
// eslint-disable-next-line
}, [isLoggedIn, isOnboardingComplete]);
useEffect(() => {
if (
onboardingSteps.findIndex(
(event) => onboardingState[event.step] === 0
) === -1
) {
setisOnboardingComplete(true);
}
//eslint-disable-next-line
}, [onboardingState]);
useEffect(() => {
if (router.nextRouter.pathname === "/welcome") {
const newOnboardingState = {
...onboardingState,
[`${onboardingSteps[onboardingStep].step}`]:
onboardingState[onboardingSteps[onboardingStep].step] + 1,
};
setOnboardingState(newOnboardingState);
}
// eslint-disable-next-line
}, [onboardingStep, router.nextRouter.pathname]);
// const ONBOARDING_STEP_NUM = steps.length;
// ********************************************************
return (
@ -188,6 +257,12 @@ const UIProvider = ({ children }) => {
currentTransaction,
setCurrentTransaction,
isEntryDetailView,
onboardingStep,
setOnboardingStep,
isOnboardingComplete,
setisOnboardingComplete,
onboardingSteps,
setOnboardingState,
}}
>
{children}