Skip to content

Commit 1cc5654

Browse files
Merge branch 'develop' into 'master'
Develop See merge request papers/airgap/airgap-vault!354
2 parents b6c2634 + c80d2d0 commit 1cc5654

23 files changed

+197
-56
lines changed

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@
3737
"apply-diagnostic-modules": "node apply-diagnostic-modules.js"
3838
},
3939
"dependencies": {
40-
"@airgap/angular-core": "0.0.30",
41-
"@airgap/angular-ngrx": "0.0.30",
42-
"@airgap/coinlib-core": "0.13.4",
40+
"@airgap/angular-core": "0.0.31",
41+
"@airgap/angular-ngrx": "0.0.31",
42+
"@airgap/coinlib-core": "0.13.5",
4343
"@airgap/sapling-wasm": "0.0.7",
4444
"@angular/common": "13.2.5",
4545
"@angular/core": "13.2.5",
@@ -192,4 +192,4 @@
192192
"cordova-plugin-device-motion": {}
193193
}
194194
}
195-
}
195+
}

src/app/app.component.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ import { StartupChecksService } from './services/startup-checks/startup-checks.s
3434

3535
declare let window: Window & { airGapHasStarted: boolean }
3636

37+
const defer = (fn: () => void) => {
38+
// fn()
39+
setTimeout(fn, 200)
40+
}
41+
3742
@Component({
3843
selector: 'airgap-root',
3944
templateUrl: 'app.component.html'
@@ -113,23 +118,25 @@ export class AppComponent implements AfterViewInit {
113118
})
114119
} else {
115120
this.ngZone.run(async () => {
116-
this.iacService.handleRequest(data.url, IACMessageTransport.DEEPLINK).catch(handleErrorLocal(ErrorCategory.SCHEME_ROUTING))
121+
// We defer this call because on iOS the app would sometimes get stuck on a black screen when handling deeplinks.
122+
defer(() =>
123+
this.iacService.handleRequest(data.url, IACMessageTransport.DEEPLINK).catch(handleErrorLocal(ErrorCategory.SCHEME_ROUTING))
124+
)
117125
})
118126
}
119127
})
120128
}
121129

122130
private async initializeTranslations(): Promise<void> {
123131
return this.languageService.init({
124-
supportedLanguages: ['en', 'de', 'zh-cn'],
132+
supportedLanguages: ['en', 'de', 'zh'],
125133
defaultLanguage: 'en'
126134
})
127135
}
128136

