Skip to content

Commit

Permalink
Merge pull request #31 from brolag/feature/add-noir-section
Browse files Browse the repository at this point in the history
feat: add noir section
  • Loading branch information
brolag authored Sep 12, 2024
2 parents 34a21d8 + 7e42d72 commit d3a8d47
Show file tree
Hide file tree
Showing 27 changed files with 253 additions and 22 deletions.
9 changes: 6 additions & 3 deletions packages/nextjs/app/engineering/circom/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type PageProps = {
};

export default async function Page({ params }: PageProps) {
const { title, steps } = await getSteps(params.slug);
const { title, steps } = await getSteps(params.slug, "circom");
const challengeId = params.slug;

return (
Expand All @@ -26,7 +26,7 @@ export default async function Page({ params }: PageProps) {

{params.slug !== "1" && (
<>
<Statement challengeId={challengeId} />
<Statement challengeId={challengeId} lang={"circom"} />
<hr className="border-0 h-px bg-blue-500 shadow-[0_0_10px_2px_rgba(59,130,246,0.7)]" />
<Verifier challengeId={challengeId} />
<hr className="border-0 h-px bg-blue-500 shadow-[0_0_10px_2px_rgba(59,130,246,0.7)] mb-10" />
Expand All @@ -45,7 +45,10 @@ export default async function Page({ params }: PageProps) {
<p>{item.description}</p>
<pre>{item.commands.join("\n")}</pre>
{item.files && (
<a href={`/challenges/challenge_${challengeId}/files/${item.files}`} download={`${item.files}`}>
<a
href={`/challenges/circom/challenge_${challengeId}/files/${item.files}`}
download={`${item.files}`}
>
Download: {item.files}
</a>
)}
Expand Down
59 changes: 59 additions & 0 deletions packages/nextjs/app/engineering/noir/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import Image from "next/image";
import Statement from "~~/components/Statement";
import Verifier from "~~/components/Verifier";
import AccordionItem from "~~/components/ui/AccordionItem";
import { getSteps } from "~~/utils/getStep";

type PageProps = {
params: {
slug: string;
};
};

export default async function Page({ params }: PageProps) {
const { title, steps } = await getSteps(params.slug, "noir");
const challengeId = params.slug;

return (
<div className="flex items-center flex-col w-full pt-10">
<div className="w-full max-w-[67rem] p-4 bg-gray-800 rounded-lg">
<div className="flex items-center mb-4">
<Image src="/images/mission.png" alt="Mission" width={40} height={40} className="mr-2" />
<h2 className="text-3xl font-play font-bold text-white">
Challenge #{params.slug} - {title}
</h2>
</div>

{params.slug !== "1" && (
<>
<Statement challengeId={challengeId} lang={"noir"} />
<hr className="border-0 h-px bg-blue-500 shadow-[0_0_10px_2px_rgba(59,130,246,0.7)]" />
<Verifier challengeId={challengeId} />
<hr className="border-0 h-px bg-blue-500 shadow-[0_0_10px_2px_rgba(59,130,246,0.7)] mb-10" />
</>
)}

{steps.map((item: any, index: number) => (
<AccordionItem
key={index}
id={`${index}`}
challengeId={challengeId}
slug={params.slug}
title={item.step}
content={
<div>
<p>{item.description}</p>
<pre>{item.commands.join("\n")}</pre>
{item.files && (
<a href={`/challenges/noir/challenge_${challengeId}/files/${item.files}`} download={`${item.files}`}>
Download: {item.files}
</a>
)}
</div>
}
/>
))}
</div>
</div>
);
}
18 changes: 18 additions & 0 deletions packages/nextjs/app/engineering/noir/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import "@rainbow-me/rainbowkit/styles.css";
import "~~/styles/globals.css";
import { getMetadata } from "~~/utils/scaffold-eth/getMetadata";

export const metadata = getMetadata({
title: "Zk Multiverse",
description: "Learn ZK with ZK Multiverse",
});

const ScaffoldEthApp = ({ children }: { children: React.ReactNode }) => {
return (
<section className="bg-local h-screen bg-repeat" style={{ backgroundImage: "url(/images/inner-bg.png)" }}>
{children}
</section>
);
};

export default ScaffoldEthApp;
78 changes: 78 additions & 0 deletions packages/nextjs/app/engineering/noir/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"use client";

import React, { Fragment } from "react";
import Image from "next/image";
import Link from "next/link";
import type { NextPage } from "next";
import { chunkArray } from "~~/utils/chunkArray";

const challenges = [
{ id: 1, title: "Challenge #1", isLocked: false },
{ id: 2, title: "Challenge #2", isLocked: true },
{ id: 3, title: "Challenge #3", isLocked: true },
{ id: 4, title: "Challenge #4", isLocked: true },
{ id: 5, title: "Challenge #5", isLocked: true },
{ id: 6, title: "Challenge #6", isLocked: true },
];

const Home: NextPage = () => {
const rows = chunkArray(challenges, 3);

return (
<>
<div className="flex items-center justify-items-stretch flex-col flex-grow mt-40">
<h2 className="font-play text-[2.5rem] font-bold mb-20">Noir</h2>
{rows.map((row, rowIndex) => (
<div key={rowIndex} className="flex flex-row items-center mb-8">
{row.map((step, index) => {
const content = (
<div className={`flex flex-col items-center mx-4 ${step.isLocked ? "opacity-50" : ""} group`}>
<div className="flex flex-col items-center">
<div
className={`flex flex-row text-center items-center font-play text-3xl cursor-pointer ${!step.isLocked && "group-hover:glow-title"}`}
>
{step.title}
{step.isLocked && (
<Image
src="/images/locked.png"
alt="Locked"
width={20}
height={20}
className="ml-2 opacity-75"
/>
)}
</div>
<div
style={{ backgroundImage: "url(/images/step.png)" }}
className={`bg-local flex flex-row justify-around items-start w-40 h-40 bg-cover ${step.isLocked ? "grayscale" : "transition-transform duration-300 group-hover:scale-105"}`}
/>
</div>
</div>
);

return (
<Fragment key={step.id}>
{step.isLocked ? (
content
) : (
<Link href={`/engineering/noir/${step.id}`} passHref>
{content}
</Link>
)}
{index < row.length - 1 && <div className="line-glow w-16 h-px mx-4"></div>}
</Fragment>
);
})}
</div>
))}
</div>
<style jsx>{`
.group:hover .group-hover\:glow-title {
text-shadow: 0 0 10px rgba(255, 255, 255, 0.8);
}
`}</style>
</>
);
};

export default Home;
2 changes: 1 addition & 1 deletion packages/nextjs/app/engineering/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const languages = [
title: "Noir",
image: "/images/languages/noir.png",
link: "/engineering/noir",
locked: true,
locked: false,
},
{
title: "Python",
Expand Down
70 changes: 54 additions & 16 deletions packages/nextjs/components/Statement.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,64 @@
import React from "react";
// Import the statement for every challenge
import Challenge2Content from "../public/challenges/challenge_2/statement";
import Challenge4Content from "../public/challenges/challenge_4/statement";
"use client";

import React, { useEffect, useState } from "react";

type StatementProps = {
challengeId: string;
lang: string;
};

const NotFoundContent = () => <p>Challenge not found</p>;

const Statement = ({ challengeId }: StatementProps) => {
let ChallengeContent;

switch (challengeId) {
case "2":
ChallengeContent = Challenge2Content;
break;
case "4":
ChallengeContent = Challenge4Content;
break;
default:
ChallengeContent = NotFoundContent;
const Statement = ({ challengeId, lang }: StatementProps) => {
const [ChallengeContent, setChallengeContent] = useState<React.FC | null>(null);

useEffect(() => {
const loadChallengeContent = async () => {
try {
switch (lang) {
case "circom":
switch (challengeId) {
case "2":
const { default: Challenge2Content } = await import(
"../public/challenges/circom/challenge_2/statement"
);
setChallengeContent(() => Challenge2Content);
break;
case "4":
const { default: Challenge4Content } = await import(
"../public/challenges/circom/challenge_4/statement"
);
setChallengeContent(() => Challenge4Content);
break;
default:
setChallengeContent(() => NotFoundContent);
break;
}
break;

case "noir": // Otro caso para otro lenguaje
switch (challengeId) {
default:
setChallengeContent(() => NotFoundContent);
break;
}
break;

default:
setChallengeContent(() => NotFoundContent);
break;
}
} catch (error) {
console.error("Error loading challenge content", error);
setChallengeContent(() => NotFoundContent);
}
};

loadChallengeContent();
}, [challengeId, lang]);

if (!ChallengeContent) {
return <div>Loading...</div>;
}

return <ChallengeContent />;
Expand Down
35 changes: 35 additions & 0 deletions packages/nextjs/public/challenges/noir/challenge_1/steps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"title": "Noir installation",
"steps": [
{
"step": "Step 1 - Install Rust",
"description": ["Noir is built using Rust, so you need to install Rust first. Run the following command to install Rust:"],
"commands": ["curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"]
},
{
"step": "Step 2",
"description": "After installation, you need to add Rust to your PATH by running:",
"commands": ["gsource $HOME/.cargo/env"]
},
{
"step": "Step 3",
"description": "Verify that Rust is installed correctly by checking its version:",
"commands": ["rustc --version"]
},
{
"step": "Step 4 - Install Noir",
"description": "With Rust installed, you can now install Noir using Cargo, the Rust package manager. To install the Noir version manager (noirup), run:",
"commands": ["cargo install noirup"]
},
{
"step": "Step 5",
"description": "This installs the version manager, noirup, which helps you install and manage versions of Noir. To install the latest version of Noir, run:",
"commands": ["noirup install"]
},
{
"step": "Step 6 - Create a New Noir Project",
"description": "Once Noir is installed, you can create a new project to start writing zk-SNARK circuits. To initialize a new Noir project inside a directory, use this command:",
"commands": ["nargo init"]
}
]
}
4 changes: 2 additions & 2 deletions packages/nextjs/utils/getStep.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import fs from "fs/promises";
import path from "path";

export const getSteps = async (slug: string) => {
const filePath = path.join(process.cwd(), "public", "challenges", `challenge_${slug}/steps.json`);
export const getSteps = async (slug: string, lang: string) => {
const filePath = path.join(process.cwd(), "public", "challenges", `${lang}/challenge_${slug}/steps.json`);
const jsonData = await fs.readFile(filePath, "utf-8");
const data = JSON.parse(jsonData);

Expand Down

0 comments on commit d3a8d47

Please sign in to comment.