Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/entropy-explorer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env*.local
7 changes: 7 additions & 0 deletions apps/entropy-explorer/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.next/
coverage/
node_modules/
*.tsbuildinfo
.env*.local
.env
.DS_Store
1 change: 1 addition & 0 deletions apps/entropy-explorer/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { nextjs as default } from "@cprussin/eslint-config";
1 change: 1 addition & 0 deletions apps/entropy-explorer/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { nextjs as default } from "@cprussin/jest-config/next";
5 changes: 5 additions & 0 deletions apps/entropy-explorer/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
54 changes: 54 additions & 0 deletions apps/entropy-explorer/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const config = {
reactStrictMode: true,

pageExtensions: ["ts", "tsx", "mdx"],

logging: {
fetches: {
fullUrl: true,
},
},

webpack(config) {
config.module.rules.push({
test: /\.svg$/i,
use: ["@svgr/webpack"],
});

config.resolve.extensionAlias = {
".js": [".js", ".ts", ".tsx"],
};

return config;
},

headers: async () => [
{
source: "/:path*",
headers: [
{
key: "X-XSS-Protection",
value: "1; mode=block",
},
{
key: "Referrer-Policy",
value: "strict-origin-when-cross-origin",
},
{
key: "Strict-Transport-Security",
value: "max-age=2592000",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "Permissions-Policy",
value:
"vibrate=(), geolocation=(), midi=(), notifications=(), push=(), sync-xhr=(), microphone=(), camera=(), magnetometer=(), gyroscope=(), speaker=(), vibrate=(), fullscreen=self",
},
],
},
],
};
export default config;
57 changes: 57 additions & 0 deletions apps/entropy-explorer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "@pythnetwork/entropy-explorer",
"version": "0.0.0",
"private": true,
"type": "module",
"engines": {
"node": "22"
},
"scripts": {
"build:vercel": "next build",
"fix:format": "prettier --write .",
"fix:lint:eslint": "eslint --fix .",
"fix:lint:stylelint": "stylelint --fix 'src/**/*.scss'",
"pull:env": "[ $CI ] || VERCEL_ORG_ID=team_BKQrg3JJFLxZyTqpuYtIY0rj VERCEL_PROJECT_ID=prj_TBkf9EyQjQF37gs4Vk0sQKJj97kE vercel env pull",
"start:dev": "next dev --port 3006",
"start:prod": "next start --port 3006",
"test:format": "prettier --check .",
"test:lint:eslint": "eslint . --max-warnings 0",
"test:lint:stylelint": "stylelint 'src/**/*.scss' --max-warnings 0",
"test:types": "tsc"
},
"dependencies": {
"@phosphor-icons/react": "catalog:",
"@pythnetwork/component-library": "workspace:*",
"clsx": "catalog:",
"connectkit": "catalog:",
"next": "catalog:",
"nuqs": "catalog:",
"react": "catalog:",
"react-aria": "catalog:",
"react-dom": "catalog:",
"viem": "catalog:",
"wagmi": "catalog:",
"zod": "catalog:"
},
"devDependencies": {
"@cprussin/eslint-config": "catalog:",
"@cprussin/jest-config": "catalog:",
"@cprussin/prettier-config": "catalog:",
"@cprussin/tsconfig": "catalog:",
"@svgr/webpack": "catalog:",
"@types/jest": "catalog:",
"@types/node": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"autoprefixer": "catalog:",
"eslint": "catalog:",
"jest": "catalog:",
"postcss": "catalog:",
"prettier": "catalog:",
"sass": "catalog:",
"stylelint": "catalog:",
"stylelint-config-standard-scss": "catalog:",
"typescript": "catalog:",
"vercel": "catalog:"
}
}
1 change: 1 addition & 0 deletions apps/entropy-explorer/prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { base as default } from "@cprussin/prettier-config";
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/entropy-explorer/public/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/entropy-explorer/public/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/entropy-explorer/public/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/entropy-explorer/public/favicon-light.ico
Binary file not shown.
Binary file added apps/entropy-explorer/public/favicon.ico
Binary file not shown.
3 changes: 3 additions & 0 deletions apps/entropy-explorer/src/app/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"use client";

export { ErrorPage as default } from "@pythnetwork/component-library/ErrorPage";
16 changes: 16 additions & 0 deletions apps/entropy-explorer/src/app/global-error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"use client";

import { ErrorPage } from "@pythnetwork/component-library/ErrorPage";
import { LoggerProvider } from "@pythnetwork/component-library/useLogger";
import type { ComponentProps } from "react";

const GlobalError = (props: ComponentProps<typeof ErrorPage>) => (
<LoggerProvider>
<html lang="en" dir="ltr">
<body>
<ErrorPage {...props} />
</body>
</html>
</LoggerProvider>
);
export default GlobalError;
2 changes: 2 additions & 0 deletions apps/entropy-explorer/src/app/layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Root as default } from "../components/Root";
export { metadata, viewport } from "../metadata";
25 changes: 25 additions & 0 deletions apps/entropy-explorer/src/app/manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { MetadataRoute } from "next";

import { metadata, viewport } from "../metadata";

