Add remark42 to Mixnode page
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
"use client";
|
||||
|
||||
import * as React from 'react'
|
||||
import * as React from "react";
|
||||
import {
|
||||
Alert,
|
||||
AlertTitle,
|
||||
@@ -8,75 +8,102 @@ import {
|
||||
CircularProgress,
|
||||
Grid,
|
||||
Typography,
|
||||
} from '@mui/material'
|
||||
import { ColumnsType, DetailTable } from '@/app/components/DetailTable'
|
||||
import { BondBreakdownTable } from '@/app/components/MixNodes/BondBreakdown'
|
||||
} from "@mui/material";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
|
||||
import { ColumnsType, DetailTable } from "@/app/components/DetailTable";
|
||||
import { BondBreakdownTable } from "@/app/components/MixNodes/BondBreakdown";
|
||||
import {
|
||||
DelegatorsInfoTable,
|
||||
EconomicsInfoColumns,
|
||||
EconomicsInfoRows,
|
||||
} from '@/app/components/MixNodes/Economics'
|
||||
import { ComponentError } from '@/app/components/ComponentError'
|
||||
import { ContentCard } from '@/app/components/ContentCard'
|
||||
import { TwoColSmallTable } from '@/app/components/TwoColSmallTable'
|
||||
import { UptimeChart } from '@/app/components/UptimeChart'
|
||||
import { WorldMap } from '@/app/components/WorldMap'
|
||||
import { MixNodeDetailSection } from '@/app/components/MixNodes/DetailSection'
|
||||
} from "@/app/components/MixNodes/Economics";
|
||||
import { ComponentError } from "@/app/components/ComponentError";
|
||||
import { ContentCard } from "@/app/components/ContentCard";
|
||||
import { TwoColSmallTable } from "@/app/components/TwoColSmallTable";
|
||||
import { UptimeChart } from "@/app/components/UptimeChart";
|
||||
import { WorldMap } from "@/app/components/WorldMap";
|
||||
import { MixNodeDetailSection } from "@/app/components/MixNodes/DetailSection";
|
||||
import {
|
||||
MixnodeContextProvider,
|
||||
useMixnodeContext,
|
||||
} from '@/app/context/mixnode'
|
||||
import { Title } from '@/app/components/Title'
|
||||
import { useIsMobile } from '@/app/hooks/useIsMobile'
|
||||
import { useParams } from 'next/navigation'
|
||||
} from "@/app/context/mixnode";
|
||||
import { Title } from "@/app/components/Title";
|
||||
import { useIsMobile } from "@/app/hooks/useIsMobile";
|
||||
import { useParams } from "next/navigation";
|
||||
import Script from "next/script";
|
||||
import Head from "next/head";
|
||||
import { useEffect } from "react";
|
||||
import { useMainContext } from "@/app/context/main";
|
||||
|
||||
const columns: ColumnsType[] = [
|
||||
{
|
||||
field: 'owner',
|
||||
title: 'Owner',
|
||||
width: '15%',
|
||||
field: "owner",
|
||||
title: "Owner",
|
||||
width: "15%",
|
||||
},
|
||||
{
|
||||
field: 'identity_key',
|
||||
title: 'Identity Key',
|
||||
width: '15%',
|
||||
field: "identity_key",
|
||||
title: "Identity Key",
|
||||
width: "15%",
|
||||
},
|
||||
|
||||
{
|
||||
field: 'bond',
|
||||
title: 'Stake',
|
||||
width: '12.5%',
|
||||
field: "bond",
|
||||
title: "Stake",
|
||||
width: "12.5%",
|
||||
},
|
||||
{
|
||||
field: 'stake_saturation',
|
||||
title: 'Stake Saturation',
|
||||
width: '12.5%',
|
||||
field: "stake_saturation",
|
||||
title: "Stake Saturation",
|
||||
width: "12.5%",
|
||||
tooltipInfo:
|
||||
'Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is 940k NYMs, computed as S/K where S is target amount of tokens staked in the network and K is the number of nodes in the reward set.',
|
||||
"Level of stake saturation for this node. Nodes receive more rewards the higher their saturation level, up to 100%. Beyond 100% no additional rewards are granted. The current stake saturation level is 940k NYMs, computed as S/K where S is target amount of tokens staked in the network and K is the number of nodes in the reward set.",
|
||||
},
|
||||
{
|
||||
field: 'self_percentage',
|
||||
width: '10%',
|
||||
title: 'Bond %',
|
||||
field: "self_percentage",
|
||||
width: "10%",
|
||||
title: "Bond %",
|
||||
tooltipInfo:
|
||||
"Percentage of the operator's bond to the total stake on the node",
|
||||
},
|
||||
|
||||
{
|
||||
field: 'host',
|
||||
width: '10%',
|
||||
title: 'Host',
|
||||
field: "host",
|
||||
width: "10%",
|
||||
title: "Host",
|
||||
},
|
||||
{
|
||||
field: 'location',
|
||||
title: 'Location',
|
||||
field: "location",
|
||||
title: "Location",
|
||||
},
|
||||
|
||||
{
|
||||
field: 'layer',
|
||||
title: 'Layer',
|
||||
field: "layer",
|
||||
title: "Layer",
|
||||
},
|
||||
]
|
||||
];
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
remark_config: {
|
||||
host: string;
|
||||
site_id: string;
|
||||
components: string[];
|
||||
max_shown_comments: number;
|
||||
theme: string;
|
||||
page_title: string;
|
||||
locale: string;
|
||||
show_email_subscription: boolean;
|
||||
simple_view: boolean;
|
||||
no_footer: boolean;
|
||||
};
|
||||
REMARK42: {
|
||||
createInstance: (config: typeof window.remark_config) => void;
|
||||
changeTheme: (theme: "light" | "dark") => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows mix node details
|
||||
@@ -90,10 +117,54 @@ const PageMixnodeDetailWithState = () => {
|
||||
status,
|
||||
uptimeStory,
|
||||
uniqDelegations,
|
||||
} = useMixnodeContext()
|
||||
const isMobile = useIsMobile()
|
||||
} = useMixnodeContext();
|
||||
const isMobile = useIsMobile();
|
||||
const { mode } = useMainContext();
|
||||
|
||||
console.log("toggleMode :>> ", mode);
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
// Set Remark42 configuration on the window object
|
||||
window.remark_config = {
|
||||
host: "http://localhost:8081",
|
||||
site_id: "remark42",
|
||||
components: ["embed", "last-comments"],
|
||||
max_shown_comments: 100,
|
||||
theme: mode === "light" ? "light" : "dark",
|
||||
page_title: "My custom title for a page",
|
||||
locale: "en",
|
||||
show_email_subscription: false,
|
||||
simple_view: true,
|
||||
no_footer: false,
|
||||
};
|
||||
|
||||
// Dynamically load the Remark42 script if it doesn't exist
|
||||
if (!document.getElementById("remark42-script")) {
|
||||
const script = document.createElement("script");
|
||||
script.src = `${window.remark_config.host}/web/embed.js`;
|
||||
script.async = true;
|
||||
script.defer = true;
|
||||
script.id = "remark42-script";
|
||||
document.body.appendChild(script);
|
||||
} else if (window.REMARK42) {
|
||||
// Re-initialize if the script is already loaded
|
||||
window.REMARK42.createInstance(window.remark_config);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
// React to mode changes and update Remark42 theme
|
||||
useEffect(() => {
|
||||
if (window.REMARK42 && window.REMARK42.changeTheme) {
|
||||
window.REMARK42.changeTheme(mode === "dark" ? "dark" : "light");
|
||||
}
|
||||
}, [mode]);
|
||||
|
||||
return (
|
||||
<Box component="main">
|
||||
<Head>
|
||||
<title>Mixnode Detail</title>
|
||||
</Head>
|
||||
<Title text="Mixnode Detail" />
|
||||
<Grid container spacing={2} mt={1} mb={6}>
|
||||
<Grid item xs={12}>
|
||||
@@ -105,9 +176,9 @@ const PageMixnodeDetailWithState = () => {
|
||||
)}
|
||||
{mixNodeRow?.blacklisted && (
|
||||
<Typography
|
||||
textAlign={isMobile ? 'left' : 'right'}
|
||||
textAlign={isMobile ? "left" : "right"}
|
||||
fontSize="smaller"
|
||||
sx={{ color: 'error.main' }}
|
||||
sx={{ color: "error.main" }}
|
||||
>
|
||||
This node is having a poor performance
|
||||
</Typography>
|
||||
@@ -153,7 +224,7 @@ const PageMixnodeDetailWithState = () => {
|
||||
loading={stats.isLoading}
|
||||
error={stats?.error?.message}
|
||||
title="Since startup"
|
||||
keys={['Received', 'Sent', 'Explicitly dropped']}
|
||||
keys={["Received", "Sent", "Explicitly dropped"]}
|
||||
values={[
|
||||
stats?.data?.packets_received_since_startup || 0,
|
||||
stats?.data?.packets_sent_since_startup || 0,
|
||||
@@ -164,7 +235,7 @@ const PageMixnodeDetailWithState = () => {
|
||||
loading={stats.isLoading}
|
||||
error={stats?.error?.message}
|
||||
title="Since last update"
|
||||
keys={['Received', 'Sent', 'Explicitly dropped']}
|
||||
keys={["Received", "Sent", "Explicitly dropped"]}
|
||||
values={[
|
||||
stats?.data?.packets_received_since_last_update || 0,
|
||||
stats?.data?.packets_sent_since_last_update || 0,
|
||||
@@ -204,7 +275,7 @@ const PageMixnodeDetailWithState = () => {
|
||||
<TwoColSmallTable
|
||||
loading={status.isLoading}
|
||||
error={status?.error?.message}
|
||||
keys={['Mix port', 'Verloc port', 'HTTP port']}
|
||||
keys={["Mix port", "Verloc port", "HTTP port"]}
|
||||
values={[1789, 1790, 8000].map((each) => each)}
|
||||
icons={
|
||||
(status?.data?.ports && Object.values(status.data.ports)) || [
|
||||
@@ -237,29 +308,41 @@ const PageMixnodeDetailWithState = () => {
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<div id="remark42"></div>
|
||||
<Script
|
||||
id="remark-init"
|
||||
strategy="afterInteractive"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
if (window.REMARK42) {
|
||||
window.REMARK42.createInstance(window.remark_config);
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Guard component to handle loading and not found states
|
||||
*/
|
||||
const PageMixnodeDetailGuard = () => {
|
||||
const { mixNode } = useMixnodeContext()
|
||||
const { id } = useParams()
|
||||
const { mixNode } = useMixnodeContext();
|
||||
const { id } = useParams();
|
||||
|
||||
if (mixNode?.isLoading) {
|
||||
return <CircularProgress />
|
||||
return <CircularProgress />;
|
||||
}
|
||||
|
||||
if (mixNode?.error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(mixNode?.error)
|
||||
console.error(mixNode?.error);
|
||||
return (
|
||||
<Alert severity="error">
|
||||
Oh no! Could not load mixnode <code>{id || ''}</code>
|
||||
Oh no! Could not load mixnode <code>{id || ""}</code>
|
||||
</Alert>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// loaded, but not found
|
||||
@@ -267,31 +350,31 @@ const PageMixnodeDetailGuard = () => {
|
||||
return (
|
||||
<Alert severity="warning">
|
||||
<AlertTitle>Mixnode not found</AlertTitle>
|
||||
Sorry, we could not find a mixnode with id <code>{id || ''}</code>
|
||||
Sorry, we could not find a mixnode with id <code>{id || ""}</code>
|
||||
</Alert>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return <PageMixnodeDetailWithState />
|
||||
}
|
||||
return <PageMixnodeDetailWithState />;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper component that adds the mixnode content based on the `id` in the address URL
|
||||
*/
|
||||
const PageMixnodeDetail = () => {
|
||||
const { id } = useParams()
|
||||
const { id } = useParams();
|
||||
|
||||
if (!id || typeof id !== 'string') {
|
||||
if (!id || typeof id !== "string") {
|
||||
return (
|
||||
<Alert severity="error">Oh no! No mixnode identity key specified</Alert>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<MixnodeContextProvider mixId={id}>
|
||||
<PageMixnodeDetailGuard />
|
||||
</MixnodeContextProvider>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default PageMixnodeDetail
|
||||
export default PageMixnodeDetail;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
version: "2"
|
||||
|
||||
services:
|
||||
remark:
|
||||
# remove the next line in case you want to use this Docker Compose file separately
|
||||
# as otherwise it would complain for absence of Dockerfile
|
||||
build: .
|
||||
image: umputun/remark42:latest
|
||||
container_name: "explorer_remark42"
|
||||
hostname: "remark42"
|
||||
restart: always
|
||||
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "10m"
|
||||
max-file: "5"
|
||||
|
||||
# uncomment to expose directly (no proxy)
|
||||
ports:
|
||||
- "8081:8080"
|
||||
- "443:8443"
|
||||
|
||||
environment:
|
||||
- REMARK_URL=http://localhost:8081
|
||||
- SECRET=secret-key
|
||||
- AUTH_ANON=true
|
||||
- SITE=remark42
|
||||
# - DEBUG=true
|
||||
# - AUTH_GOOGLE_CID
|
||||
# - AUTH_GOOGLE_CSEC
|
||||
# - AUTH_GITHUB_CID
|
||||
# - AUTH_GITHUB_CSEC
|
||||
# - AUTH_FACEBOOK_CID
|
||||
# - AUTH_FACEBOOK_CSEC
|
||||
# - AUTH_DISQUS_CID
|
||||
# - AUTH_DISQUS_CSEC
|
||||
# Enable it only for the initial comment import or for manual backups.
|
||||
# Do not leave the server running with the ADMIN_PASSWD set if you don't have an intention
|
||||
# to keep creating backups manually!
|
||||
- ADMIN_PASSWD=password
|
||||
volumes:
|
||||
- ./var:/srv/var
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 976 B |
Binary file not shown.
Reference in New Issue
Block a user