Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
466 changes: 241 additions & 225 deletions apps/deploy-web/src/components/authorizations/Authorizations.tsx

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ describe(CreateCertificateButton.name, () => {
isCreatingCert?: boolean;
isLocalCertExpired?: boolean;
localCert?: LocalCert;
isBlockchainDown?: boolean;
}
) {
const { createCertificate, isCreatingCert, isLocalCertExpired, localCert, ...props } = input ?? {};
const { createCertificate, isCreatingCert, isLocalCertExpired, localCert, isBlockchainDown, ...props } = input ?? {};

return render(
<CreateCertificateButton
Expand All @@ -53,7 +54,23 @@ describe(CreateCertificateButton.name, () => {
isCreatingCert: isCreatingCert ?? false,
isLocalCertExpired: isLocalCertExpired ?? false,
localCert: localCert ?? null
})) as (typeof CREATE_CERTIFICATE_BUTTON_DEPENDENCIES)["useCertificate"]
})) as (typeof CREATE_CERTIFICATE_BUTTON_DEPENDENCIES)["useCertificate"],
useSettings: () => ({
settings: {
apiEndpoint: "https://api.example.com",
rpcEndpoint: "https://rpc.example.com",
isCustomNode: false,
nodes: [],
selectedNode: null,
customNode: null,
isBlockchainDown: isBlockchainDown ?? false
},
setSettings: jest.fn(),
isLoadingSettings: false,
isSettingsInit: true,
refreshNodeStatuses: jest.fn(),
isRefreshingNodeStatus: false
})
}}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { Alert, Button, Spinner } from "@akashnetwork/ui/components";
import { cn } from "@akashnetwork/ui/utils";

import { useCertificate } from "@src/context/CertificateProvider";
import { useSettings } from "@src/context/SettingsProvider";

export const DEPENDENCIES = {
Alert,
Button,
Spinner,
useCertificate
useCertificate,
useSettings
};