const manifest = (): MetadataRoute.Manifest => ({
name: metadata.applicationName,
short_name: metadata.applicationName,
description: metadata.description,
theme_color: viewport.themeColor,
background_color: viewport.themeColor,
icons: [
{
src: "/android-chrome-192x192.png",
sizes: "192x192",
type: "image/png",
},
{
src: "/android-chrome-512x512.png",
sizes: "512x512",
type: "image/png",
},
],
display: "standalone",
});
export default manifest;
1 change: 1 addition & 0 deletions apps/entropy-explorer/src/app/not-found.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { NotFoundPage as default } from "@pythnetwork/component-library/NotFoundPage";
1 change: 1 addition & 0 deletions apps/entropy-explorer/src/app/page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Home as default } from "../components/Home";
11 changes: 11 additions & 0 deletions apps/entropy-explorer/src/app/robots.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { MetadataRoute } from "next";

import { IS_PRODUCTION_SERVER } from "../config/server";

const robots = (): MetadataRoute.Robots => ({
rules: {
userAgent: "*",
...(IS_PRODUCTION_SERVER ? { allow: "/" } : { disallow: "/" }),
},
});
export default robots;
15 changes: 15 additions & 0 deletions apps/entropy-explorer/src/components/Home/chain-select.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@use "@pythnetwork/component-library/theme";

.searchBar {
display: grid;
grid-template-columns: max-content 1fr;
gap: theme.spacing(2);
width: 100%;
}

.chainSelectItem {
display: grid;
grid-template-columns: max-content 1fr;
gap: theme.spacing(2);
align-items: center;
}
107 changes: 107 additions & 0 deletions apps/entropy-explorer/src/components/Home/chain-select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"use client";

import type { Props as SelectProps } from "@pythnetwork/component-library/Select";
import { Select } from "@pythnetwork/component-library/Select";
import { ChainIcon } from "connectkit";
import type { ComponentProps } from "react";
import { Suspense, useCallback, useMemo } from "react";
import { useCollator } from "react-aria";
import * as viemChains from "viem/chains";

import styles from "./chain-select.module.scss";
import { useQuery } from "./use-query";
import { EntropyDeployments } from "../../entropy-deployments";
import type { ConstrainedOmit } from "../../type-utils";

export const ChainSelect = (
props: ComponentProps<typeof ResolvedChainSelect>,
) => (
<Suspense
fallback={
<Select
{...defaultProps}
{...props}
isPending
options={[]}
defaultSelectedKey={undefined}
/>
}
>
<ResolvedChainSelect {...props} />
</Suspense>
);

type Deployment = ReturnType<typeof entropyDeploymentsByNetwork>[number];

const ResolvedChainSelect = (
props: ConstrainedOmit<
SelectProps<Deployment>,
keyof typeof defaultProps | keyof ReturnType<typeof useResolvedProps>
>,
) => {
const resolvedProps = useResolvedProps();

return <Select {...defaultProps} {...resolvedProps} {...props} />;
};

const useResolvedProps = () => {
const collator = useCollator();
const { chain, setChain } = useQuery();
const chains = useMemo(
() => [
{
name: "MAINNET",
options: entropyDeploymentsByNetwork("mainnet", collator),
},
{
name: "TESTNET",
options: entropyDeploymentsByNetwork("testnet", collator),
},
],
[collator],
);

const showChain = useCallback(
(chain: Deployment) => (
<div className={styles.chainSelectItem}>
<ChainIcon id={chain.chainId} />
{chain.name}
</div>
),
[],
);

const chainTextValue = useCallback((chain: Deployment) => chain.name, []);

return {
selectedKey: chain ?? undefined,
onSelectionChange: setChain,
optionGroups: chains,
show: showChain,
textValue: chainTextValue,
};
};

const defaultProps = {
label: "Chain",
hideLabel: true,
defaultButtonLabel: "Select Chain",
} as const;

const entropyDeploymentsByNetwork = (
network: "mainnet" | "testnet",
collator: ReturnType<typeof useCollator>,
) =>
Object.entries(EntropyDeployments)
.map(([slug, chain]) => {
// eslint-disable-next-line import/namespace
const viemChain = viemChains[slug as keyof typeof EntropyDeployments];
return {
...chain,
name: viemChain.name,
chainId: viemChain.id,
id: slug as keyof typeof EntropyDeployments,
};
})
.filter((chain) => chain.network === network)
.toSorted((a, b) => collator.compare(a.name, b.name));
30 changes: 30 additions & 0 deletions apps/entropy-explorer/src/components/Home/index.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
@use "@pythnetwork/component-library/theme";

.home {
.header {
@include theme.h3;
@include theme.max-width;

// stylelint-disable-next-line no-duplicate-selectors
& {
color: theme.color("heading");
margin-bottom: theme.spacing(4);
}

@include theme.breakpoint("sm") {
margin-bottom: theme.spacing(6);
}
}

.body {
@include theme.max-width;

.searchBar {
width: 100%;

@include theme.breakpoint("lg") {
width: theme.spacing(100);
}
}
}
}
27 changes: 27 additions & 0 deletions apps/entropy-explorer/src/components/Home/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ListDashes } from "@phosphor-icons/react/dist/ssr/ListDashes";
import { Card } from "@pythnetwork/component-library/Card";

import { ChainSelect } from "./chain-select";
import styles from "./index.module.scss";
import { Results } from "./results";
import { SearchBar } from "./search-bar";

export const Home = () => (
<div className={styles.home}>
<h1 className={styles.header}>Requests</h1>
<div className={styles.body}>
<Card
title="Request Log"
icon={<ListDashes />}
toolbar={
<>
<ChainSelect variant="outline" size="sm" placement="bottom right" />
<SearchBar className={styles.searchBar ?? ""} />
</>
}
>
<Results />
</Card>
</div>
</div>
);
Loading
Loading