Skip to content

Commit

Permalink
Fixed transfer implementation and reports response for each NFT trans…
Browse files Browse the repository at this point in the history
…ferred
  • Loading branch information
amenconi committed Feb 28, 2024
1 parent c2dee60 commit c0342a9
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 50 deletions.
1 change: 0 additions & 1 deletion src/app/@api/base.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export class BaseApi<T> {

protected request<T>(func: WEN_FUNC, req: Build5Request<any>): Observable<T | undefined> {
const origin = environment.production ? BUILD5_PROD_ADDRESS_API : BUILD5_TEST_ADDRESS_API;
console.log('base.api request - origin: ', origin);
return <any>this.httpClient.post(origin + func, req);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { WenError } from '@build-5/interfaces';

@Injectable({
providedIn: 'root'
})
export class Build5ErrorLookupService {

constructor() { }

public getErrorMessage(errorCode: number): string {
// Convert WenError object to an array and find the entry by error code
const errorEntry = Object.values(WenError).find(entry => entry.code === errorCode);
return errorEntry ? errorEntry.key : 'Unknown error';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export interface MemberNftsFilters {
collection?: string[];
owner?: string[];
};
refresh?: number;
}

export type Filters =
Expand Down
14 changes: 7 additions & 7 deletions src/app/@core/services/nft-selection/nft-selection.service.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { BehaviorSubject, Subject } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class NftSelectionService {
private selectedNftIdsSubject = new BehaviorSubject<string[]>([]);
selectedNftIds$ = this.selectedNftIdsSubject.asObservable();
private modalClosedSource = new Subject<void>();
public modalClosed$ = this.modalClosedSource.asObservable();

public selectNft(nftId: string) {
//console.log('nft-selection.service deselectNft - initial nfts: ', this.selectedNftIdsSubject.getValue());
const currentSelectedNftIds = this.selectedNftIdsSubject.getValue();
if (!currentSelectedNftIds.includes(nftId)) {
//console.log('nft-selection.service deselectNft - selected nfts missing this nft, adding this nft: ', nftId);
this.selectedNftIdsSubject.next([...currentSelectedNftIds, nftId]);
}
//console.log('nft-selection.service deselectNft - ending nfts: ', this.selectedNftIdsSubject.getValue());
}

public deselectNft(nftId: string) {
//console.log('nft-selection.service deselectNft - initial nfts: ', this.selectedNftIdsSubject.getValue());
const updatedSelectedNftIds = this.selectedNftIdsSubject.getValue().filter(id => id !== nftId);
this.selectedNftIdsSubject.next(updatedSelectedNftIds);
//console.log('nft-selection.service deselectNft - ending nfts: ', updatedSelectedNftIds);
}

public clearSelection() {
//console.log('nft-selection.service clearSelection fired.');
this.selectedNftIdsSubject.next([]);
}

public notifyModalClosed(): void {
this.modalClosedSource.next();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ export class NftSelectionToolbarComponent implements OnInit {
});

modal.afterClose.subscribe(result => {
console.log('Transfer modal closed', result);
const componentInstance = modal.getContentComponent();
componentInstance.onModalClose();
this.nftSelectionService.notifyModalClosed();
});
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,34 @@
<div *ngIf="selectedNfts.length > 0">
<form #transferForm="ngForm" (ngSubmit)="onSubmit()">
<nz-row class="nft-header underline font-bold" nzGutter="[16, 16]">
<nz-col nzSpan="6">Image</nz-col>
<nz-col nzSpan="6">Name</nz-col>
<nz-col nzSpan="6">Network</nz-col>
<nz-col nzSpan="5">Image</nz-col>
<nz-col nzSpan="5">Name</nz-col>
<nz-col nzSpan="5">Network</nz-col>
<nz-col nzSpan="3">Transfer</nz-col>
<nz-col nzSpan="3">Withdraw</nz-col>
<nz-col nzSpan="3">Status</nz-col>
</nz-row>
<nz-row *ngFor="let nft of selectedNfts; let i = index" class="nft-row mb-2 bg-backgrounds-secondary dark:bg-backgrounds-secondary-dark lg:grow rounded-xl p-1" nzGutter="[16, 16]">
<nz-col nzSpan="6" nzXs="24" nzSm="12" nzMd="6">
<img [src]="nft.media" [alt]="nft.name" class="w-10 h-10 object-cover rounded">
<nz-row *ngFor="let nft of selectedNfts; let i = index" [class.disabled]="nft.disabled" class="nft-row mb-2 bg-backgrounds-secondary dark:bg-backgrounds-secondary-dark lg:grow rounded-xl p-1" nzGutter="[16, 16]">
<nz-col nzSpan="5" nzXs="24" nzSm="12" nzMd="5">
<img [src]="nft.media" [alt]="nft.name" class="w-10 h-10 object-cover rounded">
</nz-col>
<nz-col nzSpan="6" nzXs="24" nzSm="12" nzMd="6">
<span>{{ nft.name }}</span>
<nz-col nzSpan="5" nzXs="24" nzSm="12" nzMd="5">
<span [ngClass]="{'strikethrough': nft.disabled}">{{ nft.name }}</span>
</nz-col>
<nz-col nzSpan="6" nzXs="24" nzSm="12" nzMd="6">
<span>{{ nft.mintingData?.network }}</span>
<nz-col nzSpan="5" nzXs="24" nzSm="12" nzMd="5">
<span [ngClass]="{'strikethrough': nft.disabled}" class="uppercase">{{ nft.mintingData?.network }}</span>
</nz-col>
<nz-col nzSpan="3" nzXs="12" nzSm="6" nzMd="3">
<label nz-checkbox [(ngModel)]="nft.transfer" name="transfer-{{i}}" (ngModelChange)="updateNetworkSelection(nft)" [nzDisabled]="!isTransferEnabled(nft)"></label>
<label nz-checkbox [(ngModel)]="nft.transfer" name="transfer-{{i}}" (ngModelChange)="updateNetworkSelection(nft)" [nzDisabled]="!isTransferEnabled(nft) || nft.disabled"></label>
</nz-col>
<nz-col nzSpan="3" nzXs="12" nzSm="6" nzMd="3">
<label nz-checkbox [(ngModel)]="nft.withdraw" name="withdraw-{{i}}" [nzDisabled]="!nft.transfer"></label>
<label nz-checkbox [(ngModel)]="nft.withdraw" name="withdraw-{{i}}" [nzDisabled]="!nft.transfer || nft.disabled"></label>
</nz-col>
<nz-col nzSpan="3" nzXs="24" nzSm="12" nzMd="3">
<span>{{ nft.statusMessage }}</span>
</nz-col>
</nz-row>
<nz-row nzGutter="16">
<nz-row nzGutter="16" class="pb-2">
<nz-col nzSpan="24">
<div class="flex flex-col justify-between mt-5 space-y-4 lg:space-y-0 lg:flex-row lg:space-x-4">
<div
Expand Down Expand Up @@ -93,7 +97,7 @@
<nz-col nzSpan="24">
<button
nz-button
nzType="text"
type="button"
nzType="default"
class="wen-btn-link m-2"
(click)="handleCancel()"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@
@apply text-accent-primary dark:text-accent-primary-dark;
}
}

.uppercase {
text-transform: uppercase;
}

.strikethrough {
text-decoration: line-through;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, OnInit, ChangeDetectorRef } from '@angular/core';
import { NzModalRef } from 'ng-zorro-antd/modal';
import { NftSelectionService } from '@core/services/nft-selection/nft-selection.service';
import { Nft, NftTransferRequest, Transaction, Timestamp, COL, Member, NftAccess, } from '@build-5/interfaces';
import { Nft, NftTransferRequest, Transaction, Timestamp, COL, Member, NftAccess, WenError, RETRY_UNCOFIRMED_PAYMENT_DELAY } from '@build-5/interfaces';
import { BehaviorSubject, Subscription, catchError, forkJoin, from, map, of, switchMap, take, tap } from 'rxjs';
import { NftApi } from '@api/nft.api';
import { OrderApi } from '@api/order.api';
Expand All @@ -13,10 +13,14 @@ import { NzSelectOptionInterface } from 'ng-zorro-antd/select';
import { AlgoliaService } from '@components/algolia/services/algolia.service';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Build5ErrorLookupService } from '@core/services/build5-error-lookup/build5-error-lookup.service';
import { Router, ActivatedRoute } from '@angular/router';

interface TransNft extends Nft {
transfer: boolean;
withdraw: boolean;
statusMessage?: string;
disabled: boolean;
}

interface HistoryItem {
Expand Down Expand Up @@ -63,6 +67,9 @@ export class TransferModalComponent implements OnInit {
private orderApi: OrderApi,
private notification: NotificationService,
public readonly algoliaService: AlgoliaService,
private errorLookupService: Build5ErrorLookupService,
private router: Router,
private activatedRoute: ActivatedRoute,
) {
this.form = new FormGroup({
selectedAccess: this.selectedAccessControl,
Expand All @@ -71,45 +78,38 @@ export class TransferModalComponent implements OnInit {
}

ngOnInit(): void {
console.log('nft-transfer.component ngOnInit fired.');
this.nftSelectionSubscription$.add(
this.nftSelectionService.selectedNftIds$
.pipe(
tap(ids => console.log('IDs before switchMap:', ids)),
switchMap(ids => {
if (ids.length === 0) {
console.log('nft-transfer.component ngOnInit - there are no nft IDs, return empty array.');
return of([]);
}
console.log('mid check');
return forkJoin(ids.map(id => this.nftApi.getNftById(id).pipe(
tap(_ => console.log(`Fetched NFT with ID ${id}`)),
take(1),
catchError(error => {
console.error(`Error fetching NFT with ID ${id}:`, error);
return of(null);
})
)));
}),
tap(nfts => console.log('NFTs after filtering:', nfts)),
map(nfts => nfts.filter((nft): nft is Nft => nft !== null && nft !== undefined)
.map(nft => ({
...nft,
transfer: false,
withdraw: false
withdraw: false,
disabled: false,
}))
)
)
.subscribe(nfts => {
this.selectedNfts = this.sortNfts(nfts);
console.log('Sorted NFTs: ', this.selectedNfts);
this.cd.markForCheck();
})

);

this.targetAccessOption$.pipe(untilDestroyed(this)).subscribe((targetAccessOption) => {
console.log('nft-transfer.component ngOnInit - ')
this.selectedAccessControl.setValue(targetAccessOption);
});
}
Expand All @@ -131,7 +131,6 @@ export class TransferModalComponent implements OnInit {
}),

);
console.log('nft-transfer.component subscribeMemberList - filteredMembers$: ', this.filteredMembers$);
});
}

