Skip to content

Commit

Permalink
feat(farcaster): init draft for supporting farcaster login
Browse files Browse the repository at this point in the history
  • Loading branch information
AmAzing129 authored and MarioJames committed Jun 21, 2024
1 parent 868e6fc commit 32a0f06
Show file tree
Hide file tree
Showing 18 changed files with 629 additions and 6 deletions.
5 changes: 5 additions & 0 deletions packages/farcaster/.fatherrc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineConfig } from 'father';

export default defineConfig({
extends: '../../.fatherrc.base.ts',
});
1 change: 1 addition & 0 deletions packages/farcaster/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# TODO
61 changes: 61 additions & 0 deletions packages/farcaster/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"name": "@ant-design/web3-farcaster",
"version": "1.0.0",
"main": "dist/lib/index.js",
"module": "dist/esm/index.js",
"typings": "dist/esm/index.d.ts",
"exports": {
"import": "./dist/esm/index.js",
"require": "./dist/lib/index.js",
"types": "./dist/esm/index.d.ts"
},
"sideEffects": false,
"files": [
"dist",
"CHANGELOG.md",
"README.md"
],
"keywords": [
"ant",
"component",
"components",
"design",
"framework",
"frontend",
"react",
"react-component",
"ui",
"web3",
"farcaster"
],
"homepage": "https://web3.ant.design",
"bugs": {
"url": "https://github.com/ant-design/ant-design-web3/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/ant-design/ant-design-web3"
},
"scripts": {
"dev": "father dev",
"build": "father build"
},
"dependencies": {
"@farcaster/auth-client": "^0.1.1",
"@farcaster/auth-kit": "^0.3.1"
},
"devDependencies": {
"father": "^4.4.4",
"typescript": "^5.4.5"
},
"publishConfig": {
"registry": "https://registry.npmjs.org",
"access": "public"
},
"browserslist": [
"last 2 versions",
"Firefox ESR",
"> 1%",
"ie >= 11"
]
}
2 changes: 2 additions & 0 deletions packages/farcaster/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './provider';
export { QRCode } from '@farcaster/auth-kit';
19 changes: 19 additions & 0 deletions packages/farcaster/src/provider/__tests__/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { FC } from 'react';
import { render } from '@testing-library/react';

type RenderResult = ReturnType<typeof render>;
type RenderWithUtils = RenderResult & {
selector: <T extends Element = Element>(selector: string) => T | null;
selectors: <T extends Element = Element>(selector: string) => NodeListOf<T>;
};
type XRender = (Comp: FC, options?: Parameters<typeof render>[1]) => RenderWithUtils;

export const xrender: XRender = (Comp, options) => {
const { baseElement, ...others } = render(<Comp />, options);
return {
baseElement,
...others,
selector: (selector) => baseElement.querySelector(selector),
selectors: (selector) => baseElement.querySelectorAll(selector),
};
};
72 changes: 72 additions & 0 deletions packages/farcaster/src/provider/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { useCallback, useEffect } from 'react';
import type { Provider } from '@farcaster/auth-client';
import { AuthKitProvider, useSignIn, type UseSignInArgs } from '@farcaster/auth-kit';

import '@farcaster/auth-kit/styles.css';

// declares locally in '@farcaster/auth-kit', but it is not exported
interface AuthKitConfig {
relay?: string;
domain?: string;
siweUri?: string;
rpcUrl?: string;
redirectUrl?: string;
version?: string;
provider?: Provider;
}

interface IFarcasterContext extends Partial<ReturnType<typeof useSignIn>> {
farcasterSupported: boolean;
farcasterLogin: () => void;
}

const FarcasterContext = React.createContext<IFarcasterContext>({
farcasterSupported: false,
farcasterLogin: () => {},
});

