Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(web): split project page tabs into individual components #130

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
120 changes: 93 additions & 27 deletions web/src/components/Avatars.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,100 @@
import React from "react"
import { Avatar, Box } from "@chakra-ui/react"
import React, { useState, useEffect, useRef } from "react";
import { Avatar, Box } from "@chakra-ui/react";
import { useProjectPageContext } from "../context/ProjectPageContext";

interface ScrollingAvatarsProps {
images?: string[]
}

/**
* Display the participants Avatars in a scrolling list
* @param {ScrollingAvatarsProps} - the images to show
* Hook to observe when a specific avatar is in the viewport
*/
const ScrollingAvatars: React.FC<ScrollingAvatarsProps> = ({ images }) => {
return (
<Box
maxW={"container.md"}
width="50%"
overflowX="auto"
whiteSpace="nowrap"
py={4}
px={2}
borderColor="gray.200"
>
{images && images.length > 0 && images.map((image: string, index: any) => (
<Avatar
const useIntersectionObserver = (setInView: (inView: boolean) => void) => {
const observerRef = useRef<IntersectionObserver | null>(null);

const observe = (element: HTMLElement | null) => {
if (observerRef.current) {
observerRef.current.disconnect();
}

observerRef.current = new IntersectionObserver(([entry]) => {
setInView(entry.isIntersecting);
});

if (element) {
observerRef.current.observe(element);
}
};

useEffect(() => {
return () => {
if (observerRef.current) {
observerRef.current.disconnect();
}
};
}, []);

return observe;
};

/**
* Display the participants' Avatars in a scrolling list with lazy loading.
*/
const ScrollingAvatars: React.FC = () => {
const { avatars } = useProjectPageContext();
const [loadedAvatars, setLoadedAvatars] = useState<{ [key: number]: boolean }>({});

const handleAvatarInView = (index: number, inView: boolean) => {
if (inView) {
setLoadedAvatars((prev) => ({ ...prev, [index]: true }));
}
};

return (
<Box
maxW={"container.md"}
width="50%"
overflowX="auto"
whiteSpace="nowrap"
py={4}
px={2}
borderColor="gray.200"
>
{avatars &&
avatars.length > 0 &&
avatars.map((image: string, index: number) => (
<LazyAvatar
key={index}
src={image}
mx={2}
index={index}
isLoaded={loadedAvatars[index]}
onInViewChange={(inView) => handleAvatarInView(index, inView)}
/>
))}
</Box>
)
}

export default ScrollingAvatars
</Box>
);
};

/**
* Lazy loading Avatar component
*/
const LazyAvatar: React.FC<{
src: string;
index: number;
isLoaded: boolean;
onInViewChange: (inView: boolean) => void;
}> = ({ src, index, isLoaded, onInViewChange }) => {
const avatarRef = useRef<HTMLDivElement | null>(null);
const observe = useIntersectionObserver((inView) => onInViewChange(inView));

useEffect(() => {
if (avatarRef.current) {
observe(avatarRef.current);
}
}, [avatarRef, observe]);

return (
<Box ref={avatarRef} display="inline-block">
{isLoaded ? <Avatar src={src} mx={2} /> : <Avatar mx={2} />}
</Box>
);
};

export default ScrollingAvatars;

62 changes: 62 additions & 0 deletions web/src/components/CircuitAbout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {
Box,
Stat,
StatLabel,
StatNumber,
Heading,
Flex,
SimpleGrid,
} from "@chakra-ui/react";

import {
truncateString,
parseRepoRoot
} from "../helpers/utils";

export const CircuitAbout: React.FC = ({ circuit }) => {
return (
<Box borderWidth={1} borderRadius="lg" p={4}>
<Heading fontSize={16} size="md" mb={2}>
{circuit.name} - {circuit.description}
</Heading>
<SimpleGrid columns={[2, 2]} spacing={4}>
<Flex justify="space-between" align="center">
<Stat>
<StatLabel fontSize={12}>Parameters</StatLabel>
<StatNumber fontSize={16}>
{
circuit.template.paramsConfiguration && circuit.template.paramsConfiguration.length > 0 ?
circuit.template.paramsConfiguration.join(" ") :
circuit.template.paramConfiguration && circuit.template.paramConfiguration.length > 0 ?
circuit.template.paramConfiguration.join(" ") :
"No parameters"
}
</StatNumber>
</Stat>
</Flex>
<Stat>
<StatLabel fontSize={12}>Commit Hash</StatLabel>
<StatNumber fontSize={16}>
<a href={`${parseRepoRoot(circuit.template.source)}/tree/${circuit.template.commitHash}`} target="_blank">
{truncateString(circuit.template.commitHash, 6)}
</a>
</StatNumber>
</Stat>
<Stat>
<StatLabel fontSize={12}>Template Link</StatLabel>
<StatNumber fontSize={16}>
<a href={circuit.template.source} target="_blank">
{truncateString(circuit.template.source, 16)}
</a>
</StatNumber>
</Stat>
<Stat>
<StatLabel fontSize={12}>Compiler Version</StatLabel>
<StatNumber fontSize={16}>
{circuit.compiler.version}
</StatNumber>
</Stat>
</SimpleGrid>
</Box>
);
}
74 changes: 74 additions & 0 deletions web/src/components/CircuitStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import {
Box,
Stat,
StatLabel,
StatNumber,
Tag,
Heading,
Flex,
Icon,
SimpleGrid,
} from "@chakra-ui/react";
import { FiTarget, FiZap, FiEye, FiUser, FiMapPin, FiWifi } from "react-icons/fi";

export const CircuitStats: React.FC = ({ circuit }) => {
return (
<Box borderWidth={1} borderRadius="lg" p={4}>
<Heading fontSize={16} size="md" mb={2}>
{circuit.name} - {circuit.description}
</Heading>
<Flex wrap="wrap" mb={4}>
<Tag fontSize={10} size="sm" colorScheme="purple" mr={2} mb={2}>
<Icon as={FiTarget} mr={1} />
Constraints: {circuit.constraints}
</Tag>
<Tag fontSize={10} size="sm" colorScheme="cyan" mr={2} mb={2}>
<Icon as={FiZap} mr={1} />
Pot: {circuit.pot}
</Tag>
<Tag fontSize={10} size="sm" colorScheme="yellow" mr={2} mb={2}>
<Icon as={FiEye} mr={1} />
Private Inputs: {circuit.privateInputs}
</Tag>
<Tag fontSize={10} size="sm" colorScheme="pink" mr={2} mb={2}>
<Icon as={FiUser} mr={1} />
Public Inputs: {circuit.publicInputs}
</Tag>
<Tag fontSize={10} size="sm" colorScheme="blue" mr={2} mb={2}>
<Icon as={FiMapPin} mr={1} />
Curve: {circuit.curve}
</Tag>
<Tag fontSize={10} size="sm" colorScheme="teal" mr={2} mb={2}>
<Icon as={FiWifi} mr={1} />
Wires: {circuit.wires}
</Tag>
</Flex>
<SimpleGrid columns={[2, 2]} spacing={6}>
<Flex justify="space-between" align="center">
<Stat>
<StatLabel fontSize={12}>Completed Contributions</StatLabel>
<StatNumber fontSize={16}>
{circuit.completedContributions}
</StatNumber>
</Stat>
</Flex>
<Stat>
<StatLabel fontSize={12}>Memory Requirement</StatLabel>
<StatNumber fontSize={16}>
{circuit.memoryRequirement} mb
</StatNumber>
</Stat>
<Stat>
<StatLabel fontSize={12}>Avg Contribution Time</StatLabel>
<StatNumber fontSize={16}>
{circuit.avgTimingContribution}s
</StatNumber>
</Stat>
<Stat>
<StatLabel fontSize={12}>Max Contribution Time</StatLabel>
<StatNumber fontSize={16}>{circuit.maxTiming}s</StatNumber>
</Stat>
</SimpleGrid>
</Box>
);
}
40 changes: 40 additions & 0 deletions web/src/components/ProjectTabAbout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
Box,
VStack,
SimpleGrid,
TabPanel,
} from "@chakra-ui/react";

import { useProjectPageContext } from "../context/ProjectPageContext";
import { CircuitAbout } from "../components/CircuitAbout";

export const ProjectTabAbout: React.FC = () => {
const { circuitsClean } = useProjectPageContext();
return (
<TabPanel>
<VStack
alignSelf={"stretch"}
alignItems={"center"}
justifyContent={"center"}
spacing={8}
py={0}
>
<Box alignItems="center" alignSelf={"stretch"} w="full">
<SimpleGrid
alignSelf={"stretch"}
maxW={["392px", "390px", "100%"]}
columns={1}
spacing={6}
>
{circuitsClean.map((circuit, index) => (
<CircuitAbout
key={index}
{...{circuit}}
/>
))}
</SimpleGrid>
</Box>
</VStack>
</TabPanel>
);
}
Loading
Loading