export interface Props extends Omit<ButtonProps, "onClick"> {
Expand All @@ -20,6 +22,7 @@ export interface Props extends Omit<ButtonProps, "onClick"> {
}

export const CreateCertificateButton: FC<Props> = ({ afterCreate, containerClassName, dependencies: d = DEPENDENCIES, ...buttonProps }) => {
const { settings } = d.useSettings();
const { isCreatingCert, createCertificate, isLocalCertExpired, localCert } = d.useCertificate();

const _createCertificate = useCallback(async () => {
Expand All @@ -38,7 +41,12 @@ export const CreateCertificateButton: FC<Props> = ({ afterCreate, containerClass
<d.Alert variant="warning" className={cn({ "py-2 text-sm": buttonProps?.size === "sm" }, "truncate")}>
{warningText}
</d.Alert>
<d.Button className={warningText ? "mt-4" : ""} {...buttonProps} disabled={buttonProps?.disabled || isCreatingCert} onClick={_createCertificate}>
<d.Button
className={warningText ? "mt-4" : ""}
{...buttonProps}
disabled={buttonProps?.disabled || settings.isBlockchainDown || isCreatingCert}
onClick={_createCertificate}
>
{isCreatingCert ? <d.Spinner size="small" /> : buttonText}
</d.Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,12 @@ export const DeploymentList: React.FunctionComponent = () => {
{isSignedInWithTrial && !user && <p className="text-sm">If you are expecting to see some, you may need to sign-in or connect a wallet</p>}

{isWalletConnected ? (
<Link href={UrlService.newDeployment()} className={cn(buttonVariants({ variant: "default", size: "lg" }), "mt-4")} onClick={onDeployClick}>
<Link
href={UrlService.newDeployment()}
className={cn(buttonVariants({ variant: "default", size: "lg" }), "mt-4")}
onClick={onDeployClick}
aria-disabled={settings.isBlockchainDown}
>
Deploy
<Rocket className="ml-4 rotate-45 text-sm" />
</Link>
Expand Down
9 changes: 8 additions & 1 deletion apps/deploy-web/src/components/home/YourAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { AddFundsLink } from "@src/components/user/AddFundsLink";
import { browserEnvConfig } from "@src/config/browser-env.config";
import { UAKT_DENOM } from "@src/config/denom.config";
import { usePricing } from "@src/context/PricingProvider";
import { useSettings } from "@src/context/SettingsProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useUsdcDenom } from "@src/hooks/useDenom";
import { useFlag } from "@src/hooks/useFlag";
Expand Down Expand Up @@ -41,6 +42,7 @@ type Props = {
};

export const YourAccount: React.FunctionComponent<Props> = ({ isLoadingBalances, walletBalance, activeDeployments, leases, providers }) => {
const { settings } = useSettings();
const { resolvedTheme } = useTheme();
const tw = useTailwind();
const { address, isManaged: isManagedWallet, isTrialing } = useWallet();
Expand Down Expand Up @@ -229,7 +231,12 @@ export const YourAccount: React.FunctionComponent<Props> = ({ isLoadingBalances,
)}

<div className="mt-4 flex gap-2">
<Link href={UrlService.newDeployment()} className={cn(buttonVariants({ variant: "default" }))} onClick={onDeployClick}>
<Link
href={UrlService.newDeployment()}
className={cn(buttonVariants({ variant: "default" }))}
onClick={onDeployClick}
aria-disabled={settings.isBlockchainDown}
>
Deploy
<Rocket className="ml-2 rotate-45 text-sm" />
</Link>
Expand Down
33 changes: 15 additions & 18 deletions apps/deploy-web/src/components/layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ import { millisecondsInMinute } from "date-fns/constants";

import { ACCOUNT_BAR_HEIGHT } from "@src/config/ui.config";
import { useSettings } from "@src/context/SettingsProvider";
import { TopBannerProvider, useTopBanner } from "@src/context/TopBannerProvider/TopBannerProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useHasCreditCardBanner } from "@src/hooks/useHasCreditCardBanner";
import { useVariant } from "@src/hooks/useVariant";
import { LinearLoadingSkeleton } from "../shared/LinearLoadingSkeleton";
import { Nav } from "./Nav";
import { Sidebar } from "./Sidebar";
import { CreditCardBanner, MaintenanceBanner } from "./TopBanner";
import { TopBanner } from "./TopBanner";
import { TrackingScripts } from "./TrackingScripts";
import { WelcomeToTrialModal } from "./WelcomeToTrialModal";

Expand All @@ -40,21 +39,22 @@ const Layout: React.FunctionComponent<Props> = ({ children, isLoading, isUsingSe

return (
<IntlProvider locale={locale} defaultLocale="en-US">
<LayoutApp
isLoading={isLoading}
isUsingSettings={isUsingSettings}
isUsingWallet={isUsingWallet}
disableContainer={disableContainer}
containerClassName={containerClassName}
>
{children}
</LayoutApp>
<TopBannerProvider>
<LayoutApp
isLoading={isLoading}
isUsingSettings={isUsingSettings}
isUsingWallet={isUsingWallet}
disableContainer={disableContainer}
containerClassName={containerClassName}
>
{children}
</LayoutApp>
</TopBannerProvider>
</IntlProvider>
);
};

const LayoutApp: React.FunctionComponent<Props> = ({ children, isLoading, isUsingSettings, isUsingWallet, disableContainer, containerClassName = "" }) => {
const maintenanceBannerFlag = useVariant("maintenance_banner");
const muiTheme = useMuiTheme();
const smallScreen = useMediaQuery(muiTheme.breakpoints.down("md"));
const [isNavOpen, setIsNavOpen] = useState(() => {
Expand All @@ -67,11 +67,9 @@ const LayoutApp: React.FunctionComponent<Props> = ({ children, isLoading, isUsin
return true;
});
const [isMobileOpen, setIsMobileOpen] = useState(false);
const [isMaintenanceBannerOpen, setIsMaintenanceBannerOpen] = useState(!!maintenanceBannerFlag.enabled);
const { refreshNodeStatuses, isSettingsInit } = useSettings();
const { isWalletLoaded } = useWallet();
const hasCreditCardBanner = useHasCreditCardBanner(isMaintenanceBannerOpen);
const hasBanner = hasCreditCardBanner || isMaintenanceBannerOpen;
const { hasBanner } = useTopBanner();

useEffect(() => {
const refreshNodeIntervalId = setInterval(async () => {
Expand Down Expand Up @@ -99,8 +97,7 @@ const LayoutApp: React.FunctionComponent<Props> = ({ children, isLoading, isUsin

return (
<div className="flex h-full">
{hasCreditCardBanner && <CreditCardBanner />}
{isMaintenanceBannerOpen && <MaintenanceBanner onClose={() => setIsMaintenanceBannerOpen(false)} />}
<TopBanner />

<div className="w-full flex-1" style={{ marginTop: `${ACCOUNT_BAR_HEIGHT + (hasBanner ? 40 : 0)}px` }}>
<div className="h-full">
Expand Down
3 changes: 3 additions & 0 deletions apps/deploy-web/src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { useAtom } from "jotai";
import Image from "next/image";
import Link from "next/link";

import { useSettings } from "@src/context/SettingsProvider";
import { useWallet } from "@src/context/WalletProvider";
import { useFlag } from "@src/hooks/useFlag";
import { useUser } from "@src/hooks/useUser";
Expand All @@ -60,6 +61,7 @@ const DRAWER_WIDTH = 240;
const CLOSED_DRAWER_WIDTH = 57;

export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, handleDrawerToggle, isNavOpen, onOpenMenuClick, mdDrawerClassName }) => {
const { settings } = useSettings();
const [, setDeploySdl] = useAtom(sdlStore.deploySdl);
const muiTheme = useMuiTheme();
const smallScreen = useMediaQuery(muiTheme.breakpoints.down("md"));
Expand Down Expand Up @@ -310,6 +312,7 @@ export const Sidebar: React.FunctionComponent<Props> = ({ isMobileOpen, handleDr
href={UrlService.newDeployment()}
onClick={onDeployClick}
data-testid="sidebar-deploy-button"
aria-disabled={settings.isBlockchainDown}
>
{isNavOpen && "Deploy "}
<Rocket className={cn("rotate-45", { ["ml-4"]: isNavOpen })} fontSize="small" />
Expand Down
31 changes: 29 additions & 2 deletions apps/deploy-web/src/components/layout/TopBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { FormattedDate } from "react-intl";
import { Button } from "@akashnetwork/ui/components";
import { Xmark } from "iconoir-react";

import { useTopBanner } from "@src/context/TopBannerProvider";
import { useWallet } from "@src/context/WalletProvider/WalletProvider";
import { useVariant } from "@src/hooks/useVariant";
import { ConnectManagedWalletButton } from "../wallet/ConnectManagedWalletButton";

export function CreditCardBanner() {
function CreditCardBanner() {
const { hasManagedWallet } = useWallet();

return (
Expand All @@ -18,7 +19,15 @@ export function CreditCardBanner() {
);
}

export function MaintenanceBanner({ onClose }: { onClose: () => void }) {
function NetworkDownBanner() {
return (
<div className="fixed top-0 z-10 flex h-[40px] w-full items-center justify-center bg-primary px-3 py-2 md:space-x-4">
<span className="text-xs font-semibold text-white md:text-sm">The network is down. Unable to change deployments at the moment.</span>
</div>
);
}

function MaintenanceBanner({ onClose }: { onClose: () => void }) {
const maintenanceBannerFlag = useVariant("maintenance_banner");

const { message, date } = maintenanceBannerFlag.enabled
Expand All @@ -36,3 +45,21 @@ export function MaintenanceBanner({ onClose }: { onClose: () => void }) {
</div>
);
}

export function TopBanner() {
const { isMaintenanceBannerOpen, setIsMaintenanceBannerOpen, isBlockchainDown, hasCreditCardBanner } = useTopBanner();

if (isMaintenanceBannerOpen) {
return <MaintenanceBanner onClose={() => setIsMaintenanceBannerOpen(false)} />;
}

if (isBlockchainDown) {
return <NetworkDownBanner />;
}

if (hasCreditCardBanner) {
return <CreditCardBanner />;
}

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,44 @@ describe(CreateLease.name, () => {
jest.useRealTimers();
});

it("disables Accept Bid button when blockchain is down", async () => {
const BidGroup = jest.fn(ComponentMock);
const bids = [
buildRpcBid({
bid: {
bid_id: {
gseq: 1
},
state: "open"
}
}),
buildRpcBid({
bid: {
bid_id: {
gseq: 1
},
state: "open"
}
})
];
setup({
BidGroup,
bids,
isBlockchainDown: true
});

await waitFor(() => {
expect((BidGroup as jest.Mock).mock.calls.length).toBeGreaterThan(0);
});
const bidGroupProps = (BidGroup as jest.Mock).mock.calls[0][0];
act(() => {
bidGroupProps.handleBidSelected(mapToBidDto(bids[0]));
});
await waitFor(() => {
expect(screen.queryByRole("button", { name: /Accept Bid/i })).toBeDisabled();
});
});

describe("lease creation", () => {
it("creates lease on chain and submits manifest to provider", async () => {
const signAndBroadcastTx = jest.fn().mockResolvedValue({ code: 0 });
Expand Down Expand Up @@ -362,6 +400,7 @@ describe(CreateLease.name, () => {
updateSelectedCertificate?: (cert: CertificatePem) => Promise<LocalCert>;
isTrialWallet?: boolean;
getBlock?: () => Promise<BlockDetail>;
isBlockchainDown?: boolean;
}) {
const favoriteProviders: string[] = [];
const useLocalNotes = (() => ({
Expand Down Expand Up @@ -456,6 +495,22 @@ describe(CreateLease.name, () => {
useRouter: () => mock(),
useManagedDeploymentConfirm: () => ({
closeDeploymentConfirm: () => Promise.resolve(true)
}),
useSettings: () => ({
settings: {
apiEndpoint: "https://api.example.com",
rpcEndpoint: "https://rpc.example.com",
isCustomNode: false,
nodes: [],
selectedNode: null,
customNode: null,
isBlockchainDown: input?.isBlockchainDown ?? false
},
setSettings: jest.fn(),
isLoadingSettings: false,
isSettingsInit: true,
refreshNodeStatuses: jest.fn(),
isRefreshingNodeStatus: false
})
}}
/>
Expand Down
Loading