diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 346c28b84..9137fc7b7 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - + @@ -41,6 +42,9 @@ + + diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 20410941e..8e0380132 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -372,7 +372,7 @@ DEVELOPMENT_TEAM = G7NB5YCKAP; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = G7NB5YCKAP; INFOPLIST_FILE = App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 0.82.4; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; @@ -399,7 +399,7 @@ DEVELOPMENT_TEAM = G7NB5YCKAP; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = G7NB5YCKAP; INFOPLIST_FILE = App/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MARKETING_VERSION = 0.82.4; PRODUCT_BUNDLE_IDENTIFIER = io.numbersprotocol.capturelite; diff --git a/ios/App/Podfile.lock b/ios/App/Podfile.lock index 8c9d684f9..fac8f30db 100644 --- a/ios/App/Podfile.lock +++ b/ios/App/Podfile.lock @@ -18,7 +18,7 @@ PODS: - Capacitor - CapacitorClipboard (5.0.4): - Capacitor - - CapacitorCommunityAdvertisingId (1.0.0): + - CapacitorCommunityAdvertisingId (5.0.0): - Capacitor - CapacitorCommunityBluetoothLe (2.2.3): - Capacitor @@ -223,7 +223,7 @@ SPEC CHECKSUMS: CapacitorBrowser: 2688852d02f1e89560a31b70521c71a5e7348860 CapacitorCamera: 9b5c8e809c1042f263994f97ba846aa37e974f12 CapacitorClipboard: 46f3959735fa0d96b9989dafcc4aed52e624d163 - CapacitorCommunityAdvertisingId: ffbeee30080f0057f7af6e465a7552b68a3d3fdf + CapacitorCommunityAdvertisingId: 41543d8212fb12b6913b798bf1442c2a6bc1ae91 CapacitorCommunityBluetoothLe: 7de4f21022a05b15195abfb002872884d00715fc CapacitorCommunityHttp: 7be90668527ef14ee10d08135b0e00fac9cf8247 CapacitorCommunityWifi: 47188c74f2c6bcaefb619380863be8c67b1917c8 diff --git a/package-lock.json b/package-lock.json index 94f5640cc..f880c4d11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "@angular/router": "^14.2.0", "@awesome-cordova-plugins/core": "^5.46.0", "@awesome-cordova-plugins/in-app-purchase-2": "^5.43.0", - "@capacitor-community/advertising-id": "^1.0.0", + "@capacitor-community/advertising-id": "^5.0.0", "@capacitor-community/bluetooth-le": "^2.2.3", "@capacitor-community/http": "github:numbersprotocol/http#fix-catch-disabled-Local-Network-case-on-iOS", "@capacitor-community/wifi": "github:numbersprotocol/community-capacitor-wifi#capacitor3", @@ -3141,11 +3141,11 @@ } }, "node_modules/@capacitor-community/advertising-id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@capacitor-community/advertising-id/-/advertising-id-1.0.0.tgz", - "integrity": "sha512-KOpmE/mWqDYmIFhGjlo/ppiEoI3R8SW6rEJHIY75CdziTwz67byn+fyV2E+T3YB9UoFWMDyImnTK/MZXgkZNug==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@capacitor-community/advertising-id/-/advertising-id-5.0.0.tgz", + "integrity": "sha512-gCY0Ycxym/hbdOUfJyORaGMtDbLZsLX+u8LHq4vF8iVNqDMjlZSkbFIBXHw5IBTigBOipmHK/pDT6J1U5HR4yQ==", "peerDependencies": { - "@capacitor/core": "^3.0.0" + "@capacitor/core": "^5.0.0" } }, "node_modules/@capacitor-community/bluetooth-le": { @@ -26181,9 +26181,9 @@ } }, "@capacitor-community/advertising-id": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@capacitor-community/advertising-id/-/advertising-id-1.0.0.tgz", - "integrity": "sha512-KOpmE/mWqDYmIFhGjlo/ppiEoI3R8SW6rEJHIY75CdziTwz67byn+fyV2E+T3YB9UoFWMDyImnTK/MZXgkZNug==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@capacitor-community/advertising-id/-/advertising-id-5.0.0.tgz", + "integrity": "sha512-gCY0Ycxym/hbdOUfJyORaGMtDbLZsLX+u8LHq4vF8iVNqDMjlZSkbFIBXHw5IBTigBOipmHK/pDT6J1U5HR4yQ==", "requires": {} }, "@capacitor-community/bluetooth-le": { diff --git a/package.json b/package.json index 2fe4f6ced..8ce1ae06b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@angular/router": "^14.2.0", "@awesome-cordova-plugins/core": "^5.46.0", "@awesome-cordova-plugins/in-app-purchase-2": "^5.43.0", - "@capacitor-community/advertising-id": "^1.0.0", + "@capacitor-community/advertising-id": "^5.0.0", "@capacitor-community/bluetooth-le": "^2.2.3", "@capacitor-community/http": "github:numbersprotocol/http#fix-catch-disabled-Local-Network-case-on-iOS", "@capacitor-community/wifi": "github:numbersprotocol/community-capacitor-wifi#capacitor3", diff --git a/src/app/features/home/activities/activities.page.html b/src/app/features/home/activities/activities.page.html index 1cf546521..2b4120494 100644 --- a/src/app/features/home/activities/activities.page.html +++ b/src/app/features/home/activities/activities.page.html @@ -22,7 +22,7 @@ [text]="t('userGuide.viewNetworkActionsHistory')" > - {{ t('networkActions') }} + {{ t('orders') }} diff --git a/src/app/features/home/activities/activities.page.scss b/src/app/features/home/activities/activities.page.scss index f9aa9a8dc..931eee181 100644 --- a/src/app/features/home/activities/activities.page.scss +++ b/src/app/features/home/activities/activities.page.scss @@ -3,3 +3,11 @@ mat-toolbar { padding-right: 40px; } } + +ion-segment { + display: flex; + + ion-segment-button { + flex: 1; + } +} diff --git a/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.html b/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.html index ff5fff471..5a8bc8653 100644 --- a/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.html +++ b/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.html @@ -1,118 +1,16 @@ - - - {{ t('networkActionOrderDetails') }} - + + + + {{ t('networkActionOrderDetails') }} + + - -
-