const FarcasterConfigProvider: React.FC<React.PropsWithChildren<UseSignInArgs>> = ({
children,
...signInArgs
}) => {
const signInState = useSignIn(signInArgs);
const { isError, reconnect, signIn, channelToken, connect } = signInState;

const farcasterLogin = useCallback(() => {
if (isError) {
reconnect();
}
signIn();
}, [isError, reconnect, signIn]);

useEffect(() => {
if (!channelToken) {
connect();
}
}, [channelToken, connect]);

return (
<FarcasterContext.Provider value={{ farcasterSupported: true, farcasterLogin, ...signInState }}>
{children}
</FarcasterContext.Provider>
);
};

interface Web3FarcasterProviderProps extends UseSignInArgs {
config?: AuthKitConfig;
}

export const useFarcaster = () => {
const farcaster = React.useContext(FarcasterContext);
return farcaster;
};

export const FarcasterWeb3ConfigProvider: React.FC<
React.PropsWithChildren<Web3FarcasterProviderProps>
> = ({ children, config = {}, ...signInArgs }) => {
return (
<AuthKitProvider config={config}>
<FarcasterConfigProvider {...signInArgs}>{children}</FarcasterConfigProvider>
</AuthKitProvider>
);
};
4 changes: 4 additions & 0 deletions packages/farcaster/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src", "global.d.ts"]
}
1 change: 1 addition & 0 deletions packages/web3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@ant-design/web3-assets": "workspace:*",
"@ant-design/web3-common": "workspace:*",
"@ant-design/web3-icons": "workspace:*",
"@ant-design/web3-farcaster": "workspace:*",
"@ctrl/tinycolor": "^4.1.0",
"@inline-svg-unique-id/react": "^1.2.3",
"antd": "^5.17.3",
Expand Down
33 changes: 33 additions & 0 deletions packages/web3/src/connect-modal/components/FarcasterCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { QRCode, useFarcaster } from '@ant-design/web3-farcaster';

import { connectModalContext } from '../context';
import MainPanelHeader from './MainPanelHeader';

const FarcasterCard = () => {
const { url, error, isError } = useFarcaster();
const { prefixCls } = React.useContext(connectModalContext);

return (
<>
<div className={`${prefixCls}-qr-code-container`}>
<MainPanelHeader title="Sign in with Farcaster" />
<div className={`${prefixCls}-qr-code-box`}>
{isError ? (
<>
<div>Error</div>
<div>{error?.message ?? 'Unknown error, please try again.'}</div>
</>
) : url ? (
<QRCode uri={url} size={300} logoSize={28} logoMargin={16} />
) : null}
</div>
<div className={`${prefixCls}-qr-code-tips`}>
<div>Scan with your phone&apos;s camera to continue.</div>
</div>
</div>
</>
);
};

export default FarcasterCard;
2 changes: 2 additions & 0 deletions packages/web3/src/connect-modal/components/MainPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useContext } from 'react';
import { connectModalContext } from '../context';
import type { ConnectModalProps } from '../interface';
import DefaultGuidePanel from './DefaultGuidePanel';
import FarcasterCard from './FarcasterCard';
import LinkPanel from './LinkPanel';
import QrCode from './QrCode';
import WalletCard from './WalletCard';
Expand All @@ -28,6 +29,7 @@ const MainPanel: React.FC<MainPanelProps> = (props) => {
{panelRoute === 'downloadQrCode' && selectedWallet ? (
<QrCode wallet={selectedWallet} simple={simple} download />
) : null}
{panelRoute === 'farcaster' ? <FarcasterCard /> : null}
</div>
);
};
Expand Down
55 changes: 52 additions & 3 deletions packages/web3/src/connect-modal/components/WalletList.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { forwardRef, useContext, useImperativeHandle, useMemo } from 'react';
import { QrcodeOutlined } from '@ant-design/icons';
import { useFarcaster } from '@ant-design/web3-farcaster';
import { Button, List, Space, Typography } from 'antd';
import classNames from 'classnames';

