diff --git a/examples/example-auth-react/src/App.tsx b/examples/example-auth-react/src/App.tsx index 0bc1743..88780bf 100644 --- a/examples/example-auth-react/src/App.tsx +++ b/examples/example-auth-react/src/App.tsx @@ -3,10 +3,19 @@ import reactLogo from './assets/react.svg' import viteLogo from '/vite.svg' import './App.css' -import { IUserInfo, LoginType, OpenPageMode, PageType, XterEventEmiter, XterioAuth } from '@xterio-sdk/auth' +import { + IUserInfo, + LoginType, + OpenPageMode, + PageType, + XterEventEmiter, + XTERIO_EVENTS, + XterioAuth +} from '@xterio-sdk/auth' function App() { - const [userinfo, setUserinfo] = useState('') + const [userinfo, setUserinfo] = useState('') + //这种形式记录不够准确,比如登录态中途变更时,该页面无法及时获悉。所以对于需要判断登录的操作,直接调用XterioAuth.isLogin即可 const [isLogin, setIsLogin] = useState(XterioAuth.isLogin) const [currentPage, setCurrentPage] = useState(PageType.asset) @@ -17,8 +26,17 @@ function App() { setUserinfo(JSON.stringify(res)) setIsLogin(XterioAuth.isLogin) }) + + //退出登录刷新本地islogin跟userinfo状态 + const logout_unsub = XterEventEmiter.subscribe(() => { + console.log('logout auth, and deal page state data') + setIsLogin(XterioAuth.isLogin) + setUserinfo(JSON.stringify(XterioAuth.userinfo)) + }, XTERIO_EVENTS.LOGOUT) + return () => { unsubscribe?.() + logout_unsub?.() } }, []) @@ -27,8 +45,6 @@ function App() { } const logout = () => { XterioAuth.logout() - setUserinfo('') - setIsLogin(XterioAuth.isLogin) } const openPage = async (_t: OpenPageMode) => { const res = await XterioAuth.openPage(currentPage, _t) @@ -55,6 +71,7 @@ function App() {

是否登录: {isLogin ? 'true' : 'false'}

用户信息: {userinfo}

+ diff --git a/examples/example-auth-vue/src/App.vue b/examples/example-auth-vue/src/App.vue index aafece0..a50d190 100644 --- a/examples/example-auth-vue/src/App.vue +++ b/examples/example-auth-vue/src/App.vue @@ -1,13 +1,14 @@ diff --git a/examples/example-auth/src/index.js b/examples/example-auth/src/index.js index 618342c..6958303 100644 --- a/examples/example-auth/src/index.js +++ b/examples/example-auth/src/index.js @@ -1,44 +1,91 @@ import '@xterio-sdk/auth/style/main.css' //方式1,先授权,再登录 -import { XterioAuth, XterEventEmiter, Env, LoginType } from '@xterio-sdk/auth' +import { XterioAuth, XterEventEmiter, Env, LoginType, PageType, OpenPageMode } from '@xterio-sdk/auth' console.log('initial') const redirect_uri = 'http://localhost:3000/' const client_id = '4gsmgur6gkp8u9ps8dlco3k7eo' const client_secret = 'ABC23' -const app_id = '' +const app_id = 'apiautotest' //4gsmgur6gkp8u9ps8dlco3k7eo, 4gsmgur6gkp8u9ps8dlco3aaaa -XterioAuth.init({ client_id, client_secret, redirect_uri }, Env.Dev) +XterioAuth.init({ client_id, client_secret, redirect_uri, app_id }, Env.Dev) XterEventEmiter.subscribe((info) => { console.log('emit userinfo=', info) - p.innerText = JSON.stringify(info) + updateInfo(info) }) -const btn = document.getElementById('login') -const logoutBtn = document.getElementById('logout') -const emailBtn = document.getElementById('email') -const miniBtn = document.getElementById('login_mini') const p = document.getElementById('userinfo') - -if (miniBtn) { - miniBtn.onclick = () => { - // XterioAuth. - XterioAuth.login(LoginType.Mini) - } -} -if (btn) { - btn.onclick = () => { - XterioAuth.login() +const updateInfo = (info) => { + if (p) { + p.innerText = JSON.stringify(info) } } -if (emailBtn) { - emailBtn.onclick = () => { - XterioAuth.login(LoginType.Email) +const addClick = (id, callback) => { + const btn = document.getElementById(id) + if (btn) { + btn.onclick = () => { + callback() + } } } -if (logoutBtn) { - logoutBtn.onclick = () => { - XterioAuth.logout() - p.innerText = '' +addClick('login', () => { + XterioAuth.login() +}) +addClick('login_email', () => { + XterioAuth.login(LoginType.Email) +}) +addClick('login_mini', () => { + XterioAuth.login(LoginType.Mini) +}) +addClick('logout', () => { + XterioAuth.logout() + updateInfo({}) +}) +addClick('isLogin', () => { + alert(XterioAuth.isLogin) +}) +addClick('getIdToken', async () => { + const id_token = await XterioAuth.getIdToken() + alert(id_token) +}) + +let currentPageName = PageType.asset +const changePage = () => { + const el = document.getElementById('current-page') + if (el) { + el.innerText = currentPageName } } +addClick('openAsset', () => { + XterioAuth.openPage(currentPageName) +}) +addClick('openAsset-new', () => { + XterioAuth.openPage(currentPageName, OpenPageMode.page) +}) +addClick('openAsset-dom', async () => { + const dom = await XterioAuth.openPage(currentPageName, OpenPageMode.iframeDom) + console.log('dom=', dom) + alert(dom) +}) +addClick('openAsset-uri', async () => { + const uri = await XterioAuth.openPage(currentPageName, OpenPageMode.iframeUri) + console.log('uri=', uri) + alert(uri) +}) +addClick('page-asset', () => { + currentPageName = PageType.asset + changePage() +}) +addClick('page-account', () => { + currentPageName = PageType.account + changePage() +}) +addClick('page-wallet', () => { + currentPageName = PageType.wallet + changePage() +}) +addClick('page-nft', () => { + currentPageName = PageType.nft + changePage() +}) +changePage() diff --git a/examples/example-wallet-react/src/App.tsx b/examples/example-wallet-react/src/App.tsx index f69f48f..96811f8 100644 --- a/examples/example-wallet-react/src/App.tsx +++ b/examples/example-wallet-react/src/App.tsx @@ -1,25 +1,14 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' import './App.css' import { ERC20_ABI } from './abi' import { getContract, NETWORK_NAME } from './common' import { useXterioWalletContext, useXterioTransaction } from '@xterio-sdk/wallet' -import { LoginType } from '@xterio-sdk/auth' +import { IUserInfo, LoginType, XterEventEmiter, XTERIO_EVENTS, XterioAuth } from '@xterio-sdk/auth' function App() { - const { - userinfo, - isLogin, - login, - logout, - aaAddress, - isConnect, - disconnectWallet, - openWallet, - obtainWallet, - connectWallet, - signMessage - } = useXterioWalletContext() + const { aaAddress, isConnect, disconnectWallet, openWallet, obtainWallet, connectWallet, signMessage } = + useXterioWalletContext() const contractAddress = '0x12065F0d03cd1Bd280565069164F9E803c2DA988' const abi = ERC20_ABI @@ -50,17 +39,35 @@ function App() { await sendUserOperation?.(tx) } + const [userinfo, setUserInfo] = useState({}) + useEffect(() => { + console.log('[xtest] ---- add listener') + const unsubscribe_Info = XterEventEmiter.subscribe((res: IUserInfo) => { + setUserInfo(res) + }, XTERIO_EVENTS.ACCOUNT) + + const unsubscribe_logout = XterEventEmiter.subscribe(() => { + setUserInfo({}) + }, XTERIO_EVENTS.LOGOUT) + return () => { + console.log('[xtest] ---- remove listener') + unsubscribe_Info?.() + unsubscribe_logout?.() + } + }, []) + return ( <>

Xterio SDK

xterio auth sdk
-

是否登录: {isLogin ? 'true' : 'false'}

+

是否登录: {XterioAuth.isLogin ? 'true' : 'false'}

用户信息: {userinfo ? JSON.stringify(userinfo) : ''}

- - - - + + + + +
xterio wallet sdk
@@ -68,9 +75,9 @@ function App() {
pn aa wallet address: {aaAddress}
pn aa wallet connected status: {isConnect ? 'true' : 'false'}
- - - + + +
xterio wallet transaction
diff --git a/sh/publish.ts b/sh/publish.ts index a2f920e..6c18d81 100644 --- a/sh/publish.ts +++ b/sh/publish.ts @@ -1,8 +1,10 @@ import chalk from 'chalk' import consola from 'consola' -import { emptyDir, ensureDir, readJSONSync, writeJSONSync } from 'fs-extra' +import { appendFileSync, readFileSync, readJSONSync, writeJSONSync } from 'fs-extra' import { pathAuth, pathWallet, pathAuthJson, pathWalletJson, pathSh, pathRoot } from './paths' import { run } from './run' +import * as readline from 'readline' +import { resolve } from 'path' const AuthJsonData = readJSONSync(pathAuthJson, { encoding: 'utf-8' }) const WalletJsonData = readJSONSync(pathWalletJson, { encoding: 'utf-8' }) @@ -14,50 +16,104 @@ consola.info('_flag=', chalk.blue(_flag), _version) async function init() { const isExecuteAuth = !_flag || _flag === 'auth' const isExecuteWallet = !_flag || _flag === 'wallet' + if (isExecuteAuth) { + await publishAuth() + } + if (isExecuteWallet) { + await publishWallet() + } +} + +const publishAuth = async () => { let authVersion = AuthJsonData.version + if (_version) { + authVersion = _version + AuthJsonData.version = _version + writeJSONSync(pathAuthJson, AuthJsonData, { encoding: 'utf-8', spaces: 2 }) + } + await run(`pnpm run build:auth`, pathRoot) + + const result = await run('npm publish', pathAuth) + if (result !== 0) { + consola.error(chalk.red('auth publish failed.')) + return + } + // publish success + updateReleaseDoc(authVersion) + await commitVersionFile('auth', authVersion) + await run(`bash release.sh auth ${authVersion}`, pathSh) + return authVersion +} + +const publishWallet = async () => { let walletVersion = WalletJsonData.version + if (_version) { + walletVersion = _version + WalletJsonData.version = _version + writeJSONSync(pathWalletJson, WalletJsonData, { encoding: 'utf-8', spaces: 2 }) + } + await run(`pnpm run build:wallet`, pathRoot) + changeWalletPackageJson('publish') - if (isExecuteAuth) { - if (_version) { - authVersion = _version - AuthJsonData.version = _version - writeJSONSync(pathAuthJson, AuthJsonData, { encoding: 'utf-8', spaces: 2 }) - await commitVersionFile('auth', _version) - await run(`pnpm run build:auth`, pathRoot) - } - await run('npm publish', pathAuth) - // publish success, commit all change content and push release lock. - await run(`bash release.sh auth ${authVersion}`, pathSh) + const result = await run('npm publish', pathWallet) + if (result !== 0) { + consola.error(chalk.red('wallet publish failed.')) + changeWalletPackageJson('reset') + return } + // publish success + updateReleaseDoc(walletVersion) + await run(`bash release.sh wallet ${walletVersion}`, pathSh) + changeWalletPackageJson('reset') + await commitVersionFile('wallet', walletVersion) +} - if (isExecuteWallet) { - if (_version) { - walletVersion = _version - WalletJsonData.version = _version - writeJSONSync(pathWalletJson, WalletJsonData, { encoding: 'utf-8', spaces: 2 }) - await commitVersionFile('wallet', _version) - await run(`pnpm run build:wallet`, pathRoot) - } - //tip: change package.json content - //remove dep, add peer +const changeWalletPackageJson = (flag: string) => { + const authVersion = AuthJsonData.version + if (flag === 'publish') { WalletJsonData.peerDependencies['@xterio-sdk/auth'] = '^' + authVersion.split('.').slice(0, 2).join('.') delete WalletJsonData.dependencies['@xterio-sdk/auth'] writeJSONSync(pathWalletJson, WalletJsonData, { encoding: 'utf-8', spaces: 2 }) - await run('npm publish', pathWallet) - await run(`bash release.sh wallet ${walletVersion}`, pathSh) - await reset() + } else { + WalletJsonData.peerDependencies['@xterio-sdk/auth'] = 'workspace:^' + WalletJsonData.dependencies['@xterio-sdk/auth'] = 'workspace:^' + writeJSONSync(pathWalletJson, WalletJsonData, { encoding: 'utf-8', spaces: 2 }) } } + const commitVersionFile = async (_f: string, _v: string) => { const path = _f === 'auth' ? pathAuth : pathWallet await run(`git push origin main`) - await run(`git add package.json && git commit -m "feat: npm pkg(${_f}) publish(${_v})"`, path) + await run(`git add . && git commit -m "feat: npm pkg(${_f}) publish(${_v})"`, path) await run(`git push origin main`) } -const reset = () => { - WalletJsonData.peerDependencies['@xterio-sdk/auth'] = 'workspace:^' - WalletJsonData.dependencies['@xterio-sdk/auth'] = 'workspace:^' - writeJSONSync(pathWalletJson, WalletJsonData, { encoding: 'utf-8', spaces: 2 }) + +const updateReleaseDoc = async (v: string) => { + const docPath = resolve(pathAuth, 'RELEASE.md') + const data = readFileSync(docPath, 'utf-8') + const pattern = new RegExp(`# ${v.replace(/\./g, '\\.')}(\\s|$)`) + if (!pattern.test(data)) { + appendFileSync(docPath, `\n# ${v}\n`) + appendFileSync(docPath, lines.map((i) => `- ${i} \n`).join('')) + } } -init() +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}) +const lines: string[] = [] +consola.info(chalk.blue('Please enter some message for this publish.\n')) +const promptForInput = () => { + rl.question('', (line: string) => { + if (line === '') { + rl.close() + init() + } else { + lines.push(line) + promptForInput() + } + }) +} +promptForInput() +// init() diff --git a/xterio-auth/README.md b/xterio-auth/README.md index eb5ae5b..0965ac4 100644 --- a/xterio-auth/README.md +++ b/xterio-auth/README.md @@ -26,7 +26,7 @@ unsubscribe() //unsubscribe //or -XterEventEmiter.unsubscribe()//unsubscribe +XterEventEmiter.unsubscribe()//unsubscribe all account event //3. SignIn XterioAuth.login() @@ -95,9 +95,9 @@ XterioAuth.logout() ``` #### `getIdToken()` -check whether the idToken is valid. If the idToken is invalid, empty string is returned, else the idToken. +check whether the idToken is valid. If the idToken is invalid, empty string is returned, else the non-empty str. ```ts -await XterioAuth.getIdToken() //string +XterioAuth.getIdToken() //Promise ``` #### `getUserInfo(p:Function)` @@ -207,4 +207,43 @@ export enum PageType { } ``` +### `PageOptionParam` +```ts +export interface PageOptionParam { + /** asset page */ + active?: 'ingame' | 'onchain' + /** nft page */ + keyword?: string + /** nft page */ + collection?: string + /** nft page */ + features?: { k: string; initValues: (number | string)[]; type?: string }[] + /** whether hide wallet entry */ + hide_wallet_entrance?: boolean + /** whether hide account */ + hide_account_entrance?: boolean + /** whether hide top nav menu */ + hide_menu_entrance?: boolean + /** whether hide logout btn */ + hide_sign_out?: boolean + /** whether hide footer */ + hide_footer?: boolean + /** whether disable logo click event */ + disable_logo_click?: boolean + /** whether hide game select, only asset page */ + hide_game_select?: boolean + /** whether hide game tokens, only asset page */ + hide_game_tokens?: boolean + /** whether hide game filter, only nft page */ + hide_game_filter?: boolean + /** set alert configs */ + alertConfig?: { + placement: 'left' | 'right' | 'center' //default: 'right' + style: Partial //default: { width: '400px', height: '100%' } + showCloseIcon?: boolean + } +} +``` + + diff --git a/xterio-auth/RELEASE.md b/xterio-auth/RELEASE.md new file mode 100644 index 0000000..6e2c61a --- /dev/null +++ b/xterio-auth/RELEASE.md @@ -0,0 +1,6 @@ + +# 0.0.11 +- 1. 用户数据解耦&优化 +- 2. isLogin 变更逻辑处理 +- 3. idToken 刷新处理 +- 4. 定制页面UI&样式配置参数增加 diff --git a/xterio-auth/index.html b/xterio-auth/index.html index 367c5bc..010de13 100644 --- a/xterio-auth/index.html +++ b/xterio-auth/index.html @@ -13,6 +13,9 @@ margin-right: 10px; margin-bottom: 10px; cursor: pointer; + width: 100px; + height: 40px; + background-color: red; } } @@ -22,27 +25,28 @@

Hello, Webpack!

-
默认登录
-
邮箱登录
-
小程序登录
-
退出登录
-
IdToken
+
是否登录
+
默认登录
+
邮箱登录
+
小程序登录
+
退出登录
+
IdToken
用户信息

设置要打开的页面:

-
资产页
-
账户页
-
钱包页
-
nft页
+
资产页
+
账户页
+
钱包页
+
nft页
-
打开页面(弹框)
-
打开页面(新页)
-
打开页面(dom)
-
打开页面(uri)
+
打开页面(弹框)
+
打开页面(新页)
+
打开页面(dom)
+
打开页面(uri)
diff --git a/xterio-auth/package.json b/xterio-auth/package.json index 3d2411b..7e3b3e6 100644 --- a/xterio-auth/package.json +++ b/xterio-auth/package.json @@ -1,6 +1,6 @@ { "name": "@xterio-sdk/auth", - "version": "0.0.10", + "version": "0.0.11", "description": "xterio-auth", "author": "xterio platform", "license": "ISC", diff --git a/xterio-auth/src/interfaces/loginInfo.ts b/xterio-auth/src/interfaces/loginInfo.ts index ef6e600..ba9bcf8 100644 --- a/xterio-auth/src/interfaces/loginInfo.ts +++ b/xterio-auth/src/interfaces/loginInfo.ts @@ -123,9 +123,38 @@ export enum PageType { account = 'account', wallet = 'wallet' } + export interface PageOptionParam { + /** asset page */ active?: 'ingame' | 'onchain' + /** nft page */ keyword?: string + /** nft page */ collection?: string + /** nft page */ features?: { k: string; initValues: (number | string)[]; type?: string }[] + /** whether hide wallet entry */ + hide_wallet_entrance?: boolean + /** whether hide account */ + hide_account_entrance?: boolean + /** whether hide top nav menu */ + hide_menu_entrance?: boolean + /** whether hide logout btn */ + hide_sign_out?: boolean + /** whether hide footer */ + hide_footer?: boolean + /** whether disable logo click event */ + disable_logo_click?: boolean + /** whether hide game select, only asset page */ + hide_game_select?: boolean + /** whether hide game tokens, only asset page */ + hide_game_tokens?: boolean + /** whether hide game filter, only nft page */ + hide_game_filter?: boolean + /** set alert configs */ + alertConfig?: { + placement: 'left' | 'right' | 'center' + style: Partial + showCloseIcon?: boolean + } } diff --git a/xterio-auth/src/main.ts b/xterio-auth/src/main.ts index 930e323..098f8be 100644 --- a/xterio-auth/src/main.ts +++ b/xterio-auth/src/main.ts @@ -1,5 +1,5 @@ import { OpenPageMode, PageType } from 'interfaces/loginInfo' -import { Env, IUserInfo, LoginType, XterEventEmiter, XTERIO_EVENTS, XterioAuth } from './index' +import { Env, IUserInfo, LoginType, XterioAuth } from './index' import './styles/main.scss' const p = document.getElementById('userinfo') @@ -41,7 +41,9 @@ const changePage = () => { el.innerText = currentPageName } } - +addClick('isLogin', () => { + alert(XterioAuth.isLogin) +}) addClick('login', () => { XterioAuth.login() }) @@ -60,7 +62,7 @@ addClick('login_mini', () => { }) addClick('getIdToken', async () => { const id_token = await XterioAuth.getIdToken() - console.log('id_token=', id_token) + alert(id_token) }) addClick('openAsset', () => { XterioAuth.openPage(currentPageName) @@ -74,7 +76,10 @@ addClick('openAsset-dom', async () => { alert(dom) }) addClick('openAsset-uri', async () => { - const uri = await XterioAuth.openPage(currentPageName, OpenPageMode.iframeUri) + const uri = await XterioAuth.openPage(currentPageName, OpenPageMode.iframeUri, { + hide_account_entrance: true, + hide_footer: true + }) console.log('uri=', uri) alert(uri) }) diff --git a/xterio-auth/src/modules/AuthService.ts b/xterio-auth/src/modules/AuthService.ts index 45440ae..4d6655d 100644 --- a/xterio-auth/src/modules/AuthService.ts +++ b/xterio-auth/src/modules/AuthService.ts @@ -15,6 +15,7 @@ import { XterioAuthInfo, XterioAuthTokensManager, XterioAuthUserInfoManager } fr import { XterEventEmiter } from './XterEventEmitter' import { XTERIO_EVENTS } from 'utils/const' import { getFetcher, postFetcher } from 'utils/fetchers' +import { XterioAuth } from './XterAuth' export class XterioAuthService { /** @@ -34,6 +35,7 @@ export class XterioAuthService { .then((res) => { XLog.info('login success.') XterioAuthTokensManager.setTokens(res) + XterioAuth.setIsLogin(true) return res }) .catch((err) => { @@ -114,6 +116,7 @@ export class XterioAuthService { XLog.info('ttl login', res?.error ? 'failed' : 'success') if (!res?.error) { XterioAuthTokensManager.setTokens(res) + XterioAuth.setIsLogin(true) } return res?.error ? { ...res, error: true } : { ...res, error: false } } @@ -181,6 +184,7 @@ export class XterioAuthService { }) if (!res?.error) { XterioAuthTokensManager.setTokens(res) + XterioAuth.setIsLogin(true) } return res?.error ? res : { ...res, error: false } } diff --git a/xterio-auth/src/modules/XterAuth.ts b/xterio-auth/src/modules/XterAuth.ts index 2b77ec2..53c4132 100644 --- a/xterio-auth/src/modules/XterAuth.ts +++ b/xterio-auth/src/modules/XterAuth.ts @@ -9,14 +9,27 @@ import qs from 'query-string' import { XterioCache } from './XterCache' import { decode } from 'js-base64' import { openPage } from './XterPage' +import { LoadingState, XTimeOut } from 'utils/timer' export class XterioAuth { - static get isLogin() { - return !!XterioAuthUserInfoManager.userInfo?.uuid - } static get userinfo() { return XterioAuthUserInfoManager.userInfo } + private static setTokenTimer(duration: number) { + // const duration = 10000 + if (duration < 0) return + XTimeOut.getInstance().addTimeout(() => { + //idToken expired logic + const refreshToken = XterioAuthTokensManager.refreshToken + this.setIsLogin(false) + XterioCache.deleteTokens(XTERIO_CONST.ID_TOKEN) + XLog.info('the token timer, reset isLogin:', false) + if (refreshToken) { + XLog.info('the token timer, refresh token again') + this.checkToken('tokenTimer') + } + }, duration) + } private static get isVaildIdToken() { const id_token = XterioAuthTokensManager.idToken @@ -32,6 +45,7 @@ export class XterioAuth { try { const { aud, exp = 0, sub } = JSON.parse(decode(payload)) as Payload + this.setTokenTimer((exp - 60) * 1000 - Date.now()) const isExpire = !aud || Date.now() > (exp - 60) * 1000 return !isExpire } catch (error) { @@ -40,21 +54,32 @@ export class XterioAuth { } } + private static _islogin: boolean + static get isLogin() { + return this._islogin + } + static setIsLogin(f: boolean) { + XLog.debug('set xterio auth islogin=', f) + this._islogin = f + } + private static async checkToken(_flag: string = 'init') { const _tokens = XterioCache.tokens if (_tokens) { XterioAuthTokensManager.setTokens(_tokens) } const refresh_token = XterioAuthTokensManager.refreshToken - if (!XterioAuth.isVaildIdToken && refresh_token) { - //req tokens by refresh - XLog.info('refresh tokens') + let isvalid = this.isVaildIdToken + if (!isvalid && refresh_token) { + // token invalid, req tokens by refresh + XLog.info('check token and refresh tokens') const res = await XterioAuthService.refreshTokenService(refresh_token) XterioAuthTokensManager.setTokens({ refresh_token, id_token: res.id_token, access_token: res.access_token }) + //again check + isvalid = this.isVaildIdToken } - - const isvalid = XterioAuth.isVaildIdToken - XLog.info('check the tokens valid status:', isvalid) + XLog.info('check token and the idToken isvalid=', isvalid) + this.setIsLogin(isvalid) if (!isvalid) { XLog.info('clear cache data') this.clearData() @@ -67,7 +92,7 @@ export class XterioAuth { private static async checkCode() { const _type = XterioCache.loginType - XLog.debug('check the authorize status', _type) + XLog.debug('check authorize status and _type=', _type) if (_type !== LoginType.Default && _type !== LoginType.Email) return @@ -93,6 +118,7 @@ export class XterioAuth { } static async getIdToken() { + /// idtoken not expired or refreshed if the promise return non-empty string return await this.checkToken('getIdToken') } @@ -132,14 +158,25 @@ export class XterioAuth { qs.stringify({ client_id, redirect_uri, response_type, scope, mode, logout }) XterioAuthInfo.config = _config - XterEventEmiter.clear() + XLog.debug('auth initial') + + // XterEventEmiter.clear() + XterEventEmiter.subscribe((info: IUserInfo) => { XLog.debug('the userinfo callback count=', XterioAuthInfo.onAccount.length) XterioAuthInfo.onAccount.map((f) => f(info)) }) - XterEventEmiter.subscribe(() => { - //req expired logic - this.clearData() + + XterEventEmiter.subscribe(async () => { + //req expired logic, remove idToken + //loadingState 防止并发401处理 + LoadingState.getInstance().execute(async () => { + XLog.debug('req 401, refresh token') + this.setIsLogin(false) + XterEventEmiter.emit(XTERIO_EVENTS.LOGOUT) + XterioCache.deleteTokens(XTERIO_CONST.ID_TOKEN) + await this.checkToken() + }) }, XTERIO_EVENTS.Expired) // init XterAuthLoginModal @@ -161,6 +198,8 @@ export class XterioAuth { static logout() { XLog.debug('logout success') this.clearData() + this.setIsLogin(false) + XterEventEmiter.emit(XTERIO_EVENTS.LOGOUT) XterAuthModal?.instance?.store?.removeUserState() } static async login(mode?: LoginType) { @@ -176,15 +215,7 @@ export class XterioAuth { `/account/v1/oauth2/authorize?` + qs.stringify({ client_id, redirect_uri, response_type, scope, mode, logout }) } - - if (XterioAuth.isLogin) { - //logined, callback the account info - XLog.debug('already logined.') - XterEventEmiter.emit(XTERIO_EVENTS.ACCOUNT, XterioAuthUserInfoManager.userInfo) - return - } - if (XterioAuth.isVaildIdToken) { - //idtoken not expired + if (this.isLogin) { XLog.debug('get userinfo') return XterioAuthService.getUserInfo() } diff --git a/xterio-auth/src/modules/XterAuthInfo.ts b/xterio-auth/src/modules/XterAuthInfo.ts index 7ffab16..9e8acc1 100644 --- a/xterio-auth/src/modules/XterAuthInfo.ts +++ b/xterio-auth/src/modules/XterAuthInfo.ts @@ -1,6 +1,8 @@ import type { Env, ISSoTokensParams, ITokenRes, IUserInfo } from 'interfaces/loginInfo' import { LoginType } from 'interfaces/loginInfo' import { XterioCache } from './XterCache' +import { XterEventEmiter } from './XterEventEmitter' +import { XTERIO_EVENTS } from 'utils' export class XterioAuthInfo { /** client id */ diff --git a/xterio-auth/src/modules/XterAuthModal/XertAuthModalStore.ts b/xterio-auth/src/modules/XterAuthModal/XertAuthModalStore.ts index a30f627..5070e0e 100644 --- a/xterio-auth/src/modules/XterAuthModal/XertAuthModalStore.ts +++ b/xterio-auth/src/modules/XterAuthModal/XertAuthModalStore.ts @@ -103,7 +103,7 @@ export class XterAuthModalStore extends ModalObservable { private async refreshUserInfo() { XterEventEmiter.subscribe((res: IUserInfo) => { this.userState.userInfo = res - this.userState.isLogin = XterioAuth.isLogin + this.userState.isLogin = !!res.uuid }) /* const { refreshToken, idToken } = this.tokenManager diff --git a/xterio-auth/src/modules/XterCache.ts b/xterio-auth/src/modules/XterCache.ts index 83f6480..499a90a 100644 --- a/xterio-auth/src/modules/XterCache.ts +++ b/xterio-auth/src/modules/XterCache.ts @@ -16,7 +16,7 @@ export class XterioCache { Cookies.set(XTERIO_CONST.ID_TOKEN, id_token, { expires: 1 }) Cookies.set(XTERIO_CONST.REFRESH_TOKEN, refresh_token, { expires: 180 }) } - static get tokens(): ITokenRes | undefined { + static get tokens(): ITokenRes { const _t: ITokenRes = { access_token: Cookies.get(XTERIO_CONST.ACCESS_TOKEN) || '', id_token: Cookies.get(XTERIO_CONST.ID_TOKEN) || '', @@ -24,10 +24,14 @@ export class XterioCache { } return _t } - static deleteTokens() { - Cookies.remove(XTERIO_CONST.ACCESS_TOKEN) - Cookies.remove(XTERIO_CONST.REFRESH_TOKEN) - Cookies.remove(XTERIO_CONST.ID_TOKEN) + static deleteTokens(key?: string) { + if (key) { + Cookies.remove(key) + } else { + Cookies.remove(XTERIO_CONST.ACCESS_TOKEN) + Cookies.remove(XTERIO_CONST.REFRESH_TOKEN) + Cookies.remove(XTERIO_CONST.ID_TOKEN) + } } static set userInfo(value: IUserInfo) { diff --git a/xterio-auth/src/modules/XterPage.ts b/xterio-auth/src/modules/XterPage.ts index 1560647..8f8f86a 100644 --- a/xterio-auth/src/modules/XterPage.ts +++ b/xterio-auth/src/modules/XterPage.ts @@ -1,14 +1,22 @@ import { OpenPageMode, PageOptionParam, PageType } from 'interfaces/loginInfo' -import { XterioAuthInfo, XterioAuthTokensManager } from './XterAuthInfo' +import { XterioAuthInfo } from './XterAuthInfo' import { XLog } from 'utils/logger' import qs from 'query-string' -import { getIframe } from 'utils/dom' +import { addCssText, convertStyleToArray, getDiv, getIframe } from 'utils/dom' import { XterioAuth } from './XterAuth' import { XterioAuthService } from './AuthService' +import { iconContentMap } from 'ui/svg-icon' const getOtac = async () => { - const idToken = await XterioAuth.getIdToken() - if (idToken) { + //tip: otac invalid when used once + if (XterioAuth.isLogin) { + const otac = await XterioAuthService.getOtacByTokens() + return otac || '' + } + return '' + + /* + if (XterioAuth.isLogin) { // User is logged in or the login token is expiring soon if (!XterioAuthInfo?.otac) { const otac = await XterioAuthService.getOtacByTokens() @@ -25,10 +33,11 @@ const getOtac = async () => { } } return XterioAuthInfo?.otac || '' + */ } export const openPage = async (page: PageType, mode?: OpenPageMode, options?: PageOptionParam) => { - const { active = 'ingame', keyword, collection, features } = options || {} + const { active = 'ingame', keyword, collection, features, alertConfig, ...rest } = options || {} const _type = mode || OpenPageMode.alert const app_id = XterioAuthInfo.config?.app_id || '' if (!app_id) { @@ -56,6 +65,9 @@ export const openPage = async (page: PageType, mode?: OpenPageMode, options?: Pa uri = `${basePage}/marketplace?${qs.stringify(query)}` } + if (rest) { + uri += `&${qs.stringify(rest)}` + } const otac = await getOtac() if (otac) { uri += `&_otac=${otac}` @@ -66,13 +78,39 @@ export const openPage = async (page: PageType, mode?: OpenPageMode, options?: Pa return uri } if (_type === OpenPageMode.iframeDom) { - return getIframe(uri).querySelector('iframe') + const { iframe } = getIframe(uri) + return iframe + } + if (_type === OpenPageMode.page) { + location.href = uri } if (_type === OpenPageMode.alert) { - document.body.appendChild(getIframe(uri)) + alertIframeLogic(uri, alertConfig) return } - if (_type === OpenPageMode.page) { - location.href = uri +} + +const alertIframeLogic = (uri: string, config: PageOptionParam['alertConfig']) => { + const { placement = 'right', style = { width: '100%', height: '100%' }, showCloseIcon = true } = config || {} + const { iframeDiv, shadow } = getIframe(uri) + if (showCloseIcon) { + const { element: closeDiv, remove: unsubscribe } = getDiv('close-icon pointer', () => { + shadow.remove() + unsubscribe?.() + }) + addCssText(closeDiv, `position:absolute;top:16px;left:16px;`) + closeDiv.innerHTML = iconContentMap['icon-close-iframe'] + iframeDiv.appendChild(closeDiv) + } + document.body.appendChild(shadow) + //update style, style > placement + const _styArr = [] + _styArr.push('margin-top', `calc((100% - ${style.height}) / 2)`) + if (placement === 'right') { + _styArr.push('margin-left', `calc(100% - ${style.width})`) + } else if (placement === 'center') { + _styArr.push('margin-left', `calc((100% - ${style.width}) / 2)`) } + _styArr.push(...convertStyleToArray(style)) + addCssText(iframeDiv, ..._styArr) } diff --git a/xterio-auth/src/styles/main.scss b/xterio-auth/src/styles/main.scss index 9a6602c..c1d1bae 100644 --- a/xterio-auth/src/styles/main.scss +++ b/xterio-auth/src/styles/main.scss @@ -28,11 +28,7 @@ z-index: 1000; } - &-modal-container-iframe { - height: 100%; - width: 100%; - max-width: 400px; - margin: auto; + &-modal-container-iframe { z-index: 1010; position: relative; } diff --git a/xterio-auth/src/ui/svg-icon/index.ts b/xterio-auth/src/ui/svg-icon/index.ts index df30bf7..81ef30d 100644 --- a/xterio-auth/src/ui/svg-icon/index.ts +++ b/xterio-auth/src/ui/svg-icon/index.ts @@ -1,17 +1,8 @@ -const iconContentMap = { +export const iconContentMap = { 'icon-close': ``, 'icon-show': ``, - 'icon-hide': `` -} - -const getCssText = (...args: any[]): string => { - let _css: string = '' - for (let i = 0; i < Math.floor(args.length / 2); i++) { - const key = args[i * 2] - const val = args[i * 2 + 1] - _css += `${key}: ${val};` - } - return _css + 'icon-hide': ``, + 'icon-close-iframe': `` } export const generateSVGIcon = ( diff --git a/xterio-auth/src/utils/const.ts b/xterio-auth/src/utils/const.ts index 9904262..ec1c6ee 100644 --- a/xterio-auth/src/utils/const.ts +++ b/xterio-auth/src/utils/const.ts @@ -27,7 +27,8 @@ export const XTERIO_EVENTS = { LOGIN: 'xter_auth_login_success', ACCOUNT: 'xter_auth_response_userinfo', REQ_ACCOUNT: 'xter_auth_request_userinfo', - Expired: 'xter_auth_req_expired' + Expired: 'xter_auth_req_expired', + LOGOUT: 'xter_auth_logout' } export const XTERIO_CONST = { LOGIN_TYPE: 'xter_auth_login_type', diff --git a/xterio-auth/src/utils/dom.ts b/xterio-auth/src/utils/dom.ts index 0059d3e..b405223 100644 --- a/xterio-auth/src/utils/dom.ts +++ b/xterio-auth/src/utils/dom.ts @@ -1,11 +1,121 @@ -export const getCssName = (name: string) => { - const a = name.split(' ').reduce((prev, cur) => prev + `xterioauth-${cur} `, '') +/** + * 给class统一加前缀 + * example: getCssName('iframe div') => xterioauth-iframe xterioauth-div + * @param name + * @returns string + */ +export const getCssName = (name: string, prefix: string = 'xterioauth') => { + const a = name.split(' ').reduce((prev, cur) => prev + `${prefix}-${cur} `, '') return a } -const getDiv = (cssName: string, callback?: () => void) => { - const div = document.createElement('div') - div.className = getCssName(cssName) +/** + * dom元素,添加指定 class + * example: addClass(ele, 'iframe div') + * @param ele 目标元素 + * @param classNames 类名,以空格隔开 + */ +export const addClass = (ele: HTMLElement, classNames: string) => { + const _names = classNames.split(' ').filter(Boolean) + if (ele.classList) { + _names.forEach((_c: string) => { + ele.classList.add(_c) + }) + } else { + const classes = ele.className.split(' ') + _names.forEach((_c: string) => { + if (classes.indexOf(_c) === -1) { + classes.push(_c) + } + }) + ele.className = classes.join(' ') + } +} + +/** + * dom元素,移除指定 class + * example: removeClass(ele, 'iframe div') + * @param ele 目标元素 + * @param classNames 类名,以空格隔开 + */ +export const removeClass = (ele: HTMLElement, classNames: string) => { + const _names = classNames.split(' ').filter(Boolean) + if (ele.classList) { + _names.forEach((_c: string) => { + ele.classList.remove(_c) + }) + } else { + const classes = ele.className.split(' ') + _names.forEach((_c: string) => { + const index = classes.indexOf(_c) + if (index !== -1) { + classes.splice(index, 1) + } + }) + ele.className = classes.join(' ') + } +} + +/** + * 将给定参数转换成cssText形式 + * example: generateCssText('width', '200px', 'height', '200px', ...) + * @param args any[] + * @returns string + */ +export const generateCssText = (...args: any[]): string => { + let _css: string = '' + for (let i = 0; i < Math.floor(args.length / 2); i++) { + //转换一下,对于marginTop => margin-top + const key = args[i * 2].replace(/[A-Z]/g, (m: string) => `-${m.toLowerCase()}`) + const val = args[i * 2 + 1] + _css += `${key}: ${val};` + } + return _css +} + +/** + * dom元素,设置样式 + * example1. addCssText(ele, `position:absolute;top:16px;left:16px;`) + * example2. addCssText(ele, 'width', '200px', ...) + * @param ele dom元素 + * @param args 系列参数 + */ +export const addCssText = (ele: HTMLElement, ...args: any[]) => { + const isCssStr = args.length === 1 && typeof args?.[0] === 'string' && args?.[0].indexOf(':') !== -1 + if (isCssStr) { + ele.style.cssText = ele.style.cssText + args?.[0] + } else { + // console.log('dddd', generateCssText(...args)) + ele.style.cssText = ele.style.cssText + generateCssText(...args) + } +} + +/** + * 将Style对象样式转成数组形式[propertyName, propertyValue, ...] + * example: convertStyleToArray({width: '200px', marginTop:'20px'}) + * @param sty {width: '200px', marginTop:'20px'} + * @returns ['width', '200px', 'marginTop', '20px'] + */ +export const convertStyleToArray = (sty: Partial) => { + const _arr = [] + for (const key in sty) { + _arr.push(key, sty[key]) + } + return _arr +} + +/** + * 创建一个dom元素,并返回该dom元素对象,如果可点击再补充返回移除点击事件的监听函数 + * example1: getDiv('test') + * example2: getDiv('test', ()=> {}) + * example3: getDiv('test', undefined, 'span') + * @param cssName 指定类名 + * @param callback 点击事件 + * @returns {dom元素,移除点击监听函数?} + */ +export const getDiv = (cssName: string, callback?: () => void, tagName: string = 'div') => { + const div = document.createElement(tagName) + addClass(div, getCssName(cssName)) if (callback) { const clickHandler = (e: Event) => { callback?.() @@ -21,6 +131,7 @@ const getDiv = (cssName: string, callback?: () => void) => { return { element: div } } +//创建全局iframe弹框 export const getIframe = (url: string) => { const { element: shadow } = getDiv('modal-shadow') const { element: bg, remove } = getDiv('modal-bg pointer', () => { @@ -37,5 +148,5 @@ export const getIframe = (url: string) => { iframe.frameBorder = '0' iframe.src = url div.appendChild(iframe) - return shadow + return { shadow, iframe, iframeDiv: div } } diff --git a/xterio-auth/src/utils/timer.ts b/xterio-auth/src/utils/timer.ts new file mode 100644 index 0000000..a6d952d --- /dev/null +++ b/xterio-auth/src/utils/timer.ts @@ -0,0 +1,47 @@ +import { XLog } from './logger' + +class Singleton { + private static instances: { [key: string]: any } = {} + constructor() { + // + } + static getInstance(this: new () => T): T { + const className = this.name + if (!Singleton.instances[className]) { + Singleton.instances[className] = new this() + } + return Singleton.instances[className] + } +} + +export class XTimeOut extends Singleton { + private _timer?: NodeJS.Timeout + addTimeout(callback: () => void, timeout: number) { + this.removeTimeout() + XLog.debug('the timer init') + this._timer = setTimeout(() => { + callback() + }, timeout) + } + removeTimeout() { + if (this._timer) { + XLog.debug('the timer clear') + clearTimeout(this._timer) + } + } +} +// XTimeOut.getInstance().addTimeout(() => { }, 2000) + +export class LoadingState extends Singleton { + private isLoading: boolean = false + + async execute(action: () => void | Promise) { + if (this.isLoading) return + this.isLoading = true + try { + await action() + } finally { + this.isLoading = false + } + } +} diff --git a/xterio-auth/vite.config.mts b/xterio-auth/vite.config.mts index be42f41..92b0b79 100644 --- a/xterio-auth/vite.config.mts +++ b/xterio-auth/vite.config.mts @@ -27,6 +27,15 @@ export default defineConfig(({ command, mode }) => { const minify = mode === 'production' return { + css: { + preprocessorOptions: { + //pro: Deprecation Warning: The legacy JS API is deprecated and will be removed in Dart Sass 2.0.0 + //fix: https://stackoverflow.com/questions/68147471/how-to-set-sassoptions-in-vite/78997875#78997875 + scss: { + api: 'modern-compiler' + } + } + }, //插件配置 plugins: [ tsconfigPaths(), diff --git a/xterio-wallet/README.md b/xterio-wallet/README.md index c288301..b2ba6f6 100644 --- a/xterio-wallet/README.md +++ b/xterio-wallet/README.md @@ -65,10 +65,6 @@ import { LoginType } from '@xterio-sdk/auth' function App() { const { - userinfo, - isLogin, - login, - logout, aaAddress, isConnect, disconnectWallet, @@ -105,17 +101,35 @@ function App() { await sendUserOperation?.(tx) } + const [userinfo, setUserInfo] = useState({}) + useEffect(() => { + console.log('[xtest] ---- add listener') + const unsubscribe_Info = XterEventEmiter.subscribe((res: IUserInfo) => { + setUserInfo(res) + }, XTERIO_EVENTS.ACCOUNT) + + const unsubscribe_logout = XterEventEmiter.subscribe(() => { + setUserInfo({}) + }, XTERIO_EVENTS.LOGOUT) + return () => { + console.log('[xtest] ---- remove listener') + unsubscribe_Info?.() + unsubscribe_logout?.() + } + }, []) + return ( <>

Xterio SDK

xterio auth
-

isLogin: {isLogin ? 'true' : 'false'}

+

isLogin: {XterioAuth.isLogin ? 'true' : 'false'}

userinfo: {userinfo ? JSON.stringify(userinfo) : ''}

- - - - + + + + +
xterio wallet
@@ -194,14 +208,14 @@ aa wallet connection status #### `state` transaction status -#### `sendTransaction({gasLimit,txValue}, ...args)` +#### `sendTransaction(...args:any[], tx?:Transaction)` send a transaction ```ts -await sendTransaction?.({ gasLimit: '', txValue:'' }, toAddr, amount) +await sendTransaction?.(toAddr, amount, {value:'', gasLimit:''}) ``` -#### `sendUserOperation(transaction|transaction[])` +#### `sendUserOperation(tx: Transaction|Transaction[])` send a transaction ```ts diff --git a/xterio-wallet/RELEASE.md b/xterio-wallet/RELEASE.md new file mode 100644 index 0000000..e69de29 diff --git a/xterio-wallet/src/App.tsx b/xterio-wallet/src/App.tsx index 304babc..0989174 100644 --- a/xterio-wallet/src/App.tsx +++ b/xterio-wallet/src/App.tsx @@ -1,9 +1,9 @@ import './App.css' import { useXterioTransaction, useXterioWalletContext } from './index' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { Contract, ContractInterface, ethers } from 'ethers' import { ERC20_ABI } from './common/abi' -import { LoginType } from '@xterio-sdk/auth' +import { IUserInfo, LoginType, XterEventEmiter, XTERIO_EVENTS, XterioAuth } from '@xterio-sdk/auth' /** * 区分区块链网络的名称,平台前后端统一定义的枚举类型 @@ -69,19 +69,8 @@ export const getContract = (network: NETWORK_NAME, contractAddress: string, abi: } function App() { - const { - userinfo, - isLogin, - login, - logout, - aaAddress, - isConnect, - disconnectWallet, - openWallet, - obtainWallet, - connectWallet, - signMessage - } = useXterioWalletContext() + const { aaAddress, isConnect, disconnectWallet, openWallet, obtainWallet, connectWallet, signMessage } = + useXterioWalletContext() const contractAddress = '0x12065F0d03cd1Bd280565069164F9E803c2DA988' const abi = ERC20_ABI @@ -95,9 +84,10 @@ function App() { const toAddr = '0xF4Ae736B14a7B5FDb803172B242074D6DFe655bb' const amount = '0x0de0b6b3a7640000' try { - await sendTransaction?.({ gasLimit: '0x90de' }, toAddr, amount) - } catch (err) { - console.log('ddd', err) + await sendTransaction?.(toAddr, amount, { gasLimit: '0x90de' }) + // await sendTransaction?.({ gasLimit: '0x90de' }, toAddr, amount) + } catch (err: any) { + console.log('ddd', err, err?.message) } } @@ -120,17 +110,47 @@ function App() { } } + const [userinfo, setUserInfo] = useState({}) + useEffect(() => { + console.log('[xtest] ---- add listener') + const unsubscribe_Info = XterEventEmiter.subscribe((res: IUserInfo) => { + setUserInfo(res) + }, XTERIO_EVENTS.ACCOUNT) + + const unsubscribe_logout = XterEventEmiter.subscribe(() => { + setUserInfo({}) + }, XTERIO_EVENTS.LOGOUT) + return () => { + console.log('[xtest] ---- remove listener') + unsubscribe_Info?.() + unsubscribe_logout?.() + } + }, []) + + useEffect(() => { + const status = state.status + console.log('status=', status) + if (status === 'Mining' || status === 'PendingSignature') { + console.log('trade ing') + } else if (status === 'Success') { + console.log('trade success') + } else if (status === 'Exception' || status === 'Fail') { + console.log('trade failed') + } + }, [state.status]) + return ( <>

Xterio SDK

xterio auth sdk
-

是否登录: {isLogin ? 'true' : 'false'}

+

是否登录: {XterioAuth.isLogin ? 'true' : 'false'}

用户信息: {userinfo ? JSON.stringify(userinfo) : ''}

- - - - + + + + +
xterio wallet sdk
diff --git a/xterio-wallet/src/contexts/index.tsx b/xterio-wallet/src/contexts/index.tsx index d9e3fe2..af2509b 100644 --- a/xterio-wallet/src/contexts/index.tsx +++ b/xterio-wallet/src/contexts/index.tsx @@ -1,12 +1,5 @@ import { createContext, PropsWithChildren, useCallback, useContext, useEffect, useRef, useState } from 'react' -import { - IUserInfo, - LoginType, - XterEventEmiter, - XTERIO_EVENTS, - XterioAuth, - XterioAuthTokensManager -} from '@xterio-sdk/auth' +import { IUserInfo, XterEventEmiter, XTERIO_EVENTS, XterioAuth, XterioAuthTokensManager } from '@xterio-sdk/auth' import { AuthCoreContextProvider, getAuthCoreModalOptions, usePnWallet } from './pnWallet' import { PnWalletModal } from 'src/templates/PnWalletModal' import { createRoot } from 'react-dom/client' @@ -25,10 +18,6 @@ const initState = { obtainWallet: () => {} } interface IWalletContextState extends Pick { - userinfo: IUserInfo | undefined - isLogin: boolean - login(mode?: LoginType): Promise - logout(): Promise aaAddress: string isConnect: boolean openWallet(): void @@ -54,8 +43,6 @@ const WalletContextProvider: React.FC() const [aaAddress, setAaAddress] = useState('') - const [userinfo, setUserInfo] = useState(XterioAuth.userinfo) - const [isLogin, setIsLogin] = useState(XterioAuth.isLogin) const { getWalletIFrame, @@ -79,7 +66,7 @@ const WalletContextProvider: React.FC { - if (!isLogin) { + if (!XterioAuth.isLogin) { XLog.info('please login first') return } @@ -120,13 +107,13 @@ const WalletContextProvider: React.FC { XLog.debug('connect wallet') - if (isPnLoginedRef.current) { - XLog.info('connected') + if (!XterioAuth.isLogin) { + XLog.info('please login first') return } await connectPnEoAAndAA(XterioAuthTokensManager.idToken, chainId) @@ -170,35 +157,37 @@ const WalletContextProvider: React.FC { - await XterioAuth.login(mode) - }, []) - - const logout = useCallback(async () => { - await disconnectWallet() - await XterioAuth.logout() - setUserInfo(undefined) - setIsLogin(false) - setAaAddress('') - }, [disconnectWallet]) - const initLogic = useCallback( async (info?: IUserInfo) => { const _addr = info?.wallet?.find((i) => i.source === 2)?.address || '' - const _islogin = !!info?.uuid - setUserInfo(info) - setIsLogin(_islogin) setAaAddress(_addr) + const _uuid = info?.uuid + const pn_jwt_id = _p?.jwt_id - if (_islogin && _addr && !isPnLoginedRef.current) { - XLog.debug('init logic, reconnect wallet') - await connectWallet() + //1.当前用户无aa地址,上次登录用户有地址且pn已连接,断开连接 + //2.当前用户有aa地址 + //2.1 pn未连接,去连 + //2.2 pn已连接,与上次登录用户不一致,断开重连 + if (XterioAuth.isLogin && _addr) { + XLog.debug('init logic', isPnLoginedRef.current, _uuid, pn_jwt_id) + if (!isPnLoginedRef.current) { + XLog.debug('init logic, reconnect wallet') + await connectWallet() + } else if (_uuid && pn_jwt_id && !pn_jwt_id.endsWith(_uuid)) { + XLog.debug('init logic, aa address not equal, disconnect and reconnect') + await disconnectWallet() + await connectWallet() + } + } else if (XterioAuth.isLogin && !_addr && isPnLoginedRef.current) { + XLog.debug('init logic', isPnLoginedRef.current, 'aa address is null only disconnect') + await disconnectWallet() } }, - [connectWallet] + [_p?.jwt_id, connectWallet, disconnectWallet] ) useEffect(() => { + //init if (mounted) return setMounted(true) setLogLevel(rest?.logLevel || 1) @@ -211,17 +200,20 @@ const WalletContextProvider: React.FC { //request token expired, clear state data - XLog.info('emiter req expired') - setUserInfo(undefined) - setIsLogin(false) + XLog.info('emiter logout') setAaAddress('') disconnectWallet() - }, XTERIO_EVENTS.Expired) + }, XTERIO_EVENTS.LOGOUT) + return () => { if (mounted) { - unsubscribe?.() + XLog.debug('remove listens') + unsubscribe_logout?.() } } }, [disconnectWallet, enableAuthInit, env, initLogic, mounted, rest]) @@ -229,10 +221,6 @@ const WalletContextProvider: React.FC> { - sendTransaction( - { gasLimit, txValue }: Partial>, - ...args: Params - ): Promise + // sendTransaction( + // { gasLimit, txValue }: Partial>, + // ...args: Params + // ): Promise + sendTransaction(...args: [...Params, Transaction?]): Promise sendUserOperation(tx: Transaction | Transaction[]): Promise state: TransactionStatus resetState(): void @@ -119,16 +119,16 @@ export const useXterioTransaction = ) => { + async (...args: [...Params, Transaction?]) => { if (!contract || !functionName) { throw new Error(`contract null or undefined`) } const numberOfArgs = contract.interface.getFunction(functionName).inputs.length - const hasOpts = args.length > numberOfArgs if (args.length !== numberOfArgs && args.length !== numberOfArgs + 1) { throw new Error(`Invalid number of arguments for function "${functionName}".`) } - const opts = hasOpts ? args[args.length - 1] : undefined + const hasOpts = args.length > numberOfArgs + const opts = hasOpts ? (args[args.length - 1] as Transaction) : undefined const modifiedArgs = hasOpts ? args.slice(0, args.length - 1) : args if (!pnAA) { @@ -143,8 +143,9 @@ export const useXterioTransaction = >, - ...args: Params - ): Promise => { - // disable feeData - const txArgs = [...args, { gasLimit, value: txValue }] as Params - return send(...txArgs) - }, - [send] - ) + // const customSend = useCallback( + // async ( + // { txValue }: Partial>, + // ...args: Params + // ): Promise => { + // // disable feeData + // const txArgs = [...args, { value: txValue }] as Params + // return send(...txArgs) + // }, + // [send] + // ) const sendUserOperation = useCallback( async (tx: Transaction | Transaction[]) => { @@ -224,5 +225,5 @@ export const useXterioTransaction =