Skip to content

Commit 5aeafcb

Browse files
authored
Merge pull request #3291 from numbersprotocol/fix-network-action-value
fix(features/home): network action default value
2 parents c70d5dc + d156166 commit 5aeafcb

12 files changed

+19
-488
lines changed

src/app/features/home/details/actions/action-details/action-details.page.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@
3333
<div class="price-number">{{ price$ | async }} NUM</div>
3434
</div>
3535

36-
<button
36+
<ion-button
3737
type="submit"
38-
class="custom-button"
38+
shape="round"
3939
[disabled]="!form.form.valid"
4040
(click)="performAction(form.model)"
4141
>
4242
{{ title$ | async | uppercase }}
43-
</button>
43+
</ion-button>
4444
</ion-footer>
4545
</ng-container>
4646

src/app/features/home/details/actions/action-details/action-details.page.scss

-22
Original file line numberDiff line numberDiff line change
@@ -72,28 +72,6 @@ ion-footer {
7272
font-weight: 700;
7373
}
7474
}
75-
76-
.custom-button {
77-
padding: 16px 20px;
78-
background-color: #3880ff;
79-
color: white;
80-
border: none;
81-
border-radius: 37px;
82-
cursor: pointer;
83-
white-space: nowrap;
84-
overflow: hidden;
85-
text-overflow: ellipsis;
86-
font-size: 13px;
87-
font-style: normal;
88-
font-weight: 600;
89-
line-height: normal;
90-
letter-spacing: 0.26px;
91-
text-transform: uppercase;
92-
93-
&:hover {
94-
background-color: #0056b3;
95-
}
96-
}
9775
}
9876

