Skip to content

Commit

Permalink
Add account-selection UI to ChooseAddress request
Browse files Browse the repository at this point in the history
It's user-friendlier for onboarding in the CPL dashboard, to select their account, and not their address. Also makes sense for other uses.

It's opt-in with the `ui: 2` request option.
  • Loading branch information
sisou committed Nov 23, 2023
1 parent af4fd07 commit 77ccfba
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 40 deletions.
1 change: 1 addition & 0 deletions client/PublicRequestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export interface ChooseAddressRequest extends BasicRequest {
disableLegacyAccounts?: boolean;
disableBip39Accounts?: boolean;
disableLedgerAccounts?: boolean;
ui?: number;
}

export interface ChooseAddressResult extends Address {
Expand Down
4 changes: 2 additions & 2 deletions demos/Demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class Demo {

document.querySelector('button#choose-address').addEventListener('click', async () => {
try {
const result = await demo.client.chooseAddress({ appName: 'Hub Demos' }, demo._defaultBehavior);
const result = await demo.client.chooseAddress({ appName: 'Hub Demos', ui: 2 }, demo._defaultBehavior);
console.log('Result', result);
document.querySelector('#result').textContent = `Address was chosen: ${result ? result.address : '-'}`;
} catch (e) {
Expand All @@ -173,7 +173,7 @@ class Demo {

document.querySelector('button#choose-address-and-btc').addEventListener('click', async () => {
try {
const result = await demo.client.chooseAddress({ appName: 'Hub Demos', returnBtcAddress: true }, demo._defaultBehavior);
const result = await demo.client.chooseAddress({ appName: 'Hub Demos', returnBtcAddress: true, ui: 2 }, demo._defaultBehavior);
console.log('Result', result);
document.querySelector('#result').textContent = `Address was chosen: ${result ? result.address : '-'}`;
} catch (e) {
Expand Down
3 changes: 3 additions & 0 deletions src/components/icons/LedgerIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template functional>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 27 46" width="27" height="46"><path d="M14.574 4.246a6.253 6.253 0 00-1.252.086 6.165 6.165 0 00-5.056 7.129l5.154 30.332a1.664 1.664 0 001.937 1.373l8.875-1.508a1.664 1.664 0 001.373-1.937L20.451 9.39a6.166 6.166 0 00-5.877-5.145zm.006 2.701a3.625 3.625 0 013.29 4.56 3.627 3.627 0 11-3.29-4.56zM6.816 14.873L.196 39.582a.754.754 0 00.53.924l10.485 2.808a.753.753 0 00.723-.191 2.156 2.156 0 01-.485-.996l-.937-5.518a.994.994 0 01-1.057.37l-4.039-1.083a.995.995 0 01-.605-.466.995.995 0 01-.102-.758l3.35-12.494-1.243-7.305z" opacity=".7" fill="currentColor"/></svg>
</template>
24 changes: 24 additions & 0 deletions src/components/icons/LoginFileIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template functional>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 46" fill="#fff">
<g opacity="0.4"><!-- Identicon circle -->
<path d="M15.75,8.89l-1-1.69a.36.36,0,0,0-.33-.2H12.54a.38.38,0,0,0-.34.2l-1,1.69a.39.39,0,0,0,0,.39l1,1.69a.39.39,0,0,0,.34.19h1.92a.37.37,0,0,0,.33-.19l1-1.69A.39.39,0,0,0,15.75,8.89Z"/>
<path d="M15.75,18.72l-1-1.69a.37.37,0,0,0-.33-.19H12.54a.39.39,0,0,0-.34.19l-1,1.69a.39.39,0,0,0,0,.39l1,1.69a.38.38,0,0,0,.34.2h1.92a.36.36,0,0,0,.33-.2l1-1.69A.39.39,0,0,0,15.75,18.72Z"/>
<path d="M11.56,11.35l-1-1.7a.39.39,0,0,0-.33-.19H8.35A.39.39,0,0,0,8,9.65l-1,1.7a.39.39,0,0,0,0,.39l1,1.69a.39.39,0,0,0,.34.19h1.92a.39.39,0,0,0,.33-.19l1-1.69A.39.39,0,0,0,11.56,11.35Z"/>
<path d="M11.56,16.26l-1-1.69a.39.39,0,0,0-.33-.19H8.35a.39.39,0,0,0-.34.19l-1,1.69a.39.39,0,0,0,0,.39l1,1.7a.39.39,0,0,0,.34.19h1.92a.39.39,0,0,0,.33-.19l1-1.7A.39.39,0,0,0,11.56,16.26Z"/>
<path d="M20,11.35l-1-1.7a.39.39,0,0,0-.34-.19H16.73a.39.39,0,0,0-.33.19l-1,1.7a.39.39,0,0,0,0,.39l1,1.69a.39.39,0,0,0,.33.19h1.92a.39.39,0,0,0,.34-.19l1-1.69A.39.39,0,0,0,20,11.35Z"/>
<path d="M20,16.26l-1-1.69a.39.39,0,0,0-.34-.19H16.73a.37.37,0,0,0-.33.19l-1,1.69a.39.39,0,0,0,0,.39l1,1.7a.37.37,0,0,0,.33.19h1.92a.39.39,0,0,0,.34-.19l1-1.7A.39.39,0,0,0,20,16.26Z"/>
</g>
<g opacity="0.5"><!-- QR code -->
<path d="M10.79,31.33H7.54A.54.54,0,0,1,7,30.79V27.54A.54.54,0,0,1,7.54,27h3.25a.54.54,0,0,1,.54.54v3.25A.54.54,0,0,1,10.79,31.33ZM8.22,28.08a.14.14,0,0,0-.14.14v1.89a.14.14,0,0,0,.14.14h1.89a.14.14,0,0,0,.14-.14V28.22a.14.14,0,0,0-.14-.14Z"/>
<path d="M7.54,35.67h3.25a.54.54,0,0,1,.54.54v3.25a.54.54,0,0,1-.54.54H7.54A.54.54,0,0,1,7,39.46V36.21A.54.54,0,0,1,7.54,35.67Zm2.57,3.25a.14.14,0,0,0,.14-.14V36.89a.14.14,0,0,0-.14-.14H8.22a.14.14,0,0,0-.14.14v1.89a.14.14,0,0,0,.14.14Z"/>
<path d="M16.21,27h3.25a.54.54,0,0,1,.54.54v3.25a.54.54,0,0,1-.54.54H16.21a.54.54,0,0,1-.54-.54V27.54A.54.54,0,0,1,16.21,27Zm2.57,3.25a.14.14,0,0,0,.14-.14V28.22a.14.14,0,0,0-.14-.14H16.89a.14.14,0,0,0-.14.14v1.89a.14.14,0,0,0,.14.14Z"/>
<path d="M12.42,29.3H13a.41.41,0,0,0,0-.81.14.14,0,0,1-.14-.14v-.81a.41.41,0,0,0-.81,0V28.9A.4.4,0,0,0,12.42,29.3Z"/>
<path d="M14,28a.13.13,0,0,1,.14.13v3a.41.41,0,1,0,.81,0V27.54a.4.4,0,0,0-.41-.4H14A.41.41,0,0,0,14,28Z"/>
<path d="M10.11,32.69a.41.41,0,0,0,.41.4h1.9a.4.4,0,0,0,.4-.4V30.52a.41.41,0,1,0-.81,0v1.63a.13.13,0,0,1-.13.13H10.52A.41.41,0,0,0,10.11,32.69Z"/><path d="M8.62,32.28a.4.4,0,0,0-.4.41V34a.14.14,0,0,1-.14.14H7.54a.41.41,0,0,0,0,.81h7a.41.41,0,0,0,.41-.41V33a.41.41,0,1,0-.81,0V34a.14.14,0,0,1-.14.14H9.17A.14.14,0,0,1,9,34V32.69A.41.41,0,0,0,8.62,32.28Z"/>
<path d="M14.45,36.21A.41.41,0,0,0,14,35.8H12.42a.41.41,0,0,0-.41.41v2.17a.41.41,0,0,0,.81,0V36.75a.14.14,0,0,1,.14-.14H14A.4.4,0,0,0,14.45,36.21Z"/><path d="M19.46,39.05H14.85a.13.13,0,0,1-.13-.13V37.83a.41.41,0,0,0-.81,0v1.63a.4.4,0,0,0,.4.4h5.15a.41.41,0,0,0,0-.81Z"/>
<path d="M17.56,38H15.94a.41.41,0,0,1-.41-.41V35.94a.41.41,0,0,1,.41-.41h1.62a.41.41,0,0,1,.41.41v1.62A.41.41,0,0,1,17.56,38Zm-1.08-1.63a.14.14,0,0,0-.14.14V37a.14.14,0,0,0,.14.14H17a.14.14,0,0,0,.14-.14v-.54a.14.14,0,0,0-.14-.14Z"/>
<path d="M19.19,33.91a.4.4,0,0,0-.41.4v3.52a.41.41,0,1,0,.81,0V34.31A.4.4,0,0,0,19.19,33.91Z"/>
<path d="M19.59,32.42a.4.4,0,0,0-.4-.41h-3a.41.41,0,0,0-.41.41V34a.41.41,0,1,0,.81,0V33a.14.14,0,0,1,.14-.14h2.44A.4.4,0,0,0,19.59,32.42Z"/>
</g>
</svg>
</template>
19 changes: 18 additions & 1 deletion src/i18n/en.po
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"

#: src/views/ChooseAddress.vue:14
msgid "{appName} is asking for an account to use."
msgstr ""

#: src/views/ChooseAddress.vue:7
msgid "{appName} is asking for an address to use."
msgstr ""
Expand Down Expand Up @@ -275,8 +279,13 @@ msgstr ""
msgid "Choose a new Address"
msgstr ""

#: src/views/ChooseAddress.vue:12
msgid "Choose an Account"
msgstr ""

#: src/views/CashlinkReceive.vue:64
#: src/views/ChooseAddress.vue:4
#: src/views/ChooseAddress.vue:18
#: src/views/ChooseAddress.vue:5
msgid "Choose an Address"
msgstr ""

Expand All @@ -302,6 +311,10 @@ msgstr ""
msgid "Choose Sender"
msgstr ""

#: src/views/ChooseAddress.vue:20
msgid "Choose which Nimiq address to use."
msgstr ""

#: src/views/CashlinkReceive.vue:297
msgid "Claim Cashlink"
msgstr ""
Expand Down Expand Up @@ -673,6 +686,10 @@ msgstr ""
msgid "Login File saved!"
msgstr ""

#: src/views/ChooseAddress.vue:67
msgid "Login to another account"
msgstr ""

#: src/views/CashlinkReceive.vue:91
msgid "Login to existing account"
msgstr ""
Expand Down
1 change: 1 addition & 0 deletions src/lib/RequestParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ export class RequestParser {
disableLegacyAccounts: !!chooseAddressRequest.disableLegacyAccounts,
disableBip39Accounts: !!chooseAddressRequest.disableBip39Accounts,
disableLedgerAccounts: !!chooseAddressRequest.disableLedgerAccounts,
ui: chooseAddressRequest.ui,
} as ParsedChooseAddressRequest;
case RequestType.SIGNUP:
case RequestType.LOGIN:
Expand Down
1 change: 1 addition & 0 deletions src/lib/RequestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface ParsedChooseAddressRequest extends ParsedBasicRequest {
disableLegacyAccounts: boolean;
disableBip39Accounts: boolean;
disableLedgerAccounts: boolean;
ui?: number;
}

export interface ParsedSignTransactionRequest extends ParsedBasicRequest {
Expand Down
12 changes: 10 additions & 2 deletions src/lib/WalletInfoCollector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export default class WalletInfoCollector {
};
}

public static async detectBitcoinAddresses(xpub: string, startIndex = 0): Promise<{
public static async detectBitcoinAddresses(xpub: string, startIndex = 0, onlyUnusedExternal = Infinity): Promise<{
internal: BtcAddressInfo[],
external: BtcAddressInfo[],
}> {
Expand Down Expand Up @@ -147,7 +147,7 @@ export default class WalletInfoCollector {

const addresses: [BtcAddressInfo[], BtcAddressInfo[]] = [[], []];

for (const INDEX of [EXTERNAL_INDEX, INTERNAL_INDEX]) {
addressTypeLoop: for (const INDEX of [EXTERNAL_INDEX, INTERNAL_INDEX]) {
const baseKey = extendedKey.derive(INDEX);
const basePath = `${BTC_ACCOUNT_KEY_PATH[xPubType][Config.bitcoinNetwork]}/${INDEX}`;

Expand Down Expand Up @@ -178,6 +178,14 @@ export default class WalletInfoCollector {
balance,
));

if (INDEX === EXTERNAL_INDEX && !used) {
// Found an unused external address, reducing remaining counter
onlyUnusedExternal -= 1;

// When all found, break outer loop and return
if (onlyUnusedExternal <= 0) break addressTypeLoop;
}

if (used) {
gap = 0;
} else {
Expand Down
Loading

0 comments on commit 77ccfba

Please sign in to comment.