This is an automated email from the ASF dual-hosted git repository. robin0716 pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/incubator-answer-plugins.git
commit 852f575ce6c867f1b6d61f60dd3969c01e424f5e Author: Ourai Lin <[email protected]> AuthorDate: Tue Oct 22 15:10:00 2024 +0800 chore: add `route` standard UI plugin for `connector-wallet` move codes from PR #236 --- connector-wallet/.eslintrc.cjs | 37 ++++++++ connector-wallet/.prettierrc.json | 8 ++ connector-wallet/Component.tsx | 33 ++++++++ connector-wallet/WalletAuthorizer.tsx | 107 ++++++++++++++++++++++++ connector-wallet/WalletProvider.tsx | 46 ++++++++++ connector-wallet/go.mod | 1 + connector-wallet/go.sum | 2 + connector-wallet/i18n/en_US.yaml | 10 ++- connector-wallet/i18n/index.ts | 26 ++++++ connector-wallet/i18n/zh_CN.yaml | 10 ++- connector-wallet/index.ts | 32 +++++++ connector-wallet/{i18n/zh_CN.yaml => info.yaml} | 15 ++-- connector-wallet/package.json | 57 +++++++++++++ connector-wallet/tsconfig.json | 28 +++++++ connector-wallet/tsconfig.node.json | 11 +++ connector-wallet/vite.config.ts | 69 +++++++++++++++ connector-wallet/wallet.go | 18 ++-- 17 files changed, 494 insertions(+), 16 deletions(-) diff --git a/connector-wallet/.eslintrc.cjs b/connector-wallet/.eslintrc.cjs new file mode 100644 index 0000000..398c39f --- /dev/null +++ b/connector-wallet/.eslintrc.cjs @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parser: '@typescript-eslint/parser', + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/connector-wallet/.prettierrc.json b/connector-wallet/.prettierrc.json new file mode 100644 index 0000000..c707926 --- /dev/null +++ b/connector-wallet/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "trailingComma": "all", + "tabWidth": 2, + "singleQuote": true, + "jsxBracketSameLine": true, + "printWidth": 80, + "endOfLine": "auto" +} diff --git a/connector-wallet/Component.tsx b/connector-wallet/Component.tsx new file mode 100644 index 0000000..ab02e18 --- /dev/null +++ b/connector-wallet/Component.tsx @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import WalletProvider from './WalletProvider'; +import WalletAuthorizer from './WalletAuthorizer'; + +const Component = () => { + return ( + <WalletProvider> + <div style={{ margin: '0 auto', paddingTop: '8rem', maxWidth: 480 }}> + <WalletAuthorizer /> + </div> + </WalletProvider> + ); +}; + +export default Component; diff --git a/connector-wallet/WalletAuthorizer.tsx b/connector-wallet/WalletAuthorizer.tsx new file mode 100644 index 0000000..4436620 --- /dev/null +++ b/connector-wallet/WalletAuthorizer.tsx @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ConnectButton } from '@rainbow-me/rainbowkit'; +import { useConfig, useSwitchChain, useSignMessage } from 'wagmi'; +import { sha256 } from 'js-sha256'; +import { useTranslation } from 'react-i18next'; +import { Button } from 'react-bootstrap'; + +function getSearchParamValue(key: string, defaultValue: string = '') { + return location.search && new URLSearchParams(location.search.slice()).get(key) || defaultValue; +} + +function WalletAuthorizer() { + const { chains } = useConfig(); + const { switchChain } = useSwitchChain(); + const { signMessageAsync } = useSignMessage(); + + const { t } = useTranslation('plugin', { + keyPrefix: 'wallet_connector.frontend', + }); + + return ( + <ConnectButton.Custom> + {({ + account, + chain, + openAccountModal, + openChainModal, + openConnectModal, + authenticationStatus, + mounted, + }) => { + const ready = mounted && authenticationStatus !== 'loading'; + const connected = ready && account && chain && (!authenticationStatus || authenticationStatus === 'authenticated'); + const { address } = account || { address: '' }; + + const handleSwitchNetwork = () => { + if (chains.length === 1) { + switchChain({ chainId: chains[0].id }); + } else { + openChainModal(); + } + }; + + const handleAuthorize = async () => { + const nonce = getSearchParamValue('nonce', sha256(address)); + const signature = await signMessageAsync({ message: nonce }); + + location.href = `/answer/api/v1/connector/redirect/wallet?message=${nonce}&signature=${signature}&address=${address}&redirect=${getSearchParamValue('redirect')}`; + } + + return ( + <div> + {(() => { + let description: React.ReactNode; + let actionList: React.ReactNode; + + if (!connected) { + description = <>{t('wallet_needed')}</>; + actionList = <Button onClick={() => openConnectModal()}>{t('connect_button')}</Button>; + } else if (chain.unsupported) { + description = <>{t('wrong_network')}</>; + actionList = <Button onClick={handleSwitchNetwork}>{t('switch_button')}</Button>; + } else { + const translatedArr = t('connected_wallet').split('${ADDRESS}') as React.ReactNode[]; + translatedArr.splice(1, 0, <strong style={{ cursor: 'pointer' }} onClick={() => openAccountModal()}>{address.replace(address.slice(6, -4), '...')}</strong>); + description = <>{translatedArr.map((c, i) => <span key={i}>{c}</span>)}</>; + actionList = ( + <> + <Button onClick={handleAuthorize}>{t('authorize_button')}</Button> + <Button variant="outline-secondary" onClick={() => openAccountModal()}>{t('disconnect_button')}</Button> + </> + ); + } + + return ( + <> + <p>{description}</p> + <div className="d-grid gap-2">{actionList}</div> + </> + ); + })()} + </div> + ); + }} + </ConnectButton.Custom> + ); +} + +export default WalletAuthorizer; diff --git a/connector-wallet/WalletProvider.tsx b/connector-wallet/WalletProvider.tsx new file mode 100644 index 0000000..11066b2 --- /dev/null +++ b/connector-wallet/WalletProvider.tsx @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import '@rainbow-me/rainbowkit/styles.css'; +import { getDefaultConfig, RainbowKitProvider } from '@rainbow-me/rainbowkit'; +import { WagmiProvider } from 'wagmi'; +import { mainnet } from 'wagmi/chains'; +import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; + +const config = getDefaultConfig({ + appName: 'Apache Answer', + projectId: 'xxx', + chains: [mainnet], // There's no on-chain operations, so only ETH mainnet is enough. +}); + +const queryClient = new QueryClient(); + +function WalletProvider({ children }: React.PropsWithChildren) { + return ( + <WagmiProvider config={config}> + <QueryClientProvider client={queryClient}> + <RainbowKitProvider> + {children} + </RainbowKitProvider> + </QueryClientProvider> + </WagmiProvider> + ); +} + +export default WalletProvider; diff --git a/connector-wallet/go.mod b/connector-wallet/go.mod index c3682e0..1d61d70 100644 --- a/connector-wallet/go.mod +++ b/connector-wallet/go.mod @@ -4,6 +4,7 @@ go 1.22 require ( github.com/apache/incubator-answer v1.4.0 + github.com/apache/incubator-answer-plugins/util v1.0.2 github.com/ethereum/go-ethereum v1.14.11 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa ) diff --git a/connector-wallet/go.sum b/connector-wallet/go.sum index 54b24ca..ef4ca7c 100644 --- a/connector-wallet/go.sum +++ b/connector-wallet/go.sum @@ -4,6 +4,8 @@ github.com/LinkinStars/go-i18n/v2 v2.2.2 h1:ZfjpzbW13dv6btv3RALKZkpN9A+7K1JA//2Q github.com/LinkinStars/go-i18n/v2 v2.2.2/go.mod h1:hLglSJ4/3M0Y7ZVcoEJI+OwqkglHCA32DdjuJJR2LbM= github.com/apache/incubator-answer v1.4.0 h1:W3y4TAQ4sdzgcqntGqNBPe0BdyeW7+l8FWYBDs9g8+Y= github.com/apache/incubator-answer v1.4.0/go.mod h1:Q4NkQmBd0sV7t3Cd8NBsWh9w8jFRo/2qjzOw9MlRNwk= +github.com/apache/incubator-answer-plugins/util v1.0.2 h1:PontocVaiEm+oTj+4aDonwWDZnxywUeHsaTwlQgclfA= +github.com/apache/incubator-answer-plugins/util v1.0.2/go.mod h1:KPMSiM4ec4uEl2njaGINYuSl6zVmHdvPB2nHUxVcQDo= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= diff --git a/connector-wallet/i18n/en_US.yaml b/connector-wallet/i18n/en_US.yaml index 079721d..339f848 100644 --- a/connector-wallet/i18n/en_US.yaml +++ b/connector-wallet/i18n/en_US.yaml @@ -24,4 +24,12 @@ plugin: name: other: Wallet Connector description: - other: Connect to Wallet for third-party login \ No newline at end of file + other: Connect to Wallet for third-party login + frontend: + wallet_needed: You haven't connect wallet yet. + connect_button: Connect + wrong_network: Current network isn't supported. + switch_button: Switch network + connected_wallet: "You've connected to ${ADDRESS}, then you can:" + authorize_button: Authorize + disconnect_button: Change account diff --git a/connector-wallet/i18n/index.ts b/connector-wallet/i18n/index.ts new file mode 100644 index 0000000..7f7b453 --- /dev/null +++ b/connector-wallet/i18n/index.ts @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import en_US from './en_US.yaml'; +import zh_CN from './zh_CN.yaml'; + +export default { + en_US, + zh_CN, +}; diff --git a/connector-wallet/i18n/zh_CN.yaml b/connector-wallet/i18n/zh_CN.yaml index 6005879..ddc06cd 100644 --- a/connector-wallet/i18n/zh_CN.yaml +++ b/connector-wallet/i18n/zh_CN.yaml @@ -23,4 +23,12 @@ plugin: info: other: Wallet 连接器 description: - other: 用于接入 Wallet 第三方登录 \ No newline at end of file + other: 用于接入 Wallet 第三方登录 + frontend: + wallet_needed: 你还没连接钱包。 + connect_button: 连接钱包 + wrong_network: 不支持当前连接的网络。 + switch_button: 切换网络 + connected_wallet: "你已经连接到地址 ${ADDRESS},接下来可以:" + authorize_button: 授权登录 + disconnect_button: 更换账号 diff --git a/connector-wallet/index.ts b/connector-wallet/index.ts new file mode 100644 index 0000000..e69dbce --- /dev/null +++ b/connector-wallet/index.ts @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Component from './Component'; +import i18nConfig from './i18n'; +import info from './info.yaml'; + +export default { + info: { + slug_name: info.slug_name, + type: info.type, + route: info.route, + }, + component: Component, + i18nConfig, +}; diff --git a/connector-wallet/i18n/zh_CN.yaml b/connector-wallet/info.yaml similarity index 80% copy from connector-wallet/i18n/zh_CN.yaml copy to connector-wallet/info.yaml index 6005879..58edf34 100644 --- a/connector-wallet/i18n/zh_CN.yaml +++ b/connector-wallet/info.yaml @@ -15,12 +15,9 @@ # specific language governing permissions and limitations # under the License. -plugin: - wallet_connector: - backend: - name: - other: Wallet - info: - other: Wallet 连接器 - description: - other: 用于接入 Wallet 第三方登录 \ No newline at end of file +slug_name: wallet_connector +type: route +version: 0.0.1 +author: i-Lucifer,ourai +link: https://github.com/apache/incubator-answer-plugins/tree/main/connector-wallet +route: /connector-wallet-auth diff --git a/connector-wallet/package.json b/connector-wallet/package.json new file mode 100644 index 0000000..c8daa74 --- /dev/null +++ b/connector-wallet/package.json @@ -0,0 +1,57 @@ +{ + "name": "connector-wallet", + "version": "0.0.1", + "description": "Connect to Web3 wallet for third-party login", + "author": "Ourai L. <[email protected]>", + "type": "module", + "files": [ + "dist", + "README.md" + ], + "main": "./dist/connector-wallet.umd.js", + "module": "./dist/connector-wallet.es.js", + "types": "./dist/connector-wallet.d.ts", + "exports": { + ".": { + "import": "./dist/connector-wallet.es.js", + "require": "./dist/connector-wallet.umd.js" + } + }, + "scripts": { + "dev": "vite build --mode development --watch", + "build": "tsc && vite build", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" + }, + "peerDependencies": { + "@types/react": "^18.0.17", + "js-sha256": "0.11.0", + "react": "^18.2.0", + "react-bootstrap": "^2.10.0", + "react-dom": "^18.2.0", + "react-i18next": "^11.18.3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + }, + "devDependencies": { + "@modyfi/vite-plugin-yaml": "^1.1.0", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react-swc": "^3.3.2", + "eslint": "^8.45.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "typescript": "^5.0.2", + "vite": "^4.4.5", + "vite-plugin-css-injected-by-js": "^3.3.0", + "vite-plugin-dts": "^3.9.1" + }, + "dependencies": { + "@rainbow-me/rainbowkit": "^2.2.0", + "@tanstack/react-query": "^5.59.15", + "viem": "2.x", + "wagmi": "^2.12.19" + } +} diff --git a/connector-wallet/tsconfig.json b/connector-wallet/tsconfig.json new file mode 100644 index 0000000..8308948 --- /dev/null +++ b/connector-wallet/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "types": [ + "@modyfi/vite-plugin-yaml/modules" + ] + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/connector-wallet/tsconfig.node.json b/connector-wallet/tsconfig.node.json new file mode 100644 index 0000000..97ede7e --- /dev/null +++ b/connector-wallet/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/connector-wallet/vite.config.ts b/connector-wallet/vite.config.ts new file mode 100644 index 0000000..8093d10 --- /dev/null +++ b/connector-wallet/vite.config.ts @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react-swc'; +import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'; +import ViteYaml from '@modyfi/vite-plugin-yaml'; +import dts from 'vite-plugin-dts'; + +import packageJson from './package.json'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + react(), + cssInjectedByJsPlugin(), + ViteYaml(), + dts({ + insertTypesEntry: true, + }), + ], + build: { + lib: { + entry: 'index.ts', + name: packageJson.name, + fileName: (format) => `${packageJson.name}.${format}.js`, + }, + rollupOptions: { + external: [ + 'react', + 'react-dom', + 'react-i18next', + 'react-bootstrap', + '@rainbow-me/rainbowkit', + '@tanstack/react-query', + 'viem', + 'wagmi', + ], + output: { + globals: { + react: 'React', + 'react-dom': 'ReactDOM', + 'react-i18next': 'reactI18next', + 'react-bootstrap': 'reactBootstrap', + '@rainbow-me/rainbowkit': 'rainbow-meRainbowkit', + '@tanstack/react-query': 'tanstackReactQuery', + 'viem': 'viem', + 'wagmi': 'wagmi', + }, + }, + }, + }, +}); diff --git a/connector-wallet/wallet.go b/connector-wallet/wallet.go index 2716c99..9aca4be 100644 --- a/connector-wallet/wallet.go +++ b/connector-wallet/wallet.go @@ -20,17 +20,22 @@ package wallet import ( + "embed" "encoding/hex" "fmt" "log" "strings" "github.com/apache/incubator-answer-plugins/connector-wallet/i18n" + "github.com/apache/incubator-answer-plugins/util" "github.com/apache/incubator-answer/plugin" "github.com/ethereum/go-ethereum/crypto" "golang.org/x/exp/rand" ) +//go:embed info.yaml +var Info embed.FS + type Connector struct { } @@ -39,14 +44,17 @@ func init() { } func (g *Connector) Info() plugin.Info { + info := &util.Info{} + info.GetInfo(Info) + return plugin.Info{ Name: plugin.MakeTranslator(i18n.InfoName), - SlugName: "wallet_connector", + SlugName: info.SlugName, Description: plugin.MakeTranslator(i18n.InfoDescription), - Author: "i-Luicfer", - Version: "0.0.1", - Link: "https://github.com/apache/incubator-answer-plugins/tree/main/connector-wallet", - } + Author: info.Author, + Version: info.Version, + Link: info.Link, + } } func (g *Connector) ConnectorLogoSVG() string {
