Skip to content

Commit

Permalink
feat(billing): implement wallet type switch
Browse files Browse the repository at this point in the history
refs #247
  • Loading branch information
ygrishajev committed Aug 29, 2024
1 parent bd4c06b commit cdde636
Show file tree
Hide file tree
Showing 8 changed files with 257 additions and 151 deletions.
96 changes: 54 additions & 42 deletions apps/deploy-web/src/components/layout/WalletStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,32 @@ import {
TooltipContent,
TooltipTrigger
} from "@akashnetwork/ui/components";
import { Bank, HandCard, LogOut, MoreHoriz, Wallet } from "iconoir-react";
import { Bank, CoinsSwap, HandCard, LogOut, MoreHoriz, Wallet } from "iconoir-react";
import Link from "next/link";
import { useRouter } from "next/navigation";

import { LoginRequiredLink } from "@src/components/user/LoginRequiredLink";
import { ConnectManagedWalletButton } from "@src/components/wallet/ConnectManagedWalletButton";
import { envConfig } from "@src/config/env.config";
import { useWallet } from "@src/context/WalletProvider";
import { useLoginRequiredEventHandler } from "@src/hooks/useLoginRequiredEventHandler";
import { useTotalWalletBalance } from "@src/hooks/useWalletBalance";
import { udenomToDenom } from "@src/utils/mathHelpers";
import { UrlService } from "@src/utils/urlUtils";
import { FormattedDecimal } from "../shared/FormattedDecimal";
import { ConnectWalletButton } from "../wallet/ConnectWalletButton";

const goToCheckout = () => {
window.location.href = "/api/proxy/v1/checkout";
};

const withBilling = envConfig.NEXT_PUBLIC_BILLING_ENABLED;