- {{ order['Created Date'] | date: 'short' }} -

- - - - - - - - -

{{ order.network_app_name_text }}

-
-
- - - {{ t('order') + ' ID' }}: - - - {{ order.order_id_text }} - - - - - - - - - {{ t('resultUrl') }}: - - - - {{ order.result_url_text }} - - - - {{ resultUrlFromAssetId(order.asset_id_text) }} - - - - - - - - {{ t('payment.price') }}: - - - {{ order.price_number | number: '1.4-4' }} - {{ order.cost_token_ticker_text || 'NUM' }} - - - - - - {{ t('payment.fee') }}: - - - {{ order.gas_fee_number | number: '1.4-4' }} - {{ order.cost_token_ticker_text || 'NUM' }} - - - - - - {{ t('payment.totalCost') }}: - - - {{ order.total_cost_number | number: '1.4-4' }} - {{ order.cost_token_ticker_text || 'NUM' }} - - - - - - - - - -
-
-
-
+ + + + + + diff --git a/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.scss b/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.scss index 6c8e1bfdc..9b00b70d6 100644 --- a/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.scss +++ b/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.scss @@ -1,9 +1,35 @@ mat-toolbar { span { padding-right: 40px; + font-style: normal; + font-weight: 500; + font-size: 16px; + line-height: 21px; + text-align: center; + color: white; } } +.no-network-text { + font-size: 18px; + margin: auto; +} + +iframe { + background-color: black; + width: 100vw; + height: 100vh; + border: 0; +} + +ion-spinner { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + scale: 1.5; +} + ion-content { .datetime { width: 90vw; diff --git a/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.ts b/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.ts index 7a867cf01..7661f06bf 100644 --- a/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.ts +++ b/src/app/features/home/activities/network-action-order-details/network-action-order-details.page.ts @@ -2,12 +2,24 @@ import { Component } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { ActivatedRoute } from '@angular/router'; import { Plugins } from '@capacitor/core'; +import { NavController } from '@ionic/angular'; import { TranslocoService } from '@ngneat/transloco'; -import { UntilDestroy } from '@ngneat/until-destroy'; -import { combineLatest } from 'rxjs'; -import { catchError, first, map } from 'rxjs/operators'; +import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; +import { BehaviorSubject, combineLatest, fromEvent } from 'rxjs'; +import { + catchError, + concatMap, + first, + map, + switchMap, + tap, +} from 'rxjs/operators'; import { OrderHistoryService } from '../../../../shared/actions/service/order-history.service'; +import { BUBBLE_IFRAME_URL } from '../../../../shared/dia-backend/secret'; +import { DiaBackendStoreService } from '../../../../shared/dia-backend/store/dia-backend-store.service'; import { ErrorService } from '../../../../shared/error/error.service'; +import { BubbleToIonicPostMessage } from '../../../../shared/iframe/iframe'; +import { NetworkService } from '../../../../shared/network/network.service'; import { isNonNullable } from '../../../../utils/rx-operators/rx-operators'; import { getAssetProfileForNSE } from '../../../../utils/url'; @@ -19,26 +31,99 @@ const { Browser, Clipboard } = Plugins; styleUrls: ['./network-action-order-details.page.scss'], }) export class NetworkActionOrderDetailsPage { + readonly orderId$ = this.route.paramMap.pipe( + map(params => params.get('order_id')), + isNonNullable() + ); + readonly assetId$ = this.orderId$.pipe( + switchMap(orderId => this.storeService.retrieveNetworkAppOrder$(orderId)), + map(order => { + if ('nid' in order.action_args) return order.action_args.nid as string; + if ('cid' in order.action_args) return order.action_args.cid as string; + return undefined; + }) + ); readonly order$ = combineLatest([ this.orderHistoryService.networkActionOrders$, - this.route.paramMap.pipe( - map(params => params.get('order_id')), - isNonNullable() - ), + this.orderId$, ]).pipe( first(), map(([orders, orderId]) => orders.find(o => o.order_id_text === orderId)), isNonNullable(), catchError((err: unknown) => this.errorService.toastError$(err)) ); + readonly isOffline$ = this.networkService.connected$.pipe( + map(connected => connected === false) + ); + + readonly iframeUrl$ = combineLatest([this.orderId$, this.assetId$]).pipe( + map(([orderId, assetId]) => { + const queryParams = new URLSearchParams(); + + queryParams.append('order_id', orderId); + + /** + * Some network action orders might not have releated asset id (aka nid, cid). + * For example: + * - "Creator Gallery" + * - "One-NUM-price" + * - "CustodialWalletWithdraw" + * - etc + */ + if (assetId) queryParams.append('asset_id', assetId); + + return `${BUBBLE_IFRAME_URL}/version-test/order_details?${queryParams}`; + }) + ); + + readonly iframeLoaded$ = new BehaviorSubject(false); constructor( private readonly route: ActivatedRoute, private readonly orderHistoryService: OrderHistoryService, + private readonly storeService: DiaBackendStoreService, private readonly errorService: ErrorService, private readonly snackBar: MatSnackBar, - private readonly translocoService: TranslocoService - ) {} + private readonly translocoService: TranslocoService, + private readonly networkService: NetworkService, + private readonly navController: NavController + ) { + this.processIframeEvents(); + } + + private processIframeEvents() { + fromEvent(window, 'message') + .pipe( + tap(event => { + const postMessageEvent = event as MessageEvent; + const data = postMessageEvent.data as BubbleToIonicPostMessage; + switch (data) { + case BubbleToIonicPostMessage.IFRAME_ON_LOAD: + this.iframeLoaded$.next(true); + break; + case BubbleToIonicPostMessage.IFRAME_BACK_BUTTON_CLICKED: + this.navController.back(); + break; + case BubbleToIonicPostMessage.IFRAME_COPY_TO_CLIPBOARD_ORDER_ID: + this.copyToClipboardOrderId(); + break; + default: + break; + } + }), + untilDestroyed(this) + ) + .subscribe(); + } + + private copyToClipboardOrderId() { + this.orderId$ + .pipe( + first(), + concatMap(orderId => this.copyToClipboard(orderId)) + ) + .subscribe(); + } // eslint-disable-next-line class-methods-use-this openResultUrl(url: string) { diff --git a/src/app/features/home/activities/network-action-orders/network-action-orders.component.html b/src/app/features/home/activities/network-action-orders/network-action-orders.component.html index 357bea774..9a7429238 100644 --- a/src/app/features/home/activities/network-action-orders/network-action-orders.component.html +++ b/src/app/features/home/activities/network-action-orders/network-action-orders.component.html @@ -1,42 +1,42 @@ - - -
-
- -
- {{ t('noNetworkActionOrdersFound') }} -
- + +
+ {{ t('noNetworkActionOrdersFound') }} +
+ + - - -
{{ order.network_app_name_text }}
-
{{ order.asset_id_text }}
-
{{ order['Created Date'] | date: 'short' }}
- -
- -
-
-
+ +
{{ order.network_app_name }}
+
{{ order.action_args.nid || order.action_args.cid }}
+
{{ order.created_at | date: 'short' }}
+ + + + +
+ + + + diff --git a/src/app/features/home/activities/network-action-orders/network-action-orders.component.scss b/src/app/features/home/activities/network-action-orders/network-action-orders.component.scss index a08bc20b8..e1326af6a 100644 --- a/src/app/features/home/activities/network-action-orders/network-action-orders.component.scss +++ b/src/app/features/home/activities/network-action-orders/network-action-orders.component.scss @@ -1,7 +1,5 @@ mat-spinner { - margin-left: auto; - margin-right: auto; - margin-top: 10%; + margin: 10% auto; width: 100%; } @@ -28,24 +26,24 @@ mat-list-item { min-width: 25%; } - button.completed { + button.success { color: var(--ion-color-dark-contrast); border-color: var(--noir-simple); background-color: var(--noir-simple); } - button.paid { + button.pending { color: var(--noir-secondary-dark); border-color: var(--noir-secondary-dark); } - button.submitted { + button.created { color: var(--ion-color-dark-contrast); border-color: var(--noir-secondary-dark); background-color: var(--noir-secondary-dark); } - button.failed, + button.failure, button.payment_failed { color: var(--ion-color-dark-contrast); border-color: var(--noir-warn); diff --git a/src/app/features/home/activities/network-action-orders/network-action-orders.component.ts b/src/app/features/home/activities/network-action-orders/network-action-orders.component.ts index 6e0d8d3da..6b330eda8 100644 --- a/src/app/features/home/activities/network-action-orders/network-action-orders.component.ts +++ b/src/app/features/home/activities/network-action-orders/network-action-orders.component.ts @@ -1,11 +1,11 @@ import { Component } from '@angular/core'; -import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { BehaviorSubject } from 'rxjs'; -import { catchError, finalize } from 'rxjs/operators'; +import { UntilDestroy } from '@ngneat/until-destroy'; +import { BehaviorSubject, combineLatest } from 'rxjs'; +import { catchError, finalize, first, map, tap } from 'rxjs/operators'; import { - BubbleOrderHistoryRecord, - OrderHistoryService, -} from '../../../../shared/actions/service/order-history.service'; + DiaBackendStoreService, + NetworkAppOrderWithThumbnail, +} from '../../../../shared/dia-backend/store/dia-backend-store.service'; import { ErrorService } from '../../../../shared/error/error.service'; @UntilDestroy({ checkProperties: true }) @@ -15,30 +15,46 @@ import { ErrorService } from '../../../../shared/error/error.service'; styleUrls: ['./network-action-orders.component.scss'], }) export class NetworkActionOrdersComponent { - readonly networkActionOrders$ = this.orderHistoryService.networkActionOrders$; - + readonly orders$ = new BehaviorSubject([]); readonly isFetching$ = new BehaviorSubject(false); + readonly isOrdersEmpty$ = combineLatest([ + this.isFetching$, + this.orders$, + ]).pipe(map(([isFetching, items]) => !isFetching && items.length === 0)); + + private readonly limit = 15; + private offset = 0; + constructor( - private readonly orderHistoryService: OrderHistoryService, + private readonly storeService: DiaBackendStoreService, private readonly errorService: ErrorService ) { + this.loadData(); + } + + loadData(event?: any) { this.isFetching$.next(true); - this.orderHistoryService - .refresh$() + this.storeService + .listAllNetworkAppOrderWithThumbnail$({ + offset: this.offset, + limit: this.limit, + }) .pipe( + first(), + tap(({ results }) => { + this.orders$.next([...this.orders$.value, ...results]); + this.offset += this.limit; + event?.target?.complete(); + }), catchError((err: unknown) => this.errorService.toastError$(err)), - finalize(() => this.isFetching$.next(false)), - untilDestroyed(this) + finalize(() => this.isFetching$.next(false)) ) .subscribe(); } // eslint-disable-next-line class-methods-use-this - trackNetworkActionOrderHistoryRecords( - _: number, - item: BubbleOrderHistoryRecord - ) { - return item._id; + trackByNetworkActionOrder(_: number, item: NetworkAppOrderWithThumbnail) { + return item.id; } } diff --git a/src/app/features/settings/settings.page.ts b/src/app/features/settings/settings.page.ts index 1a43b65b1..4c3072f49 100644 --- a/src/app/features/settings/settings.page.ts +++ b/src/app/features/settings/settings.page.ts @@ -5,7 +5,7 @@ import { Clipboard } from '@capacitor/clipboard'; import { IonModal } from '@ionic/angular'; import { TranslocoService } from '@ngneat/transloco'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { EMPTY, Subject, defer } from 'rxjs'; +import { EMPTY, Subject, defer, forkJoin } from 'rxjs'; import { catchError, concatMapTo, @@ -98,6 +98,12 @@ export class SettingsPage { private readonly customCameraService: CustomCameraService ) {} + ionViewWillEnter() { + forkJoin([this.diaBackendAuthService.syncProfile$()]) + .pipe(untilDestroyed(this)) + .subscribe(); + } + ionViewDidEnter() { this.hiddenOptionClicks$ .pipe( diff --git a/src/app/shared/apps-flyer/apps-flyer.service.ts b/src/app/shared/apps-flyer/apps-flyer.service.ts index 57dd03f4f..9177f0bfb 100644 --- a/src/app/shared/apps-flyer/apps-flyer.service.ts +++ b/src/app/shared/apps-flyer/apps-flyer.service.ts @@ -1,7 +1,8 @@ import { Injectable } from '@angular/core'; import { AdvertisingId } from '@capacitor-community/advertising-id'; +import { Capacitor } from '@capacitor/core'; import { Platform } from '@ionic/angular'; -import { AFEvent, AFInit, AppsFlyer } from 'appsflyer-capacitor-plugin'; +import { AFInit, AppsFlyer } from 'appsflyer-capacitor-plugin'; import { APPS_FLYER_DEV_KEY } from '../dia-backend/secret'; @Injectable({ @@ -25,43 +26,33 @@ export class AppsFlyerService { constructor(private readonly platform: Platform) {} async initAppsFlyerSDK() { - await this.platform.ready(); + try { + await this.platform.ready(); - if (this.shouldInit === false) return; + if (this.shouldInit === false) return; - if (this.platform.is('ios')) { await AdvertisingId.requestTracking(); - } - - await AppsFlyer.initSDK(this.afConfig); - } - private get shouldInit() { - /** - * Do not init apps flyer SDK if dev key is not provided. - */ - // eslint-disable-next-line no-extra-boolean-cast, @typescript-eslint/no-unnecessary-condition - if (!!APPS_FLYER_DEV_KEY) { - return false; - } - /** - * Do not init apps flyer SDK in Web environment. - */ - if (!this.isNativePlatform) { - return false; + await AppsFlyer.initSDK(this.afConfig); + } catch (error) { + // TODO: Report error to Crashlytics or any other error reporting service if available. } - return true; } - async trackUserOpenedWalletsPage() { - if (this.isNativePlatform) return; - - const data: AFEvent = { eventName: 'open-wallets-page' }; - - return AppsFlyer.logEvent(data).catch(() => ({})); - } - - private get isNativePlatform() { - return this.platform.is('hybrid'); + /** + * Determines whether AppsFlyer should be initialized. + * In APK debug or QA builds, we pass an empty string ("") as the APPS_FLYER_DEV_KEY + * to prevent AppsFlyer initialization. This approach helps avoid unnecessary analytics + * or install counts in development (DEV) or quality assurance (QA) builds. + * AppsFlyer will only be initialized if the following conditions are met: + * 1. The APPS_FLYER_DEV_KEY is truthy (not an empty string). + * 2. The app is running on a native platform (e.g., a mobile device). + * + * @returns {boolean} True if AppsFlyer should be initialized, otherwise false. + */ + // eslint-disable-next-line class-methods-use-this + private get shouldInit() { + const isTruthy = Boolean(APPS_FLYER_DEV_KEY); + return isTruthy && Capacitor.isNativePlatform(); } } diff --git a/src/app/shared/dia-backend/store/dia-backend-store.service.ts b/src/app/shared/dia-backend/store/dia-backend-store.service.ts index a2bf961c4..fb03b607d 100644 --- a/src/app/shared/dia-backend/store/dia-backend-store.service.ts +++ b/src/app/shared/dia-backend/store/dia-backend-store.service.ts @@ -1,7 +1,8 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { defer } from 'rxjs'; -import { concatMap } from 'rxjs/operators'; +import { EMPTY, Observable, defer } from 'rxjs'; +import { concatMap, map } from 'rxjs/operators'; +import { DiaBackendAssetRepository } from '../asset/dia-backend-asset-repository.service'; import { DiaBackendAuthService } from '../auth/dia-backend-auth.service'; import { PaginatedResponse } from '../pagination'; import { BASE_URL } from '../secret'; @@ -12,9 +13,92 @@ import { BASE_URL } from '../secret'; export class DiaBackendStoreService { constructor( private readonly httpClient: HttpClient, - private readonly authService: DiaBackendAuthService + private readonly authService: DiaBackendAuthService, + private readonly diaBackendAssetRepository: DiaBackendAssetRepository ) {} + listAllNetworkAppOrders$({ + offset, + limit, + }: { + offset?: number; + limit?: number; + }) { + return defer(() => this.authService.getAuthHeaders()).pipe( + concatMap(headers => { + let params = new HttpParams(); + if (offset !== undefined) { + params = params.set('offset', offset); + } + + if (limit !== undefined) { + params = params.set('limit', limit); + } + + return this.httpClient.get>( + `${BASE_URL}/api/v3/store/network-app-orders/`, + { headers, params } + ); + }) + ); + } + + /** + * Fetches a paginated list of network app orders along with their associated thumbnails, + * if available. The result will be a paginated response containing an array of + * `NetworkAppOrderWithThumbnail` objects. Since the basic `NetworkAppOrder` type does + * not include thumbnail information, we extend it with the `NetworkAppOrderWithThumbnail` + * type to incorporate thumbnail details when available. + */ + listAllNetworkAppOrderWithThumbnail$({ + offset, + limit, + }: { + offset?: number; + limit?: number; + }): Observable> { + return this.listAllNetworkAppOrders$({ offset, limit }).pipe( + map(({ count, results, next, previous }) => { + const resultsWithThumbnail = results.map( + order => ({ + ...order, + assetThumbnailUrl$: this.fetchAssetThumbnailUrl$(order), + }) + ); + return { count, results: resultsWithThumbnail, next, previous }; + }) + ); + } + + fetchAssetThumbnailUrl$(order: NetworkAppOrder) { + let id: string | undefined; + + if (typeof order.action_args.nid === 'string') { + id = order.action_args.nid; + } else if (typeof order.action_args.cid === 'string') { + id = order.action_args.cid; + } + + if (id) { + return this.diaBackendAssetRepository + .fetchById$(id) + .pipe(map(asset => asset.asset_file_thumbnail)); + } + + return EMPTY; + } + + retrieveNetworkAppOrder$(id: string) { + return defer(() => this.authService.getAuthHeaders()).pipe( + concatMap(headers => { + return this.httpClient.get( + `${BASE_URL}/api/v3/store/network-app-orders/${id}`, + { headers } + ); + }) + ); + } + createNetworkAppOrder(networkApp: string, actionArgs: any, price = 0) { return defer(() => this.authService.getAuthHeadersWithApiKey()).pipe( concatMap(headers => { @@ -95,6 +179,10 @@ export interface NetworkAppOrder { expired_at: string; } +export interface NetworkAppOrderWithThumbnail extends NetworkAppOrder { + assetThumbnailUrl$?: Observable; +} + export interface Product { id: string; associated_id: string; diff --git a/src/app/shared/iframe/iframe.ts b/src/app/shared/iframe/iframe.ts index 5f1ec4ceb..d7ed89250 100644 --- a/src/app/shared/iframe/iframe.ts +++ b/src/app/shared/iframe/iframe.ts @@ -7,6 +7,7 @@ export enum BubbleToIonicPostMessage { IFRAME_COPY_TO_CLIPBOARD_ASSET_WALLET = 'iframe-copy-to-clipboard-asset-wallet', IFRAME_COPY_TO_CLIPBOARD_INTEGRITY_WALLET = 'iframe-copy-to-clipboard-integrity-wallet', IFRAME_COPY_TO_CLIPBOARD_PRIVATE_KEY = 'iframe-copy-to-clipboard-private-key', + IFRAME_COPY_TO_CLIPBOARD_ORDER_ID = 'iframe-copy-to-clipboard-order-id', } export enum IonicToBubblePostMessage { diff --git a/src/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index f526f8910..e492466a0 100644 --- a/src/assets/i18n/en-us.json +++ b/src/assets/i18n/en-us.json @@ -109,6 +109,7 @@ "yourPrivateKey": "Your Private Key", "copyToClipboard": "Copy To Clipboard", "order": "Order", + "orders": "Orders", "resultUrl": "Result URL", "noResultUrlAvailable": "No Result URL available", "insufficientNum": "Insufficient NUM", @@ -251,11 +252,10 @@ "waitingToBeAccepted": "In Progress" }, "networkActionOrderState": { - "submitted": "Submitted", - "paid": "Paid", - "payment_failed": "Payment Failed", - "completed": "Completed", - "failed": "Failed" + "created": "Created", + "pending:": "Pending", + "success": "Success", + "failure": "Failure" }, "payment": { "confirmPayment": "Your Order", diff --git a/src/assets/i18n/zh-tw.json b/src/assets/i18n/zh-tw.json index 5a947accf..83a38adc1 100644 --- a/src/assets/i18n/zh-tw.json +++ b/src/assets/i18n/zh-tw.json @@ -109,6 +109,7 @@ "yourPrivateKey": "您的私鑰", "copyToClipboard": "複製到剪貼簿", "order": "訂單", + "orders": "訂單", "resultUrl": "結果連結", "insufficientNum": "NUM 餘額不足", "noResultUrlAvailable": "沒有可以顯示的結果連結", @@ -251,11 +252,10 @@ "waitingToBeAccepted": "等待中" }, "networkActionOrderState": { - "submitted": "已送出", - "paid": "繳費成功", - "payment_failed": "繳費失敗", - "completed": "已完成", - "failed": "已失敗" + "created": "已創建", + "pending": "處理中", + "success": "成功", + "failure": "已失敗" }, "payment": { "confirmPayment": "你的訂單",