Skip to content

Commit

Permalink
Merge pull request #758 from telosnetwork/754-telos-cloud-login--zero…
Browse files Browse the repository at this point in the history
…-account-selector

Telos Cloud Login | Zero account selector
  • Loading branch information
Viterbo authored Feb 9, 2024
2 parents c337d86 + b5d0ee3 commit fed9e7d
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 20 deletions.
6 changes: 3 additions & 3 deletions env.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ const sharedEnv = {
PROJECT_ID: '2392473d6d98499c7138cd2d705a791f',
METAKEEP_APP_ID_NATIVE: 'ad5e05fb-280a-41ae-b186-5a2654567b92',
METAKEEP_APP_ID_EVM: 'd190c88f-1bb5-4e16-bc48-96dbf33b77e0',
// GOOGLE_APP_ID: '56634824599-ff3iu788c32c3s7ec65cs4bieop9gpgv.apps.googleusercontent.com',
GOOGLE_APP_ID: '639241197544-kcubenhmti6u7ef3uj360n2lcl5cmn8c.apps.googleusercontent.com',
// FIXME: This is a auxillary key for the app, it should be replaced for the one commented
// GOOGLE_APP_ID: '56634824599-ff3iu788c32c3s7ec65cs4bieop9gpgv.apps.googleusercontent.com', // official App id
GOOGLE_APP_ID: '639241197544-kcubenhmti6u7ef3uj360n2lcl5cmn8c.apps.googleusercontent.com', // Viter's app id
};

