From 0710c044ae2b2db08c7d5fc2d6a42fe6bf71df64 Mon Sep 17 00:00:00 2001 From: Viterbo Date: Tue, 12 Dec 2023 21:19:32 -0300 Subject: [PATCH] add loggin with google + metakeep --- .bash_history | 18 + env.js | 2 + src/antelope/stores/account.ts | 2 + .../wallets/authenticators/MetaKeepAuth.ts | 6 + src/antelope/wallets/ual/MetakeepUAL.ts | 427 ++++++++++++++++++ src/assets/sicial-icons.zip | Bin 0 -> 1438 bytes src/boot/antelope.ts | 6 +- src/boot/ual.js | 5 + src/pages/home/EVMLoginButtons.vue | 170 +------ src/pages/home/GoogleOneTap.ts | 182 ++++++++ src/pages/home/HomePage.vue | 5 +- src/pages/home/NativeLoginButton.vue | 305 +++++++++++-- src/pages/native/BalanceInfo.vue | 3 +- src/store/account/actions.js | 1 + 14 files changed, 940 insertions(+), 192 deletions(-) create mode 100644 .bash_history create mode 100644 src/antelope/wallets/ual/MetakeepUAL.ts create mode 100644 src/assets/sicial-icons.zip create mode 100644 src/pages/home/GoogleOneTap.ts diff --git a/.bash_history b/.bash_history new file mode 100644 index 000000000..430f47fad --- /dev/null +++ b/.bash_history @@ -0,0 +1,18 @@ +alias telosmain='cleos --url https://telos.caleos.io ' +alias telostest='cleos --url https://testnet.telos.caleos.io ' +alias ll='ls -las' +telosmain get info +telostest get info +cleos wallet import --private-key 5HsSMqDdf1gdniM2eiyZ8VDW8cjh9r9VfUWygKPKF2tVU6cwgvf +cleos wallet import --private-key 5JGFskbuaf9fEMnLESz4CPWKBJigiVX9VXcoh7b67WK4VA1e4u5 +telostest system newaccount --stake-net "0.5000 TLOS" --stake-cpu "0.5000 TLOS" --buy-ram-kbytes 3 gqydoobuhege vitermetkeep EOS8FyAaCkvzYegJNikJNUjU5x5PPcSHsAW6t6QdnTJg1Js3VJWTE EOS8FyAaCkvzYegJNikJNUjU5x5PPcSHsAW6t6QdnTJg1Js3VJWTE -p gqydoobuhege@active +telostest get info +telostest get info +exit +alias ll='ls -las' +alias telosmain='cleos --url https://telos.caleos.io ' +alias telostest='cleos --url https://testnet.telos.caleos.io ' +telostest get table --help +telostest get table eosio eosio userres +telostest get table eosio viterbo4test userres +exit diff --git a/env.js b/env.js index 8fd798203..919870c98 100644 --- a/env.js +++ b/env.js @@ -24,6 +24,7 @@ const TESTNET = { CHAIN_NAME: 'telos-testnet', OREID_APP_ID: 't_75a4d9233ec441d18c4221e92b379197', OREID_APP_ID_NATIVE: 't_a61e9926d5204387a9ac113dfce7cbc5', + METAKEEP_APP_ID_NATIVE: 'ad5e05fb-280a-41ae-b186-5a2654567b92', }; const MAINNET = { @@ -39,6 +40,7 @@ const MAINNET = { CHAIN_NAME: 'telos', OREID_APP_ID: 'p_e5b81fcc20a04339993b0cc80df7e3fd', OREID_APP_ID_NATIVE: 'p_751f87258d5b40998b55c626d612fd4e', + METAKEEP_APP_ID_NATIVE: 'ad5e05fb-280a-41ae-b186-5a2654567b92', }; const env = process.env.NETWORK === 'mainnet' ? MAINNET : TESTNET; diff --git a/src/antelope/stores/account.ts b/src/antelope/stores/account.ts index 1ef84f283..27453fdee 100644 --- a/src/antelope/stores/account.ts +++ b/src/antelope/stores/account.ts @@ -145,6 +145,8 @@ export const useAccountStore = defineStore(store_name, { localStorage.setItem('isNative', 'true'); localStorage.setItem('autoLogin', authenticator.getName()); + console.log('---------------------------------'); + success = true; this.fetchAccountDataFor(CURRENT_CONTEXT, nativeAccount); getAntelope().events.onLoggedIn.next(nativeAccount); diff --git a/src/antelope/wallets/authenticators/MetaKeepAuth.ts b/src/antelope/wallets/authenticators/MetaKeepAuth.ts index e1c207472..9eb75e88f 100644 --- a/src/antelope/wallets/authenticators/MetaKeepAuth.ts +++ b/src/antelope/wallets/authenticators/MetaKeepAuth.ts @@ -79,11 +79,17 @@ export class MetaKeepAuth extends InjectedProviderAuth { useFeedbackStore().unsetLoading(`${this.getName()}.login`); + localStorage.setItem('metakeep', JSON.stringify({ + email: this.accountEmail, + account: this.accountAddress, + })); + return this.accountAddress as addressString; } async logout(): Promise { this.trace('logout'); + localStorage.removeItem('metakeep'); return Promise.resolve(); } diff --git a/src/antelope/wallets/ual/MetakeepUAL.ts b/src/antelope/wallets/ual/MetakeepUAL.ts new file mode 100644 index 000000000..46e42cd6d --- /dev/null +++ b/src/antelope/wallets/ual/MetakeepUAL.ts @@ -0,0 +1,427 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { + Authenticator, + Chain, + UALError, + UALErrorType, + User, +} from 'universal-authenticator-library'; +import { JsonRpc } from 'eosjs'; +import { SignTransactionResponse } from 'universal-authenticator-library/dist/interfaces'; +import { MetaKeep } from 'metakeep'; +import axios from 'axios'; +import { APIClient, Serializer } from '@greymass/eosio'; + +const Logo = 'data:image/svg+xml,%3C%3Fxml version=\'1.0\' %3F%3E%3Csvg height=\'24\' version=\'1.1\' width=\'24\' xmlns=\'http://www.w3.org/2000/svg\' xmlns:cc=\'http://creativecommons.org/ns%23\' xmlns:dc=\'http://purl.org/dc/elements/1.1/\' xmlns:rdf=\'http://www.w3.org/1999/02/22-rdf-syntax-ns%23\'%3E%3Cg transform=\'translate(0 -1028.4)\'%3E%3Cpath d=\'m3 1030.4c-1.1046 0-2 0.9-2 2v7 2 7c0 1.1 0.8954 2 2 2h9 9c1.105 0 2-0.9 2-2v-7-2-7c0-1.1-0.895-2-2-2h-9-9z\' fill=\'%232c3e50\'/%3E%3Cpath d=\'m3 1049.4c-1.1046 0-2-0.9-2-2v-7-2-3h22v3 2 7c0 1.1-0.895 2-2 2h-9-9z\' fill=\'%2334495e\'/%3E%3Cpath d=\'m4 1032.9v1.1l2 2.4-2 2.3v1.1l3-3.4-3-3.5z\' fill=\'%23ecf0f1\'/%3E%3Cpath d=\'m3 2c-1.1046 0-2 0.8954-2 2v7 2 3h22v-3-2-7c0-1.1046-0.895-2-2-2h-9-9z\' fill=\'%2334495e\' transform=\'translate(0 1028.4)\'/%3E%3Cpath d=\'m4 5.125v1.125l3 1.75-3 1.75v1.125l5-2.875-5-2.875zm5 4.875v1h5v-1h-5z\' fill=\'%23ecf0f1\' transform=\'translate(0 1028.4)\'/%3E%3C/g%3E%3C/svg%3E'; + +export interface MetakeepOptions { + appId: string; + appName: string; + rpc?: JsonRpc; +} +let metakeep: MetaKeep | null = null; +// credentials +interface MetakeepData { + [email:string]: { + [chainId:string]: { + accounts: string[]; + wallet: { + eosAddress: string; + solAddress: string; + ethAddress: string; + } + } + } +} + + +export class MetakeepAuthenticator extends Authenticator { + private chainId: string; + private rpc: JsonRpc; + private accountEmail: string; + private cache: MetakeepData = {}; + private appId: string; + + constructor(chains: Chain[], options: MetakeepOptions) { + super(chains, options); + this.chainId = chains[0].chainId; + const [chain] = chains; + const [rpc] = chain.rpcEndpoints; + + if (options && options.rpc) { + this.rpc = options.rpc; + } else { + this.rpc = new JsonRpc(`${rpc.protocol}://${rpc.host}:${rpc.port}`); + } + if (!options?.appId) { + throw new Error('MetakeepAuthenticator: Missing appId'); + } + this.appId = options.appId; + this.chains = chains; + this.accountEmail = ''; + try { + this.accountEmail = window.localStorage.getItem('metakeep.logged') ?? ''; + } catch (error) { + console.error('error', error); + } + + try { + this.cache = JSON.parse(window.localStorage.getItem('metakeep.data') || '{}'); + } catch (error) { + console.error('error', error); + } + } + + saveCache() { + try { + window.localStorage.setItem('metakeep.data', JSON.stringify(this.cache)); + } catch (error) { + console.error('error', error); + } + } + + async init() { + //this.users = await this.login(); + } + + setEmail(email: string): void { + this.accountEmail = email; + window.localStorage.setItem('metakeep.logged', email); + } + + /** + * Resets the authenticator to its initial, default state then calls init method + */ + reset() { + this.init(); + } + + /** + * Returns true if the authenticator has errored while initializing. + */ + isErrored() { + return false; + } + + getName() { + return 'metakeep_native'; + } + + /** + * Returns a URL where the user can download and install the underlying authenticator + * if it is not found by the UAL Authenticator. + */ + getOnboardingLink() { + return 'https://developers.eos.io/manuals/eos/latest/cleos/index'; + } + + /** + * Returns error (if available) if the authenticator has errored while initializing. + */ + getError(): UALError | null { + return null; + } + + /** + * Returns true if the authenticator is loading while initializing its internal state. + */ + isLoading() { + return false; + } + + /** + * Returns the style of the Button that will be rendered. + */ + getStyle() { + return { + // An icon displayed to app users when selecting their authentication method + icon: Logo, + // Name displayed to app users + text: 'metakeep', + // Background color displayed to app users who select your authenticator + background: '#030238', + // Color of text used on top the `backgound` property above + textColor: '#FFFFFF', + }; + } + + /** + * Returns whether or not the button should render based on the operating environment and other factors. + * ie. If your Authenticator App does not support mobile, it returns false when running in a mobile browser. + */ + shouldRender() { + return false; + } + + /** + * Returns whether or not the dapp should attempt to auto login with the Authenticator app. + * Auto login will only occur when there is only one Authenticator that returns shouldRender() true and + * shouldAutoLogin() true. + */ + shouldAutoLogin() { + return true; + } + + /** + * Returns whether or not the button should show an account name input field. + * This is for Authenticators that do not have a concept of account names. + */ + async shouldRequestAccountName() { + return false; + } + + + async createAccount(publicKey: string): Promise { + return axios.post(`${this.rpc.endpoint}/v1/accounts/random`, { + ownerKey: publicKey, + activeKey: publicKey, + }).then(response => response.data.accountName); + } + + resolveAccountName() { + console.log('resolveAccountName() '); + return new Promise(async (resolve, reject) => { + let accountName = ''; + if (!metakeep) { + return reject(new Error('metakeep is not initialized')); + } + if (this.accountEmail === '') { + return reject(new Error('No account email')); + } + // we check if we have the account name in the cache + console.log('resolveAccountName() ', this.cache); + const data = this.cache[this.accountEmail]; + if (data) { + accountName = data[this.chainId]?.accounts[0]; + if (accountName) { + console.log('resolveAccountName() from cache -->', accountName); + resolve(accountName); + return; + } + } + + // if not, we fetch all the accounts for the email + console.log('resolveAccountName() getting credentials...'); + const credentials = await metakeep.getWallet(); + const publicKey = credentials.wallet.eosAddress; + + this.cache[this.accountEmail] = { + [this.chainId]: { + accounts: [], + wallet: credentials.wallet, + }, + }; + + console.log('resolveAccountName() ', this.cache); + + try { + // we try to get the account name from the public key + const response = await axios.post(`${this.rpc.endpoint}/v1/history/get_key_accounts`, { + public_key: publicKey, + }); + console.log('resolveAccountName() get_key_accounts: ', response); + const accountExists = response?.data?.account_names.length>0; + if (accountExists) { + accountName = response.data.account_names[0]; + } else { + accountName = await this.createAccount(publicKey); + } + this.cache[this.accountEmail][this.chainId].accounts.push(accountName); + this.saveCache(); + return resolve(accountName); + } catch (error) { + console.error('error', error); + throw new Error('Error getting account name'); + } + }); + } + + /** + * Login using the Authenticator App. This can return one or more users depending on multiple chain support. + * + * @param accountName The account name of the user for Authenticators that do not store accounts (optional) + */ + login: () => Promise<[User]> = async () => { + console.error('login'); + if (this.accountEmail === '') { + console.error('No account email'); + throw new Error('No account email'); + } + + console.log('Tenemos mail: ', this.accountEmail); + + metakeep = new MetaKeep({ + // App id to configure UI + appId: this.appId, + // Signed in user's email address + user: { + email: this.accountEmail, + }, + }); + + const accountName = await this.resolveAccountName(); + const publicKey = this.cache[this.accountEmail][this.chainId].wallet.eosAddress; + + try { + const permission = 'active'; + return [ + new MetakeepUser({ + accountName, + permission, + publicKey, + chainId: this.chainId, + rpc: this.rpc, + }), + ]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + throw new UALError(err.messsage, UALErrorType.Login, err, 'MetakeepAuthenticator'); + } + }; + + /** + * Logs the user out of the dapp. This will be strongly dependent on each + * Authenticator app's patterns. + */ + logout = async (): Promise => { + window.localStorage.removeItem('accountEmail'); + window.localStorage.removeItem('accountName'); + window.localStorage.removeItem('permission'); + window.localStorage.removeItem('publicKey'); + window.localStorage.removeItem('metakeep.logged'); + return; + }; + + /** + * Returns true if user confirmation is required for `getKeys` + */ + requiresGetKeyConfirmation() { + return false; + } +} + +// ------------------------------------------------------ + + +class MetakeepUser extends User { + private keys: string[]; + private accountName: string; + private permission: string; + private chainId: string; + rpc: JsonRpc; + constructor({ + accountName, + permission, + publicKey, + chainId, + rpc, + }: { + accountName: string, + permission: string, + publicKey: string, + chainId: string, + rpc: JsonRpc, + }) { + super(); + this.keys = [publicKey]; + this.accountName = accountName; + this.permission = permission; + this.chainId = chainId; + this.rpc = rpc; + } + + protected eosioCore: APIClient = new APIClient({ url: 'https://testnet.telos.net' }); + + + async serializeActionData(account: string, name: string, data: unknown): Promise { + const { abi } = await this.eosioCore.v1.chain.get_abi(account); + if (!abi) { + throw new Error(`No ABI for ${account}`); + } + + const { hexString } = Serializer.encode({ object: data, abi, type: name }); + return hexString; + } + + + /** + * @param transaction The transaction to be signed (a object that matches the RpcAPI structure). + */ + signTransaction = async (transaction: any): Promise => { + console.log('transaction', transaction); + if (!metakeep) { + throw new Error('metakeep is not initialized'); + } + + const info = await this.eosioCore.v1.chain.get_info(); + const ref_block_num = info.last_irreversible_block_num.toNumber(); + const block = await this.eosioCore.v1.chain.get_block(info.last_irreversible_block_num); + const ref_block_prefix = block.ref_block_prefix.toNumber(); + const action = transaction.actions[0]; + + const serializedData = await this.serializeActionData(action.account, action.name, action.data); + action.data = serializedData; + + const expiration = new Date(Date.now() + 120000).toISOString().split('.')[0]; + + const complete_transaction = { + 'transactionObject': { + 'rawTransaction': { + 'expiration': expiration, + 'ref_block_num': ref_block_num, + 'ref_block_prefix': ref_block_prefix, + 'max_net_usage_words': 0, + 'max_cpu_usage_ms': 0, + 'delay_sec': 0, + 'context_free_actions': [], + 'actions': [action], + 'transaction_extensions': [], + }, + 'extraSigningData': { + // If chainId is part of the signature generation, + // send it inside extraSigningData field. + 'chainId': '1eaa0824707c8c16bd25145493bf062aecddfeb56c736f6ba6397f3195f33c9f', // TESTNET + }, + }, + 'reason': 'test', + }; + + console.log('await metakeep.signTransactio()...', complete_transaction); + const response = await metakeep.signTransaction(complete_transaction, 'TESTING_REASONS'); + + + console.log('response', response); + + return this.returnEosjsTransaction(false, {}); + }; + + /** + * @param publicKey The public key to use for signing. + * @param data The data to be signed. + * @param helpText Help text to explain the need for arbitrary data to be signed. + * + * @returns The signature + */ + signArbitrary = async (): Promise => { + throw new Error('cleos does not support signing arbitrary data'); + }; + + /** + * @param challenge Challenge text sent to the authenticator. + * + * @returns Whether the user owns the private keys corresponding with provided public keys. + */ + async verifyKeyOwnership() { + return true; + } + + getAccountName = async (): Promise => this.accountName; + + getAccountPermission = async (): Promise => this.permission; + + getChainId = async (): Promise => this.chainId; + + getKeys = async (): Promise => this.keys; +} + + diff --git a/src/assets/sicial-icons.zip b/src/assets/sicial-icons.zip new file mode 100644 index 0000000000000000000000000000000000000000..29d166cd8a9f22435b1d4940c561a933fb12421d GIT binary patch literal 1438 zcmWIWW@Zs#-~d9O;Jk1KC{Sl&U=U(ZV8~3)&(qaSOH58p%FoZ%D=te94dG>AZ!8T? zRhKGF1<|Dy+zgB?FPIq^z(j1IVcuZ_o;#np9QhY$dQCZ^bS^+&^43-7?M$V~%$Y(a zZgZYbFM6z|k>Fr6vD{Af`t#H0d=4MKasApt(^~U4zdml?$8+~f&7Nywyi(zI*>>%J z_ibx`ym0fgUt8_xWZ&+8{Bq^0WtmMU4}Yu<7gP$<$}wO4^=0jwv+lX^Hx(9?%_(qp z;19U^Rmo^l7|#pAm5!4eyi;XT@>!IoH8Qwxsbnwj6*#q1RUj|_NbbxGTaK3%#{I%a zT8zV$R^Rb46Py$6AguUl@6;(ry$&DwKT)eXyiU0Bt0K>&8+YDasXoQE=mQhe0+Hq2 zJ6ZJhx=c8bW*Gl0>gQ+0InTw_J_`AS&ATO1;G{KOF?qrY^;EY!7K^7bI=`muaa%f5 zN%gAQBAHW{rrF%JeZun6@NfIC+{h1C_>G}ETIjJboa!sZPYqU(-UDRwK;96aOw9hW#;_r!udQOv-GJ}_#?&&&W{_)y084C_h%Wqe| z)}C!E%#ggZ@At!?%@)4Tm#^~DOboL4H%IHF<$8}9vSq(NKNoozvDy6KMXRR=78u67 z67RlK{ra9RTVe;Z*4j1MOkD1TA6}?lZFCma;A32Cbcx~X+MO4_|C%&?#o64ogZHuvhx4_Xv_Kk{x|N#-&kZ; z`Tr2x-uch%?>(Moe@fbOoV7f5?Q?i>x438G<^d)4UVt3FVxWPzc>5)N16BijE5>> zYrS_UKKNbuA$z8Lvb^2&bZ-fR2W&#$Z2f}0pQXOzw5ebA^PbTDb= oH-;^^!780 z_83a_o_txbr*^H}I$cZSk~Yn!4aFt9f?v2CX1$T8F0!d5``i?^wq2W7_{<8e>CLp# z3ve|mj!BOcRP|QRHCbK2xw5Ha-yNl;OQ%!^solRFd#?1RV*bCM9#1@_;^%(y>T*^t zy|dLi&MQ}`(QNzrX>4mN{%+nRl)BG-nwdsU;hF;5TVa*2KVJLf)NrhM`QiC|^`Ow) zBU { if (window.location.pathname !== '/') { (await getRouter(app)).push({ path: '/' }); } + // we also need to clear Google One Tap Controller + googleCtrl.logout(); }, }); @@ -96,7 +99,8 @@ export default boot(({ app }) => { // constants -- ant.config.setIndexerHealthThresholdSeconds(10); - ant.config.setIndexerHealthCheckInterval(5000); + // ant.config.setIndexerHealthCheckInterval(5000); + ant.config.setIndexerHealthCheckInterval(500000); // FIXME: // Finally, we check if the url has the network parameter and if so, we connect to that network // Otherwise we just let the store decide which network to connect to diff --git a/src/boot/ual.js b/src/boot/ual.js index 7271a10c6..2c0afc0f6 100644 --- a/src/boot/ual.js +++ b/src/boot/ual.js @@ -6,6 +6,7 @@ import { CleosAuthenticator } from '@telosnetwork/ual-cleos'; import { WebPopup } from 'oreid-webpopup'; import { OreIdAuthenticator, AuthProvider } from 'ual-oreid'; import { Dialog, Notify, copyToClipboard } from 'quasar'; +import { MetakeepAuthenticator } from 'src/antelope/wallets/ual/MetakeepUAL'; export default boot(async ({ app, store }) => { @@ -154,6 +155,10 @@ export default boot(async ({ app, store }) => { plugins: { popup: WebPopup() }, }, AuthProvider.Google), + new MetakeepAuthenticator([chain], { + appName: process.env.APP_NAME, + appId: process.env.METAKEEP_APP_ID_NATIVE, + }), ]; const ual = new UAL([chain], 'ual', authenticators); diff --git a/src/pages/home/EVMLoginButtons.vue b/src/pages/home/EVMLoginButtons.vue index 56ebb4a78..fa24aa53f 100644 --- a/src/pages/home/EVMLoginButtons.vue +++ b/src/pages/home/EVMLoginButtons.vue @@ -6,34 +6,7 @@ import { MetaKeepAuth, OreIdAuth } from 'src/antelope/wallets'; import { Menu } from 'src/pages/home/MenuType'; import InlineSvg from 'vue-inline-svg'; import { isTodayBeforeTelosCloudDown } from 'src/App.vue'; -import { AntelopeError } from 'src/antelope/types'; - -import * as Buffer from 'buffer'; - -interface GoogleOneTap { - accounts: { - id: { - initialize: (config: { client_id: string, callback: (notification: GoogleNotification) => void }) => void; - prompt: (callback: (notification: GoogleNotification) => void) => void; - renderButton: (element: HTMLElement, config: { theme: string, size: string }) => void; - } - } -} -interface GoogleNotification { - getMomentType: () => string; - isDisplayed: () => boolean; - isNotDisplayed: () => boolean; - isSkippedMoment: () => boolean; - isDismissedMoment: () => boolean; - getNotDisplayedReason: () => string; - getSkippedReason: () => string; - getDismissedReason: () => string; - credential: string; -} - -let google: GoogleOneTap | null = null; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const _window = (window as any); +import { googleCtrl } from 'src/pages/home/GoogleOneTap'; export default defineComponent({ name: 'EVMLoginButtons', @@ -168,126 +141,21 @@ export default defineComponent({ emit('update:modelValue', Menu.CLOUD); }; - // --- Google One Tap (ini) ------------------------------- - const onGoogleOneTap = () => { - setAuthenticator('Metakeep', CURRENT_CONTEXT); - }; - - const onGoogleOneTapSuccess = (response: any) => { - console.log('response: ', response); - console.log('response.payload: ', response.payload); - console.log('response.payload.email: ', response.payload.email); - setMetaKeepAuthenticator(response.payload.email); - }; - - const onGoogleOneTapError = (error: any) => { - console.error('-------------------------------'); - console.error('google one tap error', error); - console.error('-------------------------------'); - }; - - const installGoogleOneTapScript = () => { - if (google) { - return; - } - console.log('installGoogleOneTapScript()'); - const script = document.createElement('script'); - script.src = 'https://accounts.google.com/gsi/client'; - script.async = true; - script.defer = true; - document.body.appendChild(script); - _window.onGoogleLibraryLoad = () => { - oneTapInit(); - }; - }; - - const oneTapInit = () => { - console.log('oneTapInit()'); - if(!google){ - if (_window.google) { - google = _window.google; - } else { - // FIXME: use i18n - throw new AntelopeError('Google One Tap library not loaded'); - } - } - if (google) { - console.log('----------------- google -->', google); - google.accounts.id.initialize({ - client_id: '639241197544-kcubenhmti6u7ef3uj360n2lcl5cmn8c.apps.googleusercontent.com', - callback: oneTapCallback, - }); - google.accounts.id.prompt((notification) => { - const momentType = notification.getMomentType(); - if(notification.isDisplayed()) { - setTimeout(()=>{ - handleOneTapMoment(momentType, 'displayed', 'displayed'); - }, 500); - } else if(notification.isNotDisplayed()){ - handleOneTapMoment(momentType, 'notdisplayed', notification.getNotDisplayedReason()); - } else if(notification.isSkippedMoment()) { - handleOneTapMoment(momentType, 'skipped', notification.getSkippedReason()); - } else if(notification.isDismissedMoment()) { - handleOneTapMoment(momentType, 'dismissed', notification.getDismissedReason()); - } - }); - } - }; - - const decodificarJWT = (token: string) => { - const parts = token.split('.'); - const header = parts[0]; - const payload = parts[1]; - - const dedodedHeader = Buffer.Buffer.from(header, 'base64').toString('utf8'); - const decodedPayload = Buffer.Buffer.from(payload, 'base64').toString('utf8'); - - return { - header: JSON.parse(dedodedHeader), - payload: JSON.parse(decodedPayload), - }; - }; - - const oneTapCallback = (response: GoogleNotification | null) => { - console.log('---------- oneTapCallback -------------->', response); - if (response) { - const credential = response.credential; - const decoded = decodificarJWT(credential); - onGoogleOneTapSuccess(decoded); - } else { - onGoogleOneTapError(response); - } - }; - - const handleOneTapMoment = (momentType: string, status: string, reason: string) => { - console.log('-- handleOneTapMoment -> ', momentType, status, reason); - }; - watch(showTelosCloudMenu, (newValue) => { - console.log('showTelosCloudMenu changed', newValue); if (newValue) { - setTimeout(() => { - showGoogleOneTap(); - }, 100); + googleCtrl.renderButton('google_btn'); } }); - - const showGoogleOneTap = () => { - const btn = document.getElementById('google_btn'); - if (google && btn) { - console.log('--- google.accounts.id.renderButton() ---'); - google.accounts.id.renderButton( - btn, { theme: 'outline', size: 'large' }, - ); - } - }; - - installGoogleOneTapScript(); - showGoogleOneTap(); - // --- Google One Tap (end) ------------------------------- - - console.log('EVMLoginButtons setup()'); + console.log('googleCtrl.onSuccessfulLogin.subscribe()'); + const googleSubscription = googleCtrl.onSuccessfulLogin.subscribe({ + next: (email) => { + console.log('googleCtrl.onSuccessfulLogin.next()', email); + if (email) { + setMetaKeepAuthenticator(email); + } + }, + }); return { isLoading, @@ -314,11 +182,13 @@ export default defineComponent({ showMainMenu, showTelosCloudMenu, setCloudMenu, - onGoogleOneTapSuccess, - onGoogleOneTapError, - onGoogleOneTap, + googleSubscription, }; }, + unmounted() { + console.log('EVM: this.googleSubscription.unsubscribe();'); // FIXME: remove this line + this.googleSubscription?.unsubscribe(); + }, }); @@ -452,10 +322,11 @@ export default defineComponent({ id="google_btn" data-client_id="639241197544-kcubenhmti6u7ef3uj360n2lcl5cmn8c.apps.googleusercontent.com" > + - - - + import { CURRENT_CONTEXT, useChainStore } from 'src/antelope'; import { defineComponent } from 'vue'; +import { QSpinnerFacebook } from 'quasar'; import { mapGetters, mapActions, mapMutations } from 'vuex'; import { OreIdAuthenticator } from 'ual-oreid'; +import { Menu } from '~/pages/home/MenuType'; +import { googleCtrl } from 'src/pages/home/GoogleOneTap'; const telosLogo = require('src/assets/logo--telos-cloud-wallet.svg'); export default defineComponent({ name: 'NativeLoginButton', + components: { + QSpinnerFacebook, + }, data() { return { showLogin: false, @@ -36,8 +42,15 @@ export default defineComponent({ NETtoBuy: 0, buyAmount: 1, // 1 TLOS resLow: false, + googleSubscription: null, }; }, + props: { + modelValue: { + type: String, + default: 'main', + }, + }, computed: { ...mapGetters('account', [ 'isAuthenticated', @@ -45,11 +58,36 @@ export default defineComponent({ 'loading', 'isAutoLoading', ]), + // menu navitgaion + showMainMenu() { + return this.modelValue === Menu.MAIN; + }, + showTelosCloudMenu() { + return this.modelValue === Menu.CLOUD; + }, }, mounted() { this.setDefaultNativeChain(); + + this.googleSubscription = googleCtrl.onSuccessfulLogin.subscribe({ + next: (email) => { + if (this.googleSubscription) { + if (email) { + this.loginWithMetaKeep(email); + } + } + }, + }); + }, + unmounted() { + this.googleSubscription.unsubscribe(); + this.googleSubscription = null; }, methods: { + // menu navitgaion + setCloudMenu() { + this.$emit('update:modelValue', Menu.CLOUD); + }, // antelope methods setDefaultNativeChain() { const network = process.env.CHAIN_NAME || 'telos'; @@ -69,6 +107,13 @@ export default defineComponent({ 'getAccountProfile', 'setLoadingWallet', ]), + async loginWithMetaKeep(email) { + console.log('ZERO: loginWithMetaKeep()', email); // FIXME: remove this line + const idx = this.$ual.authenticators.map(a => a.getName()).indexOf('metakeep_native'); + const auth = this.$ual.authenticators[idx]; + auth.setEmail(email); + this.onLogin(idx); + }, async loginAsJustViewer() { let idx = this.$ual.authenticators.map(a => a.getName()).indexOf('cleos'); this.onLogin(idx, true); @@ -223,64 +268,136 @@ export default defineComponent({ await this.checkResources(); } }, + showTelosCloudMenu(newValue) { + if (newValue) { + googleCtrl.renderButton('google_btn'); + } + }, }, });