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
57 changes: 50 additions & 7 deletions pkgs/frontend/app/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,57 @@ export const Header = () => {
return avatar ? ipfs2https(avatar) : undefined;
}, [identity]);

const handleLogout = () => {
if (isSmartWallet) {
logout();
} else {
if (wallets.find((w) => w.connectorType === "injected")) {
alert("ウォレット拡張機能から切断してください。");
const handleLogout = async () => {
try {
if (isSmartWallet) {
// スマートウォレットの場合、Privyのlogoutを使用
await logout();
} else {
Promise.all(wallets.map((wallet) => wallet.disconnect()));
// 外部ウォレット(MetaMaskなど)の場合
const hasInjectedWallet = wallets.some(
(w) => w.connectorType === "injected",
);

if (hasInjectedWallet) {
// MetaMaskの権限を無効化
try {
if (window.ethereum) {
await window.ethereum.request({
method: "wallet_revokePermissions",
params: [{ eth_accounts: {} }],
});
}
} catch (revokeError) {
console.warn("Failed to revoke MetaMask permissions:", revokeError);
}
} else {
// その他の外部ウォレットの場合
// Privyのlogoutを実行してからウォレット切断を試行
await logout();

// 切断可能なウォレットのみ切断
for (const wallet of wallets) {
if (wallet.connectorType !== "injected") {
try {
wallet.disconnect();
} catch (error) {
console.warn(
"Failed to disconnect wallet:",
wallet.address,
error,
);
}
}
}
}
}
} catch (error) {
console.error("Logout failed:", error);
// エラーが発生した場合のフォールバック処理
try {
await logout();
} catch (logoutError) {
console.error("Fallback logout also failed:", logoutError);
}
}
};
Expand Down
117 changes: 105 additions & 12 deletions pkgs/frontend/app/components/SwitchNetwork.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,115 @@
import { Box, Button, Stack, Text } from "@chakra-ui/react";
import { currentChain } from "hooks/useViem";
import { useActiveWallet } from "hooks/useWallet";
import { type FC, useEffect } from "react";
import { type FC, useEffect, useState } from "react";
import {
DialogBody,
DialogContent,
DialogFooter,
DialogHeader,
DialogRoot,
DialogTitle,
} from "./ui/dialog";

export const SwitchNetwork: FC = () => {
const { connectedWallet } = useActiveWallet();
const [isOpen, setIsOpen] = useState(false);
const [isSwitching, setIsSwitching] = useState(false);

// チェーン不一致の検出
useEffect(() => {
const switchChain = async () => {
if (
connectedWallet &&
Number(connectedWallet.chainId) !== currentChain.id
) {
await connectedWallet.switchChain(currentChain.id);
}
};

switchChain();
if (connectedWallet?.chainId) {
const isChainMismatch =
Number(connectedWallet.chainId.replace("eip155:", "")) !==
currentChain.id;
setIsOpen(isChainMismatch);
} else {
setIsOpen(false);
}
}, [connectedWallet]);

return <></>;
// チェーン切り替え処理
const handleSwitchChain = async () => {
if (!connectedWallet) return;

try {
setIsSwitching(true);
await connectedWallet.switchChain(currentChain.id);
setIsOpen(false);
} catch (error) {
console.error("Failed to switch chain:", error);
// エラー時はModalを開いたままにする
} finally {
setIsSwitching(false);
}
};

return (
<DialogRoot open={isOpen}>
<DialogContent
backdrop={true}
portalled={true}
maxW="400px"
borderRadius="16px"
>
<DialogHeader>
<DialogTitle fontSize="lg" fontWeight="bold">
ネットワークの切り替えが必要です
</DialogTitle>
</DialogHeader>

<DialogBody>
<Stack gap={4} align="stretch">
<Text fontSize="sm" color="gray.600">
現在のネットワークは対応していません。以下のネットワークに切り替えてください。
</Text>

<Box
p={3}
borderRadius="8px"
border="1px solid"
borderColor="red.200"
bg="red.50"
>
<Text fontSize="sm" fontWeight="semibold" color="red.700">
現在のネットワーク
</Text>
<Text fontSize="sm" color="red.600">
{connectedWallet?.chainId
? `Chain ID: ${connectedWallet.chainId.replace("eip155:", "")}`
: "未接続"}
</Text>
</Box>

<Box
p={3}
borderRadius="8px"
border="1px solid"
borderColor="green.200"
bg="green.50"
>
<Text fontSize="sm" fontWeight="semibold" color="green.700">
必要なネットワーク
</Text>
<Text fontSize="sm" color="green.600">
{currentChain.name} (Chain ID: {currentChain.id})
</Text>
</Box>
</Stack>
</DialogBody>

<DialogFooter>
<Button
onClick={handleSwitchChain}
loading={isSwitching}
loadingText="切り替え中"
width="100%"
size="md"
>
ネットワークを切り替える
</Button>
</DialogFooter>
</DialogContent>
</DialogRoot>
);
};