Expand All @@ -158,8 +157,8 @@ export class TransferModalComponent implements OnInit {
}

public getSubmitButtonTooltip(): string {
console.log('nft-transfer.component getSubmitButtonTooltip - this.recipientAddress, this.selectedAccessControl.value: ', this.recipientAddress, this.selectedAccessControl.value);
if (!this.recipientAddress) {
const addressTrue = (this.selectedAccessControl.value === 0 && !!this.recipientAddress) || (this.selectedAccessControl.value === 1 && !!this.transferMemberControl.value);
if (!addressTrue) {
return 'Input address or choose member to allow transfer of NFTs';
}
if (this.selectedNfts.every(nft => !nft.transfer)) {
Expand All @@ -170,35 +169,43 @@ export class TransferModalComponent implements OnInit {

public canSubmit(): boolean {
const addressTrue = (this.selectedAccessControl.value === 0 && !!this.recipientAddress) || (this.selectedAccessControl.value === 1 && !!this.transferMemberControl.value);
console.log('nft-transfer canSubmit - testing address true (this.selectedAccessControl.value, this.recipientAddress, this.transferMemberControl.value, addressTrue): ', this.selectedAccessControl.value, this.recipientAddress, this.transferMemberControl.value, addressTrue);
return addressTrue && this.selectedNfts.some(nft => nft.transfer);
}

public async onSubmit(): Promise<void> {
const sendToAddress = (this.selectedAccessControl.value === 0) ? this.recipientAddress : this.transferMemberControl.value;
console.log('nft-transfer onSubmit - calculated sendToAddress: ', sendToAddress);
const request: NftTransferRequest = {
transfers: this.selectedNfts
.filter(nft => nft.transfer)
.map(nft => ({
nft: nft.uid,
target: this.recipientAddress,
target: sendToAddress,
withdraw: nft.withdraw || undefined
}))
};
console.log('nft-transfer.component onSubmit - NftTransferRequest created: ', request);

const params = request;
await this.auth.sign(params, (sc, finish) => {
this.notification
.processRequest(this.nftApi.transferNft(sc), $localize`Order created.`, finish)
.processRequest(this.nftApi.transferNft(sc), $localize`Tranfer initiated.`, finish)
.subscribe((val: any) => {
console.log('nft-transfer.component onSubmit - subscribing to val: ', val);
//this.transSubscription$?.unsubscribe();
//setItem(StorageItem.TransferNftsTransaction, val.uid);
//this.transSubscription$ = this.orderApi.listen(val.uid).subscribe(<any>this.transaction$);
//console.log('nft-transfer.component onSubmit - new this.transSubscription$: ', this.transSubscription$);
//this.pushToHistory(val, val.uid, dayjs(), $localize`Waiting for transaction...`);
if (val && typeof val === 'object') {
Object.entries(val).forEach(([nftId, responseCode]) => {
const nftIndex = this.selectedNfts.findIndex(nft => nft.uid === nftId);
if (nftIndex !== -1) {
if (responseCode === 200) {
this.selectedNfts[nftIndex].transfer = false;
this.selectedNfts[nftIndex].withdraw = false;
this.selectedNfts[nftIndex].disabled = true;
this.selectedNfts[nftIndex].statusMessage = 'NFT successfully transferred.';
} else {
const codeDesc = this.getErrorDesc(responseCode as number);
this.selectedNfts[nftIndex].statusMessage = codeDesc;
}
}
});
}
this.cd.markForCheck();
});
});
}
Expand All @@ -210,7 +217,6 @@ export class TransferModalComponent implements OnInit {
text?: string,
link?: string,
): void {
console.log('nft-transfer.component pushToHistory fired.');
if (
this.history.find((s) => {
return s.uniqueId === uniqueId;
Expand All @@ -234,6 +240,30 @@ export class TransferModalComponent implements OnInit {
this.modalRef.destroy();
}

onModalClose(): void {
const idsOfTransferredNfts = this.selectedNfts.filter(nft => nft.disabled).map(nft => nft.uid);
if (idsOfTransferredNfts.length > 0) {
this.selectedNfts = this.selectedNfts.filter(nft => !nft.disabled);

idsOfTransferredNfts.forEach(nftId => {
this.nftSelectionService.deselectNft(nftId);
});

const memberId = this.auth.member$.value?.uid;
if (memberId) {
const expectedPath = `/member/${memberId}/nfts`;
const currentPath = this.router.url;
if (currentPath === expectedPath) {

this.router.navigateByUrl('/', { skipLocationChange: true }).then(() => {
window.location.href = `/member/${memberId}/nfts`;
});
}
}
}
}


private sortNfts(nfts: TransNft[]): TransNft[] {
return nfts.sort((a, b) => {
const networkA = a.mintingData?.network || '';
Expand All @@ -248,5 +278,7 @@ export class TransferModalComponent implements OnInit {
});
}


public getErrorDesc(errorCode: number): string {
return this.errorLookupService.getErrorMessage(errorCode);
}
}
6 changes: 5 additions & 1 deletion src/app/pages/member/pages/nfts/nfts.page.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Router, ActivatedRoute } from '@angular/router';
import { NftApi } from '@api/nft.api';
import { defaultPaginationItems } from '@components/algolia/algolia.options';
import { AlgoliaService } from '@components/algolia/services/algolia.service';
Expand All @@ -12,6 +12,7 @@ import { DataService } from '@pages/member/services/data.service';
import { COL, Member, Timestamp } from '@build-5/interfaces';
import { InstantSearchConfig } from 'angular-instantsearch/instantsearch/instantsearch';
import { BehaviorSubject } from 'rxjs';
import { NftSelectionService } from '@core/services/nft-selection/nft-selection.service';

@UntilDestroy()
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
Expand Down Expand Up @@ -39,6 +40,8 @@ export class NFTsPage implements OnInit {
private cd: ChangeDetectorRef,
private auth: AuthService,
private route: ActivatedRoute,
private router: Router,
private nftSelectionService: NftSelectionService,
) {}

public ngOnInit(): void {
Expand Down Expand Up @@ -70,6 +73,7 @@ export class NFTsPage implements OnInit {
setInterval(() => this.cd.markForCheck(), 200);
}
});

});
}

Expand Down

0 comments on commit c0342a9

Please sign in to comment.