Skip to content

Commit

Permalink
Merge pull request #10 from dalindev/banano
Browse files Browse the repository at this point in the history
Banano
  • Loading branch information
dalindev authored Oct 28, 2024
2 parents 71498a4 + 9ee9ce4 commit b5f3fe9
Show file tree
Hide file tree
Showing 38 changed files with 4,327 additions and 16 deletions.
12 changes: 11 additions & 1 deletion .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,14 @@

# Public variables
NEXT_PUBLIC_USE_SAMPLE_DATA=true # Set to true to use sample data for development
NEXT_PUBLIC_WS_URL=[Nano Websocket URL]


# Nano
NEXT_PUBLIC_WS_URL=[Nano Websocket URL]
NEXT_PUBLIC_NANO_DONATION_ACCOUNT=[Nano Donation Account]

# Banano
NEXT_PUBLIC_BANANO_WS_URL=[Banano Websocket URL]
NEXT_PUBLIC_BANANO_DONATION_ACCOUNT=[Banano Donation Account]


17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ https://github.com/user-attachments/assets/fdd6f818-8279-4f0f-9d59-22bf28de70d7

Nano Network Globe Visualization is a web application that allows you to visualize the Nano network on a globe. It uses a custom websocket client to connect to the Nano network and receive real-time data. The data is then visualized on a globe using a custom 3D globe library.

The application now also supports Banano network visualization at the `/banano` route!

## Prerequisites

Before running the application, make sure you have pnpm installed. If you don't have pnpm, you can install it using npm:
Expand All @@ -30,19 +32,34 @@ Before running the application, you need to set up your environment variables:
1. Copy the `.env.local.example` file and rename it to `.env.local`:
2. Open the `.env.local` file and update the variables:
- `NEXT_PUBLIC_USE_SAMPLE_DATA`: Set to `true` to use sample data for development, or `false` to use live data.

For Nano:
- `NEXT_PUBLIC_WS_URL`: Set this to the Nano Websocket URL you want to connect to.
- `NEXT_PUBLIC_NANO_DONATION_ACCOUNT`: Set this to your Nano donation account address.

For Banano:
- `NEXT_PUBLIC_BANANO_WS_URL`: Set this to the Banano Websocket URL.
- `NEXT_PUBLIC_BANANO_DONATION_ACCOUNT`: Set this to your Banano donation account address.

Example `.env.local` file:
```
NEXT_PUBLIC_USE_SAMPLE_DATA=false
# Nano
NEXT_PUBLIC_WS_URL=wss://example-nano-node.com/ws
NEXT_PUBLIC_NANO_DONATION_ACCOUNT=nano_1youraddress...
# Banano
NEXT_PUBLIC_BANANO_WS_URL=wss://example-banano-node.com/ws
NEXT_PUBLIC_BANANO_DONATION_ACCOUNT=ban_1youraddress...
```

## How to run

1. Clone the repository
2. Run `pnpm install`
3. Run `pnpm dev`
4. Visit `/` for Nano visualization or `/banano` for Banano visualization

## Production

Expand Down
12 changes: 12 additions & 0 deletions app/banano/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const metadata = {
title: 'Banano Network Visualizer',
description: 'A 3D visualization of the Banano network'
};

export default function BananoLayout({
children
}: {
children: React.ReactNode;
}) {
return <>{children}</>;
}
16 changes: 16 additions & 0 deletions app/banano/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client';

import { Suspense } from 'react';
import BananoThreeSceneClientWrapper from '@/banano/components/three-scene-client-wrapper';
import { BananoConfirmationProvider } from '@/banano/providers/banano-confirmation-provider';
import Loader from '@/banano/components/loading';

export default function BananoPage() {
return (
<Suspense fallback={<Loader />}>
<BananoConfirmationProvider>
<BananoThreeSceneClientWrapper />
</BananoConfirmationProvider>
</Suspense>
);
}
202 changes: 202 additions & 0 deletions banano/components/banano-confirmation-history-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
'use client';

import React, { useState, useEffect, useMemo } from 'react';
import { useBananoConfirmations } from '@/banano/providers/banano-confirmation-provider';
import { parseBananoAmount } from '@/banano/lib/parse-banano-amount';
import { getStyleByBananoAmount } from '@/banano/lib/get-style-by-banano-amount';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger
} from '@/components/ui/tooltip';
import { Button } from '@/components/ui/button';
import { BANANO_LIVE_ENV } from '@/banano/constants/banano-live-env';
import { getBananoRepName } from '@/banano/lib/get-banano-rep-name';
import { truncateAddress } from '@/lib/truncate-address';
import { formatRelativeTime } from '@/lib/format-relative-time';
import { NanoConfirmation } from '@/types/index';
import { Maximize2, Minimize2, ChevronDown, ChevronUp } from 'lucide-react';

interface BananoConfirmationHistoryTableProps {}

