chore(buddychain): improvements (#104)

This commit is contained in:
Danish Arora 2024-10-28 18:23:21 +05:30 committed by GitHub
parent 49460a2f62
commit ac506bcf5a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 250 additions and 147 deletions

11
ci/Jenkinsfile vendored
View File

@ -44,16 +44,7 @@ pipeline {
stage('dogfooding') { steps { script { buildExample() } } } stage('dogfooding') { steps { script { buildExample() } } }
stage('message-monitor') { steps { script { buildExample() } } } stage('message-monitor') { steps { script { buildExample() } } }
stage('flush-notes') { steps { script { buildNextJSExample() } } } stage('flush-notes') { steps { script { buildNextJSExample() } } }
stage('buddybook') { stage('buddybook') { steps { script { buildExample() } } }
steps {
script {
dir('examples/buddybook') {
sh 'npm install'
sh 'npm run build:ci'
}
}
}
}
} }
} }

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Buddychain Dogfood</title> <title>BuddyBook Dogfood</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -41,13 +41,8 @@ function App() {
useEffect(() => { useEffect(() => {
if (isWakuLoading || !node || node.libp2p.getConnections().length === 0 || chainsData.length > 0 || isListening) return; if (isWakuLoading || !node || node.libp2p.getConnections().length === 0 || chainsData.length > 0 || isListening) return;
setTimeout(() => {
setIsListening(true); setIsListening(true);
startMessageListening(); startMessageListening();
}, 3000);
}, [node, isWakuLoading, wakuStatus]) }, [node, isWakuLoading, wakuStatus])
const handleTelemetryOptIn = (optIn: boolean) => { const handleTelemetryOptIn = (optIn: boolean) => {
@ -117,7 +112,7 @@ function App() {
return ( return (
<div className="min-h-screen bg-background text-foreground"> <div className="min-h-screen bg-background text-foreground">
<Header wakuStatus={wakuStatus} /> <Header wakuStatus={wakuStatus} />
<main className="container mx-auto px-4 py-8"> <main className="container mx-auto px-4 py-4 md:py-8 max-w-7xl">
<Routes> <Routes>
<Route path="/create" element={<ChainCreationForm />} /> <Route path="/create" element={<ChainCreationForm />} />
<Route path="/view" element={<ChainList chainsData={chainsData} onChainUpdate={handleChainUpdate} isLoading={isLoadingChains} />} /> <Route path="/view" element={<ChainList chainsData={chainsData} onChainUpdate={handleChainUpdate} isLoading={isLoadingChains} />} />
@ -132,22 +127,20 @@ function App() {
} }
const Home: React.FC = () => ( const Home: React.FC = () => (
<div className="space-y-6 text-center"> <div className="space-y-4 md:space-y-6 p-4 md:p-6">
<h1 className="text-4xl font-bold">BuddyChain</h1> <h1 className="text-2xl md:text-4xl font-bold">BuddyBook</h1>
<div className="max-w-md mx-auto p-6 bg-card rounded-lg shadow-md"> <div className="w-full max-w-sm mx-auto p-4 md:p-6 bg-card rounded-lg shadow-md">
<Link to="/create"> <Link to="/create">
<Button <Button className="w-full mb-4">
className="w-full mb-4"
>
Create New Chain Create New Chain
</Button> </Button>
</Link> </Link>
<p className="text-muted-foreground"> <p className="text-sm md:text-base text-muted-foreground">
Click the button above to start creating a new chain. Click the button above to start creating a new chain.
</p> </p>
</div> </div>
<p className="text-sm text-muted-foreground"> <p className="text-xs md:text-sm text-muted-foreground text-center">
Welcome to BuddyChain - Create and share your chains! Welcome to BuddyBook - Create and share your chains!
</p> </p>
</div> </div>
) )

View File

@ -11,10 +11,11 @@ import { v4 as uuidv4 } from 'uuid';
interface SignChainProps { interface SignChainProps {
block: BlockPayload; block: BlockPayload;
chainsData: BlockPayload[]; // Add this prop
onSuccess: (newBlock: BlockPayload) => void; onSuccess: (newBlock: BlockPayload) => void;
} }
const SignChain: React.FC<SignChainProps> = ({ block, onSuccess }) => { const SignChain: React.FC<SignChainProps> = ({ block, chainsData, onSuccess }) => {
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [isSigning, setIsSigning] = useState(false); const [isSigning, setIsSigning] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
@ -25,17 +26,37 @@ const SignChain: React.FC<SignChainProps> = ({ block, onSuccess }) => {
useEffect(() => { useEffect(() => {
if (address) { if (address) {
const hasAlreadySigned = block.signatures.some(sig => sig.address.toLowerCase() === address.toLowerCase()); // Check if the address has signed this block or any blocks in the chain
const checkSignatures = (blockToCheck: BlockPayload): boolean => {
// Check current block's signatures
if (blockToCheck.signatures.some(
sig => sig.address.toLowerCase() === address.toLowerCase()
)) {
return true;
}
// Check parent blocks
const parentBlock = chainsData.find(b => b.blockUUID === blockToCheck.parentBlockUUID);
if (parentBlock && checkSignatures(parentBlock)) {
return true;
}
// Check child blocks
const childBlocks = chainsData.filter(b => b.parentBlockUUID === blockToCheck.blockUUID);
return childBlocks.some(childBlock => checkSignatures(childBlock));
};
const hasAlreadySigned = checkSignatures(block);
setAlreadySigned(hasAlreadySigned); setAlreadySigned(hasAlreadySigned);
} }
}, [address, block.signatures]); }, [address, block, chainsData]);
const { signMessage } = useSignMessage({ const { signMessage } = useSignMessage({
mutation: { mutation: {
async onSuccess(signature) { async onSuccess(signature) {
if (!address || !node) return; if (!address || !node) return;
// Check if the address has already signed // Double check signature before proceeding
if (block.signatures.some(sig => sig.address.toLowerCase() === address.toLowerCase())) { if (block.signatures.some(sig => sig.address.toLowerCase() === address.toLowerCase())) {
setError('You have already signed this chain.'); setError('You have already signed this chain.');
setIsSigning(false); setIsSigning(false);
@ -79,6 +100,7 @@ const SignChain: React.FC<SignChainProps> = ({ block, onSuccess }) => {
}); });
const handleSign = () => { const handleSign = () => {
// Add an additional check here before signing
if (alreadySigned) { if (alreadySigned) {
setError('You have already signed this chain.'); setError('You have already signed this chain.');
return; return;
@ -102,7 +124,7 @@ const SignChain: React.FC<SignChainProps> = ({ block, onSuccess }) => {
{alreadySigned ? 'Already Signed' : 'Sign Chain'} {alreadySigned ? 'Already Signed' : 'Sign Chain'}
</Button> </Button>
<Dialog open={isOpen} onOpenChange={setIsOpen}> <Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent> <DialogContent className="sm:max-w-md">
<DialogHeader> <DialogHeader>
<DialogTitle>Sign Chain</DialogTitle> <DialogTitle>Sign Chain</DialogTitle>
<DialogDescription> <DialogDescription>
@ -111,7 +133,14 @@ const SignChain: React.FC<SignChainProps> = ({ block, onSuccess }) => {
: 'Review the block details and sign to add your signature to the chain.'} : 'Review the block details and sign to add your signature to the chain.'}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<QRCode data={block} /> <div className="flex flex-col space-y-4">
<div className="space-y-2">
<h4 className="font-medium">Block Details</h4>
<p className="text-sm text-muted-foreground">{block.title}</p>
<p className="text-sm text-muted-foreground">{block.description}</p>
</div>
<QRCode text={`${window.location.origin}/sign/${block.chainUUID}/${block.blockUUID}`} />
</div>
{error && <p className="text-sm text-destructive">{error}</p>} {error && <p className="text-sm text-destructive">{error}</p>}
<DialogFooter> <DialogFooter>
<Button variant="secondary" onClick={() => setIsOpen(false)}>Cancel</Button> <Button variant="secondary" onClick={() => setIsOpen(false)}>Cancel</Button>

View File

@ -44,7 +44,11 @@ const SignSharedChain: React.FC<SignSharedChainProps> = ({ chainsData, onChainUp
<CardContent> <CardContent>
<h2 className="text-xl font-semibold mb-2">{block.title}</h2> <h2 className="text-xl font-semibold mb-2">{block.title}</h2>
<p className="mb-4">{block.description}</p> <p className="mb-4">{block.description}</p>
<SignChain block={block} onSuccess={onChainUpdate} /> <SignChain
block={block}
chainsData={chainsData}
onSuccess={onChainUpdate}
/>
</CardContent> </CardContent>
</Card> </Card>
); );

View File

@ -4,7 +4,7 @@ import { type BlockPayload } from '@/lib/waku';
import SignChain from '@/components/Chain/SignChain'; import SignChain from '@/components/Chain/SignChain';
import { useEnsName } from 'wagmi'; import { useEnsName } from 'wagmi';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogDescription } from "@/components/ui/dialog";
import QRCode from '@/components/QRCode'; import QRCode from '@/components/QRCode';
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
@ -50,7 +50,11 @@ const ChainList: React.FC<ChainListProps> = ({ chainsData, onChainUpdate, isLoad
Block UUID: {block.blockUUID} Block UUID: {block.blockUUID}
</p> </p>
<div className="mt-2 space-x-2"> <div className="mt-2 space-x-2">
<SignChain block={block} onSuccess={handleChainUpdate} /> <SignChain
block={block}
chainsData={chainsData}
onSuccess={handleChainUpdate}
/>
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="outline">Share</Button> <Button variant="outline">Share</Button>
@ -58,6 +62,9 @@ const ChainList: React.FC<ChainListProps> = ({ chainsData, onChainUpdate, isLoad
<DialogContent className="sm:max-w-md"> <DialogContent className="sm:max-w-md">
<DialogHeader> <DialogHeader>
<DialogTitle>Share this Chain</DialogTitle> <DialogTitle>Share this Chain</DialogTitle>
<DialogDescription>
Share this chain with others to collect their signatures.
</DialogDescription>
</DialogHeader> </DialogHeader>
<div className="flex flex-col items-center space-y-4"> <div className="flex flex-col items-center space-y-4">
<QRCode text={shareUrl} width={200} height={200} /> <QRCode text={shareUrl} width={200} height={200} />

View File

@ -56,69 +56,78 @@ const Header: React.FC<HeaderProps> = ({ wakuStatus }) => {
}; };
return ( return (
<header className="bg-background border-b border-border"> <header className="border-b">
<div className="container mx-auto px-4 py-4 flex justify-between items-center"> <div className="container mx-auto px-4 py-2 md:py-4">
<div className="flex items-center space-x-4"> <div className="flex flex-col md:flex-row justify-between items-center space-y-2 md:space-y-0">
<h1 className="text-2xl font-bold">BuddyBook</h1> <div className="flex flex-col md:flex-row items-center space-y-2 md:space-y-0 md:space-x-4 w-full md:w-auto">
<nav> <h1 className="text-xl md:text-2xl font-bold">BuddyBook</h1>
<ul className="flex space-x-4"> <nav className="w-full md:w-auto">
<li> <ul className="flex justify-center md:justify-start space-x-4">
<Link <li>
to="/create" <Link
className={`text-sm ${location.pathname === '/create' ? 'text-primary font-semibold' : 'text-muted-foreground'}`} to="/create"
> className={`text-sm ${location.pathname === '/create' ? 'text-primary font-semibold' : 'text-muted-foreground'}`}
Create Chain >
</Link> Create Chain
</li> </Link>
<li> </li>
<Link <li>
to="/view" <Link
className={`text-sm ${location.pathname === '/view' ? 'text-primary font-semibold' : 'text-muted-foreground'}`} to="/view"
> className={`text-sm ${location.pathname === '/view' ? 'text-primary font-semibold' : 'text-muted-foreground'}`}
View Existing Chains >
</Link> View Chains
</li> </Link>
<li> </li>
<Link <li>
to="/telemetry" <Link
className={`text-sm ${location.pathname === '/telemetry' ? 'text-primary font-semibold' : 'text-muted-foreground'}`} to="/telemetry"
> className={`text-sm ${location.pathname === '/telemetry' ? 'text-primary font-semibold' : 'text-muted-foreground'}`}
Telemetry >
</Link> Telemetry
</li> </Link>
</ul> </li>
</nav> </ul>
</div> </nav>
<div className="flex items-center space-x-2">
<div className="flex items-center space-x-1">
<span className="text-sm text-muted-foreground">Filter:</span>
<div className={`w-3 h-3 rounded-full ${getStatusColor(wakuStatus.filter)}`}></div>
</div> </div>
<div className="flex items-center space-x-1">
<span className="text-sm text-muted-foreground">Store:</span> <div className="flex flex-wrap justify-center md:justify-end items-center gap-2 w-full md:w-auto">
<div className={`w-3 h-3 rounded-full ${getStatusColor(wakuStatus.store)}`}></div> <div className="flex items-center space-x-2 text-xs md:text-sm">
<div className="flex items-center space-x-1">
<span className="text-muted-foreground">Filter:</span>
<div className={`w-2 h-2 md:w-3 md:h-3 rounded-full ${getStatusColor(wakuStatus.filter)}`}></div>
</div>
<div className="flex items-center space-x-1">
<span className="text-muted-foreground">Store:</span>
<div className={`w-2 h-2 md:w-3 md:h-3 rounded-full ${getStatusColor(wakuStatus.store)}`}></div>
</div>
</div>
<div className="flex items-center space-x-2">
{isWakuLoading ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : wakuError ? (
<span className="text-xs md:text-sm text-red-500">Waku Error</span>
) : (
<span className="text-xs md:text-sm text-muted-foreground hidden md:inline">
Waku Connections: {connections}
</span>
)}
{isConnected ? (
<div className="flex items-center space-x-2">
<span className="text-xs md:text-sm text-muted-foreground truncate max-w-[120px] md:max-w-none">
{ensName || (address ? `${address.slice(0, 6)}...${address.slice(-4)}` : '')}
</span>
<Button variant="outline" size="sm" onClick={() => disconnect()}>
Logout
</Button>
</div>
) : (
<ConnectKitButton />
)}
</div>
</div> </div>
{isWakuLoading ? (
<Loader2 className="h-4 w-4 animate-spin" />
) : wakuError ? (
<span className="text-sm text-red-500">Waku Error</span>
) : (
<span className="text-sm text-muted-foreground">
Waku Connections: {connections}
</span>
)}
{isConnected ? (
<>
<span className="text-sm text-muted-foreground">
{ensName || (address ? `${address.slice(0, 6)}...${address.slice(-4)}` : '')}
</span>
<Button variant="outline" size="sm" onClick={() => disconnect()}>
Logout
</Button>
</>
) : (
<ConnectKitButton />
)}
</div> </div>
</div> </div>
</header> </header>

View File

@ -1,5 +1,7 @@
import React from 'react'; import React, { useState } from 'react';
import { QRCodeSVG } from 'qrcode.react'; import { QRCodeSVG } from 'qrcode.react';
import { Button } from "@/components/ui/button";
import { Check, Copy } from "lucide-react";
interface QRCodeProps { interface QRCodeProps {
text: string; text: string;
@ -8,9 +10,32 @@ interface QRCodeProps {
} }
const QRCode: React.FC<QRCodeProps> = ({ text, width = 256, height = 256 }) => { const QRCode: React.FC<QRCodeProps> = ({ text, width = 256, height = 256 }) => {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return ( return (
<div className="flex flex-col items-center space-y-4"> <div className="flex flex-col items-center space-y-4">
<QRCodeSVG value={text} size={Math.min(width, height)} /> <QRCodeSVG value={text} size={Math.min(width, height)} />
<div className="flex items-center space-x-2">
<input
type="text"
value={text}
readOnly
className="flex-1 px-3 py-2 text-sm border rounded-md bg-muted"
/>
<Button
variant="outline"
size="icon"
onClick={handleCopy}
>
{copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
</Button>
</div>
</div> </div>
); );
}; };

View File

@ -6,41 +6,58 @@ import { ScrollArea } from "@/components/ui/scroll-area";
import { privacyPolicy } from '@/lib/privacyPolicy'; import { privacyPolicy } from '@/lib/privacyPolicy';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
interface TelemetryOptInProps { interface PrivacyPolicyOptInProps {
onOptIn: (optIn: boolean) => void; onOptIn: (optIn: boolean) => void;
} }
const TelemetryOptIn: React.FC<TelemetryOptInProps> = ({ onOptIn }) => { const PrivacyPolicyOptIn: React.FC<PrivacyPolicyOptInProps> = ({ onOptIn }) => {
const [showFullPolicy, setShowFullPolicy] = useState(false); const [showFullPolicy, setShowFullPolicy] = useState(false);
return ( return (
<div className="min-h-screen flex items-center justify-center bg-background"> <div className="min-h-screen flex items-center justify-center bg-background p-4">
<Card className="w-full max-w-md"> <Card className="w-full max-w-md">
<CardHeader> <CardHeader className="space-y-2">
<CardTitle>Telemetry Data Collection</CardTitle> <CardTitle className="text-xl sm:text-2xl">Privacy Policy & Data Collection</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<p className="text-sm text-muted-foreground mb-4"> <div className="space-y-4">
We collect telemetry data to improve our services. This data is anonymous and helps us understand how our application is used. You can opt-in or opt-out of this data collection. <p className="text-sm sm:text-base text-muted-foreground">
</p> We collect data to improve our services. This data is anonymous and helps us understand how our application is used. You can opt-in or opt-out of this data collection.
<Button variant="link" onClick={() => setShowFullPolicy(true)}> </p>
View Full Privacy Policy <Button
</Button> variant="link"
onClick={() => setShowFullPolicy(true)}
className="px-0 text-sm sm:text-base"
>
View Full Privacy Policy
</Button>
</div>
</CardContent> </CardContent>
<CardFooter className="flex justify-between"> <CardFooter className="flex flex-col sm:flex-row gap-3 sm:gap-4">
<Button variant="outline" onClick={() => onOptIn(false)}>Opt Out</Button> <Button
<Button onClick={() => onOptIn(true)}>Opt In</Button> variant="outline"
onClick={() => onOptIn(false)}
className="w-full sm:w-auto"
>
Opt Out
</Button>
<Button
onClick={() => onOptIn(true)}
className="w-full sm:w-auto"
>
Opt In
</Button>
</CardFooter> </CardFooter>
</Card> </Card>
<Dialog open={showFullPolicy} onOpenChange={setShowFullPolicy}> <Dialog open={showFullPolicy} onOpenChange={setShowFullPolicy}>
<DialogContent className="max-w-4xl max-h-[80vh]"> <DialogContent className="w-[95vw] max-w-4xl max-h-[90vh] p-4 sm:p-6">
<DialogHeader> <DialogHeader>
<DialogTitle>Privacy Policy</DialogTitle> <DialogTitle className="text-xl sm:text-2xl">Privacy Policy</DialogTitle>
</DialogHeader> </DialogHeader>
<ScrollArea className="mt-4 h-[60vh]"> <ScrollArea className="mt-4 h-[50vh] sm:h-[60vh]">
<DialogDescription className="space-y-4"> <DialogDescription className="space-y-4">
<ReactMarkdown className="prose dark:prose-invert max-w-none"> <ReactMarkdown className="prose dark:prose-invert max-w-none text-sm sm:text-base">
{privacyPolicy} {privacyPolicy}
</ReactMarkdown> </ReactMarkdown>
</DialogDescription> </DialogDescription>
@ -51,4 +68,4 @@ const TelemetryOptIn: React.FC<TelemetryOptInProps> = ({ onOptIn }) => {
); );
}; };
export default TelemetryOptIn; export default PrivacyPolicyOptIn;

View File

@ -6,44 +6,49 @@ import { privacyPolicy } from '@/lib/privacyPolicy';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
const TelemetryPage: React.FC = () => { const PrivacyPolicyPage: React.FC = () => {
const [telemetryOptIn, setTelemetryOptIn] = useState<boolean>(false); const [privacyPolicyOptIn, setPrivacyPolicyOptIn] = useState<boolean>(false);
useEffect(() => { useEffect(() => {
const storedOptIn = localStorage.getItem('telemetryOptIn'); const storedOptIn = localStorage.getItem('privacyPolicyOptIn');
if (storedOptIn !== null) { if (storedOptIn !== null) {
setTelemetryOptIn(storedOptIn === 'true'); setPrivacyPolicyOptIn(storedOptIn === 'true');
} }
}, []); }, []);
const handleToggleTelemetry = () => { const handleTogglePrivacyPolicy = () => {
const newOptIn = !telemetryOptIn; const newOptIn = !privacyPolicyOptIn;
setTelemetryOptIn(newOptIn); setPrivacyPolicyOptIn(newOptIn);
localStorage.setItem('telemetryOptIn', newOptIn.toString()); localStorage.setItem('privacyPolicyOptIn', newOptIn.toString());
}; };
return ( return (
<Card className="w-full max-w-4xl mx-auto"> <Card className="w-full max-w-4xl mx-auto p-4 sm:p-6">
<CardHeader> <CardHeader className="space-y-2">
<CardTitle>Telemetry Settings</CardTitle> <CardTitle className="text-2xl sm:text-3xl">Privacy Policy Settings</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="space-y-6"> <div className="space-y-6 sm:space-y-8">
<div> <div className="space-y-4">
<p className="text-sm text-muted-foreground mb-2"> <p className="text-sm sm:text-base text-muted-foreground">
We collect telemetry data to improve our services. This data is anonymous and helps us understand how our application is used. We collect data to improve our services. This data is anonymous and helps us understand how our application is used.
</p> </p>
<p className="font-semibold mb-2"> <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
Current status: {telemetryOptIn ? 'Opted In' : 'Opted Out'} <p className="font-semibold">
</p> Current status: {privacyPolicyOptIn ? 'Opted In' : 'Opted Out'}
<Button onClick={handleToggleTelemetry}> </p>
{telemetryOptIn ? 'Opt Out' : 'Opt In'} <Button
</Button> onClick={handleTogglePrivacyPolicy}
className="w-full sm:w-auto"
>
{privacyPolicyOptIn ? 'Opt Out' : 'Opt In'}
</Button>
</div>
</div> </div>
<div> <div>
<h3 className="text-lg font-semibold mb-4">Privacy Policy</h3> <h3 className="text-lg sm:text-xl font-semibold mb-4">Privacy Policy</h3>
<ScrollArea className="h-[60vh] border rounded-md p-4"> <ScrollArea className="h-[50vh] sm:h-[60vh] border rounded-md p-2 sm:p-4">
<ReactMarkdown className="prose dark:prose-invert max-w-none"> <ReactMarkdown className="prose dark:prose-invert max-w-none text-sm sm:text-base">
{privacyPolicy} {privacyPolicy}
</ReactMarkdown> </ReactMarkdown>
</ScrollArea> </ScrollArea>
@ -54,4 +59,4 @@ const TelemetryPage: React.FC = () => {
); );
}; };
export default TelemetryPage; export default PrivacyPolicyPage;

View File

@ -9,7 +9,7 @@ const Card = React.forwardRef<
<div <div
ref={ref} ref={ref}
className={cn( className={cn(
"rounded-xl border bg-card text-card-foreground shadow", "rounded-lg border bg-card text-card-foreground shadow-sm p-4 md:p-6",
className className
)} )}
{...props} {...props}
@ -23,7 +23,7 @@ const CardHeader = React.forwardRef<
>(({ className, ...props }, ref) => ( >(({ className, ...props }, ref) => (
<div <div
ref={ref} ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)} className={cn("flex flex-col space-y-1.5 p-4 md:p-6", className)}
{...props} {...props}
/> />
)) ))

View File

@ -56,11 +56,34 @@
--chart-5: 340 75% 55% --chart-5: 340 75% 55%
} }
} }
@layer base { @layer base {
* { * {
@apply border-border; @apply border-border;
} }
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
}
.container {
@apply px-4 md:px-6 lg:px-8;
} }
}
h1 {
@apply text-2xl md:text-4xl font-bold;
}
h2 {
@apply text-xl md:text-3xl font-semibold;
}
h3 {
@apply text-lg md:text-2xl font-semibold;
}
.section {
@apply py-4 md:py-6 lg:py-8;
}
.card {
@apply p-4 md:p-6;
}
}

View File

@ -22,7 +22,7 @@ export type BlockPayload = {
parentBlockUUID: string | null; parentBlockUUID: string | null;
} }
const contentTopic = "/buddychain-dogfood/1/chain/proto"; const contentTopic = "/buddybook-dogfood/1/chain/proto";
export const encoder = createEncoder({ export const encoder = createEncoder({
contentTopic: contentTopic, contentTopic: contentTopic,

View File

@ -12,4 +12,4 @@ export const config = createConfig(
[mainnet.id]: http(), [mainnet.id]: http(),
}, },
}), }),
) )

View File

@ -1,6 +1,6 @@
- [ x ] waku connections on header should have green/yellow/red color indicator - [ x ] waku connections on header should have green/yellow/red color indicator
- [ ] clicking on the indicator should show a list of peers - [ ] clicking on the indicator should show a list of peers
- [ ] chains can't be signed twice by an address - [ x ] chains can't be signed twice by an address
- [ ] generate waku peer id using the wallet address - [ ] generate waku peer id using the wallet address
- [ ] telemetry - [ ] telemetry
- [ x ] disclaimer - [ x ] disclaimer