Skip to content
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