@block-auth.io/angular 0.2.10 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +40 -37
- package/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-test.log +102 -0
- package/.turbo/turbo-typecheck.log +19 -0
- package/CHANGELOG.md +113 -0
- package/package.json +3 -3
- package/src/lib/components/txns/internals/permissions-required-modal.component.test.ts +9 -0
- package/src/lib/components/txns/internals/permissions-required-modal.component.ts +3 -3
- package/src/lib/components/txns/internals/phone-input.component.test.ts +119 -0
- package/src/lib/components/txns/internals/phone-input.component.ts +301 -0
- package/src/lib/components/txns/internals/solicited-data-modal.component.test.ts +561 -4
- package/src/lib/components/txns/internals/solicited-data-modal.component.ts +324 -129
- package/src/lib/components/txns/internals/txn-type.component.ts +2 -2
- package/src/lib/components/txns/internals/txn.component.test.ts +345 -0
- package/src/lib/components/txns/internals/txn.component.ts +157 -74
- package/src/lib/components/txns/txns-overview.component.test.ts +270 -2
- package/src/lib/components/txns/txns-overview.component.ts +153 -23
- package/src/lib/primitives/arrow-icon.component.test.ts +13 -0
- package/src/lib/primitives/arrow-icon.component.ts +8 -1
- package/src/public-api.ts +1 -0
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
effect,
|
|
6
6
|
input,
|
|
7
7
|
signal,
|
|
8
|
+
untracked,
|
|
8
9
|
} from '@angular/core';
|
|
9
10
|
import {
|
|
10
11
|
$extension,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
openExtensionOnTxn,
|
|
13
14
|
t as translate,
|
|
14
15
|
type TextKey,
|
|
16
|
+
type TxnDataRequest,
|
|
15
17
|
} from '@block-auth.io/core';
|
|
16
18
|
import { injectBlockAuthClient } from '../../provide-block-auth';
|
|
17
19
|
import { injectStore } from '../../inject-store';
|
|
@@ -22,7 +24,21 @@ import { PermissionsRequiredModalComponent } from './internals/permissions-requi
|
|
|
22
24
|
import {
|
|
23
25
|
SolicitedDataModalComponent,
|
|
24
26
|
type ParamChange,
|
|
27
|
+
type ShareChange,
|
|
25
28
|
} from './internals/solicited-data-modal.component';
|
|
29
|
+
import {
|
|
30
|
+
EMPTY_SOLICITED_DATA_FORM,
|
|
31
|
+
getSolicitedAcceptParams,
|
|
32
|
+
getSolicitedFields,
|
|
33
|
+
getSolicitedParams,
|
|
34
|
+
isSolicitedDataComplete,
|
|
35
|
+
saveSolicitedValue,
|
|
36
|
+
setSolicitedShared,
|
|
37
|
+
setSolicitedValue,
|
|
38
|
+
undoSolicitedValue,
|
|
39
|
+
type SolicitedDataForm,
|
|
40
|
+
type SolicitedParam,
|
|
41
|
+
} from '@block-auth.io/core';
|
|
26
42
|
|
|
27
43
|
// Orquestador de txns: lee las solicitudes pendientes de `$ws.requests` y la
|
|
28
44
|
// config de UI de `$ui.uiConfig` (vía injectStore). Se conserva la navegación
|
|
@@ -84,13 +100,15 @@ const TXN_ERROR_TEXT_KEYS: Record<string, TextKey> = {
|
|
|
84
100
|
[isCountdown]="false"
|
|
85
101
|
/>
|
|
86
102
|
} @else if (!isEmpty()) {
|
|
87
|
-
<div
|
|
103
|
+
<div
|
|
104
|
+
class="bal-ba-glass-rise bas-flex bas-flex-col bas-items-center bas-gap-3"
|
|
105
|
+
>
|
|
88
106
|
<img
|
|
89
107
|
[src]="
|
|
90
108
|
preParams().iconUrl ||
|
|
91
109
|
'https://dashboard.block-auth.io/img/blockauth_logo.png'
|
|
92
110
|
"
|
|
93
|
-
class="bas-h-16"
|
|
111
|
+
class="bal-ba-glass-logo bas-h-16"
|
|
94
112
|
alt="app logo"
|
|
95
113
|
/>
|
|
96
114
|
<h2 class="bas-m-0 bas-text-base bas-text-[var(--bal-primary-600)]">
|
|
@@ -138,9 +156,12 @@ const TXN_ERROR_TEXT_KEYS: Record<string, TextKey> = {
|
|
|
138
156
|
<ba-txn
|
|
139
157
|
[hasSolicitedData]="hasSolicitedData()"
|
|
140
158
|
[allRequiredFilled]="allRequiredFilled()"
|
|
159
|
+
[solicitedDataPending]="solicitedDataPending()"
|
|
141
160
|
[closeTxns]="closeTxns()"
|
|
142
161
|
[selectedTxnPreParams]="preParams()"
|
|
143
|
-
[userParamValues]="
|
|
162
|
+
[userParamValues]="acceptParams().userParams"
|
|
163
|
+
[grantedParams]="acceptParams().grantedParams"
|
|
164
|
+
[revokedParams]="acceptParams().revokedParams"
|
|
144
165
|
[addError]="addError"
|
|
145
166
|
(permissionsRequiredClicked)="showPermissions.set(true)"
|
|
146
167
|
(solicitedDataClicked)="showSolicitedData.set(true)"
|
|
@@ -148,7 +169,7 @@ const TXN_ERROR_TEXT_KEYS: Record<string, TextKey> = {
|
|
|
148
169
|
</div>
|
|
149
170
|
} } @if (showPermissions()) {
|
|
150
171
|
<div
|
|
151
|
-
class="bal-ba-
|
|
172
|
+
class="bal-ba-glass-backdrop bal-ba-backdrop-blur-base bal-ba-backdrop-blur-sm bas-absolute bas-z-30 bas-box-border bas-h-full bas-w-full"
|
|
152
173
|
(click)="showPermissions.set(false)"
|
|
153
174
|
></div>
|
|
154
175
|
<div class="bas-absolute bas-top-44 bas-z-40">
|
|
@@ -156,18 +177,27 @@ const TXN_ERROR_TEXT_KEYS: Record<string, TextKey> = {
|
|
|
156
177
|
</div>
|
|
157
178
|
} @if (showSolicitedData()) {
|
|
158
179
|
<div
|
|
159
|
-
class="bal-ba-
|
|
180
|
+
class="bal-ba-glass-backdrop bal-ba-backdrop-blur-base bal-ba-backdrop-blur-sm bas-absolute bas-z-30 bas-box-border bas-h-full bas-w-full"
|
|
160
181
|
(click)="showSolicitedData.set(false)"
|
|
161
182
|
></div>
|
|
162
|
-
<
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
183
|
+
<ba-solicited-data-modal
|
|
184
|
+
class="bal-ba-glass-modal-slot bas-absolute bas-inset-y-6 bas-z-40"
|
|
185
|
+
[requiredParams]="requiredParams()"
|
|
186
|
+
[optionalParams]="optionalParams()"
|
|
187
|
+
[params]="txnData()?.params"
|
|
188
|
+
[userParamValues]="solicitedForm().values"
|
|
189
|
+
[sharedParams]="solicitedForm().shared"
|
|
190
|
+
[savedParams]="solicitedForm().saved"
|
|
191
|
+
[errorTxt]="acceptTxnError() ? t('requiredDataError') : undefined"
|
|
192
|
+
[closable]="true"
|
|
193
|
+
[acceptable]="true"
|
|
194
|
+
(paramChange)="handleParamChange($event)"
|
|
195
|
+
(shareChange)="handleShareChange($event)"
|
|
196
|
+
(saveParam)="handleSaveParam($event)"
|
|
197
|
+
(undoParam)="handleUndoParam($event)"
|
|
198
|
+
(closed)="showSolicitedData.set(false)"
|
|
199
|
+
(accepted)="showSolicitedData.set(false)"
|
|
200
|
+
/>
|
|
171
201
|
}
|
|
172
202
|
</div>
|
|
173
203
|
`,
|
|
@@ -187,8 +217,12 @@ export class TxnsOverviewComponent {
|
|
|
187
217
|
protected readonly selectedIndex = signal(0);
|
|
188
218
|
protected readonly showPermissions = signal(false);
|
|
189
219
|
protected readonly showSolicitedData = signal(false);
|
|
190
|
-
protected readonly
|
|
191
|
-
|
|
220
|
+
protected readonly solicitedForm = signal<SolicitedDataForm>(
|
|
221
|
+
EMPTY_SOLICITED_DATA_FORM,
|
|
222
|
+
);
|
|
223
|
+
protected readonly txnData = signal<TxnDataRequest | null>(null);
|
|
224
|
+
private readonly isLoadingTxnData = signal(false);
|
|
225
|
+
private readonly txnDataReload = signal(0);
|
|
192
226
|
protected readonly error = signal<{ value: boolean; messageSlug: string }>({
|
|
193
227
|
value: false,
|
|
194
228
|
messageSlug: '',
|
|
@@ -198,6 +232,7 @@ export class TxnsOverviewComponent {
|
|
|
198
232
|
protected readonly did = computed(() => this.ws().did);
|
|
199
233
|
protected readonly isSigning = computed(() => this.ui().isSigning);
|
|
200
234
|
protected readonly isOtpRequired = computed(() => this.ui().isOtpRequired);
|
|
235
|
+
protected readonly acceptTxnError = computed(() => this.ui().acceptTxnError);
|
|
201
236
|
protected readonly openExtensionOnTxn = openExtensionOnTxn;
|
|
202
237
|
protected readonly isEmpty = computed(() => this.txns().length === 0);
|
|
203
238
|
protected readonly errorText = computed(() =>
|
|
@@ -244,16 +279,86 @@ export class TxnsOverviewComponent {
|
|
|
244
279
|
protected readonly optionalParams = computed(
|
|
245
280
|
() => this.requiredOptionalParams().optionalParams,
|
|
246
281
|
);
|
|
247
|
-
|
|
248
|
-
() => this.
|
|
282
|
+
private readonly selectedTxnId = computed(
|
|
283
|
+
() => this.selectedTxn()?.['id'] as number | string | undefined,
|
|
284
|
+
);
|
|
285
|
+
private readonly solicitedParams = computed(() =>
|
|
286
|
+
getSolicitedParams({
|
|
287
|
+
params: this.txnData()?.params,
|
|
288
|
+
requiredParams: this.requiredParams(),
|
|
289
|
+
optionalParams: this.optionalParams(),
|
|
290
|
+
}),
|
|
291
|
+
);
|
|
292
|
+
protected readonly hasSolicitedData = computed(() =>
|
|
293
|
+
this.txnData()
|
|
294
|
+
? this.solicitedParams().length > 0
|
|
295
|
+
: this.requiredParams().length > 0 || this.optionalParams().length > 0,
|
|
296
|
+
);
|
|
297
|
+
private readonly solicitedFields = computed(() =>
|
|
298
|
+
getSolicitedFields(
|
|
299
|
+
this.solicitedParams(),
|
|
300
|
+
this.solicitedForm(),
|
|
301
|
+
!!this.txnData(),
|
|
302
|
+
),
|
|
303
|
+
);
|
|
304
|
+
private readonly isSolicitedComplete = computed(() =>
|
|
305
|
+
isSolicitedDataComplete(this.solicitedFields()),
|
|
306
|
+
);
|
|
307
|
+
protected readonly allRequiredFilled = computed(
|
|
308
|
+
() =>
|
|
309
|
+
(!this.isLoadingTxnData() || this.solicitedParams().length === 0) &&
|
|
310
|
+
this.isSolicitedComplete(),
|
|
311
|
+
);
|
|
312
|
+
protected readonly solicitedDataPending = computed(
|
|
313
|
+
() =>
|
|
314
|
+
this.hasSolicitedData() &&
|
|
315
|
+
!this.isLoadingTxnData() &&
|
|
316
|
+
!this.isSolicitedComplete(),
|
|
317
|
+
);
|
|
318
|
+
protected readonly acceptParams = computed(() =>
|
|
319
|
+
getSolicitedAcceptParams(this.solicitedFields()),
|
|
249
320
|
);
|
|
250
321
|
|
|
251
322
|
constructor() {
|
|
252
|
-
|
|
253
|
-
|
|
323
|
+
effect((onCleanup) => {
|
|
324
|
+
const txnId = this.selectedTxnId();
|
|
325
|
+
this.txnDataReload();
|
|
326
|
+
untracked(() => {
|
|
327
|
+
this.txnData.set(null);
|
|
328
|
+
if (txnId == null) {
|
|
329
|
+
this.isLoadingTxnData.set(false);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
let active = true;
|
|
333
|
+
this.isLoadingTxnData.set(true);
|
|
334
|
+
Promise.resolve()
|
|
335
|
+
.then(() => this.client.txns.dataRequest(txnId))
|
|
336
|
+
.catch(() => null)
|
|
337
|
+
.then((result) => {
|
|
338
|
+
if (!active) return;
|
|
339
|
+
this.txnData.set(result ?? null);
|
|
340
|
+
this.isLoadingTxnData.set(false);
|
|
341
|
+
});
|
|
342
|
+
onCleanup(() => {
|
|
343
|
+
active = false;
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
254
348
|
effect(() => {
|
|
255
|
-
|
|
256
|
-
|
|
349
|
+
if (!this.acceptTxnError()) return;
|
|
350
|
+
untracked(() => {
|
|
351
|
+
this.showSolicitedData.set(true);
|
|
352
|
+
this.txnDataReload.update((n) => n + 1);
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
effect(() => {
|
|
357
|
+
this.selectedTxnId();
|
|
358
|
+
untracked(() => {
|
|
359
|
+
this.solicitedForm.set(EMPTY_SOLICITED_DATA_FORM);
|
|
360
|
+
this.client.state.$ui.setKey('acceptTxnError', null);
|
|
361
|
+
});
|
|
257
362
|
});
|
|
258
363
|
}
|
|
259
364
|
|
|
@@ -263,8 +368,33 @@ export class TxnsOverviewComponent {
|
|
|
263
368
|
this.client.state.$ui.setKey('isAcceptingTxn', false);
|
|
264
369
|
};
|
|
265
370
|
|
|
371
|
+
private updateParam(
|
|
372
|
+
slug: string,
|
|
373
|
+
update: (
|
|
374
|
+
form: SolicitedDataForm,
|
|
375
|
+
item: SolicitedParam,
|
|
376
|
+
) => SolicitedDataForm,
|
|
377
|
+
): void {
|
|
378
|
+
const item = this.solicitedParams().find((param) => param.slug === slug);
|
|
379
|
+
if (item) this.solicitedForm.update((prev) => update(prev, item));
|
|
380
|
+
}
|
|
381
|
+
|
|
266
382
|
handleParamChange({ slug, value }: ParamChange): void {
|
|
267
|
-
this.
|
|
383
|
+
this.updateParam(slug, (form, item) =>
|
|
384
|
+
setSolicitedValue(form, item, value),
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
handleShareChange({ slug, shared }: ShareChange): void {
|
|
389
|
+
this.solicitedForm.update((prev) => setSolicitedShared(prev, slug, shared));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
handleSaveParam(slug: string): void {
|
|
393
|
+
this.solicitedForm.update((prev) => saveSolicitedValue(prev, slug));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
handleUndoParam(slug: string): void {
|
|
397
|
+
this.updateParam(slug, undoSolicitedValue);
|
|
268
398
|
}
|
|
269
399
|
|
|
270
400
|
onPrevTxnClicked(): void {
|
|
@@ -9,4 +9,17 @@ describe('ArrowIconComponent', () => {
|
|
|
9
9
|
const { container } = await render(Host);
|
|
10
10
|
expect(container.querySelector('.bal-ba-arrow-left')).toBeTruthy();
|
|
11
11
|
});
|
|
12
|
+
|
|
13
|
+
it('es visible por defecto y se oculta con visible=false', async () => {
|
|
14
|
+
@Component({
|
|
15
|
+
standalone: true,
|
|
16
|
+
imports: [ArrowIconComponent],
|
|
17
|
+
template: `<ba-arrow-icon direction="right" /><ba-arrow-icon direction="left" [visible]="false" />`,
|
|
18
|
+
})
|
|
19
|
+
class Host {}
|
|
20
|
+
const { container } = await render(Host);
|
|
21
|
+
const wrappers = container.querySelectorAll('[aria-hidden="true"]');
|
|
22
|
+
expect(wrappers[0].className).not.toContain('bas-invisible');
|
|
23
|
+
expect(wrappers[1].className).toContain('bas-invisible');
|
|
24
|
+
});
|
|
12
25
|
});
|
|
@@ -7,7 +7,13 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
|
|
|
7
7
|
standalone: true,
|
|
8
8
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
9
9
|
template: `
|
|
10
|
-
<div
|
|
10
|
+
<div
|
|
11
|
+
aria-hidden="true"
|
|
12
|
+
[class]="
|
|
13
|
+
'bal-ba-transition-3 bas-flex bas-h-[30px] bas-w-[65px] bas-items-center bas-justify-center ' +
|
|
14
|
+
(visible() ? 'bas-opacity-100' : 'bas-invisible bas-opacity-0')
|
|
15
|
+
"
|
|
16
|
+
>
|
|
11
17
|
<div
|
|
12
18
|
[class]="
|
|
13
19
|
'bal-ba-arrow bas-relative bas-flex bas-h-[65px] bas-w-[65px] bas-cursor-pointer bas-justify-center ' +
|
|
@@ -25,6 +31,7 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co
|
|
|
25
31
|
})
|
|
26
32
|
export class ArrowIconComponent {
|
|
27
33
|
direction = input<'right' | 'left'>('right');
|
|
34
|
+
visible = input(true);
|
|
28
35
|
|
|
29
36
|
spanClassNames = computed(
|
|
30
37
|
() =>
|
package/src/public-api.ts
CHANGED