Skip to content

Commit

Permalink
Merge pull request #90 from ar-io/develop
Browse files Browse the repository at this point in the history
Release to production - v1.1.0
  • Loading branch information
kunstmusik authored Oct 8, 2024
2 parents 3e544b8 + 67877a4 commit 932dd1b
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 37 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@ar-io/network-portal",
"private": true,
"version": "1.0.0",
"version": "1.1.0",
"type": "module",
"scripts": {
"build": "yarn clean && tsc --build tsconfig.build.json && NODE_OPTIONS=--max-old-space-size=32768 vite build",
Expand Down
5 changes: 1 addition & 4 deletions src/components/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,7 @@ const Profile = () => {
<Popover className="relative">
<Popover.Button as={CustomPopoverButton} />

<Popover.Panel
className="absolute right-0 z-50 mt-2.5 w-fit
overflow-clip rounded-xl border border-grey-800 bg-grey-1000 text-sm shadow-xl"
>
<Popover.Panel className="absolute right-0 z-50 mt-2.5 w-fit rounded-xl border border-grey-800 bg-grey-1000 text-sm shadow-xl">
<div className="flex gap-2 px-4 py-5 ">
<WalletIcon className="size-4" />

Expand Down
160 changes: 160 additions & 0 deletions src/components/modals/LeaveNetworkModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { useGlobalState } from '@src/store';
import { formatWithCommas } from '@src/utils';
import { showErrorToast } from '@src/utils/toast';
import { useQueryClient } from '@tanstack/react-query';
import { useState } from 'react';
import Button, { ButtonType } from '../Button';
import { LinkArrowIcon } from '../icons';
import BaseModal from './BaseModal';
import BlockingMessageModal from './BlockingMessageModal';
import SuccessModal from './SuccessModal';
import { WRITE_OPTIONS } from '@src/constants';

const GATEWAY_OPERATOR_STAKE_MINIMUM = 50000;

const LeaveNetworkModal = ({ onClose }: { onClose: () => void }) => {
const queryClient = useQueryClient();

const walletAddress = useGlobalState((state) => state.walletAddress);
const arIOWriteableSDK = useGlobalState((state) => state.arIOWriteableSDK);

const ticker = useGlobalState((state) => state.ticker);

const [showBlockingMessageModal, setShowBlockingMessageModal] =
useState(false);
const [showSuccessModal, setShowSuccessModal] = useState(false);
const [txid, setTxid] = useState<string>();

const [leaveNetworkText, setLeaveNetworkText] = useState('');

const termsAccepted = leaveNetworkText === 'LEAVE NETWORK';

const processLeaveNetwork = async () => {
if (walletAddress && arIOWriteableSDK) {
setShowBlockingMessageModal(true);

try {
const { id: txID } = await arIOWriteableSDK.leaveNetwork(WRITE_OPTIONS);
setTxid(txID);

queryClient.invalidateQueries({
queryKey: ['gateway', walletAddress.toString()],
refetchType: 'all',
});
queryClient.invalidateQueries({
queryKey: ['gateways'],
refetchType: 'all',
});

setShowSuccessModal(true);
// onClose();
} catch (e: any) {
showErrorToast(`${e}`);
} finally {
setShowBlockingMessageModal(false);
}
}
};

return (
<>
<BaseModal onClose={onClose} useDefaultPadding={false}>
<div className="w-[28.4375rem] text-left">
<div className="px-8 pb-4 pt-6">
<div className="text-lg text-high">Leave Network</div>
{/* <div className="flex pt-2 text-xs text-low">
Withdraw all delegated stakes.
</div> */}
</div>

<div className="border-y border-grey-800 p-8 text-sm text-mid">
<div>
This action will begin the process of removing your gateway from
the network. Once confirmed, the following actions will be
initiated in the next epoch:
</div>
<ul className="mt-6 list-disc space-y-2 pl-8">
<li>
Your gateway&apos;s primary stake (
{formatWithCommas(GATEWAY_OPERATOR_STAKE_MINIMUM)} {ticker})
will be vaulted and subject to a 90-day withdrawal period.
</li>
<li>
Any additional operator stake above the minimum (
{formatWithCommas(GATEWAY_OPERATOR_STAKE_MINIMUM)} {ticker})
will be vaulted and subject to a 30-day withdrawal period.
</li>
<li>
Any existing delegated stakes will be vaulted and subject to
30-day withdrawal period.{' '}
</li>
<li>
Your gateway status will change to leaving and will no longer be
eligible for protocol rewards or observation duties.
</li>
</ul>
</div>

<div className="px-8 pb-8 pt-6">
<div className="mb-6 flex flex-col items-center gap-2 text-sm text-mid">
<div>Please type &quot;LEAVE NETWORK&quot; in the text box to proceed.</div>
<input
type="text"
onChange={(e) => setLeaveNetworkText(e.target.value)}
className={
'h-7 w-full rounded-md border border-grey-700 bg-grey-1000 p-3 text-sm text-mid outline-none placeholder:text-grey-400 focus:text-high'
}
value={leaveNetworkText}
/>
</div>

<div className="flex grow justify-center">
<Button
onClick={processLeaveNetwork}
buttonType={ButtonType.PRIMARY}
title="Leave Network"
text={<div className="py-2">Leave Network</div>}
className={`w-full ${!termsAccepted && 'pointer-events-none opacity-30'}`}
/>
</div>
</div>
</div>
</BaseModal>
{showBlockingMessageModal && (
<BlockingMessageModal
onClose={() => setShowBlockingMessageModal(false)}
message="Sign the following data with your wallet to proceed."
></BlockingMessageModal>
)}
{showSuccessModal && (
<SuccessModal
onClose={() => {
setShowSuccessModal(false);
onClose();
}}
title="Confirmed"
bodyText={
<div className="mb-8 text-sm text-mid">
<div>You have successfully left the network.</div>
<div className="my-2 flex flex-col justify-center gap-2">
<div>Transaction ID:</div>
<button
className="flex items-center justify-center"
title="View transaction on ao.link"
onClick={async () => {
window.open(`https://ao.link/#/message/${txid}`, '_blank');
}}
>
{txid}
<LinkArrowIcon className="ml-1 size-3" />
</button>
</div>
</div>
}
/>
)}
</>
);
};

export default LeaveNetworkModal;
5 changes: 3 additions & 2 deletions src/components/modals/SuccessModal.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ReactNode } from 'react';
import Button, { ButtonType } from '../Button';
import { SuccessCheck } from '../icons';
import BaseModal from './BaseModal';
Expand All @@ -9,7 +10,7 @@ const SuccessModal = ({
}: {
onClose: () => void;
title: string;
bodyText: string;
bodyText: ReactNode;
}) => {
return (
<BaseModal onClose={onClose}>
Expand All @@ -18,7 +19,7 @@ const SuccessModal = ({
<SuccessCheck className="size-8" />
</div>
<div className="pb-3 text-2xl text-high">{title}</div>
<div className="pb-8 text-center text-low">{bodyText}</div>
{ typeof bodyText === 'string' ? <div className="pb-8 text-center text-low">{bodyText}</div> : bodyText }
<div className="flex grow justify-center">
<Button
onClick={onClose}
Expand Down
2 changes: 1 addition & 1 deletion src/components/modals/UnstakeAllModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ const UnstakeAllModal = ({
});

setShowSuccessModal(true);
onClose();
} catch (e: any) {
showErrorToast(`${e}`);
} finally {
Expand Down Expand Up @@ -149,6 +148,7 @@ const UnstakeAllModal = ({
<SuccessModal
onClose={() => {
setShowSuccessModal(false);
onClose();
}}
title="Congratulations"
bodyText="You have successfully withdrawn all stakes."
Expand Down
37 changes: 37 additions & 0 deletions src/hooks/useGatewayArioInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useQuery } from '@tanstack/react-query';

export interface ArioInfoResponse {
wallet: string;
processId: string;
// ans104UnbundleFilter:...
// ans104IndexFilter:...
supportedManifestVersions: string[];
release:string;
}

const useGatewayArioInfo = ({
url
}: {
url?: string;
}) => {
const queryResults = useQuery({
queryKey: ['ario-info', url],
queryFn: async () => {
if (url === undefined) {
throw new Error('Error: no URL provided.')
}

const arioInfoEndpoint = `${url}/ar-io/info`;

const response = await fetch(arioInfoEndpoint);
const responseJson = await response.json();

return responseJson as ArioInfoResponse;

},
});

return queryResults;
};

export default useGatewayArioInfo;
5 changes: 5 additions & 0 deletions src/hooks/useReports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import arweaveGraphql from 'arweave-graphql';
export interface ReportTransactionData {
txid: string;
failedGateways: number;
epochNumber: number;
timestamp: number;
size: number;
version: string;
Expand Down Expand Up @@ -108,6 +109,10 @@ const useReports = (ownerId?: string, gateway?: AoGateway) => {
tags.find((tag) => tag.name === 'AR-IO-Epoch-Start-Timestamp')
?.value || '0',
),
epochNumber: parseInt(
tags.find((tag) => tag.name === 'AR-IO-Epoch-Index')?.value ||
'0',
),
size: parseInt(transaction.node.data.size),
version:
tags.find((tag) => tag.name === 'AR-IO-Observer-Report-Version')?.value || '',
Expand Down
60 changes: 43 additions & 17 deletions src/pages/Gateway/PropertyDisplayPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AoGateway, mIOToken } from '@ar.io/sdk/web';
import Button, { ButtonType } from '@src/components/Button';
import Placeholder from '@src/components/Placeholder';
import ConnectModal from '@src/components/modals/ConnectModal';
import LeaveNetworkModal from '@src/components/modals/LeaveNetworkModal';
import StakingModal from '@src/components/modals/StakingModal';
import { useGlobalState } from '@src/store';
import { useState } from 'react';
Expand Down Expand Up @@ -51,7 +52,10 @@ const DisplayRow = ({
{value}
</a>
) : (
value
<div className="flex items-center">
<div className="grow">{value}</div>
{rightComponent}
</div>
)}
</div>
</>
Expand All @@ -68,10 +72,14 @@ const PropertyDisplayPanel = ({
const walletAddress = useGlobalState((state) => state.walletAddress);
const ticker = useGlobalState((state) => state.ticker);

const isOwnGateway = ownerId && ownerId === walletAddress?.toString();

const [stakingModalWalletAddress, setStakingModalWalletAddress] =
useState<string>();

const [isConnectModalOpen, setIsConnectModalOpen] = useState<boolean>(false);
const [isLeaveNetworkModalOpen, setLeaveNetworkModalOpen] =
useState<boolean>(false);

const gatewayAddress = gateway
? `${gateway.settings.protocol}://${gateway.settings.fqdn}:${gateway.settings.port}`
Expand Down Expand Up @@ -127,6 +135,19 @@ const PropertyDisplayPanel = ({
) : (
gateway?.status
),
rightComponent:
isOwnGateway && gateway?.status == 'joined' ? (
<Button
className="*:*:text-gradient-red mr-2"
buttonType={ButtonType.PRIMARY}
active={true}
title="Leave Network"
text="Leave"
onClick={() => {
setLeaveNetworkModalOpen(true);
}}
/>
) : undefined,
},
{ label: 'Note:', value: gateway?.settings.note },
{
Expand All @@ -144,22 +165,24 @@ const PropertyDisplayPanel = ({
{
label: 'Delegated Staking:',
value: gatewayLeaving ? 'N/A' : gateway?.settings.allowDelegatedStaking,
rightComponent: gateway?.settings.allowDelegatedStaking ? (
<Button
className="mr-2"
buttonType={ButtonType.PRIMARY}
active={true}
title="Manage Stake"
text="Stake"
onClick={() => {
if (walletAddress) {
setStakingModalWalletAddress(ownerId);
} else {
setIsConnectModalOpen(true);
}
}}
/>
) : undefined,
rightComponent:
gateway?.settings.allowDelegatedStaking &&
gateway?.status == 'joined' ? (
<Button
className="mr-2"
buttonType={ButtonType.PRIMARY}
active={true}
title="Manage Stake"
text="Stake"
onClick={() => {
if (walletAddress) {
setStakingModalWalletAddress(ownerId);
} else {
setIsConnectModalOpen(true);
}
}}
/>
) : undefined,
},
...conditionalRows,
];
Expand Down Expand Up @@ -187,6 +210,9 @@ const PropertyDisplayPanel = ({
{isConnectModalOpen && (
<ConnectModal onClose={() => setIsConnectModalOpen(false)} />
)}
{isLeaveNetworkModalOpen && (
<LeaveNetworkModal onClose={() => setLeaveNetworkModalOpen(false)} />
)}
</div>
);
};
Expand Down
Loading

0 comments on commit 932dd1b

Please sign in to comment.