Expand Down Expand Up @@ -69,7 +70,52 @@ const WalletList = forwardRef<ConnectModalActionType, WalletListProps>((props, r
selectWallet,
};
});
const renderContent = (params?: { group?: string }) => {

const { farcasterSupported, farcasterLogin } = useFarcaster();

const renderFarcasterContent = () => {
return farcasterSupported ? (
<List
itemLayout="horizontal"
dataSource={[
{
key: 'farcaster',
name: 'Farcaster',
},
]}
rowKey="key"
renderItem={(item) => (
<List.Item
className={classNames(`${prefixCls}-wallet-item`)}
onClick={() => {
farcasterLogin();
updatePanelRoute('farcaster', true);
}}
>
<div className={`${prefixCls}-content`}>
<div>图标</div>
<Typography.Text ellipsis={{ tooltip: true }} className={`${prefixCls}-name`}>
{item.name}
</Typography.Text>
</div>
<Button
size="small"
className={`${prefixCls}-qr-btn`}
onClick={(e) => {
e.stopPropagation();
farcasterLogin();
updatePanelRoute('farcaster', true);
}}
>
<QrcodeOutlined />
</Button>
</List.Item>
)}
/>
) : null;
};

const renderWalletsContent = (params?: { group?: string }) => {
const { group } = params || {};
return (
<List<Wallet>
Expand Down Expand Up @@ -123,20 +169,23 @@ const WalletList = forwardRef<ConnectModalActionType, WalletListProps>((props, r

return (
<div className={`${prefixCls}-wallet-list`}>
<div className={`${prefixCls}-group`}>
<div className={`${prefixCls}-group-content`}>{renderFarcasterContent()}</div>
</div>
{internalGroup ? (
groupKeys.map((group) => (
<div className={`${prefixCls}-group`} key={group}>
<div className={`${prefixCls}-group-title`}>{group}</div>
<div className={`${prefixCls}-group-content`}>
{renderContent({
{renderWalletsContent({
group,
})}
</div>
</div>
))
) : (
<div className={`${prefixCls}-group`}>
<div className={`${prefixCls}-group-content`}>{renderContent()}</div>
<div className={`${prefixCls}-group-content`}>{renderWalletsContent()}</div>
</div>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/web3/src/connect-modal/demos/basic.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { ConnectModal, ConnectModalProps } from '@ant-design/web3';
import { ConnectModal, type ConnectModalProps } from '@ant-design/web3';
import {
metadata_MetaMask,
metadata_MobileConnect,
Expand Down
1 change: 1 addition & 0 deletions packages/web3/src/connect-modal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const ConnectModal: React.FC<ConnectModalProps> & {
onCancel={(e) => {
onCancel?.(e);
}}
destroyOnClose
>
<ModalPanel {...props} />
</Modal>,
Expand Down
9 changes: 8 additions & 1 deletion packages/web3/src/connect-modal/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,11 @@ export type ConnectModalProps = ModalProps &
connecting?: boolean;
};

export type PanelRoute = 'init' | 'guide' | 'wallet' | 'qrCode' | 'downloadQrCode' | 'link';
export type PanelRoute =
| 'init'
| 'guide'
| 'wallet'
| 'qrCode'
| 'downloadQrCode'
| 'link'
| 'farcaster';
10 changes: 9 additions & 1 deletion packages/web3/src/connector/connector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import React, { useEffect } from 'react';
import { ConnectModal, type ConnectModalActionType } from '@ant-design/web3';
import type { Chain, ConnectOptions, ConnectorTriggerProps, Wallet } from '@ant-design/web3-common';
import { useFarcaster } from '@ant-design/web3-farcaster';
import { message } from 'antd';

import useProvider from '../hooks/useProvider';
Expand Down Expand Up @@ -34,6 +35,13 @@ export const Connector: React.FC<ConnectorProps> = (props) => {
const actionRef = React.useRef<ConnectModalActionType>();
const [messageApi, contextHolder] = message.useMessage();

// close modal when login farcaster success
const { isSuccess } = useFarcaster();
useEffect(() => {
if (!isSuccess) return;
setOpen(false);
}, [isSuccess]);

const connectWallet = async (wallet?: Wallet, options?: ConnectOptions) => {
onConnect?.();
try {
Expand Down
Loading

0 comments on commit 32a0f06

Please sign in to comment.