kopia lustrzana https://github.com/bugout-dev/moonstream
Merge branch 'ABI-Defined-Dashboars' into add-crawler-of-data
commit
77371d891c
|
@ -0,0 +1,198 @@
|
|||
import argparse
|
||||
import json
|
||||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
from moonstreamdb.db import yield_db_session_ctx
|
||||
from sqlalchemy.orm.session import Session
|
||||
from web3 import Web3
|
||||
|
||||
from ..ethereum import connect
|
||||
from .deployment_crawler import ContractDeploymentCrawler, MoonstreamDataStore
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def run_crawler_asc(
|
||||
w3: Web3,
|
||||
session: Session,
|
||||
from_block: Optional[int],
|
||||
to_block: Optional[int],
|
||||
synchronize: bool,
|
||||
batch_size: int,
|
||||
respect_state: bool,
|
||||
sleep_time: int,
|
||||
):
|
||||
"""
|
||||
Runs crawler in ascending order
|
||||
"""
|
||||
moonstream_data_store = MoonstreamDataStore(session)
|
||||
contract_deployment_crawler = ContractDeploymentCrawler(w3, moonstream_data_store)
|
||||
|
||||
if respect_state:
|
||||
from_block = moonstream_data_store.get_last_labeled_block_number() + 1
|
||||
logger.info(f"Respecting state, starting from block {from_block}")
|
||||
|
||||
if from_block is None:
|
||||
from_block = moonstream_data_store.get_first_block_number()
|
||||
logger.info(f"Starting block set to : {from_block}")
|
||||
|
||||
if to_block is None:
|
||||
to_block = moonstream_data_store.get_last_block_number()
|
||||
logger.info(f"Ending block set to : {to_block}")
|
||||
|
||||
assert (
|
||||
from_block <= to_block
|
||||
), "from_block must be less than or equal to to_block in asc order, used --order desc"
|
||||
|
||||
logger.info(f"Starting crawling from block {from_block} to block {to_block}")
|
||||
contract_deployment_crawler.crawl(
|
||||
from_block=from_block,
|
||||
to_block=to_block,
|
||||
batch_size=batch_size,
|
||||
)
|
||||
if synchronize:
|
||||
last_crawled_block = to_block
|
||||
while True:
|
||||
contract_deployment_crawler.crawl(
|
||||
from_block=last_crawled_block + 1,
|
||||
to_block=None, # to_block will be set to last_crawled_block
|
||||
batch_size=batch_size,
|
||||
)
|
||||
time.sleep(sleep_time)
|
||||
|
||||
|
||||
def run_crawler_desc(
|
||||
w3: Web3,
|
||||
session: Session,
|
||||
from_block: Optional[int],
|
||||
to_block: Optional[int],
|
||||
synchronize: bool,
|
||||
batch_size: int,
|
||||
respect_state: bool,
|
||||
sleep_time: int,
|
||||
):
|
||||
"""
|
||||
Runs crawler in descending order
|
||||
"""
|
||||
moonstream_data_store = MoonstreamDataStore(session)
|
||||
contract_deployment_crawler = ContractDeploymentCrawler(w3, moonstream_data_store)
|
||||
|
||||
if respect_state:
|
||||
to_block = moonstream_data_store.get_first_block_number() - 1
|
||||
logger.info(f"Respecting state, ending at block {to_block}")
|
||||
|
||||
if from_block is None:
|
||||
from_block = moonstream_data_store.get_last_block_number()
|
||||
logger.info(f"Starting block set to : {from_block}")
|
||||
|
||||
if to_block is None:
|
||||
to_block = moonstream_data_store.get_first_block_number()
|
||||
logger.info(f"Ending block set to : {to_block}")
|
||||
|
||||
assert (
|
||||
from_block >= to_block
|
||||
), "from_block must be greater than or equal to to_block in desc order, used --order asc"
|
||||
|
||||
logger.info(f"Starting crawling from block {from_block} to block {to_block}")
|
||||
contract_deployment_crawler.crawl(
|
||||
from_block=from_block,
|
||||
to_block=to_block,
|
||||
batch_size=batch_size,
|
||||
)
|
||||
if synchronize:
|
||||
last_crawled_block = to_block
|
||||
while True:
|
||||
to_block = moonstream_data_store.get_first_block_number()
|
||||
contract_deployment_crawler.crawl(
|
||||
from_block=last_crawled_block - 1,
|
||||
to_block=to_block,
|
||||
batch_size=batch_size,
|
||||
)
|
||||
time.sleep(sleep_time)
|
||||
|
||||
|
||||
def handle_parser(args: argparse.Namespace):
|
||||
with yield_db_session_ctx() as session:
|
||||
w3 = connect()
|
||||
if args.order == "asc":
|
||||
run_crawler_asc(
|
||||
w3=w3,
|
||||
session=session,
|
||||
from_block=args.start,
|
||||
to_block=args.to,
|
||||
synchronize=args.synchronize,
|
||||
batch_size=args.batch,
|
||||
respect_state=args.respect_state,
|
||||
sleep_time=args.sleep,
|
||||
)
|
||||
elif args.order == "desc":
|
||||
run_crawler_desc(
|
||||
w3=w3,
|
||||
session=session,
|
||||
from_block=args.start,
|
||||
to_block=args.to,
|
||||
synchronize=args.synchronize,
|
||||
batch_size=args.batch,
|
||||
respect_state=args.respect_state,
|
||||
sleep_time=args.sleep,
|
||||
)
|
||||
|
||||
|
||||
def generate_parser():
|
||||
"""
|
||||
--start, -s: block to start crawling from, default: minimum block from database
|
||||
--to, -t: block to stop crawling at, default: maximum block from database
|
||||
--order: order to crawl : (desc, asc) default: asc
|
||||
--synchronize: Continious crawling, default: False
|
||||
--batch, -b : batch size, default: 10
|
||||
--respect-state: If set to True:\n If order is asc: start=last_labeled_block+1\n If order is desc: start=first_labeled_block-1
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description="Moonstream Deployment Crawler")
|
||||
parser.add_argument(
|
||||
"--start", "-s", type=int, default=None, help="block to start crawling from"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--to", "-t", type=int, default=None, help="block to stop crawling at"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--order",
|
||||
"-o",
|
||||
type=str,
|
||||
default="asc",
|
||||
choices=["asc", "desc"],
|
||||
help="order to crawl : (desc, asc)",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--synchronize", action="store_true", default=False, help="Continious crawling"
|
||||
)
|
||||
parser.add_argument("--batch", "-b", type=int, default=10, help="batch size")
|
||||
parser.add_argument(
|
||||
"--respect-state",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="If set to True:\n If order is asc: start=last_labeled_block+1\n If order is desc: start=first_labeled_block-1",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--sleep",
|
||||
type=int,
|
||||
default=3 * 60,
|
||||
help="time to sleep synzhronize mode waiting for new block crawled to db",
|
||||
)
|
||||
parser.set_defaults(func=handle_parser)
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
parser = generate_parser()
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,265 @@
|
|||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Iterator, List, Optional, Tuple, cast
|
||||
|
||||
from hexbytes import HexBytes
|
||||
from moonstreamdb.models import EthereumBlock, EthereumLabel, EthereumTransaction
|
||||
from sqlalchemy.orm import Query, Session
|
||||
from web3 import Web3
|
||||
from web3.types import TxReceipt
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ContractDeployment:
|
||||
address: str
|
||||
block_number: int
|
||||
transaction_hash: str
|
||||
deployer_address: str
|
||||
block_timestamp: int
|
||||
gas_used: int
|
||||
gas_price: int
|
||||
transaction_fee: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class RawDeploymentTx:
|
||||
transaction_hash: str
|
||||
gas_price: int
|
||||
timestamp: int
|
||||
|
||||
|
||||
class MoonstreamDataStore:
|
||||
def __init__(self, db_session: Session) -> None:
|
||||
self.db_session = db_session
|
||||
self.label = "contract_deployment"
|
||||
|
||||
def get_last_labeled_block_number(
|
||||
self,
|
||||
) -> int:
|
||||
"""
|
||||
Returns the last block number that has been labeled.
|
||||
"""
|
||||
|
||||
last_block = (
|
||||
self.db_session.query(EthereumLabel)
|
||||
.filter(EthereumLabel.label == self.label)
|
||||
.order_by(EthereumLabel.block_number.desc())
|
||||
.first()
|
||||
)
|
||||
if last_block is None:
|
||||
return 0
|
||||
else:
|
||||
return last_block.block_number
|
||||
|
||||
def get_first_labeled_block_number(self) -> int:
|
||||
"""
|
||||
Returns the first block number that has been labeled.
|
||||
"""
|
||||
first_block = (
|
||||
self.db_session.query(EthereumLabel)
|
||||
.filter(EthereumLabel.label == self.label)
|
||||
.order_by(EthereumLabel.block_number)
|
||||
.first()
|
||||
)
|
||||
if first_block is None:
|
||||
return 0
|
||||
return first_block.block_number
|
||||
|
||||
def get_last_block_number(self) -> int:
|
||||
"""
|
||||
Returns the last block number that has been processed.
|
||||
"""
|
||||
last_block = (
|
||||
self.db_session.query(EthereumBlock)
|
||||
.order_by(EthereumBlock.block_number.desc())
|
||||
.first()
|
||||
)
|
||||
if last_block is None:
|
||||
return 0
|
||||
return last_block.block_number
|
||||
|
||||
def get_first_block_number(self) -> int:
|
||||
"""
|
||||
Returns the first block number that has been processed.
|
||||
"""
|
||||
first_block = (
|
||||
self.db_session.query(EthereumBlock)
|
||||
.order_by(EthereumBlock.block_number.asc())
|
||||
.first()
|
||||
)
|
||||
if first_block is None:
|
||||
return 0
|
||||
return first_block.block_number
|
||||
|
||||
def get_raw_contract_deployment_transactions(
|
||||
self, from_block: int, to_block: int
|
||||
) -> List[RawDeploymentTx]:
|
||||
"""
|
||||
Returns a list of raw contract deployment transactions.
|
||||
"""
|
||||
result = (
|
||||
self.db_session.query(
|
||||
EthereumTransaction.hash,
|
||||
EthereumTransaction.gas_price,
|
||||
EthereumBlock.timestamp,
|
||||
)
|
||||
.join(
|
||||
EthereumBlock,
|
||||
EthereumTransaction.block_number == EthereumBlock.block_number,
|
||||
)
|
||||
.filter(EthereumBlock.block_number >= from_block)
|
||||
.filter(EthereumBlock.block_number <= to_block)
|
||||
.filter(EthereumTransaction.to_address == None)
|
||||
.all()
|
||||
)
|
||||
return [
|
||||
RawDeploymentTx(
|
||||
transaction_hash=row[0],
|
||||
gas_price=row[1],
|
||||
timestamp=row[2],
|
||||
)
|
||||
for row in result
|
||||
]
|
||||
|
||||
def save_contract_deployment_labels(
|
||||
self, contract_deployment_list: List[ContractDeployment]
|
||||
) -> None:
|
||||
"""
|
||||
Saves a list of contract deployment labels.
|
||||
"""
|
||||
transaction_hashes = [
|
||||
contract_deployment.transaction_hash
|
||||
for contract_deployment in contract_deployment_list
|
||||
]
|
||||
existing_labels = (
|
||||
self.db_session.query(EthereumLabel.transaction_hash)
|
||||
.filter(EthereumLabel.label == self.label)
|
||||
.filter(EthereumLabel.transaction_hash.in_(transaction_hashes))
|
||||
.all()
|
||||
)
|
||||
existing_labels_tx_hashes = [
|
||||
label_tx_hash[0] for label_tx_hash in existing_labels
|
||||
]
|
||||
new_labels = [
|
||||
EthereumLabel(
|
||||
transaction_hash=contract_deployment.transaction_hash,
|
||||
block_number=contract_deployment.block_number,
|
||||
block_timestamp=contract_deployment.block_timestamp,
|
||||
label=self.label,
|
||||
address=contract_deployment.address,
|
||||
label_data={
|
||||
"deployer": contract_deployment.deployer_address,
|
||||
"gasUsed": int(contract_deployment.gas_used),
|
||||
"gasPrice": int(contract_deployment.gas_price),
|
||||
"transactionFee": int(contract_deployment.transaction_fee),
|
||||
},
|
||||
)
|
||||
for contract_deployment in contract_deployment_list
|
||||
if contract_deployment.transaction_hash not in existing_labels_tx_hashes
|
||||
]
|
||||
if not new_labels:
|
||||
return
|
||||
try:
|
||||
logger.info(f"Saving {len(new_labels)} new contract deployment labels.")
|
||||
self.db_session.add_all(new_labels)
|
||||
self.db_session.commit()
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving contract deployment labels: {e}")
|
||||
self.db_session.rollback()
|
||||
|
||||
|
||||
def get_transaction_receipt(web3: Web3, tx_hash: str) -> TxReceipt:
|
||||
"""
|
||||
Returns the transaction receipt for the given transaction hash.
|
||||
"""
|
||||
|
||||
return web3.eth.get_transaction_receipt(cast(HexBytes, tx_hash))
|
||||
|
||||
|
||||
def get_contract_deployment_transactions(
|
||||
web3: Web3,
|
||||
datastore: MoonstreamDataStore,
|
||||
from_block: int,
|
||||
to_block: int,
|
||||
) -> List[ContractDeployment]:
|
||||
"""
|
||||
Returns a list of ContractDeployment objects for all contract deployment transactions in the given block range.
|
||||
"""
|
||||
logger.info(
|
||||
f"Getting contract deployment transactions from {from_block} to {to_block}"
|
||||
)
|
||||
contract_deployment_transactions = []
|
||||
for raw_deployment_tx in datastore.get_raw_contract_deployment_transactions(
|
||||
from_block, to_block
|
||||
):
|
||||
receipt = get_transaction_receipt(web3, raw_deployment_tx.transaction_hash)
|
||||
if receipt is None:
|
||||
continue
|
||||
|
||||
contract_deployment_transactions.append(
|
||||
ContractDeployment(
|
||||
address=cast(str, receipt["contractAddress"]),
|
||||
block_number=receipt["blockNumber"],
|
||||
transaction_hash=receipt["transactionHash"].hex(),
|
||||
deployer_address=receipt["from"],
|
||||
block_timestamp=raw_deployment_tx.timestamp,
|
||||
gas_used=receipt["gasUsed"],
|
||||
gas_price=raw_deployment_tx.gas_price,
|
||||
transaction_fee=receipt["gasUsed"] * raw_deployment_tx.gas_price,
|
||||
)
|
||||
)
|
||||
return contract_deployment_transactions
|
||||
|
||||
|
||||
# Function Fully Generated by copilot, looks correct, lol
|
||||
def get_batch_block_range(
|
||||
from_block: int, to_block: int, batch_size: int
|
||||
) -> Iterator[Tuple[int, int]]:
|
||||
"""
|
||||
Returns a list of block ranges with the given batch size, from_block and to_block inclusive.
|
||||
"""
|
||||
if from_block <= to_block:
|
||||
while from_block <= to_block:
|
||||
yield (from_block, min(from_block + batch_size - 1, to_block))
|
||||
from_block += batch_size
|
||||
else:
|
||||
while to_block <= from_block:
|
||||
yield (from_block, max(from_block - batch_size + 1, to_block))
|
||||
from_block -= batch_size
|
||||
|
||||
|
||||
class ContractDeploymentCrawler:
|
||||
"""
|
||||
Crawls contract deployments from MoonstreamDB transactions with the usage of web3
|
||||
to get transaction recipts
|
||||
"""
|
||||
|
||||
def __init__(self, web3: Web3, datastore: MoonstreamDataStore):
|
||||
self.web3 = web3
|
||||
self.datastore = datastore
|
||||
|
||||
def crawl(
|
||||
self, from_block: Optional[int], to_block: Optional[int], batch_size: int = 200
|
||||
) -> None:
|
||||
"""
|
||||
Crawls contract deployments in batches with the given batch size
|
||||
If from_block is None then the first block from datastore is used as start
|
||||
If to_block is None then the latest block from datastore is used
|
||||
"""
|
||||
if from_block is None:
|
||||
from_block = self.datastore.get_first_block_number()
|
||||
if to_block is None:
|
||||
to_block = self.datastore.get_last_block_number()
|
||||
|
||||
for batch_from_block, batch_to_block in get_batch_block_range(
|
||||
from_block, to_block, batch_size
|
||||
):
|
||||
contract_deployment_transactions = get_contract_deployment_transactions(
|
||||
self.web3, self.datastore, batch_from_block, batch_to_block
|
||||
)
|
||||
self.datastore.save_contract_deployment_labels(
|
||||
contract_deployment_transactions
|
||||
)
|
|
@ -0,0 +1,43 @@
|
|||
from typing import Optional
|
||||
from unittest import TestCase
|
||||
|
||||
from web3.main import Web3
|
||||
|
||||
from .deployment_crawler import get_batch_block_range
|
||||
|
||||
|
||||
class TestDeploymentCrawler(TestCase):
|
||||
def test_get_batch_block_range(self):
|
||||
from_block = 0
|
||||
to_block = 101
|
||||
batch_size = 10
|
||||
result = get_batch_block_range(from_block, to_block, batch_size)
|
||||
|
||||
last_end: Optional[int] = None
|
||||
for batch_start, batch_end in result:
|
||||
if last_end is not None:
|
||||
self.assertEqual(batch_start, last_end + 1)
|
||||
self.assertTrue(batch_start <= batch_end)
|
||||
self.assertTrue(batch_start <= to_block)
|
||||
self.assertTrue(batch_end <= to_block)
|
||||
last_end = batch_end
|
||||
|
||||
self.assertEqual(last_end, to_block)
|
||||
|
||||
def test_get_batch_block_range_with_from_block_gt_to_block(self):
|
||||
from_block = 101
|
||||
to_block = 0
|
||||
batch_size = 10
|
||||
result = get_batch_block_range(from_block, to_block, batch_size)
|
||||
|
||||
last_end: Optional[int] = None
|
||||
for batch_start, batch_end in result:
|
||||
if last_end is not None:
|
||||
self.assertEqual(batch_start, last_end - 1)
|
||||
|
||||
last_end = batch_end
|
||||
self.assertTrue(batch_start >= batch_end)
|
||||
self.assertTrue(batch_start >= to_block)
|
||||
self.assertTrue(batch_end >= to_block)
|
||||
|
||||
self.assertEqual(last_end, to_block)
|
|
@ -12,7 +12,7 @@ chardet==4.0.0
|
|||
charset-normalizer==2.0.4
|
||||
click==8.0.1
|
||||
cytoolz==0.11.0
|
||||
-e git+https://git@github.com/bugout-dev/moonstream.git@0a771ddfbca1254be331149ccf2d162aa09b7bc0#egg=moonstreamdb&subdirectory=db
|
||||
-e git+https://git@github.com/bugout-dev/moonstream.git@67fe019f1086c435dd3b58f1ade2778acc2167c7#egg=moonstreamdb&subdirectory=db
|
||||
eth-abi==2.1.1
|
||||
eth-account==0.5.5
|
||||
eth-hash==0.3.2
|
||||
|
|
|
@ -51,6 +51,7 @@ setup(
|
|||
"identity=mooncrawl.identity:main",
|
||||
"etherscan=mooncrawl.etherscan:main",
|
||||
"nft=mooncrawl.nft.cli:main",
|
||||
"contractcrawler=mooncrawl.contract.cli:main",
|
||||
]
|
||||
},
|
||||
)
|
||||
|
|
|
@ -28,10 +28,13 @@ target_metadata = MoonstreamBase.metadata
|
|||
from moonstreamdb.models import (
|
||||
EthereumBlock,
|
||||
EthereumTransaction,
|
||||
EthereumPendingTransaction,
|
||||
EthereumLabel,
|
||||
ESDEventSignature,
|
||||
PolygonBlock,
|
||||
PolygonTransaction,
|
||||
PolygonLabel,
|
||||
ESDFunctionSignature,
|
||||
ESDEventSignature,
|
||||
OpenSeaCrawlingState,
|
||||
)
|
||||
|
||||
|
||||
|
@ -40,9 +43,12 @@ def include_symbol(tablename, schema):
|
|||
EthereumBlock.__tablename__,
|
||||
EthereumTransaction.__tablename__,
|
||||
EthereumLabel.__tablename__,
|
||||
EthereumPendingTransaction.__tablename__,
|
||||
ESDEventSignature.__tablename__,
|
||||
PolygonBlock.__tablename__,
|
||||
PolygonTransaction.__tablename__,
|
||||
PolygonLabel.__tablename__,
|
||||
ESDFunctionSignature.__tablename__,
|
||||
ESDEventSignature.__tablename__,
|
||||
OpenSeaCrawlingState.__tablename__,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
"""Drop pending tx table
|
||||
|
||||
Revision ID: 3d55105b0603
|
||||
Revises: 0b46c8e17bf2
|
||||
Create Date: 2021-11-08 10:03:18.246561
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '3d55105b0603'
|
||||
down_revision = '0b46c8e17bf2'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index('ix_ethereum_pending_transactions_block_number', table_name='ethereum_pending_transactions')
|
||||
op.drop_index('ix_ethereum_pending_transactions_from_address', table_name='ethereum_pending_transactions')
|
||||
op.drop_index('ix_ethereum_pending_transactions_gas', table_name='ethereum_pending_transactions')
|
||||
op.drop_index('ix_ethereum_pending_transactions_gas_price', table_name='ethereum_pending_transactions')
|
||||
op.drop_index('ix_ethereum_pending_transactions_hash', table_name='ethereum_pending_transactions')
|
||||
op.drop_index('ix_ethereum_pending_transactions_to_address', table_name='ethereum_pending_transactions')
|
||||
op.drop_index('ix_ethereum_pending_transactions_value', table_name='ethereum_pending_transactions')
|
||||
op.drop_table('ethereum_pending_transactions')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('ethereum_pending_transactions',
|
||||
sa.Column('hash', sa.VARCHAR(length=256), autoincrement=False, nullable=False),
|
||||
sa.Column('block_number', sa.BIGINT(), autoincrement=False, nullable=False),
|
||||
sa.Column('from_address', sa.VARCHAR(length=256), autoincrement=False, nullable=True),
|
||||
sa.Column('to_address', sa.VARCHAR(length=256), autoincrement=False, nullable=True),
|
||||
sa.Column('gas', sa.NUMERIC(precision=78, scale=0), autoincrement=False, nullable=True),
|
||||
sa.Column('gas_price', sa.NUMERIC(precision=78, scale=0), autoincrement=False, nullable=True),
|
||||
sa.Column('input', sa.TEXT(), autoincrement=False, nullable=True),
|
||||
sa.Column('nonce', sa.VARCHAR(length=256), autoincrement=False, nullable=True),
|
||||
sa.Column('transaction_index', sa.BIGINT(), autoincrement=False, nullable=True),
|
||||
sa.Column('value', sa.NUMERIC(precision=78, scale=0), autoincrement=False, nullable=True),
|
||||
sa.Column('indexed_at', postgresql.TIMESTAMP(timezone=True), server_default=sa.text("timezone('utc'::text, statement_timestamp())"), autoincrement=False, nullable=False),
|
||||
sa.ForeignKeyConstraint(['block_number'], ['ethereum_blocks.block_number'], name='fk_ethereum_pending_transactions_block_number_ethereum_blocks', ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('hash', name='pk_ethereum_pending_transactions')
|
||||
)
|
||||
op.create_index('ix_ethereum_pending_transactions_value', 'ethereum_pending_transactions', ['value'], unique=False)
|
||||
op.create_index('ix_ethereum_pending_transactions_to_address', 'ethereum_pending_transactions', ['to_address'], unique=False)
|
||||
op.create_index('ix_ethereum_pending_transactions_hash', 'ethereum_pending_transactions', ['hash'], unique=False)
|
||||
op.create_index('ix_ethereum_pending_transactions_gas_price', 'ethereum_pending_transactions', ['gas_price'], unique=False)
|
||||
op.create_index('ix_ethereum_pending_transactions_gas', 'ethereum_pending_transactions', ['gas'], unique=False)
|
||||
op.create_index('ix_ethereum_pending_transactions_from_address', 'ethereum_pending_transactions', ['from_address'], unique=False)
|
||||
op.create_index('ix_ethereum_pending_transactions_block_number', 'ethereum_pending_transactions', ['block_number'], unique=False)
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,112 @@
|
|||
"""Polygon models
|
||||
|
||||
Revision ID: f991fc7493c8
|
||||
Revises: 3d55105b0603
|
||||
Create Date: 2021-11-08 10:44:05.882650
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f991fc7493c8'
|
||||
down_revision = '3d55105b0603'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('polygon_blocks',
|
||||
sa.Column('block_number', sa.BigInteger(), nullable=False),
|
||||
sa.Column('difficulty', sa.BigInteger(), nullable=True),
|
||||
sa.Column('extra_data', sa.VARCHAR(length=128), nullable=True),
|
||||
sa.Column('gas_limit', sa.BigInteger(), nullable=True),
|
||||
sa.Column('gas_used', sa.BigInteger(), nullable=True),
|
||||
sa.Column('base_fee_per_gas', sa.Numeric(precision=78, scale=0), nullable=True),
|
||||
sa.Column('hash', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('logs_bloom', sa.VARCHAR(length=1024), nullable=True),
|
||||
sa.Column('miner', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('nonce', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('parent_hash', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('receipt_root', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('uncles', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('size', sa.Integer(), nullable=True),
|
||||
sa.Column('state_root', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('timestamp', sa.BigInteger(), nullable=True),
|
||||
sa.Column('total_difficulty', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('transactions_root', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('indexed_at', sa.DateTime(timezone=True), server_default=sa.text("TIMEZONE('utc', statement_timestamp())"), nullable=False),
|
||||
sa.PrimaryKeyConstraint('block_number', name=op.f('pk_polygon_blocks'))
|
||||
)
|
||||
op.create_index(op.f('ix_polygon_blocks_block_number'), 'polygon_blocks', ['block_number'], unique=True)
|
||||
op.create_index(op.f('ix_polygon_blocks_hash'), 'polygon_blocks', ['hash'], unique=False)
|
||||
op.create_index(op.f('ix_polygon_blocks_timestamp'), 'polygon_blocks', ['timestamp'], unique=False)
|
||||
op.create_table('polygon_labels',
|
||||
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
|
||||
sa.Column('label', sa.VARCHAR(length=256), nullable=False),
|
||||
sa.Column('block_number', sa.BigInteger(), nullable=True),
|
||||
sa.Column('address', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('transaction_hash', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('label_data', postgresql.JSONB(astext_type=sa.Text()), nullable=True),
|
||||
sa.Column('block_timestamp', sa.BigInteger(), nullable=True),
|
||||
sa.Column('log_index', sa.Integer(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text("TIMEZONE('utc', statement_timestamp())"), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id', name=op.f('pk_polygon_labels')),
|
||||
sa.UniqueConstraint('id', name=op.f('uq_polygon_labels_id'))
|
||||
)
|
||||
op.create_index(op.f('ix_polygon_labels_address'), 'polygon_labels', ['address'], unique=False)
|
||||
op.create_index(op.f('ix_polygon_labels_block_number'), 'polygon_labels', ['block_number'], unique=False)
|
||||
op.create_index(op.f('ix_polygon_labels_block_timestamp'), 'polygon_labels', ['block_timestamp'], unique=False)
|
||||
op.create_index(op.f('ix_polygon_labels_label'), 'polygon_labels', ['label'], unique=False)
|
||||
op.create_index(op.f('ix_polygon_labels_transaction_hash'), 'polygon_labels', ['transaction_hash'], unique=False)
|
||||
op.create_table('polygon_transactions',
|
||||
sa.Column('hash', sa.VARCHAR(length=256), nullable=False),
|
||||
sa.Column('block_number', sa.BigInteger(), nullable=False),
|
||||
sa.Column('from_address', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('to_address', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('gas', sa.Numeric(precision=78, scale=0), nullable=True),
|
||||
sa.Column('gas_price', sa.Numeric(precision=78, scale=0), nullable=True),
|
||||
sa.Column('max_fee_per_gas', sa.Numeric(precision=78, scale=0), nullable=True),
|
||||
sa.Column('max_priority_fee_per_gas', sa.Numeric(precision=78, scale=0), nullable=True),
|
||||
sa.Column('input', sa.Text(), nullable=True),
|
||||
sa.Column('nonce', sa.VARCHAR(length=256), nullable=True),
|
||||
sa.Column('transaction_index', sa.BigInteger(), nullable=True),
|
||||
sa.Column('transaction_type', sa.Integer(), nullable=True),
|
||||
sa.Column('value', sa.Numeric(precision=78, scale=0), nullable=True),
|
||||
sa.Column('indexed_at', sa.DateTime(timezone=True), server_default=sa.text("TIMEZONE('utc', statement_timestamp())"), nullable=False),
|
||||
sa.ForeignKeyConstraint(['block_number'], ['polygon_blocks.block_number'], name=op.f('fk_polygon_transactions_block_number_polygon_blocks'), ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('hash', name=op.f('pk_polygon_transactions'))
|
||||
)
|
||||
op.create_index(op.f('ix_polygon_transactions_block_number'), 'polygon_transactions', ['block_number'], unique=False)
|
||||
op.create_index(op.f('ix_polygon_transactions_from_address'), 'polygon_transactions', ['from_address'], unique=False)
|
||||
op.create_index(op.f('ix_polygon_transactions_gas'), 'polygon_transactions', ['gas'], unique=False)
|
||||
op.create_index(op.f('ix_polygon_transactions_gas_price'), 'polygon_transactions', ['gas_price'], unique=False)
|
||||
op.create_index(op.f('ix_polygon_transactions_hash'), 'polygon_transactions', ['hash'], unique=True)
|
||||
op.create_index(op.f('ix_polygon_transactions_to_address'), 'polygon_transactions', ['to_address'], unique=False)
|
||||
op.create_index(op.f('ix_polygon_transactions_value'), 'polygon_transactions', ['value'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_polygon_transactions_value'), table_name='polygon_transactions')
|
||||
op.drop_index(op.f('ix_polygon_transactions_to_address'), table_name='polygon_transactions')
|
||||
op.drop_index(op.f('ix_polygon_transactions_hash'), table_name='polygon_transactions')
|
||||
op.drop_index(op.f('ix_polygon_transactions_gas_price'), table_name='polygon_transactions')
|
||||
op.drop_index(op.f('ix_polygon_transactions_gas'), table_name='polygon_transactions')
|
||||
op.drop_index(op.f('ix_polygon_transactions_from_address'), table_name='polygon_transactions')
|
||||
op.drop_index(op.f('ix_polygon_transactions_block_number'), table_name='polygon_transactions')
|
||||
op.drop_table('polygon_transactions')
|
||||
op.drop_index(op.f('ix_polygon_labels_transaction_hash'), table_name='polygon_labels')
|
||||
op.drop_index(op.f('ix_polygon_labels_label'), table_name='polygon_labels')
|
||||
op.drop_index(op.f('ix_polygon_labels_block_timestamp'), table_name='polygon_labels')
|
||||
op.drop_index(op.f('ix_polygon_labels_block_number'), table_name='polygon_labels')
|
||||
op.drop_index(op.f('ix_polygon_labels_address'), table_name='polygon_labels')
|
||||
op.drop_table('polygon_labels')
|
||||
op.drop_index(op.f('ix_polygon_blocks_timestamp'), table_name='polygon_blocks')
|
||||
op.drop_index(op.f('ix_polygon_blocks_hash'), table_name='polygon_blocks')
|
||||
op.drop_index(op.f('ix_polygon_blocks_block_number'), table_name='polygon_blocks')
|
||||
op.drop_table('polygon_blocks')
|
||||
# ### end Alembic commands ###
|
|
@ -155,15 +155,43 @@ class EthereumLabel(Base): # type: ignore
|
|||
)
|
||||
|
||||
|
||||
class EthereumPendingTransaction(Base): # type: ignore
|
||||
__tablename__ = "ethereum_pending_transactions"
|
||||
class PolygonBlock(Base): # type: ignore
|
||||
__tablename__ = "polygon_blocks"
|
||||
|
||||
block_number = Column(
|
||||
BigInteger, primary_key=True, unique=True, nullable=False, index=True
|
||||
)
|
||||
difficulty = Column(BigInteger)
|
||||
extra_data = Column(VARCHAR(128))
|
||||
gas_limit = Column(BigInteger)
|
||||
gas_used = Column(BigInteger)
|
||||
base_fee_per_gas = Column(Numeric(precision=78, scale=0), nullable=True)
|
||||
hash = Column(VARCHAR(256), index=True)
|
||||
logs_bloom = Column(VARCHAR(1024))
|
||||
miner = Column(VARCHAR(256))
|
||||
nonce = Column(VARCHAR(256))
|
||||
parent_hash = Column(VARCHAR(256))
|
||||
receipt_root = Column(VARCHAR(256))
|
||||
uncles = Column(VARCHAR(256))
|
||||
size = Column(Integer)
|
||||
state_root = Column(VARCHAR(256))
|
||||
timestamp = Column(BigInteger, index=True)
|
||||
total_difficulty = Column(VARCHAR(256))
|
||||
transactions_root = Column(VARCHAR(256))
|
||||
indexed_at = Column(
|
||||
DateTime(timezone=True), server_default=utcnow(), nullable=False
|
||||
)
|
||||
|
||||
|
||||
class PolygonTransaction(Base): # type: ignore
|
||||
__tablename__ = "polygon_transactions"
|
||||
|
||||
hash = Column(
|
||||
VARCHAR(256), primary_key=True, unique=True, nullable=False, index=True
|
||||
)
|
||||
block_number = Column(
|
||||
BigInteger,
|
||||
ForeignKey("ethereum_blocks.block_number", ondelete="CASCADE"),
|
||||
ForeignKey("polygon_blocks.block_number", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
|
@ -171,9 +199,12 @@ class EthereumPendingTransaction(Base): # type: ignore
|
|||
to_address = Column(VARCHAR(256), index=True)
|
||||
gas = Column(Numeric(precision=78, scale=0), index=True)
|
||||
gas_price = Column(Numeric(precision=78, scale=0), index=True)
|
||||
max_fee_per_gas = Column(Numeric(precision=78, scale=0), nullable=True)
|
||||
max_priority_fee_per_gas = Column(Numeric(precision=78, scale=0), nullable=True)
|
||||
input = Column(Text)
|
||||
nonce = Column(VARCHAR(256))
|
||||
transaction_index = Column(BigInteger)
|
||||
transaction_type = Column(Integer, nullable=True)
|
||||
value = Column(Numeric(precision=78, scale=0), index=True)
|
||||
|
||||
indexed_at = Column(
|
||||
|
@ -181,9 +212,58 @@ class EthereumPendingTransaction(Base): # type: ignore
|
|||
)
|
||||
|
||||
|
||||
class PolygonLabel(Base): # type: ignore
|
||||
"""
|
||||
Example of label_data:
|
||||
{
|
||||
"label": "ERC20",
|
||||
"label_data": {
|
||||
"name": "Uniswap",
|
||||
"symbol": "UNI"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Exchange"
|
||||
"label_data": {...}
|
||||
}
|
||||
"""
|
||||
|
||||
__tablename__ = "polygon_labels"
|
||||
|
||||
id = Column(
|
||||
UUID(as_uuid=True),
|
||||
primary_key=True,
|
||||
default=uuid.uuid4,
|
||||
unique=True,
|
||||
nullable=False,
|
||||
)
|
||||
label = Column(VARCHAR(256), nullable=False, index=True)
|
||||
block_number = Column(
|
||||
BigInteger,
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
address = Column(
|
||||
VARCHAR(256),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
transaction_hash = Column(
|
||||
VARCHAR(256),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
label_data = Column(JSONB, nullable=True)
|
||||
block_timestamp = Column(BigInteger, index=True)
|
||||
log_index = Column(Integer, nullable=True)
|
||||
created_at = Column(
|
||||
DateTime(timezone=True), server_default=utcnow(), nullable=False
|
||||
)
|
||||
|
||||
|
||||
class ESDFunctionSignature(Base): # type: ignore
|
||||
"""
|
||||
Function signature from Ethereum Signature Database.
|
||||
Function signature from blockchain (Ethereum/Polygon) Signature Database.
|
||||
"""
|
||||
|
||||
__tablename__ = "esd_function_signatures"
|
||||
|
@ -198,7 +278,7 @@ class ESDFunctionSignature(Base): # type: ignore
|
|||
|
||||
class ESDEventSignature(Base): # type: ignore
|
||||
"""
|
||||
Function signature from Ethereum Signature Database.
|
||||
Function signature from blockchain (Ethereum/Polygon) Signature Database.
|
||||
"""
|
||||
|
||||
__tablename__ = "esd_event_signatures"
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
Moonstream database version.
|
||||
"""
|
||||
|
||||
MOONSTREAMDB_VERSION = "0.1.1"
|
||||
MOONSTREAMDB_VERSION = "0.2.0"
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -18,11 +18,13 @@
|
|||
"axios": "^0.21.1",
|
||||
"color": "^4.0.1",
|
||||
"downshift": "^6.1.7",
|
||||
"core-js": "^3.19.1",
|
||||
"focus-visible": "^5.2.0",
|
||||
"framer-motion": "^4.1.17",
|
||||
"mixpanel-browser": "^2.41.0",
|
||||
"mobx": "^6.3.6",
|
||||
"moment": "^2.29.1",
|
||||
"next": "11.0.1",
|
||||
"next": "11.1.2",
|
||||
"nprogress": "^0.2.0",
|
||||
"react": "^17.0.2",
|
||||
"react-ace": "^9.5.0",
|
||||
|
@ -30,18 +32,23 @@
|
|||
"react-color": "^2.19.3",
|
||||
"react-copy-to-clipboard": "^5.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-draggable": "^4.4.4",
|
||||
"react-hook-form": "^6.9.2",
|
||||
"react-hubspot-form": "^1.3.7",
|
||||
"react-icons": "^4.2.0",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-pro-sidebar": "^0.6.0",
|
||||
"react-query": "^3.18.1",
|
||||
"react-showdown": "^2.3.0",
|
||||
"react-slick": "^0.28.1",
|
||||
"react-split-pane": "^0.1.92",
|
||||
"react-xarrows": "^2.0.2",
|
||||
"redoc": "^2.0.0-rc.57",
|
||||
"showdown": "^1.9.1",
|
||||
"showdown-highlight": "^2.1.8",
|
||||
"uuid": "^8.3.2",
|
||||
"web3-utils": "^1.6.0"
|
||||
"web3-utils": "^1.6.0",
|
||||
"slick-carousel": "^1.8.1",
|
||||
"styled-components": "^5.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.3",
|
||||
|
|
|
@ -4,8 +4,11 @@ import "/styles/nprogress.css";
|
|||
import "/styles/sidebar.css";
|
||||
import "highlight.js/styles/github.css";
|
||||
import "focus-visible/dist/focus-visible";
|
||||
import "slick-carousel/slick/slick.css";
|
||||
import "slick-carousel/slick/slick-theme.css";
|
||||
import dynamic from "next/dynamic";
|
||||
import { QueryClient, QueryClientProvider } from "react-query";
|
||||
import { ReactQueryDevtools } from "react-query/devtools";
|
||||
import HeadLinks from "../src/components/HeadLinks";
|
||||
import HeadSEO from "../src/components/HeadSEO";
|
||||
const AppContext = dynamic(() => import("../src/AppContext"), {
|
||||
|
@ -23,6 +26,16 @@ export default function CachingApp({ Component, pageProps }) {
|
|||
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
router.pathname !== "/entry-point" &&
|
||||
window &&
|
||||
localStorage.getItem("entry_point")
|
||||
) {
|
||||
localStorage.removeItem("entry_point");
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleStart = () => {
|
||||
NProgress.start();
|
||||
|
@ -64,6 +77,7 @@ export default function CachingApp({ Component, pageProps }) {
|
|||
{pageProps.metaTags && <HeadSEO {...pageProps.metaTags} />}
|
||||
<HeadLinks links={headLinks} />
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
<AppContext>{getLayout(<Component {...pageProps} />)}</AppContext>
|
||||
</QueryClientProvider>
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,344 @@
|
|||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { getLayout } from "../../src/layouts/AppLayout";
|
||||
import {
|
||||
Spinner,
|
||||
Flex,
|
||||
Heading,
|
||||
Stack,
|
||||
Text,
|
||||
Spacer,
|
||||
IconButton,
|
||||
} from "@chakra-ui/react";
|
||||
import Scrollable from "../../src/components/Scrollable";
|
||||
import RangeSelector from "../../src/components/RangeSelector";
|
||||
import useDashboard from "../../src/core/hooks/useDashboard";
|
||||
import { useRouter } from "../../src/core/hooks";
|
||||
import { BiTrash } from "react-icons/bi";
|
||||
import OverlayContext from "../../src/core/providers/OverlayProvider/context";
|
||||
|
||||
const HOUR_KEY = "Hourly";
|
||||
const DAY_KEY = "Daily";
|
||||
let timeMap = {};
|
||||
timeMap[HOUR_KEY] = "hour";
|
||||
timeMap[DAY_KEY] = "day";
|
||||
|
||||
const Analytics = () => {
|
||||
const { toggleAlert } = useContext(OverlayContext);
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
document.title = `NFT Analytics`;
|
||||
}
|
||||
}, []);
|
||||
|
||||
// const [nodesReady, setNodeReady] = useState({
|
||||
// ntx: false,
|
||||
// values: false,
|
||||
// mints: false,
|
||||
// NFTOwners: false,
|
||||
// minters: false,
|
||||
// });
|
||||
|
||||
// const nTxRef_ = useRef();
|
||||
// const valueRef_ = useRef();
|
||||
// const mintsRef_ = useRef();
|
||||
// const uniqueNFTOwnersRef_ = useRef();
|
||||
// const mintersRef_ = 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 uniqueNFTOwnersRef = useCallback(
|
||||
// (node) => {
|
||||
// if (node !== null && !nodesReady.NFTOwners) {
|
||||
// setNodeReady({ ...nodesReady, NFTOwners: true });
|
||||
// uniqueNFTOwnersRef_.current = node;
|
||||
// }
|
||||
// },
|
||||
// [nodesReady]
|
||||
// );
|
||||
|
||||
// const mintersRef = useCallback(
|
||||
// (node) => {
|
||||
// if (node !== null && !nodesReady.minters) {
|
||||
// setNodeReady({ ...nodesReady, minters: true });
|
||||
// mintersRef_.current = node;
|
||||
// }
|
||||
// },
|
||||
// [nodesReady]
|
||||
// );
|
||||
|
||||
const [timeRange, setTimeRange] = useState(HOUR_KEY);
|
||||
const router = useRouter();
|
||||
const { dashboardId } = router.params;
|
||||
console.log("router paras:", router.params, dashboardId);
|
||||
const { dashboardCache, deleteDashboard } = useDashboard(dashboardId);
|
||||
|
||||
// useLayoutEffect(() => {
|
||||
// const items = [
|
||||
// nTxRef_,
|
||||
// valueRef_,
|
||||
// mintsRef_,
|
||||
// uniqueNFTOwnersRef_,
|
||||
// mintersRef_,
|
||||
// ];
|
||||
// console.log("useeffect fired");
|
||||
// if (items.some((item) => !!item.current)) {
|
||||
// console.log("brder fun");
|
||||
// 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 (dashboardCache.isLoading) return <Spinner />;
|
||||
|
||||
const plotMinW = "500px";
|
||||
|
||||
return (
|
||||
<Scrollable>
|
||||
<Flex
|
||||
h="100%"
|
||||
w="100%"
|
||||
m={0}
|
||||
px="7%"
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
minH="100vh"
|
||||
>
|
||||
<Stack direction="row" w="100%" placeItems="center">
|
||||
<Heading as="h1" py={2} fontSize={["md", "xl"]}>
|
||||
NFT market analysis
|
||||
</Heading>
|
||||
<Spacer />
|
||||
<RangeSelector
|
||||
initialRange={timeRange}
|
||||
ranges={Object.keys(timeMap)}
|
||||
size={["sm", "md", null]}
|
||||
onChange={(e) => setTimeRange(e)}
|
||||
/>
|
||||
<IconButton
|
||||
icon={<BiTrash />}
|
||||
variant="ghost"
|
||||
colorScheme="red"
|
||||
size="sm"
|
||||
onClick={() => toggleAlert(() => deleteDashboard.mutate())}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack
|
||||
w="100%"
|
||||
wrap="wrap"
|
||||
my={2}
|
||||
h="auto"
|
||||
direction="row"
|
||||
minW="240px"
|
||||
spacing={[0, 0, null]}
|
||||
boxShadow="md"
|
||||
borderRadius="lg"
|
||||
bgColor="gray.100"
|
||||
>
|
||||
{/* <StatsCard
|
||||
ref={(node) => nTxRef(node)}
|
||||
labelKey="nft_transfers"
|
||||
totalKey="num_transactions"
|
||||
timeRange={timeMap[timeRange]}
|
||||
netLabel="Ethereum mainnet"
|
||||
label="Number of NFT purchases"
|
||||
/>
|
||||
<StatsCard
|
||||
ref={(node) => valueRef(node)}
|
||||
labelKey="nft_transfer_value"
|
||||
totalKey="total_value"
|
||||
timeRange={timeMap[timeRange]}
|
||||
netLabel="Ethereum mainnet"
|
||||
label="Money spent"
|
||||
/>
|
||||
<StatsCard
|
||||
ref={(node) => mintsRef(node)}
|
||||
labelKey="nft_mints"
|
||||
timeRange={timeMap[timeRange]}
|
||||
netLabel="Ethereum mainnet"
|
||||
label="NFTs created"
|
||||
/>
|
||||
<StatsCard
|
||||
ref={(node) => uniqueNFTOwnersRef(node)}
|
||||
labelKey="nft_owners"
|
||||
timeRange={timeMap[timeRange]}
|
||||
netLabel="Ethereum mainnet"
|
||||
label="Number of buyers"
|
||||
/>
|
||||
|
||||
<StatsCard
|
||||
ref={(node) => mintersRef(node)}
|
||||
labelKey="nft_minters"
|
||||
timeRange={timeMap[timeRange]}
|
||||
netLabel="Ethereum mainnet"
|
||||
label="Number of creators"
|
||||
/> */}
|
||||
</Stack>
|
||||
<Flex w="100%" direction="row" flexWrap="wrap-reverse">
|
||||
<Flex
|
||||
flexBasis={plotMinW}
|
||||
flexGrow={1}
|
||||
minW={plotMinW}
|
||||
minH="320px"
|
||||
maxH="420px"
|
||||
direction="column"
|
||||
boxShadow="md"
|
||||
m={2}
|
||||
>
|
||||
<Text
|
||||
w="100%"
|
||||
py={2}
|
||||
bgColor="gray.50"
|
||||
fontWeight="600"
|
||||
textAlign="center"
|
||||
>
|
||||
New NFTs
|
||||
</Text>
|
||||
{/* <NFTChart keyPosition={`nft_mints`} timeRange={timeRange} /> */}
|
||||
</Flex>
|
||||
<Flex
|
||||
flexBasis={plotMinW}
|
||||
flexGrow={1}
|
||||
minW={plotMinW}
|
||||
minH="320px"
|
||||
maxH="420px"
|
||||
direction="column"
|
||||
boxShadow="md"
|
||||
m={2}
|
||||
>
|
||||
<Text
|
||||
w="100%"
|
||||
py={2}
|
||||
bgColor="gray.50"
|
||||
fontWeight="600"
|
||||
textAlign="center"
|
||||
>
|
||||
NFT creators
|
||||
</Text>
|
||||
{/* <NFTChart keyPosition={`nft_minters`} timeRange={timeRange} /> */}
|
||||
</Flex>
|
||||
<Flex
|
||||
flexBasis={plotMinW}
|
||||
flexGrow={1}
|
||||
minW={plotMinW}
|
||||
minH="320px"
|
||||
maxH="420px"
|
||||
direction="column"
|
||||
boxShadow="md"
|
||||
m={2}
|
||||
>
|
||||
<Text
|
||||
w="100%"
|
||||
py={2}
|
||||
bgColor="gray.50"
|
||||
fontWeight="600"
|
||||
textAlign="center"
|
||||
>
|
||||
NFT Buyers
|
||||
</Text>
|
||||
{/* <NFTChart keyPosition={`nft_owners`} timeRange={timeRange} /> */}
|
||||
</Flex>
|
||||
<Flex
|
||||
flexBasis={plotMinW}
|
||||
flexGrow={1}
|
||||
minW={plotMinW}
|
||||
minH="320px"
|
||||
maxH="420px"
|
||||
direction="column"
|
||||
boxShadow="md"
|
||||
m={2}
|
||||
>
|
||||
<Text
|
||||
w="100%"
|
||||
py={2}
|
||||
bgColor="gray.50"
|
||||
fontWeight="600"
|
||||
textAlign="center"
|
||||
>
|
||||
Transaction volume
|
||||
</Text>
|
||||
{/* <NFTChart
|
||||
keyPosition={`nft_transfers`}
|
||||
keyTotal={`num_transactions`}
|
||||
timeRange={timeRange}
|
||||
/> */}
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
flexBasis={plotMinW}
|
||||
flexGrow={1}
|
||||
minW={plotMinW}
|
||||
minH="320px"
|
||||
maxH="420px"
|
||||
direction="column"
|
||||
boxShadow="md"
|
||||
m={2}
|
||||
>
|
||||
<Text
|
||||
w="100%"
|
||||
py={2}
|
||||
bgColor="gray.50"
|
||||
fontWeight="600"
|
||||
textAlign="center"
|
||||
>
|
||||
Transaction value
|
||||
</Text>
|
||||
{/* <NFTChart
|
||||
keyPosition={`nft_transfer_value`}
|
||||
keyTotal={`total_value`}
|
||||
timeRange={timeRange}
|
||||
/> */}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Scrollable>
|
||||
);
|
||||
};
|
||||
|
||||
Analytics.getLayout = getLayout;
|
||||
export default Analytics;
|
|
@ -0,0 +1,44 @@
|
|||
import React from "react";
|
||||
import { RedocStandalone } from "redoc";
|
||||
import { Box } from "@chakra-ui/react";
|
||||
import { getLayout } from "../src/layouts/RootLayout";
|
||||
import { DEFAULT_METATAGS } from "../src/core/constants";
|
||||
|
||||
const Docs = () => {
|
||||
return (
|
||||
// <Box overflowY="hidden" w="100%" maxH="100%" minH="100vh">
|
||||
<>
|
||||
<Box w="100%" maxH="100vh" overflowY="scroll">
|
||||
<RedocStandalone
|
||||
specUrl="https://api.moonstream.to/openapi.json"
|
||||
options={{
|
||||
theme: {
|
||||
colors: {
|
||||
primary: { main: "#212990" },
|
||||
success: { main: "#92D050" },
|
||||
warning: { main: "#FD5602" },
|
||||
error: { main: "#C53030" },
|
||||
gray: { 50: "#f7f8fa", 100: "#eff1f4" },
|
||||
},
|
||||
rightPanel: { backgroundColor: "#34373d" },
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
// </Box>
|
||||
);
|
||||
};
|
||||
|
||||
export async function getStaticProps() {
|
||||
const metaTags = {
|
||||
title: "Moonstream: API Documentation",
|
||||
description: "API Documentation to use moonstream.to",
|
||||
keywords: "API, docs",
|
||||
url: "https://www.moonstream.to/docs",
|
||||
};
|
||||
return { props: { metaTags: { ...DEFAULT_METATAGS, ...metaTags } } };
|
||||
}
|
||||
|
||||
Docs.getLayout = getLayout;
|
||||
export default Docs;
|
|
@ -0,0 +1,29 @@
|
|||
import { useRouter } from "next/router";
|
||||
import { useContext, useLayoutEffect } from "react";
|
||||
|
||||
import UserContext from "../src/core/providers/UserProvider/context";
|
||||
import { getLayout } from "../src/layouts/EntryPointLayout";
|
||||
|
||||
const EntryPoint = () => {
|
||||
const router = useRouter();
|
||||
const { isInit } = useContext(UserContext);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (router.isReady && isInit && router.asPath !== router.pathname + `/`) {
|
||||
if (localStorage.getItem("entry_point")) {
|
||||
router.replace("/404", router.asPath);
|
||||
} else {
|
||||
localStorage.setItem("entry_point", 1);
|
||||
router.replace(router.asPath, undefined, {
|
||||
shallow: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [router, isInit]);
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
EntryPoint.getLayout = getLayout;
|
||||
|
||||
export default EntryPoint;
|
|
@ -1,4 +1,10 @@
|
|||
import React, { useState, Suspense, useEffect, useLayoutEffect } from "react";
|
||||
import React, {
|
||||
useState,
|
||||
useContext,
|
||||
Suspense,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
} from "react";
|
||||
import {
|
||||
Fade,
|
||||
Flex,
|
||||
|
@ -23,16 +29,81 @@ import {
|
|||
MIXPANEL_PROPS,
|
||||
MIXPANEL_EVENTS,
|
||||
} from "../src/core/providers/AnalyticsProvider/constants";
|
||||
import { AWS_ASSETS_PATH } from "../src/core/constants";
|
||||
import { AWS_ASSETS_PATH, DEFAULT_METATAGS } 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"),
|
||||
import UIContext from "../src/core/providers/UIProvider/context";
|
||||
import TrustedBadge from "../src/components/TrustedBadge";
|
||||
import Slider from "react-slick";
|
||||
import SchematicPlayground from "../src/components/SchematicPlayground";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import RouteButton from "../src/components/RouteButton";
|
||||
import { FaDiscord } from "react-icons/fa";
|
||||
const SplitWithImage = dynamic(
|
||||
() => import("../src/components/SplitWithImage"),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
);
|
||||
const FaGithubSquare = dynamic(() =>
|
||||
import("react-icons/fa").then((mod) => mod.FaGithubSquare)
|
||||
);
|
||||
const GiSuspicious = dynamic(() =>
|
||||
import("react-icons/gi").then((mod) => mod.GiSuspicious)
|
||||
);
|
||||
|
||||
const GiHook = dynamic(() =>
|
||||
import("react-icons/gi").then((mod) => mod.GiHook)
|
||||
);
|
||||
|
||||
const IoTelescopeSharp = dynamic(() =>
|
||||
import("react-icons/io5").then((mod) => mod.IoTelescopeSharp)
|
||||
);
|
||||
|
||||
const AiFillApi = dynamic(() =>
|
||||
import("react-icons/ai").then((mod) => mod.AiFillApi)
|
||||
);
|
||||
|
||||
const BiTransfer = dynamic(() =>
|
||||
import("react-icons/bi").then((mod) => mod.BiTransfer)
|
||||
);
|
||||
|
||||
const RiDashboardFill = dynamic(() =>
|
||||
import("react-icons/ri").then((mod) => mod.RiDashboardFill)
|
||||
);
|
||||
const FaFileContract = dynamic(() =>
|
||||
import("react-icons/fa").then((mod) => mod.FaFileContract)
|
||||
);
|
||||
const GiMeshBall = dynamic(() =>
|
||||
import("react-icons/gi").then((mod) => mod.GiMeshBall)
|
||||
);
|
||||
|
||||
const GiLogicGateXor = dynamic(() =>
|
||||
import("react-icons/gi").then((mod) => mod.GiLogicGateXor)
|
||||
);
|
||||
|
||||
const VscOrganization = dynamic(() =>
|
||||
import("react-icons/vsc").then((mod) => mod.VscOrganization)
|
||||
);
|
||||
const FaVoteYea = dynamic(() =>
|
||||
import("react-icons/fa").then((mod) => mod.FaVoteYea)
|
||||
);
|
||||
|
||||
const RiOrganizationChart = dynamic(() =>
|
||||
import("react-icons/ri").then((mod) => mod.RiOrganizationChart)
|
||||
);
|
||||
const FiActivity = dynamic(() =>
|
||||
import("react-icons/fi").then((mod) => mod.FiActivity)
|
||||
);
|
||||
|
||||
const RiMapPinUserLine = dynamic(() =>
|
||||
import("react-icons/ri").then((mod) => mod.RiMapPinUserLine)
|
||||
);
|
||||
const AiOutlinePieChart = dynamic(() =>
|
||||
import("react-icons/ai").then((mod) => mod.AiOutlinePieChart)
|
||||
);
|
||||
|
||||
const BiBot = dynamic(() => import("react-icons/bi").then((mod) => mod.BiBot));
|
||||
|
||||
const HEADING_PROPS = {
|
||||
fontWeight: "700",
|
||||
fontSize: ["4xl", "5xl", "4xl", "5xl", "6xl", "7xl"],
|
||||
|
@ -47,14 +118,40 @@ const assets = {
|
|||
pendingTransactions: `${AWS_ASSETS_PATH}/Ethereum+pending+transactions.png`,
|
||||
priceInformation: `${AWS_ASSETS_PATH}/Price+information.png`,
|
||||
socialMediaPosts: `${AWS_ASSETS_PATH}/Social+media+posts.png`,
|
||||
cryptoTraders: `${AWS_ASSETS_PATH}/crypto+traders.png`,
|
||||
smartDevelopers: `${AWS_ASSETS_PATH}/smart+contract+developers.png`,
|
||||
cointelegraph: `${AWS_ASSETS_PATH}/featured_by/Cointelegraph_logo.png`,
|
||||
cryptoinsiders: `${AWS_ASSETS_PATH}/featured_by/crypto_insiders.png`,
|
||||
cryptoslate: `${AWS_ASSETS_PATH}/featured_by/cs-media-logo-light.png`,
|
||||
bitcoinLogo: `${AWS_ASSETS_PATH}/bitcoin.png`,
|
||||
ethereumBlackLogo: `${AWS_ASSETS_PATH}/eth-diamond-black.png`,
|
||||
ethereumRainbowLogo: `${AWS_ASSETS_PATH}/eth-diamond-rainbow.png`,
|
||||
maticLogo: `${AWS_ASSETS_PATH}/matic-token-inverted-icon.png`,
|
||||
erc20: `${AWS_ASSETS_PATH}/ERC 20.png`,
|
||||
DAO: `${AWS_ASSETS_PATH}/DAO .png`,
|
||||
NFT: `${AWS_ASSETS_PATH}/NFT.png`,
|
||||
};
|
||||
|
||||
const carousel_content = [
|
||||
{ title: "Bitcoin coming soon!", img: assets["bitcoinLogo"] },
|
||||
{ title: "Ethereum", img: assets["ethereumBlackLogo"] },
|
||||
{ title: "Ethereum transaction pool", img: assets["ethereumRainbowLogo"] },
|
||||
{ title: "Polygon coming soon!", img: assets["maticLogo"] },
|
||||
{ title: "Bitcoin coming soon!", img: assets["bitcoinLogo"] },
|
||||
{ title: "Ethereum", img: assets["ethereumBlackLogo"] },
|
||||
{ title: "Ethereum transaction pool", img: assets["ethereumRainbowLogo"] },
|
||||
{ title: "Polygon coming soon!", img: assets["maticLogo"] },
|
||||
];
|
||||
const Homepage = () => {
|
||||
const ui = useContext(UIContext);
|
||||
const [background, setBackground] = useState("background720");
|
||||
const [backgroundLoaded720, setBackgroundLoaded720] = useState(false);
|
||||
const [backgroundLoaded1920, setBackgroundLoaded1920] = useState(false);
|
||||
const [backgroundLoaded2880, setBackgroundLoaded2880] = useState(false);
|
||||
const [backgroundLoaded3840, setBackgroundLoaded3840] = useState(false);
|
||||
|
||||
const [imageIndex, setImageIndex] = useState(0);
|
||||
|
||||
const router = useRouter();
|
||||
const { isInit } = useUser();
|
||||
const { toggleModal } = useModals();
|
||||
|
@ -147,6 +244,24 @@ const Homepage = () => {
|
|||
};
|
||||
}, []);
|
||||
|
||||
const settings = {
|
||||
infinite: true,
|
||||
lazyLoad: true,
|
||||
speed: 2000,
|
||||
autoplay: true,
|
||||
autoplaySpeed: 0,
|
||||
// cssEase: "linear",
|
||||
cssEase: "cubic-bezier(0.165, 0.840, 0.440, 1.000)",
|
||||
// cssEase: "ease-in",
|
||||
slidesToScroll: 1,
|
||||
slidesToShow: ui.isMobileView ? 3 : 5,
|
||||
centerMode: true,
|
||||
centerPadding: 0,
|
||||
// nextArrow: "",
|
||||
// prevArrow: "",
|
||||
beforeChange: (current, next) => setImageIndex(next),
|
||||
};
|
||||
|
||||
return (
|
||||
<Suspense fallback="">
|
||||
<Fade in>
|
||||
|
@ -194,6 +309,7 @@ const Homepage = () => {
|
|||
alignItems="center"
|
||||
spacing={6}
|
||||
maxW={["1620px", null, null, null, "1620px", "2222px"]}
|
||||
w="100%"
|
||||
px="7%"
|
||||
h="100%"
|
||||
pt={["10vh", null, "20vh"]}
|
||||
|
@ -215,6 +331,54 @@ const Homepage = () => {
|
|||
understand exactly how people are using your smart
|
||||
contracts.
|
||||
</chakra.span>
|
||||
<Box
|
||||
w="100vw"
|
||||
minH="200px"
|
||||
// px="7%"
|
||||
py={0}
|
||||
overflowX="hidden"
|
||||
overflowY="visible"
|
||||
>
|
||||
<Slider
|
||||
{...settings}
|
||||
// adaptiveHeight={true}
|
||||
arrows={false}
|
||||
autoplay={true}
|
||||
autoplaySpeed={100}
|
||||
>
|
||||
{carousel_content.map((content_item, idx) => (
|
||||
<Box
|
||||
pt="80px"
|
||||
h="auto"
|
||||
w="150px"
|
||||
maxW="150px"
|
||||
// size="150px"
|
||||
key={uuidv4()}
|
||||
className={
|
||||
idx === imageIndex
|
||||
? "slide activeSlide"
|
||||
: "slide"
|
||||
}
|
||||
// bgColor="blue.900"
|
||||
// borderRadius="lg"
|
||||
// boxShadow="lg"
|
||||
>
|
||||
<ChakraImage
|
||||
fit="contain"
|
||||
boxSize={["64px", "96px", "130px", null]}
|
||||
src={content_item.img}
|
||||
/>
|
||||
<Text
|
||||
py={2}
|
||||
color="blue.300"
|
||||
fontSize={["sm", "md", null]}
|
||||
>
|
||||
{content_item.title}
|
||||
</Text>
|
||||
</Box>
|
||||
))}
|
||||
</Slider>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Flex>
|
||||
</Box>
|
||||
|
@ -222,84 +386,61 @@ const Homepage = () => {
|
|||
</GridItem>
|
||||
|
||||
<GridItem px="7%" colSpan="12" pt={0} minH="100vh">
|
||||
<chakra.span
|
||||
textAlign="center"
|
||||
fontWeight="600"
|
||||
fontSize="lg"
|
||||
w="100%"
|
||||
h="fit-content"
|
||||
>
|
||||
<Text
|
||||
mb={18}
|
||||
fontSize={["md", "2xl", "3xl", "3xl", "3xl", "4xl"]}
|
||||
>
|
||||
We believe that the blockchain is for everyone. This
|
||||
requires complete <b>transparency</b>. That’s why all our
|
||||
software is{" "}
|
||||
<chakra.span
|
||||
display="inline-block"
|
||||
textColor="orange.900"
|
||||
as={Link}
|
||||
href="https://github.com/bugout-dev/moonstream"
|
||||
>
|
||||
<i>open source</i>
|
||||
</chakra.span>
|
||||
</Text>
|
||||
</chakra.span>
|
||||
|
||||
<Heading
|
||||
{...HEADING_PROPS}
|
||||
textAlign="center"
|
||||
mt={48}
|
||||
mt={[24, 32, 48]}
|
||||
pb={[12, 12, 12, null, 24]}
|
||||
>
|
||||
See how your smart contracts are being used from:
|
||||
Get analytics for your:
|
||||
</Heading>
|
||||
<SimpleGrid columns={[1, 2, 2, 4, null, 4]}>
|
||||
<Stack spacing={1} px={1} alignItems="center">
|
||||
<ChakraImage
|
||||
boxSize={["220px", "220px", "xs", null, "xs"]}
|
||||
objectFit="contain"
|
||||
src={assets["minedTransactions"]}
|
||||
src={assets["NFT"]}
|
||||
alt="mined transactions"
|
||||
/>
|
||||
<Heading textAlign="center ">
|
||||
Ethereum mined transactions
|
||||
</Heading>
|
||||
<Heading textAlign="center ">NFTs</Heading>
|
||||
</Stack>
|
||||
<Stack spacing={1} px={1} alignItems="center">
|
||||
<ChakraImage
|
||||
boxSize={["220px", "220px", "xs", null, "xs"]}
|
||||
objectFit="contain"
|
||||
src={assets["pendingTransactions"]}
|
||||
src={assets["erc20"]}
|
||||
alt="mined transactions"
|
||||
/>
|
||||
<Heading textAlign="center ">
|
||||
Ethereum pending transactions
|
||||
</Heading>
|
||||
<Heading textAlign="center ">Tokens</Heading>
|
||||
</Stack>
|
||||
|
||||
<Stack spacing={1} px={1} alignItems="center">
|
||||
<ChakraImage
|
||||
boxSize={["220px", "220px", "xs", null, "xs"]}
|
||||
objectFit="contain"
|
||||
src={assets["cryptoTraders"]}
|
||||
alt="mined transactions"
|
||||
/>
|
||||
<Heading textAlign="center ">DEXs</Heading>
|
||||
</Stack>
|
||||
<Stack spacing={1} px={1} alignItems="center">
|
||||
<ChakraImage
|
||||
boxSize={["220px", "220px", "xs", null, "xs"]}
|
||||
objectFit="contain"
|
||||
src={assets["priceInformation"]}
|
||||
src={assets["DAO"]}
|
||||
alt="mined transactions"
|
||||
/>
|
||||
<Heading textAlign="center ">Centralized exchanges</Heading>
|
||||
</Stack>
|
||||
<Stack spacing={1} px={1} alignItems="center">
|
||||
<ChakraImage
|
||||
boxSize={["220px", "220px", "xs", null, "xs"]}
|
||||
objectFit="contain"
|
||||
src={assets["socialMediaPosts"]}
|
||||
alt="mined transactions"
|
||||
/>
|
||||
<Heading textAlign="center ">Social media posts</Heading>
|
||||
<Heading textAlign="center ">{`DAOs`}</Heading>
|
||||
</Stack>
|
||||
</SimpleGrid>
|
||||
<Center>
|
||||
<Heading pt="160px" pb="60px">
|
||||
Moonstream is meant for you if
|
||||
<Heading
|
||||
pt={["32px", "160px", null]}
|
||||
pb={["12px", "60px", null]}
|
||||
fontSize={["18px", "32px", null]}
|
||||
textAlign="center"
|
||||
>
|
||||
Your game changer in blockchain analytics
|
||||
</Heading>
|
||||
</Center>
|
||||
<Flex
|
||||
|
@ -307,103 +448,347 @@ const Homepage = () => {
|
|||
direction={["column", "row", "column", null, "column"]}
|
||||
flexWrap={["nowrap", "nowrap", "nowrap", null, "nowrap"]}
|
||||
pb="32px"
|
||||
placeContent="center"
|
||||
>
|
||||
<ConnectedButtons
|
||||
speedBase={0.3}
|
||||
title={"You need a fusion of..."}
|
||||
button4={{
|
||||
label: "Blockchain analytics",
|
||||
speed: 1,
|
||||
// link: "/#analytics",
|
||||
onClick: () => {
|
||||
mixpanel.get_distinct_id() &&
|
||||
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
|
||||
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Connected buttons: scroll to analytics`,
|
||||
});
|
||||
},
|
||||
}}
|
||||
button1={{
|
||||
label: "TX pool real time data",
|
||||
speed: 9,
|
||||
// link: "/#txpool",
|
||||
onClick: () => {
|
||||
mixpanel.get_distinct_id() &&
|
||||
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
|
||||
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Connected buttons: scroll to txpool`,
|
||||
});
|
||||
},
|
||||
}}
|
||||
button2={{
|
||||
label: "Exchange price stream",
|
||||
speed: 6,
|
||||
// link: "/#exchanges",
|
||||
onClick: () => {
|
||||
mixpanel.get_distinct_id() &&
|
||||
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
|
||||
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Connected buttons: scroll to exchanges`,
|
||||
});
|
||||
},
|
||||
}}
|
||||
button3={{
|
||||
label: "Social media posts",
|
||||
speed: 3,
|
||||
// link: "/#smartDeveloper",
|
||||
onClick: () => {
|
||||
mixpanel.get_distinct_id() &&
|
||||
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
|
||||
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Connected buttons: scroll to developer`,
|
||||
});
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<SchematicPlayground />
|
||||
</Flex>
|
||||
</GridItem>
|
||||
|
||||
<GridItem
|
||||
px="7%"
|
||||
colSpan="12"
|
||||
pt="66px"
|
||||
bgColor="blue.50"
|
||||
pb={["20px", "30px", "92px", null, "92px", "196px"]}
|
||||
>
|
||||
<Heading {...HEADING_PROPS} textAlign="center" pb={14} pt={0}>
|
||||
Featured by{" "}
|
||||
</Heading>
|
||||
<Flex wrap="wrap" direction="row" justifyContent="center">
|
||||
<Suspense fallback={""}>
|
||||
<TrustedBadge
|
||||
name="cointelegraph"
|
||||
caseURL=""
|
||||
ImgURL={assets["cointelegraph"]}
|
||||
/>
|
||||
<TrustedBadge
|
||||
name="CryptoInsiders"
|
||||
ImgURL={assets["cryptoinsiders"]}
|
||||
/>
|
||||
|
||||
<TrustedBadge
|
||||
name="cryptoslate"
|
||||
ImgURL={assets["cryptoslate"]}
|
||||
/>
|
||||
</Suspense>
|
||||
</Flex>
|
||||
</GridItem>
|
||||
<GridItem
|
||||
px="7%"
|
||||
colSpan="12"
|
||||
pt={["2rem", "2rem", "5.125rem", null, "5.125rem"]}
|
||||
pb={["0", "66px", null, "66px"]}
|
||||
id="txpool"
|
||||
minH={ui.isMobileView ? "100vh" : null}
|
||||
>
|
||||
<SplitWithImage
|
||||
cta={{
|
||||
label: "Want to find out more?",
|
||||
onClick: () => {
|
||||
mixpanel.get_distinct_id() &&
|
||||
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
|
||||
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Early access CTA: developer txpool button`,
|
||||
});
|
||||
toggleModal("hubspot-developer");
|
||||
},
|
||||
}}
|
||||
elementName={"element1"}
|
||||
colorScheme="green"
|
||||
badge={`NFTs`}
|
||||
title={`Custom analytics for NFTs`}
|
||||
body={`Moonstream automatically understands smart contracts. Create your own custom dashboards. Doesn’t matter what the custom behavior is, you can track it.`}
|
||||
bullets={[
|
||||
{
|
||||
text: `Who owns your NFTs?`,
|
||||
icon: AiOutlinePieChart,
|
||||
color: "green.50",
|
||||
bgColor: "green.900",
|
||||
},
|
||||
{
|
||||
text: `Who is selling your NFTs?`,
|
||||
icon: FaFileContract,
|
||||
color: "green.50",
|
||||
bgColor: "green.900",
|
||||
},
|
||||
{
|
||||
text: `How much are your NFTs being sold for on OpenSea, Nifty Gateway, Rarible?`,
|
||||
icon: RiDashboardFill,
|
||||
color: "green.50",
|
||||
bgColor: "green.900",
|
||||
},
|
||||
{
|
||||
text: `Who is using the custom features of your NFTs?`,
|
||||
icon: GiMeshBall,
|
||||
color: "green.50",
|
||||
bgColor: "green.900",
|
||||
},
|
||||
{
|
||||
text: `How are they using them?`,
|
||||
icon: RiMapPinUserLine,
|
||||
color: "green.50",
|
||||
bgColor: "green.900",
|
||||
},
|
||||
]}
|
||||
imgURL={assets["NFT"]}
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem
|
||||
px="7%"
|
||||
colSpan="12"
|
||||
pt={["2rem", "2rem", "5.125rem", null, "5.125rem"]}
|
||||
pb={["0", "66px", null, "66px"]}
|
||||
id="exchanges"
|
||||
minH={ui.isMobileView ? "100vh" : null}
|
||||
>
|
||||
<SplitWithImage
|
||||
cta={{
|
||||
label: "Want to find out more?",
|
||||
onClick: () => {
|
||||
mixpanel.get_distinct_id() &&
|
||||
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
|
||||
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Early access CTA: developer exchanges button`,
|
||||
});
|
||||
toggleModal("hubspot-developer");
|
||||
},
|
||||
}}
|
||||
elementName={"element2"}
|
||||
mirror={true}
|
||||
colorScheme="orange"
|
||||
badge={`ERC20`}
|
||||
title={`Feel the pulse of token activity`}
|
||||
body={`Visualize market activity with Moonstream dashboards. Monitor token activity on the blockchain and in the transaction pool.`}
|
||||
bullets={[
|
||||
{
|
||||
text: `Who owns your tokens?`,
|
||||
icon: GiSuspicious,
|
||||
color: "orange.50",
|
||||
bgColor: "orange.900",
|
||||
},
|
||||
{
|
||||
text: `What is your weekly, daily, or hourly transaction volume?`,
|
||||
icon: AiFillApi,
|
||||
color: "orange.50",
|
||||
bgColor: "orange.900",
|
||||
},
|
||||
{
|
||||
text: `Which exchanges is your token trending on?`,
|
||||
icon: IoTelescopeSharp,
|
||||
color: "orange.50",
|
||||
bgColor: "orange.900",
|
||||
},
|
||||
{
|
||||
text: `Which other tokens is your token being traded for?`,
|
||||
icon: BiTransfer,
|
||||
color: "orange.50",
|
||||
bgColor: "orange.900",
|
||||
},
|
||||
{
|
||||
text: `How many people are holding your token versus actively using it?
|
||||
`,
|
||||
icon: GiLogicGateXor,
|
||||
color: "orange.50",
|
||||
bgColor: "orange.900",
|
||||
},
|
||||
]}
|
||||
imgURL={assets["erc20"]}
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem
|
||||
px="7%"
|
||||
colSpan="12"
|
||||
pt={["2rem", "2rem", "5.125rem", null, "5.125rem"]}
|
||||
pb={["0", "66px", null, "66px"]}
|
||||
id="smartDeveloper"
|
||||
minH={ui.isMobileView ? "100vh" : null}
|
||||
>
|
||||
<SplitWithImage
|
||||
cta={{
|
||||
label: "Want to find out more?",
|
||||
onClick: () => {
|
||||
mixpanel.get_distinct_id() &&
|
||||
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
|
||||
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Early access CTA: developer smartDeveloper button`,
|
||||
});
|
||||
toggleModal("hubspot-developer");
|
||||
},
|
||||
}}
|
||||
elementName={"element3"}
|
||||
colorScheme="blue"
|
||||
title={`All the data you need to make a market`}
|
||||
badge={`DEXs`}
|
||||
body={`Monitor the performance of your DEX live from the blockchain and from the transaction pool. Build dashboards that show you DEX activity monthly, weekly, daily, hourly, or by the minute.`}
|
||||
bullets={[
|
||||
{
|
||||
text: `Who is providing liquidity on your DEX?`,
|
||||
icon: GiSuspicious,
|
||||
color: "blue.50",
|
||||
bgColor: "blue.900",
|
||||
},
|
||||
{
|
||||
text: `How much liquidity for each token pair?`,
|
||||
icon: GiMeshBall,
|
||||
color: "blue.50",
|
||||
bgColor: "blue.900",
|
||||
},
|
||||
{
|
||||
text: `Bot vs. human activity on your exchange`,
|
||||
icon: BiBot,
|
||||
color: "blue.50",
|
||||
bgColor: "blue.900",
|
||||
},
|
||||
{
|
||||
text: `How large is your transaction pool backlog?`,
|
||||
icon: GiHook,
|
||||
color: "blue.50",
|
||||
bgColor: "blue.900",
|
||||
},
|
||||
]}
|
||||
imgURL={assets["cryptoTraders"]}
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem
|
||||
px="7%"
|
||||
colSpan="12"
|
||||
pt={["2rem", "2rem", "5.125rem", null, "5.125rem"]}
|
||||
pb={["0", "66px", null, "66px"]}
|
||||
id="analytics"
|
||||
minH={ui.isMobileView ? "100vh" : null}
|
||||
>
|
||||
<SplitWithImage
|
||||
mirror
|
||||
cta={{
|
||||
label: "Want to find out more?",
|
||||
onClick: () => {
|
||||
mixpanel.get_distinct_id() &&
|
||||
mixpanel.track(`${MIXPANEL_EVENTS.BUTTON_CLICKED}`, {
|
||||
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Early access CTA: developer analytics button`,
|
||||
});
|
||||
toggleModal("hubspot-developer");
|
||||
},
|
||||
}}
|
||||
elementName={"element3"}
|
||||
colorScheme="red"
|
||||
badge={`DAOs`}
|
||||
title={`What really matters is community`}
|
||||
body={`Gain insight into your community. Build community dashboards to make participation more open. Monitor your DAO ecosystem.`}
|
||||
bullets={[
|
||||
{
|
||||
text: `Who are your community members?`,
|
||||
icon: VscOrganization,
|
||||
color: "red.50",
|
||||
bgColor: "red.900",
|
||||
},
|
||||
{
|
||||
text: `Who is actively participating?`,
|
||||
icon: GiSuspicious,
|
||||
color: "red.50",
|
||||
bgColor: "red.900",
|
||||
},
|
||||
{
|
||||
text: `What are the open initiatives for your DAO?`,
|
||||
icon: FaVoteYea,
|
||||
color: "red.50",
|
||||
bgColor: "red.900",
|
||||
},
|
||||
{
|
||||
text: `What is the level of participation for each initiative?`,
|
||||
icon: FiActivity,
|
||||
color: "red.50",
|
||||
bgColor: "red.900",
|
||||
},
|
||||
{
|
||||
text: `Which DAOs or other protocols interact with yours?
|
||||
`,
|
||||
icon: RiOrganizationChart,
|
||||
color: "red.50",
|
||||
bgColor: "red.900",
|
||||
},
|
||||
]}
|
||||
imgURL={assets["DAO"]}
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem
|
||||
placeItems="center"
|
||||
w="100%"
|
||||
colSpan="12"
|
||||
pt={["0", "0", "5.125rem", null, "5.125rem"]}
|
||||
pb="120px"
|
||||
px="7%"
|
||||
>
|
||||
<Center>
|
||||
<Stack placeContent="center">
|
||||
<Text fontWeight="500" fontSize="24px">
|
||||
Want to find out more? Reach out to us on{" "}
|
||||
<Link
|
||||
color="orange.900"
|
||||
onClick={() => {
|
||||
mixpanel.get_distinct_id() &&
|
||||
mixpanel.track(
|
||||
`${MIXPANEL_EVENTS.BUTTON_CLICKED}`,
|
||||
{
|
||||
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Join our discord`,
|
||||
}
|
||||
);
|
||||
}}
|
||||
isExternal
|
||||
href={"https://discord.gg/K56VNUQGvA"}
|
||||
<Stack direction="column" justifyContent="center">
|
||||
<chakra.span
|
||||
textAlign="center"
|
||||
fontWeight="600"
|
||||
fontSize="lg"
|
||||
w="100%"
|
||||
h="fit-content"
|
||||
>
|
||||
<Text
|
||||
mb={18}
|
||||
fontSize={["md", "2xl", "3xl", "3xl", "3xl", "4xl"]}
|
||||
>
|
||||
We believe that the blockchain is for everyone. This
|
||||
requires complete <b>transparency</b>. That’s why all our
|
||||
software is{" "}
|
||||
<chakra.span
|
||||
display="inline-block"
|
||||
textColor="orange.900"
|
||||
as={Link}
|
||||
href="https://github.com/bugout-dev/moonstream"
|
||||
>
|
||||
Discord
|
||||
</Link>{" "}
|
||||
or{" "}
|
||||
<Link
|
||||
color="orange.900"
|
||||
onClick={() => {
|
||||
mixpanel.get_distinct_id() &&
|
||||
mixpanel.track(
|
||||
`${MIXPANEL_EVENTS.BUTTON_CLICKED}`,
|
||||
{
|
||||
[`${MIXPANEL_PROPS.BUTTON_NAME}`]: `Early access CTA: developer want to find more button`,
|
||||
}
|
||||
);
|
||||
toggleModal({ type: MODAL_TYPES.HUBSPOT });
|
||||
}}
|
||||
>
|
||||
request early access
|
||||
</Link>
|
||||
<i>open source</i>
|
||||
</chakra.span>
|
||||
</Text>
|
||||
</Stack>
|
||||
</Center>
|
||||
</chakra.span>
|
||||
<Flex direction="row" flexWrap="wrap" placeContent="center">
|
||||
<RouteButton
|
||||
placeSelf="center"
|
||||
isExternal
|
||||
href={`https://github.com/bugout-dev/moonstream`}
|
||||
size="md"
|
||||
variant="outline"
|
||||
colorScheme="blue"
|
||||
w="250px"
|
||||
leftIcon={<FaGithubSquare />}
|
||||
>
|
||||
git clone moonstream
|
||||
</RouteButton>
|
||||
<RouteButton
|
||||
placeSelf="center"
|
||||
isExternal
|
||||
href={"https://discord.gg/K56VNUQGvA"}
|
||||
size="md"
|
||||
variant="outline"
|
||||
colorScheme="blue"
|
||||
leftIcon={<FaDiscord />}
|
||||
w="250px"
|
||||
>
|
||||
Join our Discord
|
||||
</RouteButton>
|
||||
</Flex>
|
||||
<RouteButton
|
||||
placeSelf="center"
|
||||
isExternal
|
||||
w={["100%", "100%", "fit-content", null]}
|
||||
maxW={["250px", null, "fit-content"]}
|
||||
href={`https://github.com/bugout-dev/moonstream`}
|
||||
size="lg"
|
||||
variant="solid"
|
||||
colorScheme="orange"
|
||||
>
|
||||
Sign up
|
||||
</RouteButton>
|
||||
</Stack>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
</Flex>
|
||||
|
@ -414,16 +799,6 @@ const Homepage = () => {
|
|||
};
|
||||
|
||||
export async function getStaticProps() {
|
||||
const metaTags = {
|
||||
title: "Moonstream.to: All your crypto data in one stream",
|
||||
description:
|
||||
"From the Ethereum transaction pool to Elon Musk’s latest tweets get all the crypto data you care about in one stream.",
|
||||
keywords:
|
||||
"blockchain, crypto, data, trading, smart contracts, ethereum, solana, transactions, defi, finance, decentralized",
|
||||
url: "https://www.moonstream.to",
|
||||
image: `${AWS_ASSETS_PATH}/crypto+traders.png`,
|
||||
};
|
||||
|
||||
const assetPreload = Object.keys(assets).map((key) => {
|
||||
return {
|
||||
rel: "preload",
|
||||
|
@ -436,7 +811,7 @@ export async function getStaticProps() {
|
|||
const preloads = assetPreload.concat(preconnects);
|
||||
|
||||
return {
|
||||
props: { metaTags, preloads },
|
||||
props: { metaTags: DEFAULT_METATAGS, preloads },
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,6 @@ const Product = () => {
|
|||
bgColor="transparent"
|
||||
backgroundImage={`url(${assets[`${background}`]})`}
|
||||
bgSize="cover"
|
||||
// boxSize="full"
|
||||
minH="100vh"
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
import React, { useContext, useEffect } from "react";
|
||||
import { getLayout } from "../../src/layouts/EntriesLayout";
|
||||
import StreamEntryDetails from "../../src/components/SteamEntryDetails";
|
||||
import UIContext from "../../src/core/providers/UIProvider/context";
|
||||
import {
|
||||
Box,
|
||||
Heading,
|
||||
Text,
|
||||
Stack,
|
||||
UnorderedList,
|
||||
ListItem,
|
||||
} from "@chakra-ui/react";
|
||||
import RouteButton from "../../src/components/RouteButton";
|
||||
const Entry = () => {
|
||||
console.count("render stream!");
|
||||
const ui = useContext(UIContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
if (ui?.currentTransaction) {
|
||||
document.title = `Stream details: ${ui.currentTransaction.hash}`;
|
||||
} else {
|
||||
document.title = `Stream`;
|
||||
}
|
||||
}
|
||||
}, [ui?.currentTransaction]);
|
||||
|
||||
if (ui?.currentTransaction) {
|
||||
return <StreamEntryDetails />;
|
||||
} else
|
||||
return (
|
||||
<Box px="7%" pt={12}>
|
||||
<>
|
||||
<Stack direction="column">
|
||||
<Heading>Stream view</Heading>
|
||||
<Text>
|
||||
In this view you can follow events that happen on your subscribed
|
||||
addresses
|
||||
</Text>
|
||||
<UnorderedList pl={4}>
|
||||
<ListItem>
|
||||
Click filter icon on right top corner to filter by specific
|
||||
address across your subscriptions
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
On event cards you can click at right corner to see detailed
|
||||
view!
|
||||
</ListItem>
|
||||
<ListItem>
|
||||
For any adress of interest here you can copy it and subscribe at
|
||||
subscription screen
|
||||
</ListItem>
|
||||
</UnorderedList>
|
||||
<RouteButton
|
||||
variant="solid"
|
||||
size="md"
|
||||
colorScheme="green"
|
||||
href="/welcome"
|
||||
>
|
||||
Learn how to use moonstream
|
||||
</RouteButton>
|
||||
</Stack>
|
||||
</>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
Entry.getLayout = getLayout;
|
||||
export default Entry;
|
|
@ -0,0 +1,36 @@
|
|||
import React from "react";
|
||||
import { VStack, Link, Heading, Icon } from "@chakra-ui/react";
|
||||
import { getLayout, getLayoutProps } from "../src/layouts/InfoPageLayout";
|
||||
import { MdPictureAsPdf } from "react-icons/md";
|
||||
|
||||
const Papers = () => {
|
||||
return (
|
||||
<VStack>
|
||||
<Heading py={12}>Whitepapers</Heading>
|
||||
<Link
|
||||
color="orange.900"
|
||||
href="https://github.com/bugout-dev/moonstream/blob/main/datasets/nfts/papers/ethereum-nfts.pdf"
|
||||
>
|
||||
An analysis of 7,020,950 NFT transactions on the Ethereum blockchain -
|
||||
October 22, 2021
|
||||
<Icon as={MdPictureAsPdf} color="red" display="inline-block" />
|
||||
</Link>
|
||||
</VStack>
|
||||
);
|
||||
};
|
||||
|
||||
Papers.getLayout = getLayout;
|
||||
export async function getStaticProps() {
|
||||
const metaTags = {
|
||||
title: "Moonstream: Whitepapers",
|
||||
description: "Whitepapers by moonstream.to",
|
||||
keywords:
|
||||
"blockchain, crypto, data, trading, smart contracts, ethereum, solana, transactions, defi, finance, decentralized, analytics, product, whitepapers",
|
||||
url: "https://www.moonstream.to/whitepapers",
|
||||
};
|
||||
const layoutProps = getLayoutProps();
|
||||
layoutProps.props.metaTags = { ...layoutProps.props.metaTags, ...metaTags };
|
||||
return { ...layoutProps };
|
||||
}
|
||||
|
||||
export default Papers;
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React, { useContext } from "react";
|
||||
import RouterLink from "next/link";
|
||||
import {
|
||||
Menu,
|
||||
|
@ -12,18 +12,23 @@ import {
|
|||
} from "@chakra-ui/react";
|
||||
import { RiAccountCircleLine } from "react-icons/ri";
|
||||
import useLogout from "../core/hooks/useLogout";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
import { ALL_NAV_PATHES } from "../core/constants";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
const AccountIconButton = (props) => {
|
||||
const { logout } = useLogout();
|
||||
const ui = useContext(UIContext);
|
||||
|
||||
return (
|
||||
<Menu>
|
||||
<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
|
||||
|
@ -40,6 +45,17 @@ const AccountIconButton = (props) => {
|
|||
</RouterLink>
|
||||
</MenuGroup>
|
||||
<MenuDivider />
|
||||
{ui.isMobileView &&
|
||||
ALL_NAV_PATHES.map((pathToLink) => {
|
||||
return (
|
||||
<MenuItem key={v4()}>
|
||||
<RouterLink href={pathToLink.path}>
|
||||
{pathToLink.title}
|
||||
</RouterLink>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
logout();
|
||||
|
|
|
@ -29,7 +29,7 @@ const AddNewIconButton = (props) => {
|
|||
as={IconButton}
|
||||
// onClick={ui.addNewDrawerState.onOpen}
|
||||
aria-label="Account menu"
|
||||
icon={<PlusSquareIcon />}
|
||||
icon={<PlusSquareIcon m={0} size="26px" />}
|
||||
// variant="outline"
|
||||
color="gray.100"
|
||||
/>
|
||||
|
|
|
@ -171,9 +171,10 @@ const AppNavbar = () => {
|
|||
variant="link"
|
||||
justifyContent="space-evenly"
|
||||
alignContent="center"
|
||||
h="32px"
|
||||
size={iconSize}
|
||||
colorScheme="blue"
|
||||
m={0}
|
||||
h="100%"
|
||||
/>
|
||||
{!isSearchBarActive && (
|
||||
<IconButton
|
||||
|
@ -198,10 +199,7 @@ const AppNavbar = () => {
|
|||
<Link href="/" alignSelf="center">
|
||||
<Image
|
||||
alignSelf="center"
|
||||
// as={Link}
|
||||
// to="/"
|
||||
h="2.5rem"
|
||||
minW="2.5rem"
|
||||
maxH="2.5rem"
|
||||
src={WHITE_LOGO_W_TEXT_URL}
|
||||
alt="Go to app root"
|
||||
/>
|
||||
|
|
|
@ -19,8 +19,6 @@ const ArrowCTA = (props) => {
|
|||
const box3Ref = useRef(null);
|
||||
const box4Ref = useRef(null);
|
||||
|
||||
// const gridRow = props.button4 ? [5, 4, 2, null, 2] : [4, 3, 2, null, 2];
|
||||
|
||||
const updateXarrow = useXarrow();
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import Draggable from "react-draggable";
|
||||
import { useXarrow } from "react-xarrows";
|
||||
const DragOnGrid = React.forwardRef((props, ref) => {
|
||||
const updateXarrow = useXarrow();
|
||||
|
||||
const [position, setPosition] = useState({
|
||||
x: props.defaultPosition.x * props.gridStep,
|
||||
y: props.defaultPosition.y * props.gridStep,
|
||||
});
|
||||
const [cellSize, setCellSize] = useState(props.gridStep);
|
||||
|
||||
useEffect(() => {
|
||||
setPosition({
|
||||
x: (position.x * props.gridStep) / cellSize,
|
||||
y: (position.y * props.gridStep) / cellSize,
|
||||
});
|
||||
setCellSize(props.gridStep);
|
||||
//eslint-disable-next-line
|
||||
}, [props.gridStep]);
|
||||
|
||||
const handleDrag = (e, eData) => {
|
||||
setTimeout(() => {
|
||||
updateXarrow();
|
||||
}, 50);
|
||||
setPosition({ x: position.x + eData.deltaX, y: position.y + eData.deltaY });
|
||||
};
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
nodeRef={ref}
|
||||
axis="both"
|
||||
handle=".handle"
|
||||
position={{ ...position }}
|
||||
grid={[props.gridStep, props.gridStep]}
|
||||
scale={1}
|
||||
onDrag={handleDrag}
|
||||
>
|
||||
{props.children}
|
||||
</Draggable>
|
||||
);
|
||||
});
|
||||
|
||||
export default DragOnGrid;
|
|
@ -1,517 +0,0 @@
|
|||
import React, { useEffect, useContext, useState, useCallback } from "react";
|
||||
import {
|
||||
Flex,
|
||||
Spinner,
|
||||
Button,
|
||||
Center,
|
||||
Text,
|
||||
Menu,
|
||||
MenuButton,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
MenuGroup,
|
||||
IconButton,
|
||||
Input,
|
||||
Select,
|
||||
Drawer,
|
||||
DrawerBody,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
DrawerOverlay,
|
||||
DrawerContent,
|
||||
DrawerCloseButton,
|
||||
useDisclosure,
|
||||
Tag,
|
||||
TagLabel,
|
||||
TagCloseButton,
|
||||
Stack,
|
||||
Spacer,
|
||||
} from "@chakra-ui/react";
|
||||
import { useSubscriptions } from "../core/hooks";
|
||||
import StreamEntry from "./StreamEntry";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
import { FaFilter } from "react-icons/fa";
|
||||
import useStream from "../core/hooks/useStream";
|
||||
import { ImCancelCircle } from "react-icons/im";
|
||||
import { previousEvent } from "../core/services/stream.service";
|
||||
import { PAGE_SIZE } from "../core/constants";
|
||||
import DataContext from "../core/providers/DataProvider/context";
|
||||
|
||||
const FILTER_TYPES = {
|
||||
ADDRESS: 0,
|
||||
GAS: 1,
|
||||
GAS_PRICE: 2,
|
||||
AMOUNT: 3,
|
||||
HASH: 4,
|
||||
DISABLED: 99,
|
||||
};
|
||||
const DIRECTIONS = { SOURCE: "from", DESTINATION: "to" };
|
||||
const CONDITION = {
|
||||
EQUAL: 0,
|
||||
CONTAINS: 1,
|
||||
LESS: 2,
|
||||
LESS_EQUAL: 3,
|
||||
GREATER: 4,
|
||||
GREATER_EQUAL: 5,
|
||||
NOT_EQUAL: 6,
|
||||
};
|
||||
|
||||
const EntriesNavigation = () => {
|
||||
const { cursor, setCursor, streamCache, setStreamCache } =
|
||||
useContext(DataContext);
|
||||
const ui = useContext(UIContext);
|
||||
const [firstLoading, setFirstLoading] = useState(true);
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { subscriptionsCache } = useSubscriptions();
|
||||
const [initialized, setInitialized] = useState(false);
|
||||
const [newFilterState, setNewFilterState] = useState([
|
||||
{
|
||||
type: FILTER_TYPES.ADDRESS,
|
||||
direction: DIRECTIONS.SOURCE,
|
||||
condition: CONDITION.EQUAL,
|
||||
value: null,
|
||||
},
|
||||
]);
|
||||
const [filterState, setFilterState] = useState([]);
|
||||
|
||||
const {
|
||||
eventsIsLoading,
|
||||
eventsRefetch,
|
||||
latestEventsRefetch,
|
||||
nextEventRefetch,
|
||||
previousEventRefetch,
|
||||
streamBoundary,
|
||||
setDefaultBoundary,
|
||||
loadPreviousEventHandler,
|
||||
loadNewesEventHandler,
|
||||
loadOlderEventsIsFetching,
|
||||
loadNewerEventsIsFetching,
|
||||
previousEventIsFetching,
|
||||
nextEventIsFetching,
|
||||
olderEvent,
|
||||
} = useStream(
|
||||
ui.searchTerm.q,
|
||||
streamCache,
|
||||
setStreamCache,
|
||||
cursor,
|
||||
setCursor
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!streamBoundary.start_time && !streamBoundary.end_time) {
|
||||
setDefaultBoundary();
|
||||
} else if (!initialized) {
|
||||
eventsRefetch();
|
||||
latestEventsRefetch();
|
||||
nextEventRefetch();
|
||||
previousEventRefetch();
|
||||
setInitialized(true);
|
||||
} else if (
|
||||
streamCache.length == 0 &&
|
||||
olderEvent?.event_timestamp &&
|
||||
firstLoading
|
||||
) {
|
||||
loadPreviousEventHandler();
|
||||
setFirstLoading(false);
|
||||
}
|
||||
}, [
|
||||
streamBoundary,
|
||||
initialized,
|
||||
setInitialized,
|
||||
setDefaultBoundary,
|
||||
eventsRefetch,
|
||||
latestEventsRefetch,
|
||||
nextEventRefetch,
|
||||
previousEventRefetch,
|
||||
]);
|
||||
|
||||
const setFilterProps = useCallback(
|
||||
(filterIdx, props) => {
|
||||
const newFilterProps = [...newFilterState];
|
||||
newFilterProps[filterIdx] = { ...newFilterProps[filterIdx], ...props };
|
||||
setNewFilterState(newFilterProps);
|
||||
},
|
||||
[newFilterState, setNewFilterState]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
subscriptionsCache.data?.subscriptions[0]?.id &&
|
||||
newFilterState[0]?.value === null
|
||||
) {
|
||||
setFilterProps(0, {
|
||||
value: subscriptionsCache?.data?.subscriptions[0]?.address,
|
||||
});
|
||||
}
|
||||
}, [subscriptionsCache, newFilterState, setFilterProps]);
|
||||
|
||||
const canCreate = false;
|
||||
|
||||
const canDelete = false;
|
||||
|
||||
const dropNewFilterArrayItem = (idx) => {
|
||||
const oldArray = [...newFilterState];
|
||||
|
||||
const newArray = oldArray.filter(function (ele) {
|
||||
return ele != oldArray[idx];
|
||||
});
|
||||
setNewFilterState(newArray);
|
||||
};
|
||||
|
||||
const dropFilterArrayItem = (idx) => {
|
||||
const oldArray = [...filterState];
|
||||
const newArray = oldArray.filter(function (ele) {
|
||||
return ele != oldArray[idx];
|
||||
});
|
||||
|
||||
setFilterState(newArray);
|
||||
setNewFilterState(newArray);
|
||||
ui.setSearchTerm(
|
||||
newArray
|
||||
.map((filter) => {
|
||||
return filter.direction + ":" + filter.value;
|
||||
})
|
||||
.join("+")
|
||||
);
|
||||
};
|
||||
|
||||
const handleFilterSubmit = () => {
|
||||
setFilterState(newFilterState);
|
||||
ui.setSearchTerm(
|
||||
newFilterState
|
||||
.map((filter) => {
|
||||
return filter.direction + ":" + filter.value;
|
||||
})
|
||||
.join("+")
|
||||
);
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleAddressChange = (idx) => (e) => {
|
||||
setFilterProps(idx, { value: e.target.value });
|
||||
};
|
||||
|
||||
const handleConditionChange = (idx) => (e) => {
|
||||
setFilterProps(idx, { condition: parseInt(e.target.value) });
|
||||
};
|
||||
|
||||
const handleFilterStateCallback = (props) => {
|
||||
const currentFilterState = [...filterState];
|
||||
currentFilterState.push({ ...props });
|
||||
|
||||
ui.setSearchTerm(
|
||||
currentFilterState
|
||||
.map((filter) => {
|
||||
return filter.direction + ":" + filter.value;
|
||||
})
|
||||
.join("+")
|
||||
);
|
||||
|
||||
setFilterState(currentFilterState);
|
||||
};
|
||||
if (subscriptionsCache.isLoading) return "";
|
||||
|
||||
return (
|
||||
<Flex
|
||||
id="JournalNavigation"
|
||||
height="100%"
|
||||
maxH="100%"
|
||||
overflow="hidden"
|
||||
direction="column"
|
||||
flexGrow={1}
|
||||
>
|
||||
{streamCache && !eventsIsLoading ? (
|
||||
<>
|
||||
<Drawer onClose={onClose} isOpen={isOpen} size="lg">
|
||||
<DrawerOverlay />
|
||||
<DrawerContent bgColor="gray.100">
|
||||
<DrawerCloseButton />
|
||||
<DrawerHeader>{`Filter results`}</DrawerHeader>
|
||||
<DrawerBody>
|
||||
<Text pt={2} fontWeight="600">
|
||||
Source:
|
||||
</Text>
|
||||
{newFilterState.map((filter, idx) => {
|
||||
if (filter.type === FILTER_TYPES.DISABLED) return "";
|
||||
return (
|
||||
<Flex
|
||||
key={`subscription-filter-item-${idx}`}
|
||||
direction="column"
|
||||
>
|
||||
<Flex
|
||||
mt={4}
|
||||
direction="row"
|
||||
flexWrap="nowrap"
|
||||
placeItems="center"
|
||||
bgColor="gray.300"
|
||||
borderRadius="md"
|
||||
>
|
||||
{filter.type === FILTER_TYPES.ADDRESS && (
|
||||
<>
|
||||
<Flex w="120px" placeContent="center">
|
||||
{filter.direction === DIRECTIONS.SOURCE
|
||||
? `From:`
|
||||
: `To:`}
|
||||
</Flex>
|
||||
<Select
|
||||
pr={2}
|
||||
w="180px"
|
||||
onChange={handleConditionChange(idx)}
|
||||
>
|
||||
<option value={CONDITION.EQUAL}>Is</option>
|
||||
<option value={CONDITION.NOT_EQUAL}>
|
||||
Is not
|
||||
</option>
|
||||
</Select>
|
||||
{filter.direction === DIRECTIONS.SOURCE && (
|
||||
<Select
|
||||
variant="solid"
|
||||
colorScheme="blue"
|
||||
name="address"
|
||||
onChange={handleAddressChange(idx)}
|
||||
>
|
||||
{!subscriptionsCache.isLoading &&
|
||||
subscriptionsCache?.data?.subscriptions.map(
|
||||
(subscription, idx) => {
|
||||
return (
|
||||
<option
|
||||
value={subscription.address}
|
||||
key={`subscription-filter-item-${idx}`}
|
||||
>
|
||||
{`${
|
||||
subscription.label
|
||||
} - ${subscription.address.slice(
|
||||
0,
|
||||
5
|
||||
)}...${subscription.address.slice(
|
||||
-3
|
||||
)}`}
|
||||
</option>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</Select>
|
||||
)}
|
||||
{filter.direction === DIRECTIONS.DESTINATION && (
|
||||
<Input
|
||||
type="text"
|
||||
onChange={(e) =>
|
||||
setFilterProps(idx, {
|
||||
value: e.target.value,
|
||||
})
|
||||
}
|
||||
placeholder="Type in address"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<IconButton
|
||||
placeItems="center"
|
||||
colorScheme="blue"
|
||||
variant="ghost"
|
||||
onClick={() => dropNewFilterArrayItem(idx)}
|
||||
icon={<ImCancelCircle />}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
<Menu>
|
||||
<MenuButton
|
||||
as={Button}
|
||||
mt={4}
|
||||
colorScheme="orange"
|
||||
variant="solid"
|
||||
>
|
||||
Add filter row
|
||||
</MenuButton>
|
||||
<MenuList>
|
||||
<MenuGroup title="source"></MenuGroup>
|
||||
<MenuItem
|
||||
onClick={() =>
|
||||
setNewFilterState([
|
||||
...newFilterState,
|
||||
{
|
||||
type: FILTER_TYPES.ADDRESS,
|
||||
direction: DIRECTIONS.SOURCE,
|
||||
condition: CONDITION.EQUAL,
|
||||
value:
|
||||
subscriptionsCache?.data?.subscriptions[0]
|
||||
?.address,
|
||||
},
|
||||
])
|
||||
}
|
||||
>
|
||||
Source
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() =>
|
||||
setNewFilterState([
|
||||
...newFilterState,
|
||||
{
|
||||
type: FILTER_TYPES.ADDRESS,
|
||||
direction: DIRECTIONS.DESTINATION,
|
||||
condition: CONDITION.EQUAL,
|
||||
value:
|
||||
subscriptionsCache?.data?.subscriptions[0]
|
||||
?.address,
|
||||
},
|
||||
])
|
||||
}
|
||||
>
|
||||
Destination
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</DrawerBody>
|
||||
<DrawerFooter pb={16} placeContent="center">
|
||||
<Button
|
||||
colorScheme="green"
|
||||
variant="solid"
|
||||
// type="submit"
|
||||
onClick={() => handleFilterSubmit()}
|
||||
>
|
||||
Apply selected filters
|
||||
</Button>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
<Flex h="3rem" w="100%" bgColor="gray.100" alignItems="center">
|
||||
<Flex maxW="90%">
|
||||
{filterState.map((filter, idx) => {
|
||||
if (filter.type === FILTER_TYPES.DISABLED) return "";
|
||||
return (
|
||||
<Tag
|
||||
key={`filter-badge-display-${idx}`}
|
||||
mx={1}
|
||||
size="lg"
|
||||
variant="solid"
|
||||
colorScheme="orange"
|
||||
>
|
||||
{filter?.type === FILTER_TYPES.ADDRESS && (
|
||||
<TagLabel>
|
||||
{filter.condition === CONDITION.NOT_EQUAL && "Not "}
|
||||
{filter.direction === DIRECTIONS.SOURCE
|
||||
? "From: "
|
||||
: "To: "}
|
||||
{subscriptionsCache?.data?.subscriptions.find(
|
||||
(subscription) =>
|
||||
subscription.address === filter.value
|
||||
)?.label ?? filter.value}
|
||||
</TagLabel>
|
||||
)}
|
||||
|
||||
<TagCloseButton onClick={() => dropFilterArrayItem(idx)} />
|
||||
</Tag>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
<Spacer />
|
||||
<IconButton
|
||||
mr={4}
|
||||
onClick={onOpen}
|
||||
colorScheme="blue"
|
||||
variant="ghost"
|
||||
icon={<FaFilter />}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
className="ScrollableWrapper"
|
||||
w="100%"
|
||||
overflowY="hidden"
|
||||
h="calc(100% - 3rem)"
|
||||
>
|
||||
<Flex
|
||||
className="Scrollable"
|
||||
id="StreamEntry"
|
||||
overflowY="scroll"
|
||||
direction="column"
|
||||
w="100%"
|
||||
//onScroll={(e) => handleScroll(e)}
|
||||
>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
{!loadNewerEventsIsFetching && !nextEventIsFetching ? (
|
||||
<Button
|
||||
onClick={() => {
|
||||
loadNewesEventHandler();
|
||||
}}
|
||||
variant="outline"
|
||||
colorScheme="green"
|
||||
>
|
||||
Load newer events
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
isLoading
|
||||
loadingText="Loading"
|
||||
variant="outline"
|
||||
colorScheme="green"
|
||||
></Button>
|
||||
)}
|
||||
</Stack>
|
||||
{streamCache
|
||||
.slice(
|
||||
cursor,
|
||||
streamCache.length <= cursor + PAGE_SIZE
|
||||
? streamCache.length
|
||||
: cursor + PAGE_SIZE
|
||||
)
|
||||
.map((entry, idx) => (
|
||||
<StreamEntry
|
||||
showOnboardingTooltips={false}
|
||||
key={`entry-list-${idx}`}
|
||||
entry={entry}
|
||||
disableDelete={!canDelete}
|
||||
disableCopy={!canCreate}
|
||||
filterCallback={handleFilterStateCallback}
|
||||
filterConstants={{ DIRECTIONS, CONDITION, FILTER_TYPES }}
|
||||
/>
|
||||
))}
|
||||
{previousEvent &&
|
||||
!loadOlderEventsIsFetching &&
|
||||
!previousEventIsFetching ? (
|
||||
<Center>
|
||||
<Button
|
||||
onClick={() => {
|
||||
loadPreviousEventHandler();
|
||||
}}
|
||||
variant="outline"
|
||||
colorScheme="green"
|
||||
>
|
||||
Load older events
|
||||
</Button>
|
||||
</Center>
|
||||
) : (
|
||||
<Center>
|
||||
{!previousEventIsFetching && !loadOlderEventsIsFetching ? (
|
||||
"Тransactions not found. You can subscribe to more addresses in Subscriptions menu."
|
||||
) : (
|
||||
<Button
|
||||
isLoading
|
||||
loadingText="Loading"
|
||||
variant="outline"
|
||||
colorScheme="green"
|
||||
></Button>
|
||||
)}
|
||||
</Center>
|
||||
)}
|
||||
</Flex>
|
||||
</Flex>
|
||||
</>
|
||||
) : (
|
||||
<Center>
|
||||
<Spinner
|
||||
mt="50%"
|
||||
size="lg"
|
||||
color="blue.500"
|
||||
thickness="4px"
|
||||
speed="1.5s"
|
||||
/>
|
||||
</Center>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default EntriesNavigation;
|
|
@ -1,133 +1,140 @@
|
|||
import React from "react";
|
||||
import { Flex, Text, Link, Heading } from "@chakra-ui/react";
|
||||
import CustomIcon from "../components/CustomIcon";
|
||||
import {
|
||||
Text,
|
||||
Link,
|
||||
Box,
|
||||
Container,
|
||||
SimpleGrid,
|
||||
Stack,
|
||||
Image as ChakraImage,
|
||||
useColorModeValue,
|
||||
VisuallyHidden,
|
||||
chakra,
|
||||
} from "@chakra-ui/react";
|
||||
import RouterLink from "next/link";
|
||||
|
||||
const ICONS = [
|
||||
{
|
||||
social: "discord",
|
||||
link: "https://discord.gg/K56VNUQGvA",
|
||||
},
|
||||
|
||||
{ social: "twit", link: "https://twitter.com/moonstreamto" },
|
||||
];
|
||||
|
||||
const SITEMAP_FLEX_PROPS = {
|
||||
px: 2,
|
||||
alignItems: "flex-start",
|
||||
flexGrow: 1,
|
||||
pb: 4,
|
||||
color: "white.300",
|
||||
fontWeight: "600",
|
||||
direction: "column",
|
||||
mr: 12,
|
||||
};
|
||||
import {
|
||||
WHITE_LOGO_W_TEXT_URL,
|
||||
ALL_NAV_PATHES,
|
||||
FOOTER_COLUMNS,
|
||||
} from "../core/constants";
|
||||
import { FaGithub, FaTwitter, FaDiscord } from "react-icons/fa";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
const LINKS_SIZES = {
|
||||
fontWeight: "300",
|
||||
fontSize: "lg",
|
||||
};
|
||||
|
||||
const Footer = () => (
|
||||
<Flex
|
||||
bg="brand.200"
|
||||
flexGrow="1"
|
||||
px="7%"
|
||||
width="100%"
|
||||
align="center"
|
||||
alignItems="self-end"
|
||||
justify={["center", "center", null, "space-between"]}
|
||||
direction={["column", "column", "row", null, "row"]}
|
||||
py="2.5rem"
|
||||
>
|
||||
<Flex
|
||||
p={0}
|
||||
direction={["column", "row", null, "row"]}
|
||||
flexGrow="2"
|
||||
flexWrap="wrap"
|
||||
maxW="40rem"
|
||||
const ListHeader = ({ children }) => {
|
||||
return (
|
||||
<Text
|
||||
fontWeight={"500"}
|
||||
fontSize={"lg"}
|
||||
mb={2}
|
||||
borderBottom="1px"
|
||||
borderColor="blue.700"
|
||||
textColor="blue.500"
|
||||
>
|
||||
<Flex {...SITEMAP_FLEX_PROPS}>
|
||||
<Heading pb={8} size="md">
|
||||
About
|
||||
</Heading>{" "}
|
||||
<RouterLink passHref href="/team">
|
||||
<Link {...LINKS_SIZES}>Team</Link>
|
||||
</RouterLink>
|
||||
<RouterLink passHref href="/product">
|
||||
<Link {...LINKS_SIZES}>Product</Link>
|
||||
</RouterLink>
|
||||
</Flex>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
};
|
||||
|
||||
<Flex {...SITEMAP_FLEX_PROPS}>
|
||||
<Heading pb={8} size="md">
|
||||
News
|
||||
</Heading>
|
||||
<RouterLink passHref href="http://blog.moonstream.to">
|
||||
<Link {...LINKS_SIZES}>Blog</Link>
|
||||
</RouterLink>
|
||||
{/* <RouterLink passHref href="/privacy-policy">
|
||||
<Link {...LINKS_SIZES}>Privacy policy</Link>
|
||||
</RouterLink> */}
|
||||
</Flex>
|
||||
{/* <Flex {...SITEMAP_FLEX_PROPS}>
|
||||
<Heading pb={8} size="md">
|
||||
Product
|
||||
</Heading>
|
||||
<RouterLink href="/pricing" passHref>
|
||||
<Link {...LINKS_SIZES}>Pricing</Link>
|
||||
</RouterLink>
|
||||
<RouterLink passHref href={"/case-studies/activeloop"}>
|
||||
<Link {...LINKS_SIZES}>Case studies</Link>
|
||||
</RouterLink>
|
||||
</Flex> */}
|
||||
</Flex>
|
||||
<Flex
|
||||
direction="column"
|
||||
flexGrow="1"
|
||||
w="100%"
|
||||
maxW="40rem"
|
||||
alignItems={["flex-end", "flex-end", null, "flex-end"]}
|
||||
pr={[0, null, 8]}
|
||||
const SocialButton = ({ children, label, href }) => {
|
||||
return (
|
||||
<chakra.button
|
||||
bg={useColorModeValue("blackAlpha.100", "whiteAlpha.100")}
|
||||
rounded={"full"}
|
||||
w={8}
|
||||
h={8}
|
||||
cursor={"pointer"}
|
||||
as={"a"}
|
||||
href={href}
|
||||
display={"inline-flex"}
|
||||
alignItems={"center"}
|
||||
justifyContent={"center"}
|
||||
transition={"background 0.3s ease"}
|
||||
_hover={{
|
||||
bg: useColorModeValue("blackAlpha.200", "whiteAlpha.200"),
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
color="white"
|
||||
pt={[24, 24, null, 0]}
|
||||
pb={8}
|
||||
fontSize="xl"
|
||||
fontWeight="500"
|
||||
<VisuallyHidden>{label}</VisuallyHidden>
|
||||
{children}
|
||||
</chakra.button>
|
||||
);
|
||||
};
|
||||
|
||||
const Footer = () => (
|
||||
<Box
|
||||
bg={useColorModeValue("blue.900", "gray.900")}
|
||||
color={useColorModeValue("gray.700", "gray.200")}
|
||||
>
|
||||
<Container as={Stack} maxW={"6xl"} py={10}>
|
||||
<SimpleGrid
|
||||
templateColumns={{ sm: "1fr 1fr", md: "2fr 1fr 1fr 2fr" }}
|
||||
spacing={8}
|
||||
>
|
||||
All the crypto data you care about{` `}
|
||||
<span role="img" aria-label="heart">
|
||||
💙
|
||||
</span>
|
||||
</Text>
|
||||
<Flex px={2} width="100%" maxW="30rem" justifyContent="flex-end">
|
||||
{ICONS.map((icon, index) => (
|
||||
<Link
|
||||
key={`social-footer-icons-${index}`}
|
||||
display="flex"
|
||||
mx={2}
|
||||
mb={2}
|
||||
borderRadius="13px"
|
||||
bg="blue.800"
|
||||
boxSize={["3rem", "4rem", "6rem", null, "6rem"]}
|
||||
_hover={{ bg: "blue.600" }}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
href={icon.link}
|
||||
isExternal
|
||||
p={4}
|
||||
>
|
||||
<CustomIcon icon={icon.social} />
|
||||
</Link>
|
||||
))}
|
||||
</Flex>
|
||||
<Text pt={24} alignSelf="flex-end" textColor="blue.500">
|
||||
All rights reserved.2021
|
||||
</Text>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Stack spacing={6}>
|
||||
<Box>
|
||||
<Link href="/" alignSelf="center">
|
||||
<ChakraImage
|
||||
alignSelf="center"
|
||||
// as={Link}
|
||||
// to="/"
|
||||
h="2.5rem"
|
||||
minW="2.5rem"
|
||||
src={WHITE_LOGO_W_TEXT_URL}
|
||||
alt="Go to app root"
|
||||
/>
|
||||
</Link>
|
||||
</Box>
|
||||
<Text fontSize={"sm"}>© 2021 Moonstream.to All rights reserved</Text>
|
||||
<Stack direction={"row"} spacing={6}>
|
||||
<SocialButton
|
||||
label={"Twitter"}
|
||||
href={"https://twitter.com/moonstreamto"}
|
||||
>
|
||||
<FaTwitter />
|
||||
</SocialButton>
|
||||
<SocialButton
|
||||
label={"Github"}
|
||||
href={"https://github.com/bugout-dev/moonstream"}
|
||||
>
|
||||
<FaGithub />
|
||||
</SocialButton>
|
||||
<SocialButton
|
||||
label={"Discord"}
|
||||
href={"https://discord.gg/K56VNUQGvA"}
|
||||
>
|
||||
<FaDiscord />
|
||||
</SocialButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{Object.values(FOOTER_COLUMNS).map((columnEnum) => {
|
||||
return (
|
||||
<Stack align={"flex-start"} key={v4()}>
|
||||
{ALL_NAV_PATHES.filter(
|
||||
(navPath) => navPath.footerCategory === columnEnum
|
||||
).length > 0 && (
|
||||
<>
|
||||
<ListHeader>{columnEnum}</ListHeader>
|
||||
{ALL_NAV_PATHES.filter(
|
||||
(navPath) => navPath.footerCategory === columnEnum
|
||||
).map((linkItem) => {
|
||||
return (
|
||||
<RouterLink passHref href={linkItem.path} key={v4()}>
|
||||
<Link {...LINKS_SIZES}>{linkItem.title}</Link>
|
||||
</RouterLink>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
</SimpleGrid>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export default Footer;
|
||||
|
|
|
@ -85,18 +85,18 @@ const LandingNavbar = () => {
|
|||
</Button>
|
||||
</RouterLink>
|
||||
)}
|
||||
{/* {!ui.isLoggedIn && (
|
||||
{!ui.isLoggedIn && (
|
||||
<Button
|
||||
colorScheme="whiteAlpha"
|
||||
variant="outline"
|
||||
colorScheme="orange"
|
||||
variant="solid"
|
||||
onClick={() => toggleModal("register")}
|
||||
size="sm"
|
||||
fontWeight="400"
|
||||
borderRadius="2xl"
|
||||
>
|
||||
Get started
|
||||
Sign Up
|
||||
</Button>
|
||||
)} */}
|
||||
)}
|
||||
{!ui.isLoggedIn && (
|
||||
<Button
|
||||
color="white"
|
||||
|
|
|
@ -39,9 +39,21 @@ const NewDashboard = (props) => {
|
|||
subscriptions: [
|
||||
{
|
||||
label: "",
|
||||
id: null,
|
||||
abi: false,
|
||||
subscription_id: null,
|
||||
isMethods: false,
|
||||
isEvents: false,
|
||||
generic: {
|
||||
transactions: {
|
||||
in: false,
|
||||
out: false,
|
||||
},
|
||||
value: {
|
||||
in: false,
|
||||
out: false,
|
||||
balance: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -99,20 +111,54 @@ const NewDashboard = (props) => {
|
|||
// borderWidth="1px"
|
||||
variant="simple"
|
||||
colorScheme="blue"
|
||||
justifyContent="center"
|
||||
// justifyContent="center"
|
||||
textAlign="center"
|
||||
borderBottomRadius="xl"
|
||||
alignItems="baseline"
|
||||
// alignItems="baseline"
|
||||
h="auto"
|
||||
size="sm"
|
||||
mt={0}
|
||||
>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Address</Th>
|
||||
<Th w="90px">ABI State</Th>
|
||||
<Th w="90px">Methods</Th>
|
||||
<Th w="90px">Events</Th>
|
||||
<Th w="60px"></Th>
|
||||
<Th textAlign="center">Address</Th>
|
||||
<Th textAlign="center" colSpan="3">
|
||||
ABI
|
||||
</Th>
|
||||
|
||||
<Th textAlign="center" colSpan="2">
|
||||
Transactions
|
||||
</Th>
|
||||
<Th textAlign="center" colSpan="3">
|
||||
Value
|
||||
</Th>
|
||||
</Tr>
|
||||
<Tr>
|
||||
<Th></Th>
|
||||
<Th p={1} textAlign="center">
|
||||
ABI
|
||||
</Th>
|
||||
<Th p={1} textAlign="center">
|
||||
Methods
|
||||
</Th>
|
||||
<Th p={1} textAlign="center">
|
||||
Events
|
||||
</Th>
|
||||
<Th p={1} textAlign="center">
|
||||
In
|
||||
</Th>
|
||||
<Th p={1} textAlign="center">
|
||||
Out
|
||||
</Th>
|
||||
<Th p={1} textAlign="center">
|
||||
In
|
||||
</Th>
|
||||
<Th p={1} textAlign="center">
|
||||
Out
|
||||
</Th>
|
||||
<Th p={1} textAlign="center">
|
||||
Balance
|
||||
</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
|
@ -127,7 +173,25 @@ const NewDashboard = (props) => {
|
|||
<Downshift
|
||||
onSelect={(selectedItem) => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx] = selectedItem;
|
||||
newState.subscriptions[idx] = {
|
||||
label: selectedItem.label,
|
||||
address: selectedItem.address,
|
||||
subscription_id: selectedItem.id,
|
||||
abi: selectedItem.abi,
|
||||
isMethods: false,
|
||||
isEvents: false,
|
||||
generic: {
|
||||
transactions: {
|
||||
in: false,
|
||||
out: false,
|
||||
},
|
||||
value: {
|
||||
in: false,
|
||||
out: false,
|
||||
balance: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
// isOpen={showSuggestions}
|
||||
|
@ -382,7 +446,7 @@ const NewDashboard = (props) => {
|
|||
// </AutoComplete>
|
||||
)}
|
||||
</Td>
|
||||
<Td w="90px">
|
||||
<Td p={1} textAlign="center">
|
||||
{subscibedItem.abi && subscibedItem.address && (
|
||||
<CheckCircleIcon color="green" />
|
||||
)}
|
||||
|
@ -404,24 +468,100 @@ const NewDashboard = (props) => {
|
|||
)}
|
||||
</Td>
|
||||
|
||||
<Td w="60px">
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={
|
||||
!subscibedItem.address || !subscibedItem.abi
|
||||
}
|
||||
isDisabled={!subscibedItem.abi}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx] = {
|
||||
...newState.subscriptions[idx],
|
||||
isMethods: !newState.subscriptions[idx].isMethods,
|
||||
};
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.isMethods}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td w="60px">
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={
|
||||
!subscibedItem.address || !subscibedItem.abi
|
||||
}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx] = {
|
||||
...newState.subscriptions[idx],
|
||||
isEvents: !newState.subscriptions[idx].isEvents,
|
||||
};
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.isEvents}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx].generic.transactions.in =
|
||||
!newState.subscriptions[idx].generic.transactions
|
||||
.in;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.transactions.in}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx].generic.transactions.out =
|
||||
!newState.subscriptions[idx].generic.transactions
|
||||
.out;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.transactions.out}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx].generic.value.in =
|
||||
!newState.subscriptions[idx].generic.value.in;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.value.in}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx].generic.value.out =
|
||||
!newState.subscriptions[idx].generic.value.out;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.value.out}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
<Td p={1} textAlign="center">
|
||||
<Checkbox
|
||||
isDisabled={!subscibedItem.address}
|
||||
onChange={() => {
|
||||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions[idx].generic.balance =
|
||||
!newState.subscriptions[idx].generic.balance;
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
isChecked={subscibedItem.generic.balance}
|
||||
></Checkbox>
|
||||
</Td>
|
||||
|
||||
<Td>
|
||||
<Td p={1} textAlign="center">
|
||||
{idx > 0 && (
|
||||
<CloseButton
|
||||
onClick={() => {
|
||||
|
@ -456,8 +596,21 @@ const NewDashboard = (props) => {
|
|||
const newState = { ...newDashboardForm };
|
||||
newState.subscriptions.push({
|
||||
label: "",
|
||||
id: null,
|
||||
abi: false,
|
||||
subscription_id: null,
|
||||
isMethods: false,
|
||||
isEvents: false,
|
||||
generic: {
|
||||
transactions: {
|
||||
in: false,
|
||||
out: false,
|
||||
},
|
||||
value: {
|
||||
in: false,
|
||||
out: false,
|
||||
balance: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
setNewDashboardForm(newState);
|
||||
}}
|
||||
|
|
|
@ -54,7 +54,6 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
|
|||
|
||||
useEffect(() => {
|
||||
if (initialValue && initialValue !== "") {
|
||||
console.log("iv:", initialValue, isAddress(initialValue));
|
||||
if (isAddress(initialValue)) {
|
||||
setAddress(initialValue);
|
||||
} else {
|
||||
|
@ -111,11 +110,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
|
|||
|
||||
if (!errors) return "";
|
||||
|
||||
console.log("selected type", type);
|
||||
console.log("pickerItems", pickerItems);
|
||||
|
||||
const filterFn = (item, inputValue) => {
|
||||
console.log("filterFN", item.name, inputValue);
|
||||
return (
|
||||
!inputValue || item.name.toUpperCase().includes(inputValue.toUpperCase())
|
||||
);
|
||||
|
@ -157,7 +152,6 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
|
|||
selectedItem,
|
||||
getRootProps,
|
||||
}) => {
|
||||
console.log("selected item,", selectedItem?.name);
|
||||
return (
|
||||
<Box pos="relative" w="100%">
|
||||
<Box
|
||||
|
@ -179,8 +173,6 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
|
|||
isTruncated
|
||||
fontSize="sm"
|
||||
{...getInputProps()}
|
||||
// defaultValue={selectedItem.name ?? undefined}
|
||||
// value={selectedItem.name}
|
||||
></Input>
|
||||
<InputRightAddon p={0}>
|
||||
<Button
|
||||
|
@ -189,13 +181,7 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
|
|||
m={0}
|
||||
p={0}
|
||||
colorScheme="gray"
|
||||
{...getToggleButtonProps({
|
||||
// onClick: () =>
|
||||
// console.log(
|
||||
// "ref: ",
|
||||
// downshiftRef.current.clearSelection()
|
||||
// ),
|
||||
})}
|
||||
{...getToggleButtonProps({})}
|
||||
aria-label={"toggle menu"}
|
||||
>
|
||||
↓
|
||||
|
@ -203,21 +189,8 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
|
|||
</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()}
|
||||
|
@ -290,34 +263,6 @@ const _NewSubscription = ({ onClose, setIsLoading, isModal, initialValue }) => {
|
|||
</Downshift>
|
||||
</>
|
||||
)}
|
||||
{/* {typesCache.data
|
||||
.sort((a, b) =>
|
||||
a?.name > b?.name ? 1 : b?.name > a?.name ? -1 : 0
|
||||
)
|
||||
.map((type) => {
|
||||
const radio = getRadioProps({
|
||||
value: type.id,
|
||||
isDisabled:
|
||||
(initialAddress && initialType) ||
|
||||
!type.active ||
|
||||
(isFreeOption && type.id !== "ethereum_blockchain"),
|
||||
});
|
||||
|
||||
return (
|
||||
<RadioCard
|
||||
px="8px"
|
||||
py="4px"
|
||||
mt="2px"
|
||||
w="190px"
|
||||
{...radio}
|
||||
key={`subscription_type_${type.id}`}
|
||||
label={type.description}
|
||||
iconURL={type.icon_url}
|
||||
>
|
||||
{type.name.slice(9, type.name.length)}
|
||||
</RadioCard>
|
||||
);
|
||||
})} */}
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { Stack, Container, chakra } from "@chakra-ui/react";
|
||||
|
||||
const RangeSelector_ = ({
|
||||
className,
|
||||
ranges,
|
||||
onChange,
|
||||
initialRange,
|
||||
size,
|
||||
}) => {
|
||||
const [range, setRange] = useState(initialRange ?? ranges[0]);
|
||||
const isFirstRun = useRef(true);
|
||||
|
||||
useEffect(() => {
|
||||
if (isFirstRun.current) {
|
||||
isFirstRun.current = false;
|
||||
} else {
|
||||
onChange(range);
|
||||
}
|
||||
}, [range, onChange]);
|
||||
|
||||
return (
|
||||
<Stack direction="row" className={className} h="min-content">
|
||||
{ranges.map((item, idx) => {
|
||||
const isActive = item === range ? true : false;
|
||||
return (
|
||||
<Container
|
||||
key={`date-range-${className}-${idx}`}
|
||||
bgColor={isActive ? "secondary.900" : "primary.50"}
|
||||
color={!isActive ? "primary.900" : "primary.50"}
|
||||
boxShadow="sm"
|
||||
borderRadius="md"
|
||||
fontSize={size}
|
||||
fontWeight="600"
|
||||
onClick={() => setRange(item)}
|
||||
_hover={{
|
||||
bgColor: isActive ? "secondary.900" : "secondary.50",
|
||||
}}
|
||||
cursor="pointer"
|
||||
py="2px"
|
||||
>
|
||||
{item}
|
||||
</Container>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const RangeSelector = chakra(RangeSelector_);
|
||||
|
||||
export default RangeSelector;
|
|
@ -0,0 +1,323 @@
|
|||
import React, { useRef } from "react";
|
||||
import { Box, Button, useBreakpointValue } from "@chakra-ui/react";
|
||||
import DragOnGrid from "./DragOnGrid";
|
||||
import Xarrow from "react-xarrows";
|
||||
|
||||
const SchematicPlayground = () => {
|
||||
const gridCellSize = useBreakpointValue({
|
||||
base: 24,
|
||||
sm: 32,
|
||||
md: 64,
|
||||
lg: 64,
|
||||
xl: 64,
|
||||
"2xl": 64,
|
||||
});
|
||||
const ref1 = useRef(null);
|
||||
const ref2 = useRef(null);
|
||||
const ref3 = useRef(null);
|
||||
const ref4 = useRef(null);
|
||||
const ref5 = useRef(null);
|
||||
const ref6 = useRef(null);
|
||||
const ref7 = useRef(null);
|
||||
const ref8 = useRef(null);
|
||||
if (!gridCellSize) return "";
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
h={`${(gridCellSize * 5 + 1).toString()}` + `px`}
|
||||
// h={`301px`}
|
||||
w="100%"
|
||||
bgColor="white"
|
||||
bgSize={`${(gridCellSize / 6).toString() + "px"} ${
|
||||
(gridCellSize / 6).toString() + "px"
|
||||
}, ${gridCellSize.toString() + "px"} ${gridCellSize.toString() + "px"}`}
|
||||
bgImage={`linear-gradient(to bottom, transparent ${
|
||||
(gridCellSize / 10).toString() + "px"
|
||||
}, white ${(gridCellSize / 10).toString() + "px"}),
|
||||
linear-gradient(to right, #dee3ea 1px, transparent 1px),
|
||||
linear-gradient(to right, transparent ${
|
||||
(gridCellSize / 10).toString() + "px"
|
||||
}, white ${(gridCellSize / 10).toString() + "px"}),
|
||||
linear-gradient(to bottom, #dee3ea 1px, transparent 1px)`}
|
||||
maxW={`${(gridCellSize * 11 + 1).toString()}` + `px`}
|
||||
placeSelf="center"
|
||||
>
|
||||
<DragOnGrid
|
||||
ref={ref4}
|
||||
gridStep={gridCellSize}
|
||||
defaultPosition={{ x: 5, y: 2 }}
|
||||
>
|
||||
<Button
|
||||
m={0}
|
||||
p={0}
|
||||
borderRadius="sm"
|
||||
ref={ref4}
|
||||
className="handle"
|
||||
minW={`${gridCellSize.toString}` + `px`}
|
||||
fontSize={(gridCellSize / 4).toString() + `px`}
|
||||
boxSize={`${gridCellSize.toString()}` + "px"}
|
||||
borderStyle="inset"
|
||||
bg="green.900"
|
||||
color="white"
|
||||
textAlign="center"
|
||||
zIndex={10}
|
||||
>
|
||||
MSTR
|
||||
</Button>
|
||||
</DragOnGrid>
|
||||
<DragOnGrid
|
||||
ref={ref2}
|
||||
gridStep={gridCellSize}
|
||||
defaultPosition={{ x: 4, y: 0 }}
|
||||
>
|
||||
<Button
|
||||
m={0}
|
||||
ref={ref2}
|
||||
p={0}
|
||||
borderRadius="sm"
|
||||
className="handle"
|
||||
minW={`${gridCellSize.toString}` + `px`}
|
||||
minH={`${gridCellSize.toString}` + `px`}
|
||||
fontSize={(gridCellSize / 4).toString() + `px`}
|
||||
boxSize={`${gridCellSize.toString()}` + "px"}
|
||||
bg="blue.900"
|
||||
color="white"
|
||||
textAlign="center"
|
||||
zIndex={10}
|
||||
>
|
||||
DEX
|
||||
</Button>
|
||||
</DragOnGrid>
|
||||
<DragOnGrid
|
||||
ref={ref3}
|
||||
gridStep={gridCellSize}
|
||||
defaultPosition={{ x: 3, y: 4 }}
|
||||
>
|
||||
<Button
|
||||
m={0}
|
||||
ref={ref3}
|
||||
p={0}
|
||||
borderRadius="sm"
|
||||
className="handle"
|
||||
minW={`${gridCellSize.toString}` + `px`}
|
||||
fontSize={(gridCellSize / 4).toString() + `px`}
|
||||
boxSize={`${gridCellSize.toString()}` + "px"}
|
||||
bg="orange.900"
|
||||
color="white"
|
||||
textAlign="center"
|
||||
zIndex={10}
|
||||
>
|
||||
NFT
|
||||
</Button>
|
||||
</DragOnGrid>
|
||||
<DragOnGrid
|
||||
ref={ref1}
|
||||
gridStep={gridCellSize}
|
||||
defaultPosition={{ x: -2, y: 1 }}
|
||||
>
|
||||
<Button
|
||||
m={0}
|
||||
ref={ref1}
|
||||
p={0}
|
||||
borderRadius="sm"
|
||||
className="handle"
|
||||
minW={`${gridCellSize.toString}` + `px`}
|
||||
fontSize={(gridCellSize / 4).toString() + `px`}
|
||||
boxSize={`${gridCellSize.toString()}` + "px"}
|
||||
bg="red.900"
|
||||
color="white"
|
||||
textAlign="center"
|
||||
zIndex={10}
|
||||
>
|
||||
ERC20
|
||||
</Button>
|
||||
</DragOnGrid>
|
||||
<DragOnGrid
|
||||
ref={ref4}
|
||||
gridStep={gridCellSize}
|
||||
defaultPosition={{ x: -3, y: 4 }}
|
||||
>
|
||||
<Button
|
||||
m={0}
|
||||
ref={ref5}
|
||||
p={0}
|
||||
borderRadius="sm"
|
||||
className="handle"
|
||||
minW={`${gridCellSize.toString}` + `px`}
|
||||
fontSize={(gridCellSize / 4).toString() + `px`}
|
||||
boxSize={`${gridCellSize.toString()}` + "px"}
|
||||
bg="red.900"
|
||||
color="white"
|
||||
textAlign="center"
|
||||
zIndex={10}
|
||||
>
|
||||
EIP1155
|
||||
</Button>
|
||||
</DragOnGrid>
|
||||
<DragOnGrid
|
||||
ref={ref4}
|
||||
gridStep={gridCellSize}
|
||||
defaultPosition={{ x: 3, y: 3 }}
|
||||
>
|
||||
<Button
|
||||
m={0}
|
||||
ref={ref6}
|
||||
p={0}
|
||||
borderRadius="sm"
|
||||
className="handle"
|
||||
minW={`${gridCellSize.toString}` + `px`}
|
||||
fontSize={(gridCellSize / 4).toString() + `px`}
|
||||
boxSize={`${gridCellSize.toString()}` + "px"}
|
||||
bg="blue.900"
|
||||
color="white"
|
||||
textAlign="center"
|
||||
zIndex={10}
|
||||
>
|
||||
ERC721
|
||||
</Button>
|
||||
</DragOnGrid>
|
||||
<DragOnGrid
|
||||
ref={ref4}
|
||||
gridStep={gridCellSize}
|
||||
defaultPosition={{ x: 4, y: 4 }}
|
||||
>
|
||||
<Button
|
||||
m={0}
|
||||
ref={ref7}
|
||||
p={0}
|
||||
borderRadius="sm"
|
||||
className="handle"
|
||||
minW={`${gridCellSize.toString}` + `px`}
|
||||
fontSize={(gridCellSize / 4).toString() + `px`}
|
||||
boxSize={`${gridCellSize.toString()}` + "px"}
|
||||
bg="green.900"
|
||||
color="white"
|
||||
textAlign="center"
|
||||
zIndex={10}
|
||||
>
|
||||
DAO
|
||||
</Button>
|
||||
</DragOnGrid>
|
||||
<DragOnGrid
|
||||
ref={ref4}
|
||||
gridStep={gridCellSize}
|
||||
defaultPosition={{ x: 2, y: 0 }}
|
||||
>
|
||||
<Button
|
||||
m={0}
|
||||
ref={ref8}
|
||||
p={0}
|
||||
borderRadius="sm"
|
||||
className="handle"
|
||||
minW={`${gridCellSize.toString}` + `px`}
|
||||
fontSize={(gridCellSize / 4).toString() + `px`}
|
||||
boxSize={`${gridCellSize.toString()}` + "px"}
|
||||
bg="orange.900"
|
||||
color="white"
|
||||
textAlign="center"
|
||||
zIndex={10}
|
||||
>
|
||||
Oracle
|
||||
</Button>
|
||||
</DragOnGrid>
|
||||
</Box>
|
||||
<Xarrow
|
||||
dashness={{
|
||||
strokeLen: 10,
|
||||
nonStrokeLen: 15,
|
||||
animation: 1 * 1,
|
||||
}}
|
||||
color="#920050"
|
||||
path="grid"
|
||||
gridBreak={(gridCellSize * 0.5).toString() + "px"}
|
||||
// startAnchor={"top"}
|
||||
showHead={false}
|
||||
start={ref3} //can be react ref
|
||||
end={ref4} //or an id
|
||||
/>
|
||||
<Xarrow
|
||||
dashness={{
|
||||
strokeLen: 10,
|
||||
nonStrokeLen: 15,
|
||||
animation: 1 * 1,
|
||||
}}
|
||||
color="#113350"
|
||||
path="grid"
|
||||
gridBreak={(gridCellSize * 0.5).toString() + "px"}
|
||||
showHead={false}
|
||||
start={ref2} //can be react ref
|
||||
end={ref4} //or an id
|
||||
/>
|
||||
<Xarrow
|
||||
dashness={{
|
||||
strokeLen: 10,
|
||||
nonStrokeLen: 15,
|
||||
animation: 1 * 1,
|
||||
}}
|
||||
color="#92D0F0"
|
||||
gridBreak={(gridCellSize * 0.5).toString() + "px"}
|
||||
path="grid"
|
||||
// startAnchor={"top"}
|
||||
showHead={false}
|
||||
start={ref1} //can be react ref
|
||||
end={ref4} //or an id
|
||||
/>
|
||||
<Xarrow
|
||||
dashness={{
|
||||
strokeLen: 10,
|
||||
nonStrokeLen: 15,
|
||||
animation: 1 * 1,
|
||||
}}
|
||||
color="#92D0F0"
|
||||
gridBreak={(gridCellSize * 0.5).toString() + "px"}
|
||||
path="grid"
|
||||
// startAnchor={"top"}
|
||||
showHead={false}
|
||||
start={ref5} //can be react ref
|
||||
end={ref4} //or an id
|
||||
/>
|
||||
<Xarrow
|
||||
dashness={{
|
||||
strokeLen: 10,
|
||||
nonStrokeLen: 15,
|
||||
animation: 1 * 1,
|
||||
}}
|
||||
color="#92D0F0"
|
||||
gridBreak={(gridCellSize * 0.5).toString() + "px"}
|
||||
path="grid"
|
||||
// startAnchor={"top"}
|
||||
showHead={false}
|
||||
start={ref6} //can be react ref
|
||||
end={ref4} //or an id
|
||||
/>
|
||||
<Xarrow
|
||||
dashness={{
|
||||
strokeLen: 10,
|
||||
nonStrokeLen: 15,
|
||||
animation: 1 * 1,
|
||||
}}
|
||||
color="#92D0F0"
|
||||
gridBreak={(gridCellSize * 0.5).toString() + "px"}
|
||||
path="grid"
|
||||
showHead={false}
|
||||
start={ref7} //can be react ref
|
||||
end={ref4} //or an id
|
||||
/>
|
||||
<Xarrow
|
||||
dashness={{
|
||||
strokeLen: 10,
|
||||
nonStrokeLen: 15,
|
||||
animation: 1 * 1,
|
||||
}}
|
||||
color="#92D0F0"
|
||||
gridBreak={(gridCellSize * 0.5).toString() + "px"}
|
||||
path="grid"
|
||||
showHead={false}
|
||||
start={ref8} //can be react ref
|
||||
end={ref4} //or an id
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SchematicPlayground;
|
|
@ -2,10 +2,12 @@ import { Flex, Box } from "@chakra-ui/react";
|
|||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useRouter } from "../core/hooks";
|
||||
import mixpanel from "mixpanel-browser";
|
||||
import { useXarrow, Xwrapper } from "react-xarrows";
|
||||
const Scrollable = (props) => {
|
||||
const scrollerRef = useRef();
|
||||
const router = useRouter();
|
||||
const [path, setPath] = useState();
|
||||
const updateXarrow = useXarrow();
|
||||
|
||||
const [scrollDepth, setScrollDepth] = useState(0);
|
||||
|
||||
|
@ -17,10 +19,11 @@ const Scrollable = (props) => {
|
|||
};
|
||||
|
||||
const handleScroll = (e) => {
|
||||
updateXarrow();
|
||||
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,
|
||||
});
|
||||
|
@ -48,15 +51,17 @@ const Scrollable = (props) => {
|
|||
overflowY="hidden"
|
||||
maxH="100%"
|
||||
>
|
||||
<Box
|
||||
className="Scrollable"
|
||||
direction="column"
|
||||
ref={scrollerRef}
|
||||
overflowY="scroll"
|
||||
onScroll={(e) => handleScroll(e)}
|
||||
>
|
||||
{props.children}
|
||||
</Box>
|
||||
<Xwrapper>
|
||||
<Box
|
||||
className="Scrollable"
|
||||
direction="column"
|
||||
ref={scrollerRef}
|
||||
overflowY="scroll"
|
||||
onScroll={(e) => handleScroll(e)}
|
||||
>
|
||||
{props.children}
|
||||
</Box>
|
||||
</Xwrapper>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,7 +8,14 @@ import {
|
|||
} from "react-pro-sidebar";
|
||||
import { useContext } from "react";
|
||||
import RouterLink from "next/link";
|
||||
import { Flex, Image, IconButton, Divider } from "@chakra-ui/react";
|
||||
import {
|
||||
Flex,
|
||||
Image,
|
||||
IconButton,
|
||||
Divider,
|
||||
Text,
|
||||
Button,
|
||||
} from "@chakra-ui/react";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
import React from "react";
|
||||
import {
|
||||
|
@ -17,13 +24,20 @@ import {
|
|||
ArrowRightIcon,
|
||||
LockIcon,
|
||||
} from "@chakra-ui/icons";
|
||||
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";
|
||||
import { MdSettings, MdDashboard } from "react-icons/md";
|
||||
import { WHITE_LOGO_W_TEXT_URL, ALL_NAV_PATHES } from "../core/constants";
|
||||
import { v4 } from "uuid";
|
||||
import useDashboard from "../core/hooks/useDashboard";
|
||||
import {
|
||||
DRAWER_TYPES,
|
||||
MODAL_TYPES,
|
||||
} from "../core/providers/OverlayProvider/constants";
|
||||
import OverlayContext from "../core/providers/OverlayProvider/context";
|
||||
|
||||
const Sidebar = () => {
|
||||
const ui = useContext(UIContext);
|
||||
const { dashboardsListCache } = useDashboard();
|
||||
const overlay = useContext(OverlayContext);
|
||||
return (
|
||||
<ProSidebar
|
||||
width="240px"
|
||||
|
@ -55,64 +69,103 @@ const Sidebar = () => {
|
|||
: ui.setSidebarCollapsed(!ui.sidebarCollapsed);
|
||||
}}
|
||||
/>
|
||||
<Image
|
||||
// h="full"
|
||||
// maxH="100%"
|
||||
maxW="120px"
|
||||
py="0.75rem"
|
||||
pl={5}
|
||||
src={WHITE_LOGO_W_TEXT_URL}
|
||||
alt="bugout.dev"
|
||||
/>
|
||||
<RouterLink href="/" passHref>
|
||||
<Image
|
||||
// h="full"
|
||||
// maxH="100%"
|
||||
maxW="120px"
|
||||
py="0.75rem"
|
||||
pl={5}
|
||||
src={WHITE_LOGO_W_TEXT_URL}
|
||||
alt="Moonstream To"
|
||||
/>
|
||||
</RouterLink>
|
||||
</Flex>
|
||||
</SidebarHeader>
|
||||
{ui.isLoggedIn && (
|
||||
<SidebarContent>
|
||||
<Menu iconShape="square"></Menu>
|
||||
<Menu iconShape="square"></Menu>
|
||||
{ui.isMobileView && (
|
||||
<Menu iconShape="square">
|
||||
<MenuItem icon={<HiAcademicCap />}>
|
||||
<RouterLink href="/welcome">
|
||||
Learn how to use Moonstream
|
||||
</RouterLink>
|
||||
<SidebarContent>
|
||||
<Divider borderColor="blue.600" />
|
||||
<Menu iconShape="square">
|
||||
{!ui.isLoggedIn && (
|
||||
<>
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
ui.toggleModal("register");
|
||||
ui.setSidebarToggled(false);
|
||||
}}
|
||||
>
|
||||
Sign up
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
ui.toggleModal({ type: MODAL_TYPES.LOGIN });
|
||||
ui.setSidebarToggled(false);
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</MenuItem>
|
||||
{ui.isMobileView &&
|
||||
ALL_NAV_PATHES.map((pathToLink) => {
|
||||
return (
|
||||
<MenuItem key={v4()}>
|
||||
<RouterLink href={pathToLink.path}>
|
||||
{pathToLink.title}
|
||||
</RouterLink>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</SidebarContent>
|
||||
)}
|
||||
{!ui.isLoggedIn && (
|
||||
<SidebarContent>
|
||||
{/* <Menu iconShape="square">
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
ui.toggleModal("register");
|
||||
ui.setSidebarToggled(false);
|
||||
}}
|
||||
>
|
||||
Sign up
|
||||
</MenuItem>
|
||||
</Menu> */}
|
||||
<Menu iconShape="square">
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
ui.toggleModal({ type: MODAL_TYPES.LOGIN });
|
||||
ui.setSidebarToggled(false);
|
||||
}}
|
||||
>
|
||||
Login
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
{" "}
|
||||
<RouterLink href="/product">Product </RouterLink>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
{" "}
|
||||
<RouterLink href="/team">Team </RouterLink>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</SidebarContent>
|
||||
)}
|
||||
{ui.isLoggedIn && (
|
||||
<>
|
||||
<Text
|
||||
textColor="gray.300"
|
||||
size="sm"
|
||||
justifyContent="center"
|
||||
fontWeight="600"
|
||||
pl={8}
|
||||
pt={3}
|
||||
>
|
||||
Dashboards
|
||||
</Text>
|
||||
<Menu iconShape="square">
|
||||
<>
|
||||
{dashboardsListCache.data &&
|
||||
dashboardsListCache.data.data.resources.map((dashboard) => {
|
||||
return (
|
||||
<MenuItem icon={<MdDashboard />} key={v4()}>
|
||||
<RouterLink href={`/dashboard/${dashboard?.id}`}>
|
||||
{dashboard.resource_data.name}
|
||||
</RouterLink>
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
<MenuItem>
|
||||
<Button
|
||||
variant="solid"
|
||||
colorScheme="orange"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
overlay.toggleDrawer(DRAWER_TYPES.NEW_DASHBOARD)
|
||||
}
|
||||
// w="100%"
|
||||
// borderRadius={0}
|
||||
>
|
||||
New dashboard
|
||||
</Button>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
)}
|
||||
<Divider
|
||||
colorScheme="blue"
|
||||
bgColor="gray.300"
|
||||
color="blue.700"
|
||||
borderColor="blue.700"
|
||||
/>
|
||||
</Menu>
|
||||
</SidebarContent>
|
||||
|
||||
<SidebarFooter style={{ paddingBottom: "3rem" }}>
|
||||
<Divider color="gray.300" w="100%" />
|
||||
|
@ -122,9 +175,17 @@ const Sidebar = () => {
|
|||
<RouterLink href="/account/tokens">API Tokens</RouterLink>
|
||||
</MenuItem>
|
||||
<MenuItem icon={<MdSettings />}>
|
||||
{" "}
|
||||
<RouterLink href="/subscriptions">Subscriptions </RouterLink>
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<Text
|
||||
pt={4}
|
||||
fontSize={"sm"}
|
||||
textColor="gray.700"
|
||||
textAlign="center"
|
||||
>
|
||||
© 2021 Moonstream.to
|
||||
</Text>
|
||||
</Menu>
|
||||
)}
|
||||
</SidebarFooter>
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
InputGroup,
|
||||
Button,
|
||||
Input,
|
||||
Link,
|
||||
InputRightElement,
|
||||
} from "@chakra-ui/react";
|
||||
import CustomIcon from "./CustomIcon";
|
||||
|
@ -91,19 +90,15 @@ const SignIn = ({ toggleModal }) => {
|
|||
<Box height="1px" width="100%" background="#eaebf8" mb="1.875rem" />
|
||||
</Text>
|
||||
<Text textAlign="center" fontSize="md" color="gray.1200">
|
||||
{/* Don`t have an account?{" "} */}
|
||||
We are in early access. If you would like to use Moonstream,{" "}
|
||||
<Link href={"https://discord.gg/V3tWaP36"} color="orange.900">
|
||||
contact us on Discord.
|
||||
</Link>
|
||||
{/* <Box
|
||||
Don`t have an account?{" "}
|
||||
<Box
|
||||
cursor="pointer"
|
||||
color="blue.800"
|
||||
as="span"
|
||||
onClick={() => toggleModal("register")}
|
||||
>
|
||||
Register
|
||||
</Box> */}
|
||||
</Box>
|
||||
</Text>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -127,12 +127,7 @@ const SplitWithImage = ({
|
|||
)}
|
||||
<Stack spacing={[2, 4]} justifyContent="center">
|
||||
{badge && (
|
||||
<Stack
|
||||
direction="row"
|
||||
placeContent={
|
||||
mirror && !ui.isMobileView ? "flex-end" : "flex-start"
|
||||
}
|
||||
>
|
||||
<Stack direction="row" placeContent={"flex-start"}>
|
||||
<Text
|
||||
id={`MoonBadge ${elementName}`}
|
||||
textTransform={"uppercase"}
|
||||
|
|
|
@ -69,7 +69,7 @@ const List = ({ data, revoke, isLoading, update, filter }) => {
|
|||
const filteredTokens = sortedTokens.filter((item) => {
|
||||
if (filter === null || filter === undefined || filter === "") {
|
||||
return true;
|
||||
} else return item.note.includes(filter);
|
||||
} else return item.note?.includes(filter);
|
||||
});
|
||||
|
||||
setStateData({ ...data, token: [...filteredTokens] });
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import { React } from "react";
|
||||
import { Flex, Image, Link } from "@chakra-ui/react";
|
||||
|
||||
const TrustedBadge = ({ name, caseURL, ImgURL }) => {
|
||||
return (
|
||||
<Flex
|
||||
m={1}
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
alignSelf="center"
|
||||
wrap="nowrap"
|
||||
p={8}
|
||||
direction="column"
|
||||
>
|
||||
<Image
|
||||
sx={{ filter: "grayscale(100%)" }}
|
||||
h={["2.25rem", null, "3rem", "3rem", "4rem", "6rem"]}
|
||||
src={ImgURL}
|
||||
alt={name}
|
||||
></Image>
|
||||
{caseURL && (
|
||||
// <RouterLink href={caseURL} passHref scroll={true}>
|
||||
<Link
|
||||
fontSize={["sm", null, "md", "lg"]}
|
||||
textColor="orange.900"
|
||||
href={caseURL}
|
||||
>
|
||||
{`Read more >`}
|
||||
</Link>
|
||||
// </RouterLink>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
export default TrustedBadge;
|
|
@ -1,8 +1,6 @@
|
|||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { Flex, ButtonGroup, Button, useToast, Spinner } from "@chakra-ui/react";
|
||||
import { useSubscriptions } from "../core/hooks";
|
||||
// import hljs from "highlight.js";
|
||||
// import ReactQuill from "react-quill"; // ES6
|
||||
import AceEditor from "react-ace";
|
||||
import "ace-builds/src-noconflict/theme-github";
|
||||
import "ace-builds/src-noconflict/mode-json";
|
||||
|
@ -21,7 +19,6 @@ const ABIUPLoad = (props) => {
|
|||
try {
|
||||
return JSON.parse(raw);
|
||||
} catch (err) {
|
||||
console.log("e", err.message);
|
||||
if (!toast.isActive("upload_json_fails")) {
|
||||
toast({
|
||||
id: "upload_json_fails",
|
||||
|
@ -47,7 +44,7 @@ const ABIUPLoad = (props) => {
|
|||
status: "success",
|
||||
variant: "subtle",
|
||||
});
|
||||
}, [updateSubscription.isSuccess, toggleModal]);
|
||||
}, [updateSubscription.isSuccess, toggleModal, toast]);
|
||||
|
||||
useEffect(() => {
|
||||
if (json === false) {
|
||||
|
@ -62,7 +59,6 @@ const ABIUPLoad = (props) => {
|
|||
};
|
||||
const handleSubmit = () => {
|
||||
if (json) {
|
||||
console.log("id:", props.id);
|
||||
updateSubscription.mutate({
|
||||
id: props.id,
|
||||
abi: JSON.stringify(json),
|
||||
|
|
|
@ -6,23 +6,51 @@ export const BUGOUT_ENDPOINTS = {
|
|||
};
|
||||
|
||||
export const DEFAULT_METATAGS = {
|
||||
title: "Moonstream.to: All your crypto data in one stream",
|
||||
title: "Moonstream: Open source blockchain analytics",
|
||||
description:
|
||||
"From the Ethereum transaction pool to Elon Musk’s latest tweets get all the crypto data you care about in one stream.",
|
||||
"Product analytics for Web3. Moonstream helps you understand exactly how people are using your smart contracts",
|
||||
keywords:
|
||||
"blockchain, crypto, data, trading, smart contracts, ethereum, solana, transactions, defi, finance, decentralized",
|
||||
"analytics, blockchain analytics, protocol, protocols, blockchain, crypto, data, trading, smart contracts, web3, smart contract, ethereum, solana, polygon, matic, transactions, defi, finance, decentralized, mempool, NFT, NFTs, DAO, DAOs, DEX, DEXes, DEXs, cryptocurrency, cryptocurrencies, bitcoin",
|
||||
url: "https://www.moonstream.to",
|
||||
image: `https://s3.amazonaws.com/static.simiotics.com/moonstream/assets/crypto+traders.png`,
|
||||
};
|
||||
|
||||
export const FOOTER_COLUMNS = {
|
||||
NEWS: "News",
|
||||
COMPANY: "Company",
|
||||
PRODUCT: "Product",
|
||||
};
|
||||
|
||||
export const ALL_NAV_PATHES = [
|
||||
{
|
||||
title: "Product",
|
||||
path: "/product",
|
||||
footerCategory: FOOTER_COLUMNS.PRODUCT,
|
||||
},
|
||||
{
|
||||
title: "Team",
|
||||
path: "/team",
|
||||
footerCategory: FOOTER_COLUMNS.COMPANY,
|
||||
},
|
||||
{
|
||||
title: "Docs",
|
||||
path: "/docs",
|
||||
footerCategory: FOOTER_COLUMNS.PRODUCT,
|
||||
},
|
||||
{
|
||||
title: "Whitepapers",
|
||||
path: "/whitepapers",
|
||||
footerCategory: FOOTER_COLUMNS.PRODUCT,
|
||||
},
|
||||
{
|
||||
title: "Blog",
|
||||
path: "https://blog.moonstream.to",
|
||||
footerCategory: FOOTER_COLUMNS.NEWS,
|
||||
},
|
||||
{
|
||||
title: "Status",
|
||||
path: "/status",
|
||||
footerCategory: FOOTER_COLUMNS.PRODUCT,
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -36,4 +64,10 @@ export const USER_NAV_PATHES = [
|
|||
export const PAGE_SIZE = 20;
|
||||
|
||||
export const AWS_ASSETS_PATH = `https://s3.amazonaws.com/static.simiotics.com/moonstream/assets`;
|
||||
export const WHITE_LOGO_W_TEXT_URL = `https://s3.amazonaws.com/static.simiotics.com/moonstream/assets/moon-logo%2Btext-white.svg`;
|
||||
export const WHITE_LOGO_W_TEXT_URL = `https://s3.amazonaws.com/static.simiotics.com/moonstream/assets/moon-logo%2Btext-white.png`;
|
||||
|
||||
export const TIME_RANGE_SECONDS = {
|
||||
day: 86400,
|
||||
week: 86400 * 7,
|
||||
month: 86400 * 28,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import { useMutation, useQuery } from "react-query";
|
||||
import { useToast } from ".";
|
||||
import { queryCacheProps } from "./hookCommon";
|
||||
import { DashboardService } from "../services";
|
||||
import { useContext } from "react";
|
||||
import UserContext from "../providers/UserProvider/context";
|
||||
|
||||
const useDashboard = (dashboardId) => {
|
||||
const toast = useToast();
|
||||
const { user } = useContext(UserContext);
|
||||
|
||||
const dashboardsListCache = useQuery(
|
||||
["dashboards-list"],
|
||||
DashboardService.getDashboardsList,
|
||||
{
|
||||
...queryCacheProps,
|
||||
onError: (error) => {
|
||||
toast(error, "error");
|
||||
},
|
||||
enabled: !!user,
|
||||
}
|
||||
);
|
||||
|
||||
const createDashboard = useMutation(DashboardService.createDashboard, {
|
||||
onSuccess: () => {
|
||||
toast("Created new dashboard", "success");
|
||||
},
|
||||
onError: (error) => {
|
||||
toast(error.error, "error", "Fail");
|
||||
},
|
||||
onSettled: () => {
|
||||
dashboardsListCache.refetch();
|
||||
},
|
||||
});
|
||||
|
||||
const deleteDashboard = useMutation(
|
||||
() => DashboardService.deleteDashboard(dashboardId),
|
||||
{
|
||||
onSuccess: () => {
|
||||
toast("Deleted dashboard", "success");
|
||||
},
|
||||
onError: (error) => {
|
||||
toast(error.error, "error", "Fail");
|
||||
},
|
||||
onSettled: () => {
|
||||
dashboardsListCache.refetch();
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
console.log("dashboardId in hook:", dashboardId, !!user && !!dashboardId);
|
||||
const dashboardCache = useQuery(
|
||||
["dashboards", { dashboardId }],
|
||||
() => DashboardService.getDashboard(dashboardId),
|
||||
{
|
||||
...queryCacheProps,
|
||||
onError: (error) => {
|
||||
toast(error, "error");
|
||||
},
|
||||
enabled: !!user && !!dashboardId,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
createDashboard,
|
||||
dashboardsListCache,
|
||||
dashboardCache,
|
||||
deleteDashboard,
|
||||
};
|
||||
};
|
||||
|
||||
export default useDashboard;
|
|
@ -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;
|
|
@ -1,4 +1,10 @@
|
|||
import React, { useState, useLayoutEffect, useContext, Suspense } from "react";
|
||||
import React, {
|
||||
useState,
|
||||
useLayoutEffect,
|
||||
useContext,
|
||||
Suspense,
|
||||
useEffect,
|
||||
} from "react";
|
||||
import OverlayContext from "./context";
|
||||
import { MODAL_TYPES, DRAWER_TYPES } from "./constants";
|
||||
import {
|
||||
|
@ -28,6 +34,7 @@ import {
|
|||
} from "@chakra-ui/react";
|
||||
import UserContext from "../UserProvider/context";
|
||||
import UIContext from "../UIProvider/context";
|
||||
import useDashboard from "../../hooks/useDashboard";
|
||||
const ForgotPassword = React.lazy(() =>
|
||||
import("../../../components/ForgotPassword")
|
||||
);
|
||||
|
@ -43,6 +50,7 @@ const NewSubscription = React.lazy(() =>
|
|||
const UploadABI = React.lazy(() => import("../../../components/UploadABI"));
|
||||
|
||||
const OverlayProvider = ({ children }) => {
|
||||
const { createDashboard } = useDashboard();
|
||||
const ui = useContext(UIContext);
|
||||
const { user } = useContext(UserContext);
|
||||
const [modal, toggleModal] = useState({
|
||||
|
@ -107,9 +115,19 @@ const OverlayProvider = ({ children }) => {
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ui.isAppView, ui.isAppReady, user, ui.isLoggingOut, modal.type]);
|
||||
|
||||
const finishNewDashboard = () => {
|
||||
toggleDrawer(DRAWER_TYPES.OFF);
|
||||
window.sessionStorage.removeItem("new_dashboard");
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (createDashboard.isSuccess) {
|
||||
finishNewDashboard();
|
||||
}
|
||||
}, [createDashboard.isSuccess]);
|
||||
return (
|
||||
<OverlayContext.Provider
|
||||
value={{ modal, toggleModal, drawer, toggleDrawer }}
|
||||
value={{ modal, toggleModal, drawer, toggleDrawer, toggleAlert }}
|
||||
>
|
||||
<AlertDialog
|
||||
isOpen={alertDisclosure.isOpen}
|
||||
|
@ -227,15 +245,45 @@ const OverlayProvider = ({ children }) => {
|
|||
<Button
|
||||
variant="outline"
|
||||
mr={3}
|
||||
onClick={() => toggleAlert(() => toggleDrawer(DRAWER_TYPES.OFF))}
|
||||
onClick={() => toggleAlert(() => finishNewDashboard())}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
colorScheme="blue"
|
||||
isLoading={createDashboard.isLoading}
|
||||
onClick={() => {
|
||||
//TODO: @Peersky Implement logic part
|
||||
console.log("submit clicked");
|
||||
const dashboardState = JSON.parse(
|
||||
sessionStorage.getItem("new_dashboard")
|
||||
);
|
||||
createDashboard.mutate({
|
||||
name: dashboardState.name,
|
||||
subscriptions: dashboardState.subscriptions.map(
|
||||
(pickedSubscription) => {
|
||||
const retval = {
|
||||
subscription_id: pickedSubscription.subscription_id,
|
||||
generic: [],
|
||||
all_methods: !!pickedSubscription.isMethods,
|
||||
all_events: !!pickedSubscription.isEvents,
|
||||
};
|
||||
|
||||
pickedSubscription.generic.transactions.in &&
|
||||
retval.generic.push({ name: "transactions_in" });
|
||||
pickedSubscription.generic.transactions.out &&
|
||||
retval.generic.push({ name: "transactions_out" });
|
||||
pickedSubscription.generic.value.in &&
|
||||
retval.generic.push({ name: "value_in" });
|
||||
pickedSubscription.generic.value.out &&
|
||||
retval.generic.push({ name: "value_out" });
|
||||
pickedSubscription.generic.balance &&
|
||||
retval.generic.push({ name: "balance" });
|
||||
retval["methods"] = [];
|
||||
retval["events"] = [];
|
||||
|
||||
return retval;
|
||||
}
|
||||
),
|
||||
});
|
||||
}}
|
||||
>
|
||||
Submit
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import { http } from "../utils";
|
||||
|
||||
const API_URL = process.env.NEXT_PUBLIC_MOONSTREAM_API_URL;
|
||||
|
||||
export const createDashboard = (data) => {
|
||||
return http({
|
||||
method: "POST",
|
||||
url: `${API_URL}/dashboards`,
|
||||
data,
|
||||
});
|
||||
};
|
||||
|
||||
export const getDashboardsList = () => {
|
||||
return http({
|
||||
method: "GET",
|
||||
url: `${API_URL}/dashboards`,
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteDashboard = (id) => {
|
||||
console.log("delete:", id);
|
||||
return http({
|
||||
method: "DELETE",
|
||||
url: `${API_URL}/dashboards/${id}/`,
|
||||
});
|
||||
};
|
||||
|
||||
export const getDashboard = (dashboardId) => {
|
||||
console.log("get dashboard");
|
||||
// const dashboardId = query.queryKey[2].dashboardId;
|
||||
// console.assert(
|
||||
// dashboardId,
|
||||
// "No dashboard ID found in query object that was passed to service"
|
||||
// );
|
||||
console.log("service", dashboardId);
|
||||
return http({
|
||||
method: "GET",
|
||||
url: `${API_URL}/dashboards/${dashboardId}/data_links`,
|
||||
});
|
||||
};
|
|
@ -10,6 +10,7 @@ import * as StatusService from "./status.service";
|
|||
import * as SubscriptionsService from "./subscriptions.service";
|
||||
import * as StreamService from "./stream.service";
|
||||
import * as TxInfoService from "./txinfo.service";
|
||||
import * as DashboardService from "./dashboard.service";
|
||||
export {
|
||||
AuthService,
|
||||
JournalService,
|
||||
|
@ -23,4 +24,5 @@ export {
|
|||
SubscriptionsService,
|
||||
StreamService,
|
||||
TxInfoService,
|
||||
DashboardService,
|
||||
};
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { http } from "../utils";
|
||||
// import axios from "axios";
|
||||
|
||||
const API = process.env.NEXT_PUBLIC_MOONSTREAM_API_URL;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import enableMockupRequests from "./mockupRequests";
|
||||
let axios = require("axios");
|
||||
|
||||
enableMockupRequests(axios);
|
||||
process.env.NODE_ENV !== "production" && enableMockupRequests(axios);
|
||||
|
||||
const http = (config) => {
|
||||
const token = localStorage.getItem("MOONSTREAM_ACCESS_TOKEN");
|
||||
|
|
|
@ -27,7 +27,7 @@ const randDate = () => {
|
|||
new Date(+new Date() - Math.floor(Math.random() * 10000000000))
|
||||
).format("MM/DD/YYYY");
|
||||
};
|
||||
export let MockSubscriptions = [
|
||||
let MockSubscriptions = [
|
||||
{
|
||||
label: "Bobs wallet",
|
||||
address: `0x` + makeid(24),
|
||||
|
@ -234,5 +234,28 @@ const enableMockupRequests = (axiosInstance) => {
|
|||
offset: 0,
|
||||
},
|
||||
});
|
||||
|
||||
mock.onGet(`${MOCK_API}/subscriptions/`).reply(200, {
|
||||
data: {
|
||||
is_free_subscription_availible: true,
|
||||
subscriptions: MockSubscriptions,
|
||||
},
|
||||
});
|
||||
|
||||
mock.onPost(`${MOCK_API}/subscriptions/`).reply((config) => {
|
||||
const params = config.data; // FormData of {name: ..., file: ...}
|
||||
const id = params.get("id");
|
||||
const label = params.get("label");
|
||||
const address = params.get("address");
|
||||
const subscription_type = params.get("subscription_type");
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
setTimeout(function () {
|
||||
const data = { id, label, address, subscription_type };
|
||||
MockSubscriptions.push({ ...data });
|
||||
resolve([200, { message: "OK", result: true }]);
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
};
|
||||
export default enableMockupRequests;
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
import React from "react";
|
||||
import { useBreakpointValue, Flex } from "@chakra-ui/react";
|
||||
import SplitPane, { Pane } from "react-split-pane";
|
||||
import { getLayout as getSiteLayout } from "./AppLayout";
|
||||
import EntriesNavigation from "../components/EntriesNavigation";
|
||||
import { useContext } from "react";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
const EntriesLayout = (props) => {
|
||||
const ui = useContext(UIContext);
|
||||
const defaultWidth = useBreakpointValue({
|
||||
base: "14rem",
|
||||
sm: "16rem",
|
||||
md: "18rem",
|
||||
lg: "20rem",
|
||||
xl: "22rem",
|
||||
"2xl": "24rem",
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex id="Entries" flexGrow={1} maxW="100%">
|
||||
<SplitPane
|
||||
allowResize={false}
|
||||
split="vertical"
|
||||
defaultSize={defaultWidth}
|
||||
primary="first"
|
||||
minSize={defaultWidth}
|
||||
pane1Style={
|
||||
ui.entriesViewMode === "list"
|
||||
? { transition: "1s", width: "100%" }
|
||||
: ui.entriesViewMode === "entry"
|
||||
? { transition: "1s", width: "0%" }
|
||||
: {
|
||||
overflowX: "hidden",
|
||||
height: "100%",
|
||||
width: ui.isMobileView ? "100%" : "55%",
|
||||
}
|
||||
}
|
||||
pane2Style={
|
||||
ui.entriesViewMode === "entry"
|
||||
? { transition: "1s", width: "0%" }
|
||||
: ui.entriesViewMode === "list"
|
||||
? {
|
||||
transition: "1s",
|
||||
width: "100%",
|
||||
}
|
||||
: { overflowX: "hidden", height: "100%" }
|
||||
}
|
||||
style={{
|
||||
position: "relative",
|
||||
height: "100%",
|
||||
flexBasis: "100px",
|
||||
overflowX: "hidden",
|
||||
}}
|
||||
>
|
||||
<Pane
|
||||
className="EntriesNavigation"
|
||||
style={{
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
<EntriesNavigation />
|
||||
</Pane>
|
||||
|
||||
<Pane
|
||||
className="EntryScreen"
|
||||
style={{
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</Pane>
|
||||
</SplitPane>
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const getLayout = (page) =>
|
||||
getSiteLayout(<EntriesLayout>{page}</EntriesLayout>);
|
||||
|
||||
export default EntriesLayout;
|
|
@ -0,0 +1,9 @@
|
|||
import React from "react";
|
||||
|
||||
const EntryPointLayout = (props) => {
|
||||
return props.children;
|
||||
};
|
||||
|
||||
export const getLayout = (page) => <EntryPointLayout>{page}</EntryPointLayout>;
|
||||
|
||||
export default EntryPointLayout;
|
|
@ -0,0 +1,137 @@
|
|||
import React, { useContext, useState, useEffect, useLayoutEffect } from "react";
|
||||
import { Flex, useMediaQuery, Stack } from "@chakra-ui/react";
|
||||
import UIContext from "../core/providers/UIProvider/context";
|
||||
import { DEFAULT_METATAGS, AWS_ASSETS_PATH } from "../core/constants";
|
||||
import { getLayout as getSiteLayout } from "./index";
|
||||
|
||||
const assets = {
|
||||
background720: `${AWS_ASSETS_PATH}/blog-background-720x405.png`,
|
||||
background1920: `${AWS_ASSETS_PATH}/blog-background-720x405.png`,
|
||||
background2880: `${AWS_ASSETS_PATH}/blog-background-720x405.png`,
|
||||
background3840: `${AWS_ASSETS_PATH}/blog-background-720x405.png`,
|
||||
};
|
||||
|
||||
const InfoPageLayout = ({ children }) => {
|
||||
const ui = useContext(UIContext);
|
||||
const [background, setBackground] = useState("background720");
|
||||
const [backgroundLoaded720, setBackgroundLoaded720] = useState(false);
|
||||
const [backgroundLoaded1920, setBackgroundLoaded1920] = useState(false);
|
||||
const [backgroundLoaded2880, setBackgroundLoaded2880] = useState(false);
|
||||
const [backgroundLoaded3840, setBackgroundLoaded3840] = useState(false);
|
||||
|
||||
const [
|
||||
isLargerThan720px,
|
||||
isLargerThan1920px,
|
||||
isLargerThan2880px,
|
||||
isLargerThan3840px,
|
||||
] = useMediaQuery([
|
||||
"(min-width: 720px)",
|
||||
"(min-width: 1920px)",
|
||||
"(min-width: 2880px)",
|
||||
"(min-width: 3840px)",
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
assets["background720"] = `${AWS_ASSETS_PATH}/blog-background-720x405.png`;
|
||||
assets[
|
||||
"background1920"
|
||||
] = `${AWS_ASSETS_PATH}/blog-background-1920x1080.png`;
|
||||
assets[
|
||||
"background2880"
|
||||
] = `${AWS_ASSETS_PATH}/blog-background-2880x1620.png`;
|
||||
assets[
|
||||
"background3840"
|
||||
] = `${AWS_ASSETS_PATH}/blog-background-3840x2160.png`;
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (backgroundLoaded3840) {
|
||||
setBackground("background3840");
|
||||
} else if (backgroundLoaded2880) {
|
||||
setBackground("background2880");
|
||||
} else if (backgroundLoaded1920) {
|
||||
setBackground("background1920");
|
||||
} else {
|
||||
setBackground("background720");
|
||||
}
|
||||
}, [
|
||||
isLargerThan720px,
|
||||
isLargerThan1920px,
|
||||
isLargerThan2880px,
|
||||
isLargerThan3840px,
|
||||
backgroundLoaded720,
|
||||
backgroundLoaded1920,
|
||||
backgroundLoaded2880,
|
||||
backgroundLoaded3840,
|
||||
]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const imageLoader720 = new Image();
|
||||
imageLoader720.src = `${AWS_ASSETS_PATH}/blog-background-720x405.png`;
|
||||
imageLoader720.onload = () => {
|
||||
setBackgroundLoaded720(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const imageLoader1920 = new Image();
|
||||
imageLoader1920.src = `${AWS_ASSETS_PATH}/blog-background-1920x1080.png`;
|
||||
imageLoader1920.onload = () => {
|
||||
setBackgroundLoaded1920(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const imageLoader2880 = new Image();
|
||||
imageLoader2880.src = `${AWS_ASSETS_PATH}/blog-background-2880x1620.png`;
|
||||
imageLoader2880.onload = () => {
|
||||
setBackgroundLoaded2880(true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const imageLoader3840 = new Image();
|
||||
imageLoader3840.src = `${AWS_ASSETS_PATH}/blog-background-3840x2160.png`;
|
||||
imageLoader3840.onload = () => {
|
||||
setBackgroundLoaded3840(true);
|
||||
};
|
||||
}, []);
|
||||
const margin = ui.isMobileView ? "6%" : "22%";
|
||||
return (
|
||||
<Flex
|
||||
bgPos="bottom"
|
||||
bgColor="transparent"
|
||||
bgSize="cover"
|
||||
backgroundImage={`url(${assets[`${background}`]})`}
|
||||
minH="100vh"
|
||||
direction="column"
|
||||
alignItems="center"
|
||||
w="100%"
|
||||
>
|
||||
<Stack mx={margin} my={[4, 6, 12]} maxW="1700px" textAlign="justify">
|
||||
{children}
|
||||
</Stack>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const getLayout = (page) =>
|
||||
getSiteLayout(<InfoPageLayout>{page}</InfoPageLayout>);
|
||||
|
||||
export const getLayoutProps = () => {
|
||||
const assetPreload = Object.keys(assets).map((key) => {
|
||||
return {
|
||||
rel: "preload",
|
||||
href: assets[key],
|
||||
as: "image",
|
||||
};
|
||||
});
|
||||
const preconnects = [{ rel: "preconnect", href: "https://s3.amazonaws.com" }];
|
||||
|
||||
const preloads = assetPreload.concat(preconnects);
|
||||
|
||||
return {
|
||||
props: { metaTags: { ...DEFAULT_METATAGS }, preloads },
|
||||
};
|
||||
};
|
||||
export default InfoPageLayout;
|
|
@ -1,17 +0,0 @@
|
|||
import React from "react";
|
||||
import { Scrollable, Footer } from "../components";
|
||||
import { getLayout as getSiteLayout } from "./index";
|
||||
|
||||
const DefaultLayout = (props) => {
|
||||
return (
|
||||
<Scrollable bgImg={""}>
|
||||
{props.children}
|
||||
<Footer />
|
||||
</Scrollable>
|
||||
);
|
||||
};
|
||||
|
||||
export const getLayout = (page) =>
|
||||
getSiteLayout(<DefaultLayout>{page}</DefaultLayout>);
|
||||
|
||||
export default DefaultLayout;
|
|
@ -1,3 +1,6 @@
|
|||
@import "~slick-carousel/slick/slick.css";
|
||||
@import "~slick-carousel/slick/slick-theme.css";
|
||||
|
||||
.Resizer {
|
||||
background: #000;
|
||||
opacity: 0.2;
|
||||
|
@ -198,3 +201,69 @@ code {
|
|||
transform: none;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.slide img {
|
||||
width: 20rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.slide {
|
||||
transform: scale(0.6) translate3D(0, -200px, 0px);
|
||||
transition: transform 6000ms;
|
||||
/* opacity: 0.6; */
|
||||
padding-bottom: 200px;
|
||||
animation: off 2s ease-in-out forwards;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.activeSlide {
|
||||
transform: scale(1.1);
|
||||
transition: transform 2000ms;
|
||||
animation: change 2s ease-in-out forwards;
|
||||
/* opacity: 1; */
|
||||
}
|
||||
|
||||
@keyframes change {
|
||||
to {
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
@keyframes off {
|
||||
to {
|
||||
opacity: 0.6
|
||||
}
|
||||
}
|
||||
|
||||
.arrow {
|
||||
background-color: #fff;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.arrow svg {
|
||||
transition: color 300ms;
|
||||
}
|
||||
|
||||
.arrow svg:hover {
|
||||
color: #68edff;
|
||||
}
|
||||
|
||||
.next {
|
||||
right: 0%;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.prev {
|
||||
left: 0%;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.bgGrid {
|
||||
background-color: white;
|
||||
background-size: 10px 10px, 60px 60px;
|
||||
background-image: linear-gradient(to bottom, transparent 6px, white 6px),
|
||||
linear-gradient(to right, #444 1px, transparent 1px),
|
||||
linear-gradient(to right, transparent 6px, white 6px),
|
||||
linear-gradient(to bottom, #444 1px, transparent 1px);
|
||||
}
|
||||
|
|
1895
frontend/yarn.lock
1895
frontend/yarn.lock
Plik diff jest za duży
Load Diff
Ładowanie…
Reference in New Issue