export function WalletStatus() {
const { walletName, address, walletBalances, logout, isWalletLoaded, isWalletConnected, isManaged, isWalletLoading, isTrialing } = useWallet();
const { walletName, address, walletBalances, logout, isWalletLoaded, isWalletConnected, isManaged, isWalletLoading, isTrialing, switchWalletType } =
useWallet();
const walletBalance = useTotalWalletBalance();
const router = useRouter();
function onDisconnectClick() {
logout();
}
const whenLoggedIn = useLoginRequiredEventHandler();

const onAuthorizeSpendingClick = () => {
router.push(UrlService.settingsAuthorizations());
Expand All @@ -46,29 +51,48 @@ export function WalletStatus() {
isWalletConnected ? (
<>
<div className="flex items-center pr-2">
{!isManaged && (
<div className="pl-2 pr-2">
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<MoreHoriz />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => onAuthorizeSpendingClick()}>
<Bank />
&nbsp;Authorize Spending
</DropdownMenuItem>
<DropdownMenuItem onClick={() => onDisconnectClick()}>
<LogOut />
&nbsp;Disconnect Wallet
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
)}

<div className="pl-2 pr-2">
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<MoreHoriz />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{!isManaged && (
<>
<DropdownMenuItem onClick={() => onAuthorizeSpendingClick()}>
<Bank />
&nbsp;Authorize Spending
</DropdownMenuItem>
<DropdownMenuItem onClick={logout}>
<LogOut />
&nbsp;Disconnect Wallet
</DropdownMenuItem>
{withBilling && (
<DropdownMenuItem onClick={switchWalletType}>
<CoinsSwap />
&nbsp;Switch to USD billing
</DropdownMenuItem>
)}
</>
)}
{withBilling && isManaged && (
<>
<DropdownMenuItem onClick={whenLoggedIn(goToCheckout, "Sign In or Sign Up to top up your balance")}>
<HandCard />
&nbsp;Top up balance
</DropdownMenuItem>
<DropdownMenuItem onClick={switchWalletType}>
<CoinsSwap />
&nbsp;Switch to wallet billing
</DropdownMenuItem>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="flex items-center text-left">
<div className="flex items-center text-sm font-bold">
<Wallet className="text-xs" />
Expand Down Expand Up @@ -114,18 +138,6 @@ export function WalletStatus() {
</div>
</TooltipContent>
)}
{isManaged && (
<TooltipContent>
<LoginRequiredLink
className="flex cursor-pointer flex-row text-base"
href="/api/proxy/v1/checkout"
message="Sign In or Sign Up to top up your balance"
>
<HandCard className="text-xs" />
<span className="ml-1 text-xs">Top up balance</span>
</LoginRequiredLink>
</TooltipContent>
)}
</Tooltip>
</div>
)}
Expand All @@ -134,7 +146,7 @@ export function WalletStatus() {
</>
) : (
<div>
{envConfig.NEXT_PUBLIC_BILLING_ENABLED && <ConnectManagedWalletButton className="mb-2 mr-2 w-full md:mb-0 md:w-auto" />}
{withBilling && <ConnectManagedWalletButton className="mb-2 mr-2 w-full md:mb-0 md:w-auto" />}
<ConnectWalletButton className="w-full md:w-auto" />
</div>
)
Expand Down
48 changes: 4 additions & 44 deletions apps/deploy-web/src/components/user/LoginRequiredLink.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import React, { useCallback } from "react";
import { usePopup } from "@akashnetwork/ui/context";
import React from "react";
import Link, { LinkProps } from "next/link";

import { useUser } from "@src/hooks/useUser";
import { useLoginRequiredEventHandler } from "@src/hooks/useLoginRequiredEventHandler";
import { FCWithChildren } from "@src/types/component";
import { UrlService } from "@src/utils/urlUtils";

export const LoginRequiredLink: FCWithChildren<
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps> &
Expand All @@ -13,44 +11,6 @@ export const LoginRequiredLink: FCWithChildren<
message: string;
} & React.RefAttributes<HTMLAnchorElement>
> = ({ message, ...props }) => {
const { requireAction } = usePopup();
const user = useUser();
const showLoginPrompt = useCallback(
() =>
requireAction({
message,
actions: [
{
label: "Sign in",
side: "left",
size: "lg",
variant: "secondary",
onClick: () => {
window.location.href = UrlService.login();
}
},
{
label: "Sign up",
side: "right",
size: "lg",
onClick: () => {
window.location.href = UrlService.signup();
}
}
]
}),
[message, requireAction]
);

return (
<Link
{...props}
onClick={event => {
if (!user.userId) {
event.preventDefault();
showLoginPrompt();
}
}}
/>
);
const whenLoggedIn = useLoginRequiredEventHandler();
return <Link {...props} onClick={whenLoggedIn(props.onClick || (() => {}), message)} />;
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";
import React, { ReactNode } from "react";
import { Button, ButtonProps } from "@akashnetwork/ui/components";
import { Button, ButtonProps, Spinner } from "@akashnetwork/ui/components";
import { Rocket } from "iconoir-react";

import { useWallet } from "@src/context/WalletProvider";
Expand All @@ -12,12 +12,12 @@ interface Props extends ButtonProps {
}

export const ConnectManagedWalletButton: React.FunctionComponent<Props> = ({ className = "", ...rest }) => {
const { connectManagedWallet } = useWallet();
const { connectManagedWallet, hasManagedWallet, isWalletLoading } = useWallet();

return (
<Button variant="outline" onClick={connectManagedWallet} className={cn("border-primary", className)} {...rest}>
<Rocket className="text-xs" />
<span className="m-2 whitespace-nowrap">Start Trial</span>
<Button variant="outline" onClick={connectManagedWallet} className={cn("border-primary", className)} {...rest} disabled={isWalletLoading}>
{isWalletLoading ? <Spinner size="small" className="mr-2" /> : <Rocket className="text-xs" />}
<span className="m-2 whitespace-nowrap">{hasManagedWallet ? "Switch to USD billing" : "Start Trial"}</span>
</Button>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { RestApiCertificatesResponseType } from "@src/types/certificate";
import { AnalyticsEvents } from "@src/utils/analytics";
import { networkVersion } from "@src/utils/constants";
import { TransactionMessageData } from "@src/utils/TransactionMessageData";
import { getSelectedStorageWallet, getStorageWallets, updateWallet } from "@src/utils/walletUtils";
import { getStorageWallets, updateWallet } from "@src/utils/walletUtils";
import { useSettings } from "../SettingsProvider";
import { useWallet } from "../WalletProvider";

Expand Down Expand Up @@ -122,7 +122,6 @@ export const CertificateProvider = ({ children }) => {
useEffect(() => {
if (!isSettingsInit) return;

// Clear certs when no selected wallet
setValidCertificates([]);
setSelectedCertificate(null);
setLocalCert(null);
Expand Down Expand Up @@ -152,22 +151,20 @@ export const CertificateProvider = ({ children }) => {
}, [selectedCertificate, localCert, validCertificates]);

const loadLocalCert = async () => {
// open certs for all the wallets
const wallets = getStorageWallets();
const currentWallet = getSelectedStorageWallet();
const certs: LocalCert[] = [];
const certs = wallets.reduce((acc, wallet) => {
const cert: LocalCert | null = wallet.cert && wallet.certKey ? { certPem: wallet.cert, keyPem: wallet.certKey, address: wallet.address } : null;

for (let i = 0; i < wallets.length; i++) {
const _wallet = wallets[i];

const _cert = { certPem: _wallet.cert, keyPem: _wallet.certKey, address: _wallet.address };

certs.push(_cert as LocalCert);
if (cert) {
acc.push(cert);
}

if (_wallet.address === currentWallet?.address) {
setLocalCert(_cert as LocalCert);
if (wallet.address === address) {
setLocalCert(cert);
}
}

return acc;
}, [] as LocalCert[]);

setLocalCerts(certs);
};
Expand Down
Loading

0 comments on commit cdde636

Please sign in to comment.