9977
ion-spinner {

src/app/features/home/details/actions/action-details/action-details.page.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ export class ActionDetailsPage {
123123
const formModel: any = {};
124124

125125
for (const param of params)
126-
formModel[param.name_text] = param.default_values_list_text[0] || '';
126+
formModel[param.name_text] = param.default_values_list_text?.length
127+
? param.default_values_list_text[0].trim()
128+
: '';
127129

128130
return formModel;
129131
}
@@ -140,10 +142,11 @@ export class ActionDetailsPage {
140142
key: param.name_text,
141143
type: 'select',
142144
templateOptions: {
143-
options: param.default_values_list_text.map(value => ({
144-
label: value,
145-
value: value,
146-
})),
145+
options:
146+
param.default_values_list_text?.map(value => ({
147+
label: value.trim(),
148+
value: value.trim(),
149+
})) ?? [],
147150
placeholder: param.placeholder_text,
148151
disabled: !param.user_input_boolean,
149152
required: !isOptional,
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,12 @@
11
import { Component } from '@angular/core';
2-
import { MatDialog } from '@angular/material/dialog';
3-
import { MatSnackBar } from '@angular/material/snack-bar';
42
import { ActivatedRoute, Router } from '@angular/router';
5-
import { Browser } from '@capacitor/browser';
6-
import { TranslocoService } from '@ngneat/transloco';
7-
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
8-
import { combineLatest, forkJoin, iif, of } from 'rxjs';
9-
import { catchError, concatMap, first, map, take, tap } from 'rxjs/operators';
10-
import { ActionsDialogComponent } from '../../../../shared/actions/actions-dialog/actions-dialog.component';
3+
import { UntilDestroy } from '@ngneat/until-destroy';
4+
import { catchError } from 'rxjs/operators';
115
import {
126
Action,
137
ActionsService,
148
} from '../../../../shared/actions/service/actions.service';
15-
import { OrderHistoryService } from '../../../../shared/actions/service/order-history.service';
16-
import { BlockingActionService } from '../../../../shared/blocking-action/blocking-action.service';
17-
import { DiaBackendAuthService } from '../../../../shared/dia-backend/auth/dia-backend-auth.service';
18-
import { DiaBackendSeriesRepository } from '../../../../shared/dia-backend/series/dia-backend-series-repository.service';
19-
import {
20-
DiaBackendStoreService,
21-
NetworkAppOrder,
22-
} from '../../../../shared/dia-backend/store/dia-backend-store.service';
23-
import { DiaBackendWalletService } from '../../../../shared/dia-backend/wallet/dia-backend-wallet.service';
249
import { ErrorService } from '../../../../shared/error/error.service';
25-
import { OrderDetailDialogComponent } from '../../../../shared/order-detail-dialog/order-detail-dialog.component';
26-
import { ProofRepository } from '../../../../shared/repositories/proof/proof-repository.service';
27-
import { browserToolbarColor } from '../../../../utils/constants';
28-
import {
29-
VOID$,
30-
isNonNullable,
31-
} from '../../../../utils/rx-operators/rx-operators';
32-
import { InformationSessionService } from '../information/session/information-session.service';
3310

3411
@UntilDestroy()
3512
@Component({
@@ -42,270 +19,17 @@ export class ActionsPage {
4219
.getActions$()
4320
.pipe(catchError((err: unknown) => this.errorService.toastError$(err)));
4421

45-
private readonly id$ = this.route.paramMap.pipe(
46-
map(params => params.get('id')),
47-
isNonNullable()
48-
);
49-
5022
constructor(
5123
private readonly router: Router,
5224
private readonly actionsService: ActionsService,
5325
private readonly errorService: ErrorService,
54-
private readonly translocoService: TranslocoService,
55-
private readonly blockingActionService: BlockingActionService,
56-
private readonly route: ActivatedRoute,
57-
private readonly authService: DiaBackendAuthService,
58-
private readonly snackBar: MatSnackBar,
59-
private readonly dialog: MatDialog,
60-
private readonly storeService: DiaBackendStoreService,
61-
private readonly orderHistoryService: OrderHistoryService,
62-
private readonly diaBackendStoreService: DiaBackendStoreService,
63-
private readonly diaBackendSeriesRepository: DiaBackendSeriesRepository,
64-
private readonly diaBackendWalletService: DiaBackendWalletService,
65-
private readonly informationSessionService: InformationSessionService,
66-
private readonly proofRepository: ProofRepository
26+
private readonly route: ActivatedRoute
6727
) {}
6828

69-
canPerformAction$(action: Action) {
70-
if (action.title_text === 'List in CaptureClub') {
71-
/*
72-
Workaround:
73-
Currently there isn't a simple way to check whether an asset is listed in
74-
CaptureClub or not. So I first query List all Products API with
75-
associated_id parameter set to the assets cid. And then use list series
76-
API and check through all nested collections. See discussion here
77-
https://app.asana.com/0/0/1201558520076805/1201995911008176/f
78-
*/
79-
return this.id$.pipe(
80-
concatMap(cid =>
81-
forkJoin([
82-
this.diaBackendStoreService.listAllProducts$({
83-
associated_id: cid,
84-
service_name: 'CaptureClub',
85-
}),
86-
of(cid),
87-
])
88-
),
89-
concatMap(([response, cid]) => {
90-
if (response.count > 0) {
91-
throw new Error(
92-
this.translocoService.translate('message.hasListedInCaptureApp')
93-
);
94-
}
95-
return of(cid);
96-
}),
97-
concatMap(async cid => {
98-
let currentOffset = 0;
99-
const limit = 100;
100-
while (true) {
101-
const response = await this.diaBackendSeriesRepository
102-
.fetchAll$({ offset: currentOffset, limit })
103-
.toPromise();
104-
const listedAsSeries = response.results.some(serie =>
105-
serie.collections.some(collection =>
106-
collection.assets.some(asset => asset.cid === cid)
107-
)
108-
);
109-
if (listedAsSeries) {
110-
throw new Error(
111-
this.translocoService.translate('message.hasListedInCaptureApp')
112-
);
113-
}
114-
if (response.next == null) {
115-
break;
116-
}
117-
currentOffset += response.results.length;
118-
}
119-
return VOID$;
120-
}),
121-
take(1)
122-
);
123-
}
124-
return VOID$;
125-
}
126-
127-
openActionDialog$(action: Action) {
128-
return combineLatest([
129-
this.actionsService.getParams$(action.params_list_custom_param1 ?? []),
130-
this.authService.token$,
131-
this.id$,
132-
]).pipe(
133-
first(),
134-
concatMap(([params, token, id]) => {
135-
const dialogRef = this.dialog.open<ActionsDialogComponent>(
136-
ActionsDialogComponent,
137-
{
138-
disableClose: true,
139-
data: {
140-
action: action,
141-
params: params,
142-
},
143-
}
144-
);
145-
return dialogRef.afterClosed().pipe(
146-
isNonNullable(),
147-
concatMap(data =>
148-
of({
149-
networkApp: action.network_app_id_text,
150-
actionArgs: { ...data, token: token, cid: id },
151-
} as CreateOrderInput)
152-
)
153-
);
154-
})
155-
);
156-
}
157-
158-
openOrderDialog$(orderStatus: NetworkAppOrder) {
159-
const dialogRef = this.dialog.open<OrderDetailDialogComponent>(
160-
OrderDetailDialogComponent,
161-
{
162-
disableClose: true,
163-
data: orderStatus,
164-
width: '80%',
165-
}
166-
);
167-
return dialogRef.afterClosed().pipe(
168-
isNonNullable(),
169-
concatMap((orderId: string) => of(orderId))
170-
);
171-
}
172-
173-
createOrder$(appName: string, actionArgs: any) {
174-
return this.storeService.createNetworkAppOrder(appName, actionArgs).pipe(
175-
catchError((err: unknown) =>
176-
this.errorService.toastDiaBackendError$(err)
177-
),
178-
isNonNullable()
179-
);
180-
}
181-
182-
confirmOrder$(id: string) {
183-
return this.storeService.confirmNetworkAppOrder(id).pipe(
184-
catchError((err: unknown) =>
185-
this.errorService.toastDiaBackendError$(err)
186-
),
187-
isNonNullable()
188-
);
189-
}
190-
191-
createOrderHistory$(networkAppOrder: NetworkAppOrder) {
192-
return this.id$.pipe(
193-
first(),
194-
isNonNullable(),
195-
concatMap(cid =>
196-
this.orderHistoryService.createOrderHistory$(networkAppOrder, cid)
197-
),
198-
catchError((err: unknown) => {
199-
return this.errorService.toastError$(err);
200-
})
201-
);
202-
}
203-
204-
redirectToExternalUrl(url: string, orderId: string) {
205-
this.id$
206-
.pipe(
207-
first(),
208-
isNonNullable(),
209-
tap(cid =>
210-
Browser.open({
211-
url: `${url}?cid=${cid}&order_id=${orderId}`,
212-
toolbarColor: browserToolbarColor,
213-
})
214-
),
215-
catchError((err: unknown) => {
216-
return this.errorService.toastError$(err);
217-
}),
218-
untilDestroyed(this)
219-
)
220-
.subscribe();
221-
}
222-
223-
removeCapture() {
224-
if (this.informationSessionService.activatedDetailedCapture) {
225-
this.informationSessionService.activatedDetailedCapture.proof$.subscribe(
226-
proof => {
227-
if (proof) {
228-
this.proofRepository.remove(proof);
229-
this.router.navigate(['/home']);
230-
}
231-
}
232-
);
233-
}
234-
}
235-
23629
performAction(action: Action) {
23730
this.router.navigate(['action-details'], {
23831
relativeTo: this.route,
23932
state: action,
24033
});
24134
}
242-
243-
doAction(action: Action) {
244-
this.blockingActionService
245-
.run$(this.canPerformAction$(action))
246-
.pipe(
247-
catchError((err: unknown) => {
248-
return this.errorService.toastError$(err);
249-
}),
250-
concatMap(() => this.openActionDialog$(action)),
251-
concatMap(createOrderInput =>
252-
this.blockingActionService.run$(
253-
forkJoin([
254-
this.createOrder$(
255-
createOrderInput.networkApp,
256-
createOrderInput.actionArgs
257-
),
258-
// To display "Insufficient NUM" in order confirmation dialog,
259-
// we need to sync asset wallet balance if the action cost NUM.
260-
iif(
261-
() => action.action_cost_number > 0,
262-
this.diaBackendWalletService.syncAssetWalletBalance$(),
263-
VOID$
264-
),
265-
])
266-
)
267-
),
268-
concatMap(([orderStatus, _]) => this.openOrderDialog$(orderStatus)),
269-
concatMap(orderId =>
270-
this.blockingActionService.run$(this.confirmOrder$(orderId))
271-
),
272-
tap(networkAppOrder => {
273-
/*
274-
Workaround:
275-
Create a order history record only if the total cost is > 0 to prevent race condition
276-
between app creating the order history record v.s. bubble workflow checking whether a
277-
record already exists and if not create a new one, especially for network actions that
278-
don't require any cost (and hence backend calls the webhook immediately). See
279-
https://dt42-numbers.slack.com/archives/C0323488MEJ/p1648006014291339
280-
*/
281-
if (Number(networkAppOrder.total_cost) !== 0) {
282-
this.createOrderHistory$(networkAppOrder).subscribe();
283-
}
284-
}),
285-
tap(() => {
286-
this.snackBar.open(
287-
this.translocoService.translate('message.sentSuccessfully')
288-
);
289-
}),
290-
tap(networkAppOrder => {
291-
if (action.ext_action_destination_text) {
292-
this.redirectToExternalUrl(
293-
action.ext_action_destination_text,
294-
networkAppOrder.id
295-
);
296-
}
297-
}),
298-
tap(() => {
299-
if (action.hide_capture_after_execution_boolean ?? false)
300-
this.removeCapture();
301-
}),
302-
untilDestroyed(this)
303-
)
304-
.subscribe();
305-
}
306-
}
307-
308-
interface CreateOrderInput {
309-
networkApp: string;
310-
actionArgs: any;
31135
}

0 commit comments

Comments
 (0)