diff --git a/pkgs/frontend/app/components/Header.tsx b/pkgs/frontend/app/components/Header.tsx
index b8d6fc2..c1b99a5 100644
--- a/pkgs/frontend/app/components/Header.tsx
+++ b/pkgs/frontend/app/components/Header.tsx
@@ -2,9 +2,13 @@ import { useState, useEffect, useMemo } from "react";
import { Box, Flex, Text } from "@chakra-ui/react";
import { WorkspaceIcon } from "./icon/WorkspaceIcon";
import { UserIcon } from "./icon/UserIcon";
-import { useLocation } from "@remix-run/react";
+import { useLocation, useNavigate } from "@remix-run/react";
import { useActiveWalletIdentity } from "hooks/useENS";
import { ipfs2https } from "utils/ipfs";
+import { MenuContent, MenuItem, MenuRoot, MenuTrigger } from "./ui/menu";
+import { useActiveWallet } from "hooks/useWallet";
+import { usePrivy, useWallets } from "@privy-io/react-auth";
+import CommonButton from "./common/CommonButton";
const NO_HEADER_PATHS: string[] = ["/login", "/signup"]; // 適宜ヘッダーが不要なページのパスを追加
const WORKSPACES_PATHS: string[] = ["/workspaces"]; // 適宜ワークスペースが未選択な状態のページのパスを追加
@@ -28,6 +32,7 @@ export const Header = () => {
HeaderType.NonHeader
);
+ const navigate = useNavigate();
const { pathname } = useLocation();
// ToDo: ページのパスや hooks で柔軟にロジックを実装する(切り替えてテストできます)
@@ -63,6 +68,9 @@ export const Header = () => {
workspaceName,
]);
+ const { isSmartWallet } = useActiveWallet();
+ const { logout } = usePrivy();
+ const { wallets } = useWallets();
const { identity } = useActiveWalletIdentity();
const userImageUrl = useMemo(() => {
@@ -70,6 +78,18 @@ export const Header = () => {
return avatar ? ipfs2https(avatar) : undefined;
}, [identity]);
+ const handleLogout = () => {
+ if (isSmartWallet) {
+ logout();
+ } else {
+ if (wallets.find((w) => w.connectorType === "injected")) {
+ alert("ウォレット拡張機能から切断してください。");
+ } else {
+ Promise.all(wallets.map((wallet) => wallet.disconnect()));
+ }
+ }
+ };
+
return headerType !== HeaderType.NonHeader ? (
@@ -90,7 +110,29 @@ export const Header = () => {
>
)}
-
+ {identity ? (
+
+
+
+
+
+
+
+
+ ) : (
+ {
+ navigate("/login");
+ }}
+ w="auto"
+ >
+ Login
+
+ )}
) : (
<>>
diff --git a/pkgs/frontend/app/components/ui/menu.tsx b/pkgs/frontend/app/components/ui/menu.tsx
new file mode 100644
index 0000000..763005b
--- /dev/null
+++ b/pkgs/frontend/app/components/ui/menu.tsx
@@ -0,0 +1,110 @@
+"use client"
+
+import { AbsoluteCenter, Menu as ChakraMenu, Portal } from "@chakra-ui/react"
+import * as React from "react"
+import { LuCheck, LuChevronRight } from "react-icons/lu"
+
+interface MenuContentProps extends ChakraMenu.ContentProps {
+ portalled?: boolean
+ portalRef?: React.RefObject
+}
+
+export const MenuContent = React.forwardRef(
+ function MenuContent(props, ref) {
+ const { portalled = true, portalRef, ...rest } = props
+ return (
+
+
+
+
+
+ )
+ },
+)
+
+export const MenuArrow = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.ArrowProps
+>(function MenuArrow(props, ref) {
+ return (
+
+
+
+ )
+})
+
+export const MenuCheckboxItem = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.CheckboxItemProps
+>(function MenuCheckboxItem(props, ref) {
+ return (
+
+
+
+
+ {props.children}
+
+ )
+})
+
+export const MenuRadioItem = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.RadioItemProps
+>(function MenuRadioItem(props, ref) {
+ const { children, ...rest } = props
+ return (
+
+
+
+
+
+
+ {children}
+
+ )
+})
+
+export const MenuItemGroup = React.forwardRef<
+ HTMLDivElement,
+ ChakraMenu.ItemGroupProps
+>(function MenuItemGroup(props, ref) {
+ const { title, children, ...rest } = props
+ return (
+
+ {title && (
+
+ {title}
+
+ )}
+ {children}
+
+ )
+})
+
+export interface MenuTriggerItemProps extends ChakraMenu.ItemProps {
+ startIcon?: React.ReactNode
+}
+
+export const MenuTriggerItem = React.forwardRef<
+ HTMLDivElement,
+ MenuTriggerItemProps
+>(function MenuTriggerItem(props, ref) {
+ const { startIcon, children, ...rest } = props
+ return (
+
+ {startIcon}
+ {children}
+
+
+ )
+})
+
+export const MenuRadioItemGroup = ChakraMenu.RadioItemGroup
+export const MenuContextTrigger = ChakraMenu.ContextTrigger
+export const MenuRoot = ChakraMenu.Root
+export const MenuSeparator = ChakraMenu.Separator
+
+export const MenuItem = ChakraMenu.Item
+export const MenuItemText = ChakraMenu.ItemText
+export const MenuItemCommand = ChakraMenu.ItemCommand
+export const MenuTrigger = ChakraMenu.Trigger
diff --git a/pkgs/frontend/hooks/useWallet.ts b/pkgs/frontend/hooks/useWallet.ts
index 65bd735..a2bc046 100644
--- a/pkgs/frontend/hooks/useWallet.ts
+++ b/pkgs/frontend/hooks/useWallet.ts
@@ -1,4 +1,4 @@
-import { useWallets } from "@privy-io/react-auth";
+import { usePrivy, useWallets } from "@privy-io/react-auth";
import { createSmartAccountClient, SmartAccountClient } from "permissionless";
import { toSimpleSmartAccount } from "permissionless/accounts";
import { createPimlicoClient } from "permissionless/clients/pimlico";
@@ -36,6 +36,8 @@ export const useSmartAccountClient = () => {
* @returns
*/
const create = async () => {
+ setClient(undefined);
+ console.log(wallets);
const embeddedWallet = wallets.find(
(wallet) => wallet.connectorType === "embedded"
);
@@ -78,6 +80,7 @@ export const useAccountClient = () => {
useEffect(() => {
const create = async () => {
+ setClient(undefined);
if (!wallets[0]) return;
const wallet = wallets[0];
diff --git a/pkgs/frontend/package.json b/pkgs/frontend/package.json
index 137bb70..454836d 100644
--- a/pkgs/frontend/package.json
+++ b/pkgs/frontend/package.json
@@ -14,7 +14,7 @@
"format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json}\""
},
"dependencies": {
- "@chakra-ui/react": "^3.2.0",
+ "@chakra-ui/react": "^3.2.3",
"@emotion/cache": "^11.13.5",
"@emotion/react": "^11.13.5",
"@emotion/server": "^11.11.0",
@@ -33,7 +33,7 @@
"permissionless": "^0.2.20",
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "react-icons": "^5.3.0",
+ "react-icons": "^5.4.0",
"viem": "^2.21.51"
},
"devDependencies": {
diff --git a/yarn.lock b/yarn.lock
index 9a59181..3addbee 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1308,10 +1308,10 @@
dependencies:
"@chainsafe/is-ip" "^2.0.1"
-"@chakra-ui/react@^3.2.0":
- version "3.2.2"
- resolved "https://registry.yarnpkg.com/@chakra-ui/react/-/react-3.2.2.tgz#cd846cdfaf9002d7339f15e1932ed2a886c7374b"
- integrity sha512-9GAESg6rSJ4NCfPAv2s5mOYnrFU+5x3xxR9Ab1phsZ2junR7po55PxB/Zdr1VNb1NX+6oIGMAR5s74oN5sYlfQ==
+"@chakra-ui/react@^3.2.3":
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/@chakra-ui/react/-/react-3.2.3.tgz#61615fbee75e9aedb6bb136d15de5ce99184f9da"
+ integrity sha512-KfhKkcnHPqMwrX5eZ1xVeewOy6L4+iL2684tnP7re7erferfEBeqAAkGZpzWUcjb+IMwClYFygXk0gQrsVdtaQ==
dependencies:
"@ark-ui/react" "4.4.4"
"@emotion/is-prop-valid" "1.3.1"
@@ -15524,10 +15524,10 @@ react-helmet-async@^1.3.0:
react-fast-compare "^3.2.0"
shallowequal "^1.1.0"
-react-icons@^5.3.0:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.3.0.tgz#ccad07a30aebd40a89f8cfa7d82e466019203f1c"
- integrity sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==
+react-icons@^5.4.0:
+ version "5.4.0"
+ resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-5.4.0.tgz#443000f6e5123ee1b21ea8c0a716f6e7797f7416"
+ integrity sha512-7eltJxgVt7X64oHh6wSWNwwbKTCtMfK35hcjvJS0yxEAhPM8oUKdS3+kqaW1vicIltw+kR2unHaa12S9pPALoQ==
react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0:
version "16.13.1"