From 76d1f1ef9f3418a0e334b6c9c97e067880dbf311 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 13 Jul 2023 16:12:50 +0800 Subject: [PATCH 01/21] feat(ios): set minimum deployement target to 15.6 --- ios/App/App.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index e599a40bf..08163dd9d 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.81.3; 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.81.3; PRODUCT_BUNDLE_IDENTIFIER = io.numbersprotocol.capturelite; From d622e5b5a2f731d9b0da8c307cd838e27b908936 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Fri, 28 Jul 2023 01:16:30 +0800 Subject: [PATCH 02/21] chore(plugin): bump @capacitor-community/advertising-id to 5.0.0 --- ios/App/Podfile.lock | 4 ++-- package-lock.json | 16 ++++++++-------- package.json | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/ios/App/Podfile.lock b/ios/App/Podfile.lock index 7ced0465a..7de47a90e 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 1b52af646..814304417 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 7f285ae1a..ad0147c62 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", From 35035c7948bfba3c6c316dca429cdea9b2585fbf Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Fri, 28 Jul 2023 01:19:01 +0800 Subject: [PATCH 03/21] chore(android): add permission related to appsflyer --- android/app/src/main/AndroidManifest.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 @@ + + From 2dc3dbf0a2d8cbdc57b8702e21e25f00224eccda Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Fri, 28 Jul 2023 01:32:13 +0800 Subject: [PATCH 04/21] refactor(appsflyer): document, remove unused method --- .../shared/apps-flyer/apps-flyer.service.ts | 55 ++++++++----------- 1 file changed, 23 insertions(+), 32 deletions(-) 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(); } } From 1a6ecd52265a9aea849852d758d9d763caf17898 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Wed, 26 Jul 2023 15:26:33 +0800 Subject: [PATCH 05/21] fix(settings.page): sync profile after email verified --- src/app/features/settings/settings.page.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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( From 5873803c2203226700af477dfaa66eb3afac308f Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Fri, 28 Jul 2023 01:40:23 +0800 Subject: [PATCH 06/21] fix(activities.page): change text from "Network Actions" to "Orders" --- src/app/features/home/activities/activities.page.html | 2 +- src/app/features/home/activities/activities.page.scss | 8 ++++++++ src/assets/i18n/en-us.json | 1 + src/assets/i18n/zh-tw.json | 1 + 4 files changed, 11 insertions(+), 1 deletion(-) 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/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index 0b30fd72d..317c5fcf7 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", diff --git a/src/assets/i18n/zh-tw.json b/src/assets/i18n/zh-tw.json index f40b5dfa2..b070bdae1 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": "沒有可以顯示的結果連結", From 03b7829d498ecb4e8e233ff3da49572e822da6ca Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Fri, 28 Jul 2023 02:15:58 +0800 Subject: [PATCH 07/21] feat(dia-backend-store): add listAllNetworkAppOrders, listAllNetworkAppOrderWithThumbnail methods --- .../store/dia-backend-store.service.ts | 94 ++++++++++++++++++- 1 file changed, 91 insertions(+), 3 deletions(-) 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; From 347f2af2e06837d71e007d6806cf181bfba0a2c3 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Fri, 28 Jul 2023 02:35:24 +0800 Subject: [PATCH 08/21] feat: fetch network app orders from dia backend --- .../network-action-orders.component.html | 80 +++++++++---------- .../network-action-orders.component.scss | 12 ++- .../network-action-orders.component.ts | 52 +++++++----- 3 files changed, 79 insertions(+), 65 deletions(-) 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; } } From 8c4931647764cc859c0ccee368e1dd7059fb1ef3 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Fri, 28 Jul 2023 02:44:04 +0800 Subject: [PATCH 09/21] feat: show network app order details with bubble iframe --- .../network-action-order-details.page.html | 132 ++---------------- .../network-action-order-details.page.scss | 26 ++++ .../network-action-order-details.page.ts | 103 ++++++++++++-- src/app/shared/iframe/iframe.ts | 1 + src/assets/i18n/en-us.json | 9 +- src/assets/i18n/zh-tw.json | 9 +- 6 files changed, 144 insertions(+), 136 deletions(-) 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..d17a8812c 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.order$ + .pipe( + first(), + concatMap(({ order_id_text }) => this.copyToClipboard(order_id_text)) + ) + .subscribe(); + } // eslint-disable-next-line class-methods-use-this openResultUrl(url: 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 317c5fcf7..5b3dcfd1b 100644 --- a/src/assets/i18n/en-us.json +++ b/src/assets/i18n/en-us.json @@ -252,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 b070bdae1..80ac41a6b 100644 --- a/src/assets/i18n/zh-tw.json +++ b/src/assets/i18n/zh-tw.json @@ -252,11 +252,10 @@ "waitingToBeAccepted": "等待中" }, "networkActionOrderState": { - "submitted": "已送出", - "paid": "繳費成功", - "payment_failed": "繳費失敗", - "completed": "已完成", - "failed": "已失敗" + "created": "已創建", + "pending": "處理中", + "success": "成功", + "failure": "已失敗" }, "payment": { "confirmPayment": "你的訂單", From 005fe8ac063f51c5ebd40eb54c830048462885bf Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 3 Aug 2023 14:46:11 +0800 Subject: [PATCH 10/21] Fix: Show 30MB Limit Dialog for Media This commit displays a dialog when users select a video or image from the gallery, and its size exceeds 30MB. The informative message "File size exceeds 30MB..." will appear in such cases. However, when capturing media using the capture cam, the dialog won't be shown, regardless of the media's size, even if it exceeds 30MB. This enhancement keeps users informed about the file size limit, aiding them in making better decisions while selecting media from the gallery. --- .../custom-camera/custom-camera.page.html | 1 + .../pre-publish-mode.component.ts | 32 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/app/features/home/custom-camera/custom-camera.page.html b/src/app/features/home/custom-camera/custom-camera.page.html index 301dc8ffe..a162f09a1 100644 --- a/src/app/features/home/custom-camera/custom-camera.page.html +++ b/src/app/features/home/custom-camera/custom-camera.page.html @@ -148,6 +148,7 @@ (1); + + readonly isFromGallery$ = this.curCaptureCameraSource$.pipe( + map(cameraSource => cameraSource === CameraSource.Photos) + ); + readonly curCaptureFileSize$ = new ReplaySubject(1); readonly curCaptureFilePath$ = new ReplaySubject(1); @@ -96,7 +103,12 @@ export class PrePublishModeComponent { readonly isFileSizeExceeded$ = combineLatest([ this.curCaptureFileSize$, this.maxAllowedFileSize$, - ]).pipe(map(([curSize, maxSize]) => curSize < maxSize)); + ]).pipe(map(([curSize, maxSize]) => curSize > maxSize)); + + @Input() + set curCaptureCameraSource(value: CameraSource | undefined) { + if (value) this.curCaptureCameraSource$.next(value); + } @Input() set curCaptureFileSize(value: number | undefined) { @@ -207,17 +219,21 @@ export class PrePublishModeComponent { this.showIsFileSizeExceededModal() ); - this.isFileSizeExceeded$ + const shouldShowFileSizeExeededDialog$ = combineLatest([ + this.isFromGallery$, + this.isFileSizeExceeded$, + ]).pipe(map(([c1, c2]) => c1 === true && c2 === true)); + + shouldShowFileSizeExeededDialog$ .pipe( first(), - switchMap(hasEnoughMemory => + switchMap(shouldShowFileSizeExeededDialog => iif( - () => hasEnoughMemory, - runConfirmAction$, - showIsFileSizeExceededAction$ + () => shouldShowFileSizeExeededDialog, + showIsFileSizeExceededAction$, + runConfirmAction$ ) - ), - catchError((error: unknown) => this.errorService.toastError$(error)) + ) ) .subscribe(); } From 2f0e2fcdf647b540a115ff1ffbfa50a291860bc7 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 3 Aug 2023 14:50:44 +0800 Subject: [PATCH 11/21] refactor: rename var to better convey the idea Rename showIsFileSizeExceeded to showFileSizeExceeded cause it makes more sense. --- .../pre-publish-mode/pre-publish-mode.component.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/features/home/custom-camera/pre-publish-mode/pre-publish-mode.component.ts b/src/app/features/home/custom-camera/pre-publish-mode/pre-publish-mode.component.ts index 48f625d24..4ac6eaec3 100644 --- a/src/app/features/home/custom-camera/pre-publish-mode/pre-publish-mode.component.ts +++ b/src/app/features/home/custom-camera/pre-publish-mode/pre-publish-mode.component.ts @@ -215,8 +215,8 @@ export class PrePublishModeComponent { tap(isImage => (isImage ? this.confirmImage() : this.confirmVideo())) ); - const showIsFileSizeExceededAction$ = defer(() => - this.showIsFileSizeExceededModal() + const showFileSizeExceededAction$ = defer(() => + this.showFileSizeExceededModal() ); const shouldShowFileSizeExeededDialog$ = combineLatest([ @@ -230,7 +230,7 @@ export class PrePublishModeComponent { switchMap(shouldShowFileSizeExeededDialog => iif( () => shouldShowFileSizeExeededDialog, - showIsFileSizeExceededAction$, + showFileSizeExceededAction$, runConfirmAction$ ) ) @@ -255,7 +255,7 @@ export class PrePublishModeComponent { this.confirm.emit(true); } - private showIsFileSizeExceededModal() { + private showFileSizeExceededModal() { const translations$ = this.translocoService.selectTranslateObject({ 'customCamera.error.fileSizeExeedsLimit': null, ok: null, From fe3d41717afb81ba9b933d914d1b48af89684cec Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 3 Aug 2023 15:38:54 +0800 Subject: [PATCH 12/21] =?UTF-8?q?Resolve=20merge=20conflicts=20by=20mergin?= =?UTF-8?q?g=20'mileston-v230725'=20to=20'fix-v230725-issue-Some-device-ca?= =?UTF-8?q?n=E2=80=99t-record-15s-SHORT-by-Capture-camera'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 4 +- CHANGELOG.md | 21 ++- android/app/build.gradle | 4 +- android/app/src/main/AndroidManifest.xml | 6 +- ios/App/App.xcodeproj/project.pbxproj | 8 +- ios/App/Podfile.lock | 12 +- package-lock.json | 48 +++---- package.json | 8 +- .../home/activities/activities.page.html | 2 +- .../home/activities/activities.page.scss | 8 ++ .../network-action-order-details.page.html | 132 ++---------------- .../network-action-order-details.page.scss | 26 ++++ .../network-action-order-details.page.ts | 103 ++++++++++++-- .../network-action-orders.component.html | 80 +++++------ .../network-action-orders.component.scss | 12 +- .../network-action-orders.component.ts | 52 ++++--- .../custom-camera/custom-camera.page.html | 1 + .../home/custom-camera/custom-camera.page.ts | 32 ++++- .../pre-publish-mode.component.ts | 34 ++++- src/app/features/settings/settings.page.ts | 8 +- .../shared/apps-flyer/apps-flyer.service.ts | 55 +++----- .../store/dia-backend-store.service.ts | 94 ++++++++++++- src/app/shared/iframe/iframe.ts | 1 + .../shared/media/component/media.component.ts | 20 +-- src/assets/i18n/en-us.json | 12 +- src/assets/i18n/zh-tw.json | 12 +- 26 files changed, 487 insertions(+), 308 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 118737525..16199a750 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,8 +1,6 @@ name: test -on: - push: - branches: [master, develop, 'feature-*', 'fix-*', 'hotfix-*'] +on: [push, pull_request] jobs: lint: diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d11fbec9..4fef9b3fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.82.2] +## [0.82.4] - 2023-08-01 + +### Fixed + +1. Fix User can’t register asset when using capture camera (#2930) +1. Fix can't register new account on some android device (#2953) + +## [0.82.3] - 2023-07-28 + +### Fixed + +1. Fix sign up page links for term of use and data policy. + +## [0.82.2] - 2023-07-27 ### Added @@ -15,8 +28,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -1. Fix can't register new account on some android device (#2928) -1. Fix User can’t register asset when using capture camera (#2930) 1. Fix Change capture tab VALIDATED to VERIFIED (#2939) 1. Fix 4k videos can not generate thumbnail on some android devices (Android only) (#2923). reverted (#2938) 1. Fix show correct error mesage for sign up duplicate username error (#2926) @@ -2153,7 +2164,9 @@ This is the first release! _Capture Lite_ is a cross-platform app adapted from [ - Web - see the demo [here](https://github.com/numbersprotocol/capture-lite#demo-app) - Android - the APK file `app-debug.apk` is attached to this release -[unreleased]: https://github.com/numbersprotocol/capture-lite/compare/0.82.2...HEAD +[unreleased]: https://github.com/numbersprotocol/capture-lite/compare/0.82.4...HEAD +[0.82.4]: https://github.com/numbersprotocol/capture-lite/compare/0.82.3...0.82.4 +[0.82.3]: https://github.com/numbersprotocol/capture-lite/compare/0.82.2...0.82.3 [0.82.2]: https://github.com/numbersprotocol/capture-lite/compare/0.81.2...0.82.2 [0.81.2]: https://github.com/numbersprotocol/capture-lite/compare/0.79.0...0.81.2 [0.79.0]: https://github.com/numbersprotocol/capture-lite/compare/0.78.0...0.79.0 diff --git a/android/app/build.gradle b/android/app/build.gradle index efd0be3e6..38fe0ecac 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "io.numbersprotocol.capturelite" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 822 - versionName "0.82.2" + versionCode 824 + versionName "0.82.4" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildFeatures { 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 6bd8a92ee..20410941e 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -368,13 +368,13 @@ CODE_SIGN_ENTITLEMENTS = App/App.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 822; + CURRENT_PROJECT_VERSION = 824; DEVELOPMENT_TEAM = G7NB5YCKAP; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = G7NB5YCKAP; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 0.82.2; + MARKETING_VERSION = 0.82.4; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = io.numbersprotocol.capturelite; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -395,13 +395,13 @@ CODE_SIGN_ENTITLEMENTS = App/App.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 822; + CURRENT_PROJECT_VERSION = 824; DEVELOPMENT_TEAM = G7NB5YCKAP; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = G7NB5YCKAP; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 0.82.2; + MARKETING_VERSION = 0.82.4; PRODUCT_BUNDLE_IDENTIFIER = io.numbersprotocol.capturelite; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = NumbersAppDistributionV4; diff --git a/ios/App/Podfile.lock b/ios/App/Podfile.lock index 7ced0465a..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 @@ -33,13 +33,13 @@ PODS: - Capacitor - CapacitorGeolocation (5.0.4): - Capacitor - - CapacitorLocalNotifications (5.0.4): + - CapacitorLocalNotifications (5.0.6): - Capacitor - CapacitorNativeSettings (4.0.3): - Capacitor - CapacitorNetwork (5.0.4): - Capacitor - - CapacitorPushNotifications (5.0.4): + - CapacitorPushNotifications (5.0.6): - Capacitor - CapacitorShare (5.0.4): - Capacitor @@ -223,7 +223,7 @@ SPEC CHECKSUMS: CapacitorBrowser: 2688852d02f1e89560a31b70521c71a5e7348860 CapacitorCamera: 9b5c8e809c1042f263994f97ba846aa37e974f12 CapacitorClipboard: 46f3959735fa0d96b9989dafcc4aed52e624d163 - CapacitorCommunityAdvertisingId: ffbeee30080f0057f7af6e465a7552b68a3d3fdf + CapacitorCommunityAdvertisingId: 41543d8212fb12b6913b798bf1442c2a6bc1ae91 CapacitorCommunityBluetoothLe: 7de4f21022a05b15195abfb002872884d00715fc CapacitorCommunityHttp: 7be90668527ef14ee10d08135b0e00fac9cf8247 CapacitorCommunityWifi: 47188c74f2c6bcaefb619380863be8c67b1917c8 @@ -231,10 +231,10 @@ SPEC CHECKSUMS: CapacitorDevice: eb4b5e3b42ac35d2527f20aad296b59e0785dc8d CapacitorFilesystem: e1bdfab09b95b181c844c16abcfda45ec8e8ed6b CapacitorGeolocation: 33015be1ef496585a60da9efa1c5642ff8624db3 - CapacitorLocalNotifications: 2c95d27ccf9cc28ecc59d69b54d29074c1740172 + CapacitorLocalNotifications: c2d8b14794064fd4814b1d6c4ddbac8029afa295 CapacitorNativeSettings: b6f40955945bc659f966a43fa54fc6be192d8f9b CapacitorNetwork: e2bd0bf1614aca34bb976f125a756a8a3df1c81a - CapacitorPushNotifications: 3704ac3dac68a9bfc669fb384cbc7beec9b1f100 + CapacitorPushNotifications: 7eb70469f1fcc3dec07126336d4896520e4991db CapacitorShare: 427bba238a1e3f116b2b349019aec6ea7f42cebd CapacitorSplashScreen: 93a389d4f7673c08214ae25bb6f21d867d5305c5 CapacitorStorage: 8ec2cf8fec179d829288b16c6fba6c3c43d2bdc9 diff --git a/package-lock.json b/package-lock.json index 1b52af646..f880c4d11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "capture-lite", - "version": "0.82.2", + "version": "0.82.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "capture-lite", - "version": "0.82.2", + "version": "0.82.4", "dependencies": { "packages": "^0.0.8", "@angular/animations": "^14.2.0", @@ -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", @@ -34,9 +34,9 @@ "@capacitor/filesystem": "^5.0.4", "@capacitor/geolocation": "^5.0.4", "@capacitor/ios": "^5.0.5", - "@capacitor/local-notifications": "^5.0.4", + "@capacitor/local-notifications": "^5.0.6", "@capacitor/network": "^5.0.4", - "@capacitor/push-notifications": "^5.0.4", + "@capacitor/push-notifications": "^5.0.6", "@capacitor/share": "^5.0.4", "@capacitor/splash-screen": "^5.0.4", "@capacitor/storage": "^1.2.4", @@ -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": { @@ -3426,9 +3426,9 @@ } }, "node_modules/@capacitor/local-notifications": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@capacitor/local-notifications/-/local-notifications-5.0.4.tgz", - "integrity": "sha512-In+BNDun9lkJ/0nIl0KjbJrQjkopoF1lVC2oAdUYA0+xvorq7hq4SO2shljMkqdhzQcfy7uwjgDqToik1jtsRg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/local-notifications/-/local-notifications-5.0.6.tgz", + "integrity": "sha512-DB+ZBjv3Ri/0mtSzjMxLMHNGfg5m615ewDfQxp++mu7pYUM1RkxfSiigw73+PlZXDow1YNQJRlzTkwEKs6Pf+g==", "peerDependencies": { "@capacitor/core": "^5.0.0" } @@ -3442,9 +3442,9 @@ } }, "node_modules/@capacitor/push-notifications": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@capacitor/push-notifications/-/push-notifications-5.0.4.tgz", - "integrity": "sha512-q/3iCmsvvOjgH7fhfSVGQC7e85bVAgwAuSFzWAg5Sg6hq/XZnxK/TyQJ3M2DVfIfyHUc1OBXsMgi8vCQ4UhPAg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/push-notifications/-/push-notifications-5.0.6.tgz", + "integrity": "sha512-o4/EcP13XduazhJkoVWEVrRD07YcqT9Uz0pQ48SItRNjtgDhcQW1vAEo6tSL6FVA5NkxNhJikk8TUeXobgOhEg==", "peerDependencies": { "@capacitor/core": "^5.0.0" } @@ -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": { @@ -26396,9 +26396,9 @@ "requires": {} }, "@capacitor/local-notifications": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@capacitor/local-notifications/-/local-notifications-5.0.4.tgz", - "integrity": "sha512-In+BNDun9lkJ/0nIl0KjbJrQjkopoF1lVC2oAdUYA0+xvorq7hq4SO2shljMkqdhzQcfy7uwjgDqToik1jtsRg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/local-notifications/-/local-notifications-5.0.6.tgz", + "integrity": "sha512-DB+ZBjv3Ri/0mtSzjMxLMHNGfg5m615ewDfQxp++mu7pYUM1RkxfSiigw73+PlZXDow1YNQJRlzTkwEKs6Pf+g==", "requires": {} }, "@capacitor/network": { @@ -26408,9 +26408,9 @@ "requires": {} }, "@capacitor/push-notifications": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@capacitor/push-notifications/-/push-notifications-5.0.4.tgz", - "integrity": "sha512-q/3iCmsvvOjgH7fhfSVGQC7e85bVAgwAuSFzWAg5Sg6hq/XZnxK/TyQJ3M2DVfIfyHUc1OBXsMgi8vCQ4UhPAg==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@capacitor/push-notifications/-/push-notifications-5.0.6.tgz", + "integrity": "sha512-o4/EcP13XduazhJkoVWEVrRD07YcqT9Uz0pQ48SItRNjtgDhcQW1vAEo6tSL6FVA5NkxNhJikk8TUeXobgOhEg==", "requires": {} }, "@capacitor/share": { diff --git a/package.json b/package.json index 7f285ae1a..8ce1ae06b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "capture-lite", - "version": "0.82.2", + "version": "0.82.4", "author": "numbersprotocol", "homepage": "https://numbersprotocol.io/", "scripts": { @@ -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", @@ -45,9 +45,9 @@ "@capacitor/filesystem": "^5.0.4", "@capacitor/geolocation": "^5.0.4", "@capacitor/ios": "^5.0.5", - "@capacitor/local-notifications": "^5.0.4", + "@capacitor/local-notifications": "^5.0.6", "@capacitor/network": "^5.0.4", - "@capacitor/push-notifications": "^5.0.4", + "@capacitor/push-notifications": "^5.0.6", "@capacitor/share": "^5.0.4", "@capacitor/splash-screen": "^5.0.4", "@capacitor/storage": "^1.2.4", 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..d17a8812c 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.order$ + .pipe( + first(), + concatMap(({ order_id_text }) => this.copyToClipboard(order_id_text)) + ) + .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/home/custom-camera/custom-camera.page.html b/src/app/features/home/custom-camera/custom-camera.page.html index a162f09a1..fa50de7de 100644 --- a/src/app/features/home/custom-camera/custom-camera.page.html +++ b/src/app/features/home/custom-camera/custom-camera.page.html @@ -151,6 +151,7 @@ [curCaptureCameraSource]="curCaptureCameraSource" [curCaptureFileSize]="curCaptureFileSize" [curCaptureFilePath]="curCaptureFilePath" + [curCaptureFileName]="curCaptureFileName" [curCaptureMimeType]="curCaptureMimeType" [curCaptureSrc]="curCaptureSrc" (confirm)="confirmCurrentCapture()" diff --git a/src/app/features/home/custom-camera/custom-camera.page.ts b/src/app/features/home/custom-camera/custom-camera.page.ts index 36365ee28..1a604f8f3 100644 --- a/src/app/features/home/custom-camera/custom-camera.page.ts +++ b/src/app/features/home/custom-camera/custom-camera.page.ts @@ -83,6 +83,7 @@ export class CustomCameraPage implements OnInit, OnDestroy { curCaptureFileSize?: number; curCaptureFilePath?: string; + curCaptureFileName?: string; curCaptureMimeType?: 'image/jpeg' | 'video/mp4'; curCaptureType?: MediaType = 'image'; curCaptureSrc?: string; @@ -142,8 +143,32 @@ export class CustomCameraPage implements OnInit, OnDestroy { }); } - private handleCaptureSuccessResult(result: CaptureSuccessResult) { - this.prepareForPublishing(result, CameraSource.Camera); + private async handleCaptureSuccessResult(result: CaptureSuccessResult) { + const resultCopy = await this.copyResultIfNeeded(result); + this.prepareForPublishing(resultCopy, CameraSource.Camera); + } + + private async copyResultIfNeeded(result: CaptureSuccessResult) { + /** + * WORKAROUND: https://github.com/numbersprotocol/capture-lite/issues/2904 + * On Android 13 capacitor filesystem plugin need to pass directory parameter to be + * able to re-write media file (aka when we edit image and save it the same file). + * Therefore we copy image to cache so we can re-write it if user crop/filter the image. + * + */ + if (this.platform.is('android') && result.mimeType.startsWith('image/')) { + const originalFilePath = result.path; + const readFileResult = await Filesystem.readFile({ path: result.path }); + const writeFileResult = await Filesystem.writeFile({ + data: readFileResult.data, + path: `${result.name}`, + directory: Directory.Cache, + recursive: true, + }); + result.path = writeFileResult.uri; + await Filesystem.deleteFile({ path: originalFilePath }); + } + return result; } private handleCaptureErrorResult(result: CaptureErrorResult) { @@ -156,6 +181,7 @@ export class CustomCameraPage implements OnInit, OnDestroy { ) { this.curCaptureFileSize = result.size; this.curCaptureFilePath = result.path; + this.curCaptureFileName = result.name; this.curCaptureMimeType = result.mimeType; this.curCaptureType = result.mimeType === 'image/jpeg' ? 'image' : 'video'; this.curCaptureSrc = Capacitor.convertFileSrc(result.path); @@ -328,7 +354,7 @@ export class CustomCameraPage implements OnInit, OnDestroy { const readFileResult = await Filesystem.readFile({ path: file.path }); const writeFileresult = await Filesystem.writeFile({ data: readFileResult.data, - path: `${Date.now()}/${file.name}`, + path: `${file.name}`, directory: Directory.Cache, recursive: true, }); diff --git a/src/app/features/home/custom-camera/pre-publish-mode/pre-publish-mode.component.ts b/src/app/features/home/custom-camera/pre-publish-mode/pre-publish-mode.component.ts index 4ac6eaec3..1ad933a8b 100644 --- a/src/app/features/home/custom-camera/pre-publish-mode/pre-publish-mode.component.ts +++ b/src/app/features/home/custom-camera/pre-publish-mode/pre-publish-mode.component.ts @@ -8,8 +8,8 @@ import { ViewChild, } from '@angular/core'; import { CameraSource } from '@capacitor/camera'; -import { FilesystemPlugin } from '@capacitor/filesystem'; -import { AlertController } from '@ionic/angular'; +import { Directory, FilesystemPlugin } from '@capacitor/filesystem'; +import { AlertController, Platform } from '@ionic/angular'; import { TranslocoService } from '@ngneat/transloco'; import { ColorMatrix, getEditorDefaults } from '@pqina/pintura'; import { @@ -69,6 +69,8 @@ export class PrePublishModeComponent { readonly curCaptureFilePath$ = new ReplaySubject(1); + readonly curCaptureFileName$ = new ReplaySubject(1); + readonly curCaptureMimeType$ = new ReplaySubject(1); readonly curCaptureSrc$ = new ReplaySubject(1); @@ -120,6 +122,11 @@ export class PrePublishModeComponent { if (value) this.curCaptureFilePath$.next(value); } + @Input() + set curCaptureFileName(value: string | undefined) { + if (value) this.curCaptureFileName$.next(value); + } + @Input() set curCaptureMimeType(value: CaptureMimeType | undefined) { if (value) this.curCaptureMimeType$.next(value); @@ -141,7 +148,8 @@ export class PrePublishModeComponent { private readonly filesystemPlugin: FilesystemPlugin, private readonly errorService: ErrorService, private readonly alertController: AlertController, - private readonly translocoService: TranslocoService + private readonly translocoService: TranslocoService, + private readonly platform: Platform ) {} handleEditorUpdate(imageState: any): void { @@ -162,12 +170,24 @@ export class PrePublishModeComponent { async handleEditorProcess(imageWriterResult: any): Promise { const base64 = await blobToBase64(imageWriterResult.dest as File); - combineLatest([this.curCaptureFilePath$, of(base64)]) + combineLatest([ + this.curCaptureFilePath$, + of(base64), + this.isImage$, + this.curCaptureFileName$, + ]) .pipe( first(), - switchMap(([path, data]) => - this.filesystemPlugin.writeFile({ path, data }) - ), + switchMap(([path, data, isImage, fileName]) => { + if (this.platform.is('android') && isImage) { + return this.filesystemPlugin.writeFile({ + path: fileName, + data: data, + directory: Directory.Cache, + }); + } + return this.filesystemPlugin.writeFile({ path, data }); + }), tap(() => this.isProcessingImage$.next(false)), tap(() => this.confirm.emit(true)), catchError((error: unknown) => { 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/app/shared/media/component/media.component.ts b/src/app/shared/media/component/media.component.ts index 4d5ada0ab..027a5b05d 100644 --- a/src/app/shared/media/component/media.component.ts +++ b/src/app/shared/media/component/media.component.ts @@ -175,15 +175,15 @@ export class MediaComponent implements AfterViewInit, OnDestroy { * and the native player is no longer needed. */ private removeCSSClass() { - // Remove the CSS class from the body element - this.renderer.removeClass( - this.elementRef.nativeElement.ownerDocument.body, - this.globalCSSClass - ); - // Remove the CSS class from the ion-app element - this.renderer.removeClass( - this.elementRef.nativeElement.ownerDocument.querySelector('ion-app'), - this.globalCSSClass - ); + const bodyElement = this.elementRef.nativeElement.ownerDocument.body; + const ionAppElement = + this.elementRef.nativeElement.ownerDocument.querySelector('ion-app'); + + if (bodyElement && ionAppElement) { + // Remove the CSS class from the body element + this.renderer.removeClass(bodyElement, this.globalCSSClass); + // Remove the CSS class from the ion-app element + this.renderer.removeClass(ionAppElement, this.globalCSSClass); + } } } diff --git a/src/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index 0b30fd72d..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", @@ -156,7 +157,7 @@ "pleaseWait": "Please wait...", "copiedToClipboard": "Copied to clipboard.", "invitationEmail": "Enter friend's email to send the invitation.", - "clickingSignupToAgreePolicy": "By clicking Sign Up, you agree to our Privacy Policy.", + "clickingSignupToAgreePolicy": "By clicking Sign Up you agree to our Terms of Use and Data Policy.", "sendPostCaptureAlert": "After this Capture is sent, the ownership will be transferred to the selected friend. Are you sure?", "transactionReceived": "A new Capture received.", "transactionExpired": "A Capture you sent is returned.", @@ -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 f40b5dfa2..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": "沒有可以顯示的結果連結", @@ -156,7 +157,7 @@ "pleaseWait": "請稍候...", "copiedToClipboard": "已複製至剪貼簿。", "invitationEmail": "輸入好友電子信箱發送邀請。", - "clickingSignupToAgreePolicy": "點擊「註冊」即表示你同意我們的《服務條款》和《資料政策》。", + "clickingSignupToAgreePolicy": "點擊「註冊」即表示你同意我們的《服務條款》和《資料政策》。", "sendPostCaptureAlert": "瞬時影像送出後,它的所有權將轉移至選定的朋友。您確定嗎?", "transactionReceived": "收到了新的瞬時影像。", "transactionExpired": "您寄出的瞬時影像已被退回。", @@ -251,11 +252,10 @@ "waitingToBeAccepted": "等待中" }, "networkActionOrderState": { - "submitted": "已送出", - "paid": "繳費成功", - "payment_failed": "繳費失敗", - "completed": "已完成", - "failed": "已失敗" + "created": "已創建", + "pending": "處理中", + "success": "成功", + "failure": "已失敗" }, "payment": { "confirmPayment": "你的訂單", From b52b29da708544d9ac1ab3fa816325af4a22097d Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Mon, 7 Aug 2023 15:50:20 +0800 Subject: [PATCH 13/21] =?UTF-8?q?Revert=20"Merge=20pull=20request=20#2966?= =?UTF-8?q?=20from=20numbersprotocol/fix-v230725-issue-Some-device-can?= =?UTF-8?q?=E2=80=99t-record-15s-SHORT-by-Capture-camera"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 64ee16387d18c45ed51a80b5c2a638d3681e9125, reversing changes made to 4630fb97ed34286fd8301366805e6a009e33c47d. --- .../custom-camera/custom-camera.page.html | 1 - .../pre-publish-mode.component.ts | 38 ++++++------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/app/features/home/custom-camera/custom-camera.page.html b/src/app/features/home/custom-camera/custom-camera.page.html index fa50de7de..8e439ce53 100644 --- a/src/app/features/home/custom-camera/custom-camera.page.html +++ b/src/app/features/home/custom-camera/custom-camera.page.html @@ -148,7 +148,6 @@ (1); - - readonly isFromGallery$ = this.curCaptureCameraSource$.pipe( - map(cameraSource => cameraSource === CameraSource.Photos) - ); - readonly curCaptureFileSize$ = new ReplaySubject(1); readonly curCaptureFilePath$ = new ReplaySubject(1); @@ -105,12 +98,7 @@ export class PrePublishModeComponent { readonly isFileSizeExceeded$ = combineLatest([ this.curCaptureFileSize$, this.maxAllowedFileSize$, - ]).pipe(map(([curSize, maxSize]) => curSize > maxSize)); - - @Input() - set curCaptureCameraSource(value: CameraSource | undefined) { - if (value) this.curCaptureCameraSource$.next(value); - } + ]).pipe(map(([curSize, maxSize]) => curSize < maxSize)); @Input() set curCaptureFileSize(value: number | undefined) { @@ -235,25 +223,21 @@ export class PrePublishModeComponent { tap(isImage => (isImage ? this.confirmImage() : this.confirmVideo())) ); - const showFileSizeExceededAction$ = defer(() => - this.showFileSizeExceededModal() + const showIsFileSizeExceededAction$ = defer(() => + this.showIsFileSizeExceededModal() ); - const shouldShowFileSizeExeededDialog$ = combineLatest([ - this.isFromGallery$, - this.isFileSizeExceeded$, - ]).pipe(map(([c1, c2]) => c1 === true && c2 === true)); - - shouldShowFileSizeExeededDialog$ + this.isFileSizeExceeded$ .pipe( first(), - switchMap(shouldShowFileSizeExeededDialog => + switchMap(hasEnoughMemory => iif( - () => shouldShowFileSizeExeededDialog, - showFileSizeExceededAction$, - runConfirmAction$ + () => hasEnoughMemory, + runConfirmAction$, + showIsFileSizeExceededAction$ ) - ) + ), + catchError((error: unknown) => this.errorService.toastError$(error)) ) .subscribe(); } @@ -275,7 +259,7 @@ export class PrePublishModeComponent { this.confirm.emit(true); } - private showFileSizeExceededModal() { + private showIsFileSizeExceededModal() { const translations$ = this.translocoService.selectTranslateObject({ 'customCamera.error.fileSizeExeedsLimit': null, ok: null, From 285f464fe97882421505d3c551c713e76c771859 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Mon, 7 Aug 2023 17:10:48 +0800 Subject: [PATCH 14/21] fix: iframe copy order id to clipboard --- .../network-action-order-details.page.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 d17a8812c..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 @@ -117,10 +117,10 @@ export class NetworkActionOrderDetailsPage { } private copyToClipboardOrderId() { - this.order$ + this.orderId$ .pipe( first(), - concatMap(({ order_id_text }) => this.copyToClipboard(order_id_text)) + concatMap(orderId => this.copyToClipboard(orderId)) ) .subscribe(); } From eea490799ce31a2edc813b033815f630bd7a0522 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 8 Aug 2023 00:24:53 +0800 Subject: [PATCH 15/21] fix: automatically navigate back on iOS 16 and higher --- .../capture-details-with-iframe.component.ts | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/app/features/home/details/capture-details-with-iframe/capture-details-with-iframe.component.ts b/src/app/features/home/details/capture-details-with-iframe/capture-details-with-iframe.component.ts index 91224c414..9a44c8a9c 100644 --- a/src/app/features/home/details/capture-details-with-iframe/capture-details-with-iframe.component.ts +++ b/src/app/features/home/details/capture-details-with-iframe/capture-details-with-iframe.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; +import { Device } from '@capacitor/device'; import { NavController, Platform } from '@ionic/angular'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { BehaviorSubject, ReplaySubject, combineLatest, fromEvent } from 'rxjs'; @@ -87,15 +88,7 @@ export class CaptureDetailsWithIframeComponent { switchMap(() => this.iframeLoaded$.pipe( take(1), - tap(() => { - /** - * WORKAROUND: https://github.com/numbersprotocol/capture-lite/issues/2723 - * Navigate back 1 time programmatically when iframe is reloaded - */ - if (this.platform.is('android')) { - this.navController.back(); - } - }) + tap(() => this.handleIframeReloadWorkaround()) ) ), untilDestroyed(this) @@ -103,6 +96,23 @@ export class CaptureDetailsWithIframeComponent { .subscribe(); } + private async handleIframeReloadWorkaround() { + /** + * WORKAROUND: https://github.com/numbersprotocol/capture-lite/issues/2723 + * Navigate back 1 time programmatically if iframe was reloaded. Applicable to + * - all android devices + * - iOS device (iOS 16 and higher) + */ + if (!this.platform.is('hybrid')) return; // navigate back on native platforms only. + + const iOS16 = 160000; + const iOSVersion = (await Device.getInfo()).iOSVersion; + + if (iOSVersion && iOSVersion <= iOS16) return; + + this.navController.back(); + } + private generateIframeUrl( detailedCaptureId: string, { access, refresh }: CachedQueryJWTToken From 700282c99c4e227215e883321bbeb3f90a95831d Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 8 Aug 2023 01:08:50 +0800 Subject: [PATCH 16/21] feat(ios): add associated domain applinks:capture-cam-deep-links.web.app --- ios/App/App/App.entitlements | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ios/App/App/App.entitlements b/ios/App/App/App.entitlements index ec06d6300..222bafbbb 100644 --- a/ios/App/App/App.entitlements +++ b/ios/App/App/App.entitlements @@ -4,15 +4,19 @@ aps-environment development + com.apple.developer.associated-domains + + applinks:capture-cam-deep-links.web.app + com.apple.developer.networking.HotspotConfiguration - - com.apple.developer.networking.networkextension - - app-proxy-provider - - com.apple.external-accessory.wireless-configuration - - com.apple.developer.networking.wifi-info - + + com.apple.developer.networking.networkextension + + app-proxy-provider + + com.apple.developer.networking.wifi-info + + com.apple.external-accessory.wireless-configuration + From df7c29dc5a3bd8d4d1af2f33a0d720308efde9c5 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 8 Aug 2023 01:09:34 +0800 Subject: [PATCH 17/21] feat(android): add intent-filter for deep links --- android/app/src/main/AndroidManifest.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 346c28b84..af6329554 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -83,6 +83,15 @@ + + + + + + + + + Date: Mon, 7 Aug 2023 15:29:26 +0800 Subject: [PATCH 18/21] feature: parse deep link and navigate accordingly --- src/app/app.component.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e8041ea62..ca30f652b 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,6 +1,8 @@ -import { Component } from '@angular/core'; +import { Component, NgZone } from '@angular/core'; import { MatIconRegistry } from '@angular/material/icon'; import { DomSanitizer } from '@angular/platform-browser'; +import { Router } from '@angular/router'; +import { App, URLOpenListenerEvent } from '@capacitor/app'; import { SplashScreen } from '@capacitor/splash-screen'; import { Platform } from '@ionic/angular'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; @@ -38,6 +40,8 @@ export class AppComponent { private readonly cameraService: CameraService, private readonly errorService: ErrorService, private readonly inAppStoreService: InAppStoreService, + private readonly zone: NgZone, + private readonly router: Router, appsFlyerService: AppsFlyerService, notificationService: NotificationService, pushNotificationService: PushNotificationService, @@ -58,6 +62,7 @@ export class AppComponent { .subscribe(); this.inAppStoreService.initialize(); this.initializeApp(); + this.initializeDeepLinking(); this.restoreAppState(); this.initializeCollector(); this.registerIcon(); @@ -81,6 +86,15 @@ export class AppComponent { await SplashScreen.hide(); } + async initializeDeepLinking() { + App.addListener('appUrlOpen', (event: URLOpenListenerEvent) => { + this.zone.run(() => { + const slug = event.url.split('.app').pop(); + if (slug) this.router.navigateByUrl(slug); + }); + }); + } + private restoreAppState() { this.cameraService.restoreKilledCaptureEvent$ .pipe( From fac23232a12c98ca4b07e3d0f7c7091a3a8b8963 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 8 Aug 2023 15:31:14 +0800 Subject: [PATCH 19/21] fix: change text for referral code invitation --- src/app/shared/share/share.service.ts | 2 +- src/app/utils/constants.ts | 2 +- src/assets/i18n/en-us.json | 2 +- src/assets/i18n/zh-tw.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/shared/share/share.service.ts b/src/app/shared/share/share.service.ts index eb8bebb1e..f591c9e14 100644 --- a/src/app/shared/share/share.service.ts +++ b/src/app/shared/share/share.service.ts @@ -41,7 +41,7 @@ export class ShareService { async shareReferralCode(referralCode: string) { const text = this.translocoService.translate( - 'invitation.useMyReferralCodeToSignUpForCaptureAppPassVerificationAndWeBothGetNumPointsReward', + 'invitation.useMyReferralCodeToSignUpForCaptureCamPassVerificationAndWeBothGetNumCreditsReward', { referralCode: referralCode } ); diff --git a/src/app/utils/constants.ts b/src/app/utils/constants.ts index 696ffe586..64401be12 100644 --- a/src/app/utils/constants.ts +++ b/src/app/utils/constants.ts @@ -1 +1 @@ -export const urlToDownloadApp = 'https://link.numbersprotocol.io/CaptureApp'; +export const urlToDownloadApp = 'https://link.numbersprotocol.io/CaptureCam'; diff --git a/src/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index f526f8910..0be99cecc 100644 --- a/src/assets/i18n/en-us.json +++ b/src/assets/i18n/en-us.json @@ -365,7 +365,7 @@ "inviteFriendsBenefit": "Get 3 credits for each accepted invitation", "shareInvitationCode": "Share invitation code", "shareToGetRewarded": "Share to get rewarded", - "useMyReferralCodeToSignUpForCaptureAppPassVerificationAndWeBothGetNumPointsReward": "Use my referral code {{referralCode}} to sign up for Capture App, pass verification and we both get NUM Points reward!" + "useMyReferralCodeToSignUpForCaptureCamPassVerificationAndWeBothGetNumCreditsReward": "Use my referral code {{referralCode}} to sign up for Capture Cam, pass verification and we both get NUM Credits reward!" }, "gopro": { "setup": "GoPro Setup", diff --git a/src/assets/i18n/zh-tw.json b/src/assets/i18n/zh-tw.json index 5a947accf..9072923ab 100644 --- a/src/assets/i18n/zh-tw.json +++ b/src/assets/i18n/zh-tw.json @@ -365,7 +365,7 @@ "inviteFriendsBenefit": "每個接受的邀請可獲得 3 個積分", "shareInvitationCode": "分享邀請碼", "shareToGetRewarded": "分享以獲得獎勵", - "useMyReferralCodeToSignUpForCaptureAppPassVerificationAndWeBothGetNumPointsReward": "透過我的推薦碼 {{referralCode}} 在 Capture App 註冊,一起獲得 NUM Points!" + "useMyReferralCodeToSignUpForCaptureCamPassVerificationAndWeBothGetNumCreditsReward": "透過我的推薦碼 {{referral code}} 在 Capture Cam 註冊,一起獲得 NUM Credits!" }, "gopro": { "setup": "GoPro 設置", From ef4775b6512ccebe1ea7005ef8cdeba3c1092638 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 15 Aug 2023 16:14:07 +0800 Subject: [PATCH 20/21] chore: bump app version to 0.83.2 --- android/app/build.gradle | 4 ++-- ios/App/App.xcodeproj/project.pbxproj | 8 ++++---- package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 38fe0ecac..f218d569a 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "io.numbersprotocol.capturelite" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 824 - versionName "0.82.4" + versionCode 832 + versionName "0.83.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildFeatures { diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 8e0380132..1f70872cd 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -368,13 +368,13 @@ CODE_SIGN_ENTITLEMENTS = App/App.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 824; + CURRENT_PROJECT_VERSION = 832; DEVELOPMENT_TEAM = G7NB5YCKAP; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = G7NB5YCKAP; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 0.82.4; + MARKETING_VERSION = 0.83.2; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = io.numbersprotocol.capturelite; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -395,13 +395,13 @@ CODE_SIGN_ENTITLEMENTS = App/App.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 824; + CURRENT_PROJECT_VERSION = 832; DEVELOPMENT_TEAM = G7NB5YCKAP; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = G7NB5YCKAP; INFOPLIST_FILE = App/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 0.82.4; + MARKETING_VERSION = 0.83.2; PRODUCT_BUNDLE_IDENTIFIER = io.numbersprotocol.capturelite; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = NumbersAppDistributionV4; diff --git a/package-lock.json b/package-lock.json index f880c4d11..d4a5537b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "capture-lite", - "version": "0.82.4", + "version": "0.83.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "capture-lite", - "version": "0.82.4", + "version": "0.83.2", "dependencies": { "packages": "^0.0.8", "@angular/animations": "^14.2.0", diff --git a/package.json b/package.json index 8ce1ae06b..a228aa618 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "capture-lite", - "version": "0.82.4", + "version": "0.83.2", "author": "numbersprotocol", "homepage": "https://numbersprotocol.io/", "scripts": { From 65dabb5586af20ead425689b8693f89b708ef26e Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 15 Aug 2023 16:20:55 +0800 Subject: [PATCH 21/21] chore: update CHANGELOG.md for 0.83.2 --- CHANGELOG.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fef9b3fe..b407c49df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.83.2] - 2023-08-15 + +### Added + +1. Feature v230808 configure capture app to receive deep links (#2974) +1. Feature v230725 showing order information in the transaction (#2971) +1. Feature set ios minimum version to 15.7 (#2967) +1. Feature v230725 showing order information in the transaction (#2965) + +### Fixed + +1. Fix v230808 Update Invite Friends Message Text (#2980) +1. Fix v230808 After edit caption need to press back button (#2973) +1. Fix v230725 Update Icon to Reflect Completed Email Verification Process (#2963) +1. Fix v230725 appsflyer after capacitor 5 migration (#2962) + ## [0.82.4] - 2023-08-01 ### Fixed @@ -2164,7 +2180,8 @@ This is the first release! _Capture Lite_ is a cross-platform app adapted from [ - Web - see the demo [here](https://github.com/numbersprotocol/capture-lite#demo-app) - Android - the APK file `app-debug.apk` is attached to this release -[unreleased]: https://github.com/numbersprotocol/capture-lite/compare/0.82.4...HEAD +[unreleased]: https://github.com/numbersprotocol/capture-lite/compare/0.83.2...HEAD +[0.82.4]: https://github.com/numbersprotocol/capture-lite/compare/0.82.4...0.83.2 [0.82.4]: https://github.com/numbersprotocol/capture-lite/compare/0.82.3...0.82.4 [0.82.3]: https://github.com/numbersprotocol/capture-lite/compare/0.82.2...0.82.3 [0.82.2]: https://github.com/numbersprotocol/capture-lite/compare/0.81.2...0.82.2