export const BananoConfirmationHistoryTable: React.FC<
BananoConfirmationHistoryTableProps
> = () => {
const { confirmationHistory } = useBananoConfirmations();
const [isFullView, setIsFullView] = useState(false);
const [limitedHistory, setLimitedHistory] = useState<NanoConfirmation[]>([]);
const [showLessRows, setShowLessRows] = useState(true);

const displayedConfirmations = useMemo(() => {
if (showLessRows && window.innerWidth < 768) {
return confirmationHistory.slice(0, 3);
}
return confirmationHistory;
}, [confirmationHistory, showLessRows]);

useEffect(() => {
setLimitedHistory(displayedConfirmations.slice(0, 100));
}, [displayedConfirmations]);

const toggleView = () => {
setIsFullView(!isFullView);
};

const toggleRowCount = () => {
setShowLessRows(!showLessRows);
};

return (
<div className="space-y-4 w-full md:w-auto pointer-events-none select-none">
<div className="flex justify-end items-center gap-2 pointer-events-auto">
<Button
onClick={toggleRowCount}
variant="outline"
size="sm"
className="flex select-none items-center gap-2 bg-transparent hover:bg-transparent hover:text-[#209ce9] md:hidden"
>
{showLessRows ? (
<ChevronDown className="w-4 h-4" />
) : (
<ChevronUp className="w-4 h-4" />
)}
<span className="hidden md:inline">
{showLessRows ? 'Show More' : 'Show Less'}
</span>
</Button>
<Button
onClick={toggleView}
variant="outline"
size="sm"
className="flex select-none items-center gap-2 bg-transparent hover:bg-transparent hover:text-[#209ce9]"
>
{isFullView ? (
<>
<Minimize2 className="w-4 h-4" />
<span className="hidden md:inline">Min View</span>
</>
) : (
<>
<Maximize2 className="w-4 h-4" />
<span className="hidden md:inline">Full View</span>
</>
)}
</Button>
</div>
<div className="overflow-hidden max-h-[75vh] md:max-w-[700px] lg:max-w-[800px] md:ml-auto justify-end flex">
<table className="w-fit bg-transparent border border-transparent text-[14px]">
<thead className="bg-transparent select-none text-gray-300">
<tr>
{isFullView && (
<>
<th className="p-1 md:p-2 text-left">Age</th>
{window.innerWidth >= 768 && (
<>
<th className="p-1 md:p-2 text-left">Account</th>
<th className="p-1 md:p-2 text-left">Representative</th>
<th className="p-1 md:p-2 text-left">Type</th>
</>
)}
</>
)}
<th className="p-1 md:p-2 text-left">
<div className="flex items-center">
<span className="mr-1">Amount</span>
<img src="/banano/BAN.svg" className="w-4 h-4" />
</div>
</th>
</tr>
</thead>
<tbody>
{limitedHistory.map(
(confirmation: NanoConfirmation, index: number) => {
const amount = parseBananoAmount(confirmation.message.amount);
const style = getStyleByBananoAmount(amount);
const isDonation =
confirmation.message.block.link_as_account ===
BANANO_LIVE_ENV.donationAccount;
const repName = getBananoRepName(
confirmation.message.block.representative
);
return (
<tr
key={`${confirmation.message.hash}-${index}`}
className={isDonation ? 'bg-blue-600 text-white' : ''}
>
{isFullView && (
<>
<td className="p-1 md:p-2 text-gray-400">
{formatRelativeTime(parseInt(confirmation.time))}
</td>
{window.innerWidth >= 768 && (
<>
<td className="p-1 md:p-2">
<TooltipProvider
skipDelayDuration={100}
delayDuration={0}
disableHoverableContent={false}
>
<Tooltip>
<TooltipTrigger>
<span className="cursor-help text-gray-400">
{truncateAddress(
confirmation.message.account
)}
</span>
</TooltipTrigger>
<TooltipContent className="bg-black">
<span className="bg-black text-white border-1 border-gray-300 p-2 select-text">
{confirmation.message.account}
</span>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</td>
<td className="p-1 md:p-2">
<span
className={`${
repName ? 'text-[#ffa31a]' : 'text-gray-400'
}`}
>
{repName ??
truncateAddress(
confirmation.message.block.representative
)}
</span>
</td>
<td className="p-1 md:p-2">
{isDonation ? (
'Donation💰'
) : confirmation.message.block.subtype.toLocaleUpperCase() ===
'SEND' ? (
<span className="text-red-500">Send</span>
) : (
<span className="text-green-500">Receive</span>
)}
</td>
</>
)}
</>
)}
<td className={`p-1 md:p-2 ${isFullView ? '' : 'w-full'}`}>
<div
style={{ color: style.hexColor }}
className="flex items-center"
>
<img src="/banano/BAN.svg" className="w-4 h-4 mr-1" />
<span>{amount}</span>
</div>
</td>
</tr>
);
}
)}
</tbody>
</table>
</div>
</div>
);
};

export default BananoConfirmationHistoryTable;
Loading

0 comments on commit b5f3fe9

Please sign in to comment.