Skip to content

Commit

Permalink
Merge branch 'feat/contact-book' into 'develop'
Browse files Browse the repository at this point in the history
Feat/Contact Book

See merge request papers/airgap/airgap-vault!407
  • Loading branch information
godenzim committed Mar 13, 2023
2 parents 1df24aa + d7a3257 commit c770146
Show file tree
Hide file tree
Showing 48 changed files with 2,122 additions and 129 deletions.
4 changes: 2 additions & 2 deletions build/android/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ WORKDIR /app
RUN yarn cache clean --force && yarn global add n && n 16.13.1

# Install app dependencies, using wildcard if package-lock exists and copy capacitor configs and ionic configs
COPY package.json yarn.lock apply-diagnostic-modules.js fix-qrscanner-gradle.js capacitor.config.ts ionic.config.json /app/
COPY package.json yarn.lock apply-diagnostic-modules.js fix-qrscanner-gradle.js capacitor.config.ts ionic.config.json copy-builtin-modules.js /app/

# install dependencies
# run ionic android build
Expand All @@ -30,7 +30,7 @@ COPY . /app
ARG BUILD_NR
RUN sed -i -e "s/versionCode 1/versionCode $BUILD_NR/g" /app/android/app/build.gradle

# disable pure getters due to https://github.com/angular/angular-cli/issues/11439
# disable pure getters due to https://github.com/angular/angular-cli/issues/11439
# configure mangle (keep_fnames) for bitcoinjs https://github.com/bitcoinjs/bitcoinjs-lib/issues/959
# remove unused cordova-diagnostic-plugin features
# jetify dependencies
Expand Down
72 changes: 36 additions & 36 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,45 +37,45 @@
"apply-diagnostic-modules": "node apply-diagnostic-modules.js"
},
"resolutions": {
"@airgap/aeternity": "0.13.11-beta.22",
"@airgap/astar": "0.13.11-beta.22",
"@airgap/bitcoin": "0.13.11-beta.22",
"@airgap/coinlib-core": "0.13.11-beta.22",
"@airgap/coreum": "0.13.11-beta.22",
"@airgap/cosmos": "0.13.11-beta.22",
"@airgap/cosmos-core": "0.13.11-beta.22",
"@airgap/crypto": "0.13.11-beta.22",
"@airgap/ethereum": "0.13.11-beta.22",
"@airgap/groestlcoin": "0.13.11-beta.22",
"@airgap/icp": "0.13.11-beta.22",
"@airgap/module-kit": "0.13.11-beta.22",
"@airgap/moonbeam": "0.13.11-beta.22",
"@airgap/polkadot": "0.13.11-beta.22",
"@airgap/serializer": "0.13.11-beta.22",
"@airgap/substrate": "0.13.11-beta.22",
"@airgap/tezos": "0.13.11-beta.22"
"@airgap/aeternity": "0.13.11-beta.23",
"@airgap/astar": "0.13.11-beta.23",
"@airgap/bitcoin": "0.13.11-beta.23",
"@airgap/coinlib-core": "0.13.11-beta.23",
"@airgap/coreum": "0.13.11-beta.23",
"@airgap/cosmos": "0.13.11-beta.23",
"@airgap/cosmos-core": "0.13.11-beta.23",
"@airgap/crypto": "0.13.11-beta.23",
"@airgap/ethereum": "0.13.11-beta.23",
"@airgap/groestlcoin": "0.13.11-beta.23",
"@airgap/icp": "0.13.11-beta.23",
"@airgap/module-kit": "0.13.11-beta.23",
"@airgap/moonbeam": "0.13.11-beta.23",
"@airgap/polkadot": "0.13.11-beta.23",
"@airgap/serializer": "0.13.11-beta.23",
"@airgap/substrate": "0.13.11-beta.23",
"@airgap/tezos": "0.13.11-beta.23"
},
"dependencies": {
"@airgap/aeternity": "0.13.11-beta.22",
"@airgap/angular-core": "0.0.35-beta.15",
"@airgap/angular-ngrx": "0.0.35-beta.15",
"@airgap/astar": "0.13.11-beta.22",
"@airgap/bitcoin": "0.13.11-beta.22",
"@airgap/coinlib-core": "0.13.11-beta.22",
"@airgap/coreum": "0.13.11-beta.22",
"@airgap/cosmos": "0.13.11-beta.22",
"@airgap/cosmos-core": "0.13.11-beta.22",
"@airgap/crypto": "0.13.11-beta.22",
"@airgap/ethereum": "0.13.11-beta.22",
"@airgap/groestlcoin": "0.13.11-beta.22",
"@airgap/icp": "0.13.11-beta.22",
"@airgap/module-kit": "0.13.11-beta.22",
"@airgap/moonbeam": "0.13.11-beta.22",
"@airgap/polkadot": "0.13.11-beta.22",
"@airgap/aeternity": "0.13.11-beta.23",
"@airgap/angular-core": "0.0.35-beta.22",
"@airgap/angular-ngrx": "0.0.35-beta.22",
"@airgap/astar": "0.13.11-beta.23",
"@airgap/bitcoin": "0.13.11-beta.23",
"@airgap/coinlib-core": "0.13.11-beta.23",
"@airgap/coreum": "0.13.11-beta.23",
"@airgap/cosmos": "0.13.11-beta.23",
"@airgap/cosmos-core": "0.13.11-beta.23",
"@airgap/crypto": "0.13.11-beta.23",
"@airgap/ethereum": "0.13.11-beta.23",
"@airgap/groestlcoin": "0.13.11-beta.23",
"@airgap/icp": "0.13.11-beta.23",
"@airgap/module-kit": "0.13.11-beta.23",
"@airgap/moonbeam": "0.13.11-beta.23",
"@airgap/polkadot": "0.13.11-beta.23",
"@airgap/sapling-wasm": "0.0.7",
"@airgap/serializer": "0.13.11-beta.22",
"@airgap/substrate": "0.13.11-beta.22",
"@airgap/tezos": "0.13.11-beta.22",
"@airgap/serializer": "0.13.11-beta.23",
"@airgap/substrate": "0.13.11-beta.23",
"@airgap/tezos": "0.13.11-beta.23",
"@angular/common": "13.2.5",
"@angular/core": "13.2.5",
"@angular/forms": "13.2.5",
Expand Down
16 changes: 16 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@ const routes: Routes = [
(m) => m.SocialRecoveryValidateSharePageModule
)
},
{
path: 'contact-book-contacts',
loadChildren: () => import('./pages/contact-book-contacts/contact-book-contacts.module').then((m) => m.ContactBookContactsPageModule)
},
{
path: 'contact-book-contacts-detail',
loadChildren: () => import('./pages/contact-book-contacts-detail/contact-book-contacts-detail.module').then((m) => m.ContactBookContactsDetailPageModule)
},
{
path: 'contact-book-onboarding',
loadChildren: () => import('./pages/contact-book-onboarding/contact-book-onboarding.module').then((m) => m.ContactBookOnboardingPageModule)
},
{
path: 'contact-book-settings',
loadChildren: () => import('./pages/contact-book-settings/contact-book-settings.module').then((m) => m.ContactBookOnboardingPageModule)
},
{
path: 'deserialized-detail',
loadChildren: () => import('./pages/deserialized-detail/deserialized-detail.module').then((m) => m.DeserializedDetailPageModule)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,22 @@ <h5>{{ aggregatedInfo.totalFees.toFixed() | feeConverter: { protocol: airGapTxs[
<airgap-from-to class="ion-padding-horizontal" [transaction]="airGapTx" [hideNetwork]="true"></airgap-from-to>
</ng-container>

<ng-container *ngFor="let address of addressesNotOnContactBook">
<div class="suggestion-wrapper">
<p>This address is not in your address book:</p>
<div class="divider">
<airgap-identicon class="image" [address]="address"></airgap-identicon>
<span>{{ address }}</span>
</div>
<p>Do you want to add it?</p>
<div class="divider-buttons">
<ion-button class="button" color="tertiary" (click)="onClickDontAddContact(address)">No</ion-button>
<ion-button class="button" color="tertiary" (click)="onClickAddContact(address)">Yes</ion-button>
</div>
<ion-button class="button" color="primary" (click)="onClickDisableContact()">No, disable address book feature</ion-button>
</div>
</ng-container>

<ng-container *ngIf="fallbackActivated">
<ion-row>
<ion-col size="2" class="ion-margin-top">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
.suggestion-wrapper {
display: flex;
flex-direction: column;
border: 1px solid var(--ion-color-primary);
padding: 12px;
border-radius: 12px;

p {
margin-top: 0px;
color: var(--ion-color-primary);
margin-bottom: 12px;
font-weight: 500;
}

span {
color: white;
}

.divider {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;

span {
flex: 1;
line-break: anywhere;
font-size: 14px;
font-weight: 600;
}

.image {
width: 50px;
height: 50px;
}
}

.divider-buttons {
display: flex;
align-items: center;
gap: 4px;
}

ion-button {
flex: 1;
font-size: 14px;
--padding-bottom: 12px;
--padding-top: 12px;
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { ProtocolService, SerializerService, sumAirGapTxValues } from '@airgap/angular-core'
import { Component, Input } from '@angular/core'
import {
IAirGapTransaction,
ICoinProtocol,
MainProtocolSymbols,
ProtocolSymbols,
SignedTransaction
} from '@airgap/coinlib-core'
import { IAirGapTransaction, ICoinProtocol, MainProtocolSymbols, ProtocolSymbols, SignedTransaction } from '@airgap/coinlib-core'
import BigNumber from 'bignumber.js'
import { TokenService } from 'src/app/services/token/TokenService'
import { SecretsService } from 'src/app/services/secrets/secrets.service'
import { IACMessageDefinitionObjectV3 } from '@airgap/serializer'
import { TezosSaplingProtocol } from '@airgap/tezos'
import { NavigationService } from 'src/app/services/navigation/navigation.service'
import { ContactsService } from 'src/app/services/contacts/contacts.service'
import { ErrorCategory, handleErrorLocal } from 'src/app/services/error-handler/error-handler.service'
import { AddType } from 'src/app/services/contacts/contacts.service'

@Component({
selector: 'airgap-signed-transaction',
Expand All @@ -25,6 +23,7 @@ export class SignedTransactionComponent {
@Input()
public syncProtocolString: string

public addressesNotOnContactBook: string[] = []
public airGapTxs: IAirGapTransaction[]
public fallbackActivated: boolean = false
public rawTxData: string
Expand All @@ -40,7 +39,9 @@ export class SignedTransactionComponent {
private readonly protocolService: ProtocolService,
private readonly serializerService: SerializerService,
private readonly tokenService: TokenService,
private readonly secretsService: SecretsService
private readonly secretsService: SecretsService,
private readonly navigationService: NavigationService,
private readonly contactsService: ContactsService
) {
//
}
Expand Down Expand Up @@ -103,6 +104,43 @@ export class SignedTransactionComponent {
this.rawTxData = (this.signedTxs[0].payload as SignedTransaction).transaction
}
}

this.checkAdressesNames()
}

public async checkAdressesNames() {
// Check for addresses in contact book
if (this.airGapTxs && this.airGapTxs.length > 0) {
const isBookenabled = await this.contactsService.isBookEnabled()
if (isBookenabled) {
this.addressesNotOnContactBook = []
for (let i = 0; i < this.airGapTxs.length; i++) {
this.airGapTxs[i].extra = { names: {} }
const transaction = this.airGapTxs[i]
const toAddresses = transaction.to
for (let j = 0; j < toAddresses.length; j++) {
const toAddress = toAddresses[j]
const hasContactBookAddress = await this.contactsService.isAddressInContacts(toAddress)
if (!hasContactBookAddress) this.addressesNotOnContactBook.push(toAddress)
else {
const name = await this.contactsService.getContactName(toAddress)
if (name) this.airGapTxs[i].extra.names[toAddress] = name
}
}
const fromAddresses = transaction.from
for (let j = 0; j < fromAddresses.length; j++) {
const fromAddress = fromAddresses[j]
const hasContactBookAddress = await this.contactsService.isAddressInContacts(fromAddress)
if (!hasContactBookAddress && !this.addressesNotOnContactBook.includes(fromAddress))
this.addressesNotOnContactBook.push(fromAddress)
else {
const name = await this.contactsService.getContactName(fromAddress)
if (name) this.airGapTxs[i].extra.names[fromAddress] = name
}
}
}
}
}
}

private async checkIfSaplingTransaction(transaction: SignedTransaction, protocolIdentifier: ProtocolSymbols): Promise<boolean> {
Expand All @@ -116,7 +154,7 @@ export class SignedTransactionComponent {

// TODO: find better way to check if `transaction` is a Sapling transaction
return recipients.some((recipient: string) => recipient.startsWith('zet') || recipient.toLocaleLowerCase() === 'shielded pool')
} catch(error) {
} catch (error) {
console.error(error)
return false
}
Expand All @@ -128,4 +166,23 @@ export class SignedTransactionComponent {
private async getSaplingProtocol(): Promise<TezosSaplingProtocol> {
return (await this.protocolService.getProtocol(MainProtocolSymbols.XTZ_SHIELDED)) as TezosSaplingProtocol
}

async onClickAddContact(address: string) {
this.navigationService
.routeWithState('/contact-book-contacts-detail', { isNew: true, address, addType: AddType.SIGNING })
.catch(handleErrorLocal(ErrorCategory.IONIC_NAVIGATION))
}

async onClickDontAddContact(address: string) {
await this.contactsService.addSuggestion(address)
const index = this.addressesNotOnContactBook.findIndex((address) => address === address)
if (index >= 0) {
this.addressesNotOnContactBook.splice(index, 1)
}
}

async onClickDisableContact() {
await this.contactsService.setBookEnable(false)
this.addressesNotOnContactBook = []
}
}
29 changes: 28 additions & 1 deletion src/app/components/transaction/transaction.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IAirGapTransaction, ProtocolSymbols } from '@airgap/coinlib-core'
import { Component, Input, OnInit } from '@angular/core'
import { Observable } from 'rxjs'
import { ContactsService } from 'src/app/services/contacts/contacts.service'

import { AggregatedDetails, TransactionStore } from './transaction.store'

Expand All @@ -18,15 +19,41 @@ export class TransactionComponent implements OnInit {
public airGapTxs$: Observable<IAirGapTransaction[]>
public aggregatedDetails$: Observable<AggregatedDetails | undefined>

constructor(private readonly store: TransactionStore) {
constructor(private readonly store: TransactionStore, private readonly contactsService: ContactsService) {
this.protocolIdentifier$ = this.store.selectProtocolIdentifier()
this.airGapTxs$ = this.store.selectAirGapTxs()
this.aggregatedDetails$ = this.store.selectAggregatedDetails()
}

public async ngOnInit(): Promise<void> {
if (this.airGapTxs !== undefined) {
await this.setAddressNames()
this.store.setAirGapTxs(this.airGapTxs)
}
}

public async ngOnChanges() {
await this.setAddressNames()
this.store.setAirGapTxs(this.airGapTxs)
}

private async setAddressNames() {
const isBookenabled = await this.contactsService.isBookEnabled()
if (isBookenabled) {
for (let i = 0; i < this.airGapTxs.length; i++) {
this.airGapTxs[i].extra = { names: {} }
for (let j = 0; j < this.airGapTxs[i].from.length; j++) {
const address = this.airGapTxs[i].from[j]
const name = await this.contactsService.getContactName(address)
if (name) this.airGapTxs[i].extra.names[address] = name
}

for (let j = 0; j < this.airGapTxs[i].to.length; j++) {
const address = this.airGapTxs[i].to[j]
const name = await this.contactsService.getContactName(address)
if (name) this.airGapTxs[i].extra.names[address] = name
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ComponentsModule } from '@airgap/angular-core'
import { CommonModule } from '@angular/common'
import { NgModule } from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { RouterModule, Routes } from '@angular/router'
import { IonicModule } from '@ionic/angular'
import { TranslateModule } from '@ngx-translate/core'

import { ContactBookContactsDetailPage } from './contact-book-contacts-detail.page'

const routes: Routes = [
{
path: '',
component: ContactBookContactsDetailPage
}
]

@NgModule({
imports: [CommonModule, FormsModule, ReactiveFormsModule, IonicModule, RouterModule.forChild(routes), TranslateModule, ComponentsModule],
declarations: [ContactBookContactsDetailPage]
})
export class ContactBookContactsDetailPageModule {}
Loading

0 comments on commit c770146

Please sign in to comment.