const TESTNET = {
Expand All @@ -24,6 +23,7 @@ const TESTNET = {
NETWORK_CHAIN_ID:
'1eaa0824707c8c16bd25145493bf062aecddfeb56c736f6ba6397f3195f33c9f',
TELOS_API_ENDPOINT: 'https://api-dev.telos.net/v1',
// TELOS_API_ENDPOINT: 'http://localhost:9999/v1',
HYPERION_ENDPOINT: 'https://testnet.telos.net',
NETWORK_EXPLORER: 'https://explorer-test.telos.net',
CHAIN_NAME: 'telos-testnet',
Expand Down
3 changes: 3 additions & 0 deletions src/antelope/stores/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,12 @@ export const useAccountStore = defineStore(store_name, {
this.trace('loginZero', authenticator, network);
let success = false;
try {
this.trace('loginZero', 'authenticator.init()...');
await authenticator.init();
this.trace('loginZero', 'authenticator.login()...');
const ualUsers = await authenticator.login();
if (ualUsers?.length) {
this.trace('loginZero', 'authenticator.login() OK! ualUsers:', ualUsers);
const ualUser = await initFuelUserWrapper(ualUsers[0]);
const permission = (ualUser as unknown as { requestPermission: string })
.requestPermission ?? 'active';
Expand Down
47 changes: 43 additions & 4 deletions src/antelope/wallets/ual/MetakeepUAL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ export interface MetakeepData {
}
}

const metakeepDefaultAccountSelector: MetakeepAccountSelector = {
selectAccount: (accounts: string[]) => Promise.resolve(accounts[0]),
};


export interface MetakeepAccountSelector {
selectAccount: (accounts: string[]) => Promise<string>;
}

export class MetakeepAuthenticator extends Authenticator {
private chainId: string;
private rpc: JsonRpc;
Expand All @@ -50,6 +59,8 @@ export class MetakeepAuthenticator extends Authenticator {
private loading = false;
private userCredentials: UserCredentials = { email: '', jwt: '' };

private accountSelector: MetakeepAccountSelector = metakeepDefaultAccountSelector;

constructor(chains: Chain[], options: MetakeepUALOptions) {
super(chains, options);
this.chainId = chains[0].chainId;
Expand All @@ -73,6 +84,14 @@ export class MetakeepAuthenticator extends Authenticator {
};
}

resetAccountSelector() {
this.accountSelector = metakeepDefaultAccountSelector;
}

setAccountSelector(accountSelector: MetakeepAccountSelector) {
this.accountSelector = accountSelector;
}

saveCache() {
metakeepCache.saveCache();
}
Expand Down Expand Up @@ -189,14 +208,22 @@ export class MetakeepAuthenticator extends Authenticator {
// we check if we have the account name in the cache
const accountNames = metakeepCache.getAccountNames(this.userCredentials.email, this.chainId);
if (accountNames.length > 0) {
resolve(accountNames[0]);
return;
if (accountNames.length > 1) {
// if we have more than one account, we ask the user to select one using this callback
const selectedAccount = await this.accountSelector.selectAccount(accountNames);
this.resetAccountSelector();
metakeepCache.setSelectedAccountName(this.userCredentials.email, this.chainId, selectedAccount);
return resolve(selectedAccount);
} else {
return resolve(accountNames[0]);
}
}

// if not, we fetch all the accounts for the email
const credentials = await metakeep.getWallet();
const publicKey = credentials.wallet.eosAddress;


metakeepCache.addCredentials(this.userCredentials.email, credentials.wallet);

try {
Expand All @@ -205,13 +232,25 @@ export class MetakeepAuthenticator extends Authenticator {
public_key: publicKey,
});
const accountExists = response?.data?.account_names.length>0;
let names:string[] = [];

if (accountExists) {
accountName = response.data.account_names[0];
names = response.data.account_names;
names.forEach(name => metakeepCache.addAccountName(this.userCredentials.email, this.chainId, name));
if (names.length > 1) {
// if we have more than one account, we ask the user to select one using this callback
accountName = await this.accountSelector.selectAccount(names);
this.resetAccountSelector();
} else {
accountName = names[0];
}
metakeepCache.setSelectedAccountName(this.userCredentials.email, this.chainId, accountName);
} else {
accountName = await this.createAccount(publicKey);
metakeepCache.addAccountName(this.userCredentials.email, this.chainId, accountName);
names = [accountName];
}

metakeepCache.addAccountName(this.userCredentials.email, this.chainId, accountName);
this.saveCache();
return resolve(accountName);
} catch (error) {
Expand Down
10 changes: 10 additions & 0 deletions src/antelope/wallets/ual/utils/metakeep-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ class MetakeepCache {
}

// setters --------------
public setSelectedAccountName(email: string, chainId: string, accountName: string) {
this.assertCache(email, chainId);
const index = this.cache[email].chains[chainId].accounts.indexOf(accountName);
if (index !== -1) {
this.cache[email].chains[chainId].accounts.splice(index, 1);
}
this.cache[email].chains[chainId].accounts.unshift(accountName);
this.saveCache();
}

public addAccountName(email: string, chainId: string, accountName: string) {
this.assertCache(email, chainId);
if (!this.cache[email].chains[chainId].accounts.includes(accountName)) {
Expand Down
1 change: 1 addition & 0 deletions src/i18n/en-us/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default {
connect_with_wallet: 'Connect Your Wallet',
telos_cloud_wallet: 'Telos Cloud Wallet',
telos_cloud_login: 'Telos Cloud Login',
available_accounts: 'Available Accounts',
sign_with_google: 'Sign with Google',
sign_with_facebook: 'Sign with Facebook',
sign_with_x: 'Sign with X',
Expand Down
93 changes: 80 additions & 13 deletions src/pages/home/LoginButtons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,30 @@ export default defineComponent({
const idx = ualAuthenticators.map(a => a.getName()).indexOf(name);
return ualAuthenticators[idx];
};
const selectedZeroAccount = ref('');
const availableZeroAccounts = ref([] as string[]);
let whenAccountSelected = Promise.resolve(selectedZeroAccount.value);
const setSelectedName = ref<(name: string) => void>((name: string) => {});
const selectAccount = (accounts: string[]) => new Promise<string>(async (resolveAccountSelected) => {
if (accounts.length === 1) {
resolveAccountSelected(accounts[0]);
} else {
// we create a new Promise and save its resolve in a variable
whenAccountSelected = new Promise((resolve) => {
setSelectedName.value = resolve;
});
// enable account selection
availableZeroAccounts.value = [...accounts];
// wait for the promise to resolve
selectedZeroAccount.value = await whenAccountSelected;
// take the name of the selected account and pass it to the resolve of the original promise
resolveAccountSelected(selectedZeroAccount.value);
}
});
const setMetakeepZero = (credentials:GoogleCredentials) => {
const name = 'metakeep.ual';
const auth = getZeroAuthenticator(name) as MetakeepAuthenticator;
auth.setAccountSelector({ selectAccount });
auth.setUserCredentials(credentials);
const idx = ualAuthenticators.map(a => a.getName()).indexOf(name);
loginTelosZero(idx);
Expand Down Expand Up @@ -227,6 +248,9 @@ export default defineComponent({
loginTelosZero,
setCleosViewerZero,
redirectToNewAccountWebsite,
availableZeroAccounts,
selectedZeroAccount,
setSelectedName,
};
},
unmounted() {
Expand All @@ -239,7 +263,14 @@ export default defineComponent({
<div class="c-login-buttons">

<!-- Telos Cloud Button -->
<div class="c-login-buttons__option c-login-buttons__option--telos-cloud">
<div
class="c-login-buttons__option c-login-buttons__option--telos-cloud"
:class="{
'c-login-buttons__option': true,
'c-login-buttons__option--telos-cloud': true,
'c-login-buttons__option--telos-cloud--gradient': availableZeroAccounts.length > 0
}"
>
<div class="c-login-buttons__cloud-btn-container">
<div class="c-login-buttons__cloud-btn-line-title">
<img
Expand All @@ -250,8 +281,22 @@ export default defineComponent({
<span>{{ $t('home.telos_cloud_login') }}</span>
</div>

<div v-if="selectedZeroAccount !== ''" class="c-login-buttons__zero-accounts-title">
{{ selectedZeroAccount }}
</div>
<div v-if="availableZeroAccounts.length > 0 && selectedZeroAccount === ''" class="c-login-buttons__zero-accounts-title">
{{ $t('home.available_accounts') }}
</div>
<div v-if="availableZeroAccounts.length > 0 && selectedZeroAccount === ''" class="c-login-buttons__zero-accounts">
<div
v-for="account in availableZeroAccounts"
:key="account"
class="c-login-buttons__option c-login-buttons__option--zero-account"
@click="setSelectedName(account)"
> {{ account }} </div>
</div>
<!-- Google One Tap render button -->
<div v-if="showGoogleLoading" class="c-login-buttons__google-loading">
<div v-else-if="showGoogleLoading" class="c-login-buttons__google-loading">
<div class="c-login-buttons__loading"><QSpinnerFacebook /></div>
</div>
<div
Expand Down Expand Up @@ -425,17 +470,19 @@ export default defineComponent({

<style lang="scss">
.c-login-buttons {
$width: 255px;
$gap: 15px;
color: $white;
$self: &;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 14px;
gap: $gap;
&__loading{
width: 100%;
text-align: center;
color: $white;
}
&__header{
Expand Down Expand Up @@ -476,26 +523,35 @@ export default defineComponent({
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: 8px;
gap: $gap;
}
&__title {
@include text--header-4;
color: $white;
text-align: center;
}
&__sub-title {
color: $white;
&__zero-accounts-title {
padding: 5px;
@include text--small;
text-align: center;
}
&__option {
&__zero-accounts {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 8px;
}
&__option {
display: flex;
gap: $gap;
width: 224px;
width: $width;
height: 54px;
color: $white;
outline-color: $white;
outline-width: 1px;
outline-style: solid;
Expand All @@ -506,7 +562,6 @@ export default defineComponent({
cursor: pointer;
&:hover:not(&--disabled) {
color: $white;
outline-color: $white;
outline-width: 2px;
}
Expand All @@ -527,17 +582,29 @@ export default defineComponent({
outline-width: 0;
@include gradient_border();
}
&--gradient:hover {
outline-width: 0px !important;
@include gradient_border();
}
}
&--disabled {
color: #9289b1;
outline-color: #9289b1;
cursor: not-allowed;
}
&--zero-account {
height: 32px;
width: calc(100% - 16px);
display: flex;
justify-content: center;
align-items: center;
}
}
&__hr {
width: 224px;
width: $width;
}
}
</style>

0 comments on commit fed9e7d

Please sign in to comment.