129137
private async initializeProtocols(): Promise<void> {
130-
const externalMethodProvider:
131-
| TezosSaplingExternalMethodProvider
132-
| undefined = await this.saplingNativeService.createExternalMethodProvider()
138+
const externalMethodProvider: TezosSaplingExternalMethodProvider | undefined =
139+
await this.saplingNativeService.createExternalMethodProvider()
133140

134141
const shieldedTezProtocol: TezosShieldedTezProtocol = new TezosShieldedTezProtocol(
135142
new TezosSaplingProtocolOptions(

src/app/components/mnemonic-keyboard/mnemonic-keyboard.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ export class MnemonicKeyboardComponent implements OnInit, OnDestroy {
187187

188188
async showWordlist() {
189189
const modal: HTMLIonModalElement = await this.modalController.create({
190-
component: WordlistPage
190+
component: WordlistPage,
191+
componentProps: { isModal: true }
191192
})
192193

193194
modal.present().catch(handleErrorLocal(ErrorCategory.IONIC_MODAL))

src/app/pages/account-address/account-address.page.html

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,32 @@ <h2 class="ion-no-margin">{{ wallet.protocol.name }} {{ wallet.protocol.symbol }
3636
</ion-col>
3737
</ion-row>
3838

39-
<ion-row class="ion-justify-content-center ion-padding-bottom">
40-
<airgap-qr [qrdata]="wallet.receivingPublicAddress" [level]="'L'" [size]="300"></airgap-qr>
39+
<p class="ion-padding-horizontal ion-padding-bottom">You can sync this account with:</p>
40+
41+
<ion-row *ngFor="let option of syncOptions" class="ion-padding-bottom">
42+
<ion-col size="12">
43+
<ion-button expand="block" color="light" (click)="share(option)" shape="round">
44+
<img src="assets/logos/{{option.icon}}" style="width: 24px; height: 24px" />
45+
&nbsp; {{option.name}}
46+
</ion-button>
47+
</ion-col>
4148
</ion-row>
4249

43-
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
44-
<ion-button color="light" (click)="share()" shape="round">{{ 'wallet-address.sync_label' | translate }}</ion-button>
45-
<ion-button color="primary" (click)="done()" shape="round">{{ 'wallet-address.done_label' | translate }}</ion-button>
46-
</ion-fab>
50+
<ion-modal #modal trigger="open-modal" [presentingElement]="presentingElement">
51+
<ng-template>
52+
<ion-header>
53+
<ion-toolbar>
54+
<ion-title>Address QR</ion-title>
55+
<ion-buttons slot="end">
56+
<ion-button (click)="modal.dismiss()">Close</ion-button>
57+
</ion-buttons>
58+
</ion-toolbar>
59+
</ion-header>
60+
<ion-content>
61+
<ion-row class="ion-justify-content-center ion-padding-bottom">
62+
<airgap-qr [qrdata]="wallet.receivingPublicAddress" [level]="'L'" [size]="300"></airgap-qr>
63+
</ion-row>
64+
</ion-content>
65+
</ng-template>
66+
</ion-modal>
4767
</ion-content>

src/app/pages/account-address/account-address.page.scss

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,24 @@ ion-avatar {
1111
width: 64px;
1212
height: 64px;
1313
}
14+
15+
ion-modal {
16+
--ion-background-color: var(--ion-color-secondary);
17+
ion-toolbar {
18+
--background: var(--ion-color-secondary);
19+
}
20+
--ion-text-color: var(--ion-color-secondary-contrast);
21+
ion-toggle {
22+
--handle-background: var(--ion-color-secondary-contrast);
23+
}
24+
ion-radio,
25+
ion-label,
26+
ion-segment-button,
27+
ion-toolbar {
28+
--color: var(--ion-color-secondary-contrast);
29+
}
30+
ion-item,
31+
ion-checkbox {
32+
--border-color: var(--ion-color-secondary-contrast);
33+
}
34+
}

src/app/pages/account-address/account-address.page.ts

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { ClipboardService, DeeplinkService, UiEventService } from '@airgap/angular-core'
2-
import { AirGapWallet, IACMessageDefinitionObjectV3 } from '@airgap/coinlib-core'
3-
import { Component } from '@angular/core'
4-
import { PopoverController } from '@ionic/angular'
1+
import { ClipboardService, DeeplinkService, QRType, UiEventService } from '@airgap/angular-core'
2+
import { AirGapWallet, IACMessageDefinitionObjectV3, MainProtocolSymbols } from '@airgap/coinlib-core'
3+
import { Component, ViewChild } from '@angular/core'
4+
import { Router } from '@angular/router'
5+
import { IonModal, PopoverController } from '@ionic/angular'
56

67
import { ErrorCategory, handleErrorLocal } from '../../services/error-handler/error-handler.service'
78
import { InteractionOperationType, InteractionService } from '../../services/interaction/interaction.service'
@@ -12,18 +13,55 @@ import { isWalletMigrated } from '../../utils/migration'
1213

1314
import { AccountEditPopoverComponent } from './account-edit-popover/account-edit-popover.component'
1415

16+
// TODO: add wallet definition into a service
17+
export const airgapwallet = {
18+
icon: 'airgap-wallet-app-logo.png',
19+
name: 'AirGap Wallet',
20+
qrType: QRType.V3
21+
}
22+
23+
const bluewallet = {
24+
icon: 'bluewallet.png',
25+
name: 'BlueWallet',
26+
qrType: QRType.BC_UR
27+
}
28+
29+
const sparrowwallet = {
30+
icon: 'sparrowwallet.png',
31+
name: 'Sparrow Wallet',
32+
qrType: QRType.BC_UR
33+
}
34+
35+
const metamask = {
36+
icon: 'metamask.webp',
37+
name: 'MetaMask',
38+
qrType: QRType.METAMASK
39+
}
40+
41+
export interface CompanionApp {
42+
icon: string
43+
name: string
44+
qrType: QRType
45+
}
46+
1547
@Component({
1648
selector: 'airgap-account-address',
1749
templateUrl: './account-address.page.html',
1850
styleUrls: ['./account-address.page.scss']
1951
})
2052
export class AccountAddressPage {
53+
@ViewChild(IonModal) modal: IonModal
54+
2155
public wallet: AirGapWallet
2256

57+
public syncOptions: CompanionApp[]
58+
2359
private shareObject?: IACMessageDefinitionObjectV3[]
2460
private shareObjectPromise?: Promise<void>
2561
private walletShareUrl?: string
2662

63+
presentingElement = null
64+
2765
constructor(
2866
private readonly popoverCtrl: PopoverController,
2967
private readonly clipboardService: ClipboardService,
@@ -32,21 +70,43 @@ export class AccountAddressPage {
3270
private readonly navigationService: NavigationService,
3371
private readonly uiEventService: UiEventService,
3472
private readonly migrationService: MigrationService,
35-
private readonly deepLinkService: DeeplinkService
73+
private readonly deepLinkService: DeeplinkService,
74+
private readonly router: Router
3675
) {
3776
this.wallet = this.navigationService.getState().wallet
77+
78+
if (!this.wallet) {
79+
this.router.navigate(['/'])
80+
throw new Error('No wallet found!')
81+
}
82+
83+
switch (this.wallet?.protocol.identifier) {
84+
case MainProtocolSymbols.BTC_SEGWIT:
85+
this.syncOptions = [airgapwallet, bluewallet, sparrowwallet]
86+
break
87+
case MainProtocolSymbols.ETH:
88+
this.syncOptions = [airgapwallet, metamask]
89+
break
90+
default:
91+
this.syncOptions = [airgapwallet]
92+
}
93+
}
94+
95+
ngOnInit() {
96+
this.presentingElement = document.querySelector('.ion-page')
3897
}
3998

4099
public done(): void {
41100
this.navigationService.routeToAccountsTab().catch(handleErrorLocal(ErrorCategory.IONIC_NAVIGATION))
42101
}
43102

44-
public async share(): Promise<void> {
103+
public async share(companionApp: CompanionApp = airgapwallet): Promise<void> {
45104
await this.waitWalletShareUrl()
46105

47106
this.interactionService.startInteraction({
48107
operationType: InteractionOperationType.WALLET_SYNC,
49-
iacMessage: this.shareObject
108+
iacMessage: this.shareObject,
109+
companionApp: companionApp
50110
})
51111
}
52112

@@ -55,6 +115,9 @@ export class AccountAddressPage {
55115
component: AccountEditPopoverComponent,
56116
componentProps: {
57117
wallet: this.wallet,
118+
openAddressQR: () => {
119+
this.modal.present().catch(handleErrorLocal(ErrorCategory.IONIC_MODAL))
120+
},
58121
getWalletShareUrl: async () => {
59122
await this.waitWalletShareUrl()
60123
return this.walletShareUrl

src/app/pages/account-address/account-edit-popover/account-edit-popover.component.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<ion-list-header>
33
<ion-label>{{ 'wallet-edit-delete-popover.settings_label' | translate }}</ion-label>
44
</ion-list-header>
5+
56
<ion-item button detail="false" (click)="copyAddressToClipboard()">
67
<ion-icon name="clipboard-outline" color="dark" slot="end"></ion-icon>
78
{{ 'wallet-edit-delete-popover.copy_label' | translate }}
@@ -10,6 +11,10 @@
1011
<ion-icon name="clipboard-outline" color="dark" slot="end"></ion-icon>
1112
{{ 'wallet-edit-delete-popover.copy_sync_code' | translate }}
1213
</ion-item>
14+
<ion-item button detail="false" (click)="showAddressQR()">
15+
<ion-icon name="clipboard-outline" color="dark" slot="end"></ion-icon>
16+
{{ 'wallet-edit-delete-popover.show_address_qr' | translate }}
17+
</ion-item>
1318
<ion-item button detail="false" (click)="delete()">
1419
<ion-icon name="trash" color="dark" slot="end"></ion-icon>
1520
{{ 'wallet-edit-delete-popover.account-removal_alert.delete_label' | translate }}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.dismiss {
2+
text-align: center;
3+
font-weight: bolder;
4+
color: var(--ion-color-secondary);
5+
}

src/app/pages/account-address/account-edit-popover/account-edit-popover.component.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { NavigationService } from 'src/app/services/navigation/navigation.servic
1717
export class AccountEditPopoverComponent {
1818
public readonly wallet: AirGapWallet
1919
private readonly onDelete: Function
20+
private readonly openAddressQR: () => void | undefined
2021
private readonly getWalletShareUrl: () => Promise<string>
2122

2223
constructor(
@@ -46,6 +47,12 @@ export class AccountEditPopoverComponent {
4647
await this.popoverController.dismiss()
4748
}
4849

50+
public async showAddressQR(): Promise<void> {
51+
this.openAddressQR()
52+
53+
await this.popoverController.dismiss()
54+
}
55+
4956
public async openAddressExplorer(): Promise<void> {
5057
this.navigationService.routeWithState('/address-explorer', { wallet: this.wallet })
5158

src/app/pages/account-share/account-share.page.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,15 @@
88
</ion-header>
99

1010
<ion-content class="ion-padding">
11-
<h3 class="ion-padding-bottom" [innerHTML]="'wallet-share.heading' | translate"></h3>
11+
<h3 class="ion-padding-bottom" [innerHTML]="'wallet-share.heading' | translate: { walletName: walletName }"></h3>
1212

1313
<ion-row class="ion-justify-content-center ion-padding-bottom" *ngIf="interactionUrl">
14-
<airgap-iac-qr [messageDefinitionObjects]="interactionUrl" [level]="'L'" [size]="300"></airgap-iac-qr>
14+
<airgap-iac-qr
15+
[messageDefinitionObjects]="interactionUrl"
16+
[qrFormatPreference]="companionApp.qrType"
17+
[level]="'L'"
18+
[size]="300"
19+
></airgap-iac-qr>
1520
</ion-row>
1621

1722
<ion-row><p [innerHTML]="'wallet-share.text' | translate"></p></ion-row>

0 commit comments

Comments
 (0)