From a09e1e80e999c63ec17edcb5006ab6ebaed2134f Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 14 Dec 2023 11:36:05 +0800 Subject: [PATCH 01/19] feat(capture-tab): show confirm dialog before refresh --- .../home/capture-tab/capture-tab.component.ts | 41 +++++++++++++------ src/assets/i18n/en-us.json | 4 ++ src/assets/i18n/zh-tw.json | 4 ++ 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/app/features/home/capture-tab/capture-tab.component.ts b/src/app/features/home/capture-tab/capture-tab.component.ts index 128ab9bd0..d0507eb6e 100644 --- a/src/app/features/home/capture-tab/capture-tab.component.ts +++ b/src/app/features/home/capture-tab/capture-tab.component.ts @@ -1,6 +1,7 @@ import { formatDate, KeyValue } from '@angular/common'; import { HttpErrorResponse } from '@angular/common/http'; import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; import { Router } from '@angular/router'; import { ActionSheetButton, @@ -15,7 +16,6 @@ import { catchError, concatMap, concatMapTo, - finalize, map, pluck, shareReplay, @@ -45,6 +45,7 @@ import { getOldProof } from '../../../shared/repositories/proof/old-proof-adapte import { Proof } from '../../../shared/repositories/proof/proof'; import { ProofRepository } from '../../../shared/repositories/proof/proof-repository.service'; import { reloadApp } from '../../../utils/miscellaneous'; +import { PrefetchingDialogComponent } from '../onboarding/prefetching-dialog/prefetching-dialog.component'; @UntilDestroy({ checkProperties: true }) @Component({ @@ -158,6 +159,8 @@ export class CaptureTabComponent implements OnInit { private readonly mediaStore: MediaStore, private readonly database: Database, private readonly confirmAlert: ConfirmAlert, + private readonly dialog: MatDialog, + private readonly preferenceManager: PreferenceManager, private readonly changeDetectorRef: ChangeDetectorRef, private readonly proofRepository: ProofRepository, @@ -334,17 +337,29 @@ export class CaptureTabComponent implements OnInit { return item.id; } - refreshCaptures(event: Event) { - this.diaBackendAssetRefreshingService - .refresh() - .pipe( - finalize(() => { - this.capturedTabPageIndex$.next(0); - this.collectedTabPageIndex$.next(0); - this.draftTabPageIndex$.next(0); - return (event).detail.complete(); - }) - ) - .subscribe(); + async refreshCaptures(event: Event) { + (event).detail.complete(); + + const confirmRefresh = await this.showRefreshAlert(); + if (confirmRefresh) { + this.capturedTabPageIndex$.next(0); + this.collectedTabPageIndex$.next(0); + this.draftTabPageIndex$.next(0); + + return this.dialog.open(PrefetchingDialogComponent, { + disableClose: true, + }); + } + } + + private async showRefreshAlert() { + return this.confirmAlert.present({ + header: this.translocoService.translate('syncAndRestore'), + message: this.translocoService.translate('message.confirmSyncAndRestore'), + confirmButtonText: this.translocoService.translate( + 'confirmSyncAndRestore' + ), + cancelButtonText: this.translocoService.translate('cancelSyncAndRestore'), + }); } } diff --git a/src/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index bc36a6b2b..15fb98dbb 100644 --- a/src/assets/i18n/en-us.json +++ b/src/assets/i18n/en-us.json @@ -86,6 +86,9 @@ "restorePhotos": "Restore Captures", "restore": "Restore", "skip": "Skip", + "syncAndRestore": "Sync and restore all assets?", + "confirmSyncAndRestore": "Confirm", + "cancelSyncAndRestore": "Do it later", "contactUs": "Contact us", "editUsername": "Edit Username", "forgotPassword": "Forgot Password", @@ -169,6 +172,7 @@ "networkNotConnected": "No internet connections.", "allPhotosRegistered": "All Captures registered on blockchain successfully", "confirmPrefetch": "Do you want to restore the registered Captures? It may take minutes to complete.", + "confirmSyncAndRestore": "This will download all assets again and making sure your local data is up to date", "loadingPreviousData": "Restoring Captures...", "doNotTurnOffApp": "Please keep the screen on and DO NOT leave the app.", "upgrading": "Upgrading...", diff --git a/src/assets/i18n/zh-tw.json b/src/assets/i18n/zh-tw.json index 532117fd2..5d5c69f56 100644 --- a/src/assets/i18n/zh-tw.json +++ b/src/assets/i18n/zh-tw.json @@ -86,6 +86,9 @@ "restorePhotos": "恢復已註冊的瞬時影像", "restore": "恢復", "skip": "跳過", + "syncAndRestore": "同步並還原所有資產?", + "confirmSyncAndRestore": "確認", + "cancelSyncAndRestore": "稍後再說", "contactUs": "聯絡我們", "editUsername": "修改使用者名稱", "forgotPassword": "忘記密碼", @@ -169,6 +172,7 @@ "networkNotConnected": "沒有網路連線", "allPhotosRegistered": "所有瞬時影像都已成功註冊", "confirmPrefetch": "是否要恢復已註冊的瞬時影像?這個流程需要數分鐘的時間。", + "confirmSyncAndRestore": "這將重新下載所有資產,並確保您的本地數據是最新的。", "loadingPreviousData": "正在恢復已註冊的瞬時影像...", "doNotTurnOffApp": "載入中,期間請勿關閉螢幕與離開 APP。", "upgrading": "升級中...", From 1ec67a171fced3ea33ee5c5d1836640af6a624a6 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 14 Dec 2023 15:17:07 +0800 Subject: [PATCH 02/19] fix(zh-tw): syncAndRestore --- src/assets/i18n/zh-tw.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/zh-tw.json b/src/assets/i18n/zh-tw.json index 5d5c69f56..6a673b905 100644 --- a/src/assets/i18n/zh-tw.json +++ b/src/assets/i18n/zh-tw.json @@ -86,7 +86,7 @@ "restorePhotos": "恢復已註冊的瞬時影像", "restore": "恢復", "skip": "跳過", - "syncAndRestore": "同步並還原所有資產?", + "syncAndRestore": "遠端同步並還原所有資產?", "confirmSyncAndRestore": "確認", "cancelSyncAndRestore": "稍後再說", "contactUs": "聯絡我們", From 9e8956fa4514b74f7662f38e059dfa9a06c4ce0c Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 14 Dec 2023 15:20:22 +0800 Subject: [PATCH 03/19] fix(en-us): confirmSyncAndRestore --- src/assets/i18n/en-us.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/i18n/en-us.json b/src/assets/i18n/en-us.json index 15fb98dbb..c1a437662 100644 --- a/src/assets/i18n/en-us.json +++ b/src/assets/i18n/en-us.json @@ -172,7 +172,7 @@ "networkNotConnected": "No internet connections.", "allPhotosRegistered": "All Captures registered on blockchain successfully", "confirmPrefetch": "Do you want to restore the registered Captures? It may take minutes to complete.", - "confirmSyncAndRestore": "This will download all assets again and making sure your local data is up to date", + "confirmSyncAndRestore": "This will download all assets and making sure your local data is up to date", "loadingPreviousData": "Restoring Captures...", "doNotTurnOffApp": "Please keep the screen on and DO NOT leave the app.", "upgrading": "Upgrading...", From c57a1eb8cd358260cd25fe1f9128b2518fce401c Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 19 Dec 2023 15:59:25 +0800 Subject: [PATCH 04/19] WIP: add caption field to Proof, IndexedProofView --- src/app/shared/repositories/proof/proof.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/app/shared/repositories/proof/proof.ts b/src/app/shared/repositories/proof/proof.ts index 0bc9cb69e..94bfa4861 100644 --- a/src/app/shared/repositories/proof/proof.ts +++ b/src/app/shared/repositories/proof/proof.ts @@ -26,6 +26,12 @@ export class Proof { diaBackendAssetId?: string = undefined; + /** + * When user uploades a capture we do not have option to set caption. Therefore caption is empty + * by default. Everytime caption is updated we need to update the caption in the proof as well. + */ + caption = ''; + isCollected = false; signatures: Signatures = {}; @@ -120,6 +126,7 @@ export class Proof { ); proof.setIndexedAssets(indexedProofView.indexedAssets); proof.diaBackendAssetId = indexedProofView.diaBackendAssetId; + proof.caption = indexedProofView.caption ?? ''; proof.isCollected = indexedProofView.isCollected ?? false; proof.signatureVersion = indexedProofView.signatureVersion; proof.integritySha = indexedProofView.integritySha; @@ -290,6 +297,7 @@ export class Proof { signatures: this.signatures, signatureVersion: this.signatureVersion, diaBackendAssetId: this.diaBackendAssetId, + caption: this.caption, isCollected: this.isCollected, integritySha: this.integritySha, cameraSource: this.cameraSource, @@ -424,6 +432,7 @@ export interface IndexedProofView extends Tuple { readonly signatures: Signatures; readonly signatureVersion?: string; readonly diaBackendAssetId?: string; + readonly caption?: string; readonly isCollected?: boolean; readonly integritySha?: string; readonly cameraSource: CameraSource; From 585cb4eb1cc484db52f1a8622360706f91889422 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 19 Dec 2023 16:00:21 +0800 Subject: [PATCH 05/19] WIP: sync Proof.caption with diaBackendAsset on edit --- .../details/edit-caption/edit-caption.page.ts | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/app/features/home/details/edit-caption/edit-caption.page.ts b/src/app/features/home/details/edit-caption/edit-caption.page.ts index f5208a6e5..68221df9e 100644 --- a/src/app/features/home/details/edit-caption/edit-caption.page.ts +++ b/src/app/features/home/details/edit-caption/edit-caption.page.ts @@ -4,12 +4,15 @@ import { ActivatedRoute } from '@angular/router'; import { NavController } from '@ionic/angular'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { combineLatest, fromEvent } from 'rxjs'; -import { map, tap } from 'rxjs/operators'; +import { first, map, tap } from 'rxjs/operators'; import { DiaBackendAuthService } from '../../../../shared/dia-backend/auth/dia-backend-auth.service'; import { BUBBLE_IFRAME_URL } from '../../../../shared/dia-backend/secret'; import { BubbleToIonicPostMessage } from '../../../../shared/iframe/iframe'; import { IframeService } from '../../../../shared/iframe/iframe.service'; +import { getOldProof } from '../../../../shared/repositories/proof/old-proof-adapter'; +import { ProofRepository } from '../../../../shared/repositories/proof/proof-repository.service'; import { isNonNullable } from '../../../../utils/rx-operators/rx-operators'; +import { InformationSessionService } from '../information/session/information-session.service'; @UntilDestroy() @Component({ @@ -43,7 +46,9 @@ export class EditCaptionPage { private readonly sanitizer: DomSanitizer, private readonly navController: NavController, private readonly iframeService: IframeService, - private readonly diaBackendAuthService: DiaBackendAuthService + private readonly diaBackendAuthService: DiaBackendAuthService, + private readonly informationSessionService: InformationSessionService, + private readonly proofRepository: ProofRepository ) { this.processIframeEvents(); } @@ -60,6 +65,7 @@ export class EditCaptionPage { break; case BubbleToIonicPostMessage.EDIT_CAPTION_SAVE: this.iframeService.refreshDetailsPageIframe(); + this.syncCaption(); this.navController.back(); break; } @@ -68,4 +74,26 @@ export class EditCaptionPage { ) .subscribe(); } + + syncCaption() { + if (this.informationSessionService.activatedDetailedCapture) { + combineLatest([ + this.informationSessionService.activatedDetailedCapture.proof$, + this.informationSessionService.activatedDetailedCapture.caption$, + ]) + .pipe( + first(), + tap(([proof, latestCaptionFromBackend]) => { + if (proof && latestCaptionFromBackend) { + proof.caption = latestCaptionFromBackend; + this.proofRepository.update( + [proof], + (x, y) => getOldProof(x).hash === getOldProof(y).hash + ); + } + }) + ) + .subscribe(); + } + } } From b5b43a9ad2d828546264199d993f8f252cb9b6a3 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 19 Dec 2023 17:28:34 +0800 Subject: [PATCH 06/19] WIP: sync Proof.caption with diaBackendAsset on edit --- .../details/edit-caption/edit-caption.page.ts | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/app/features/home/details/edit-caption/edit-caption.page.ts b/src/app/features/home/details/edit-caption/edit-caption.page.ts index 68221df9e..f3eb4e47b 100644 --- a/src/app/features/home/details/edit-caption/edit-caption.page.ts +++ b/src/app/features/home/details/edit-caption/edit-caption.page.ts @@ -3,8 +3,14 @@ import { DomSanitizer } from '@angular/platform-browser'; import { ActivatedRoute } from '@angular/router'; import { NavController } from '@ionic/angular'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; -import { combineLatest, fromEvent } from 'rxjs'; -import { first, map, tap } from 'rxjs/operators'; +import { combineLatest, fromEvent, of } from 'rxjs'; +import { + concatMap, + finalize, + first, + map, + tap as switchTap, +} from 'rxjs/operators'; import { DiaBackendAuthService } from '../../../../shared/dia-backend/auth/dia-backend-auth.service'; import { BUBBLE_IFRAME_URL } from '../../../../shared/dia-backend/secret'; import { BubbleToIonicPostMessage } from '../../../../shared/iframe/iframe'; @@ -56,7 +62,7 @@ export class EditCaptionPage { processIframeEvents() { fromEvent(window, 'message') .pipe( - tap(event => { + switchTap(event => { const postMessageEvent = event as MessageEvent; const data = postMessageEvent.data as BubbleToIonicPostMessage; switch (data) { @@ -65,8 +71,7 @@ export class EditCaptionPage { break; case BubbleToIonicPostMessage.EDIT_CAPTION_SAVE: this.iframeService.refreshDetailsPageIframe(); - this.syncCaption(); - this.navController.back(); + this.syncCaptionAndNavigateBack(); break; } }), @@ -75,7 +80,7 @@ export class EditCaptionPage { .subscribe(); } - syncCaption() { + syncCaptionAndNavigateBack() { if (this.informationSessionService.activatedDetailedCapture) { combineLatest([ this.informationSessionService.activatedDetailedCapture.proof$, @@ -83,15 +88,17 @@ export class EditCaptionPage { ]) .pipe( first(), - tap(([proof, latestCaptionFromBackend]) => { - if (proof && latestCaptionFromBackend) { + concatMap(([proof, latestCaptionFromBackend]) => { + if (proof) { proof.caption = latestCaptionFromBackend; - this.proofRepository.update( + return this.proofRepository.update( [proof], (x, y) => getOldProof(x).hash === getOldProof(y).hash ); } - }) + return of(null); + }), + finalize(() => this.navController.back()) ) .subscribe(); } From a60ee6128336fa450ce4916ff21ca1b26268b5ce Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 19 Dec 2023 17:55:31 +0800 Subject: [PATCH 07/19] WIP: retrieve hasCaption from Proof object instead of API call --- .../home/capture-tab/capture-item/capture-item.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/features/home/capture-tab/capture-item/capture-item.component.ts b/src/app/features/home/capture-tab/capture-item/capture-item.component.ts index cc1b6473c..3dfd8966e 100644 --- a/src/app/features/home/capture-tab/capture-item/capture-item.component.ts +++ b/src/app/features/home/capture-tab/capture-item/capture-item.component.ts @@ -72,8 +72,7 @@ export class CaptureItemComponent { ); readonly hasCaption$ = this.proof$.pipe( - switchMap(proof => this.diaBackendAssetRepository.fetchByProof$(proof)), - map(asset => asset.caption !== '') + map(proof => proof.caption.trim() !== '') ); readonly isVideo$ = this.proof$.pipe( From fcc05682bb40cc17723ed4f621b56e062c6003c3 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 19 Dec 2023 16:04:17 +0800 Subject: [PATCH 08/19] WIP: set proof.caption in storeIndexedProof storeIndexedProof is called from storeRemoteCapture. storeRemoteCapture most of the time is called when user restore (during app start) or reload (triggers pull to refresh) --- .../asset/downloading/dia-backend-downloading.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/shared/dia-backend/asset/downloading/dia-backend-downloading.service.ts b/src/app/shared/dia-backend/asset/downloading/dia-backend-downloading.service.ts index 30ea7f3d7..b91c94357 100644 --- a/src/app/shared/dia-backend/asset/downloading/dia-backend-downloading.service.ts +++ b/src/app/shared/dia-backend/asset/downloading/dia-backend-downloading.service.ts @@ -80,6 +80,7 @@ export class DiaBackendAssetDownloadingService { }, }); proof.diaBackendAssetId = diaBackendAsset.id; + proof.caption = diaBackendAsset.caption; if (diaBackendAsset.signed_metadata) proof.setSignatureVersion(); return this.proofRepository.add(proof, OnConflictStrategy.REPLACE); } From 6496bd4d7812bdda9e2d57ede5c4756ba02832c1 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 21 Dec 2023 13:23:17 +0800 Subject: [PATCH 09/19] WIP: add uploadedAt to Proof, IndexedProofView --- .../shared/repositories/proof/proof.spec.ts | 37 +++++++++++++++++ src/app/shared/repositories/proof/proof.ts | 40 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/app/shared/repositories/proof/proof.spec.ts b/src/app/shared/repositories/proof/proof.spec.ts index fcd6ead1e..301cab6a4 100644 --- a/src/app/shared/repositories/proof/proof.spec.ts +++ b/src/app/shared/repositories/proof/proof.spec.ts @@ -287,6 +287,43 @@ describe('Proof utils', () => { const expected = `{\n "asset_mime_type": "${ASSET1_MIMETYPE}",\n "caption": "",\n "created_at": ${TIMESTAMP},\n "device_name": "${DEVICE_NAME_VALUE1}",\n "information": {\n "device.device_name": "${DEVICE_NAME_VALUE2}",\n "device.humidity": 0.8,\n "geolocation.geolocation_latitude": ${GEOLOCATION_LATITUDE2},\n "geolocation.geolocation_longitude": ${GEOLOCATION_LONGITUDE2}\n },\n "location_latitude": ${GEOLOCATION_LATITUDE1},\n "location_longitude": ${GEOLOCATION_LONGITUDE1},\n "proof_hash": "${ASSET1_SHA256SUM}",\n "recorder": "Capture",\n "spec_version": "2.0.0"\n}`; expect(getSerializedSortedProofMetadata(ProofMetadata)).toEqual(expected); }); + + describe('uploadedAtOrTimestamp', () => { + it('should return timestamp in milliseconds when uploadedAt is undefined', async () => { + proof = await Proof.from(mediaStore, ASSETS, TRUTH, SIGNATURES_VALID); + expect(proof.uploadedAtOrTimestamp).toEqual(TRUTH.timestamp); + }); + + it('should return uploadedAt in milliseconds when uploadedAt is defined', async () => { + const date = '2023-12-21T01:15:17Z'; // sample returned by API + const dateInMilliseconds = Date.parse(date); + + proof = await Proof.from(mediaStore, ASSETS, TRUTH, SIGNATURES_VALID); + proof.uploadedAt = date; + + expect(proof.uploadedAtOrTimestamp).toEqual(dateInMilliseconds); + }); + + it('should return timestamp in milliseconds when uploadedAt is not a valid date', async () => { + proof = await Proof.from(mediaStore, ASSETS, TRUTH, SIGNATURES_VALID); + proof.uploadedAt = 'invalid date'; + expect(proof.uploadedAtOrTimestamp).toEqual(TRUTH.timestamp); + }); + + it('should return timestamp in milliseconds when its in seconds', async () => { + const timestamp = 1627545600; // 29th July 2021 12:00:00 GMT + const timestampInMilliseconds = timestamp * 1000; + + proof = await Proof.from( + mediaStore, + ASSETS, + { ...TRUTH, timestamp }, + SIGNATURES_VALID + ); + + expect(proof.uploadedAtOrTimestamp).toEqual(timestampInMilliseconds); + }); + }); }); const ASSET1_MIMETYPE: MimeType = 'image/png'; diff --git a/src/app/shared/repositories/proof/proof.ts b/src/app/shared/repositories/proof/proof.ts index 0bc9cb69e..34e8f0578 100644 --- a/src/app/shared/repositories/proof/proof.ts +++ b/src/app/shared/repositories/proof/proof.ts @@ -26,6 +26,13 @@ export class Proof { diaBackendAssetId?: string = undefined; + /** + * The timestamp when the asset is uploaded to the backend, in the format "2023-12-21T01:15:17Z". + * By default, it is undefined. Once the asset is successfully uploaded, the uploadedAt property + * will be set to the timestamp provided by the backend. + */ + uploadedAt?: string = undefined; + isCollected = false; signatures: Signatures = {}; @@ -41,6 +48,36 @@ export class Proof { */ cameraSource: CameraSource = CameraSource.Camera; + /** + * Used to sort the assets in the VERIFIED tab either by timestamp or uploadedAt (if available). + */ + get uploadedAtOrTimestamp() { + const MILLISECONDS_PER_SECOND = 1000; + const LENGTH_IN_MILLISECONDS = 13; + + // convert timestamp to milliseconds if needed + const proofTimestampInMilliseconds = + this.timestamp.toString().length === LENGTH_IN_MILLISECONDS + ? this.timestamp + : this.timestamp * MILLISECONDS_PER_SECOND; + + try { + const serverTimestampInMilliseconds = Date.parse(this.uploadedAt ?? ''); + return serverTimestampInMilliseconds || proofTimestampInMilliseconds; + } catch (error) { + return proofTimestampInMilliseconds; + } + } + + /** + * The timestamp when the proof was first created or captured. Different from uploadedAt + * The timestamp is generated using Date.now() and is represented in milliseconds. + * + * Note: After restoring or syncing with the backend assets, the timestamp will be in seconds. + * For more details, refer to https://github.com/numbersprotocol/storage-backend/issues/976 + * + * Note: Milliseconds are 13 digits long, while seconds are 10 digits long. + */ get timestamp() { return this.truth.timestamp; } @@ -120,6 +157,7 @@ export class Proof { ); proof.setIndexedAssets(indexedProofView.indexedAssets); proof.diaBackendAssetId = indexedProofView.diaBackendAssetId; + proof.uploadedAt = indexedProofView.uploadedAt; proof.isCollected = indexedProofView.isCollected ?? false; proof.signatureVersion = indexedProofView.signatureVersion; proof.integritySha = indexedProofView.integritySha; @@ -290,6 +328,7 @@ export class Proof { signatures: this.signatures, signatureVersion: this.signatureVersion, diaBackendAssetId: this.diaBackendAssetId, + uploadedAt: this.uploadedAt, isCollected: this.isCollected, integritySha: this.integritySha, cameraSource: this.cameraSource, @@ -424,6 +463,7 @@ export interface IndexedProofView extends Tuple { readonly signatures: Signatures; readonly signatureVersion?: string; readonly diaBackendAssetId?: string; + readonly uploadedAt?: string; readonly isCollected?: boolean; readonly integritySha?: string; readonly cameraSource: CameraSource; From 3e6adaf46ec616fac9875b8645d6633bd9300bd6 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Wed, 20 Dec 2023 13:25:35 +0800 Subject: [PATCH 10/19] WIP: add uploaded_at to DiaBackendAsset --- .../dia-backend/asset/dia-backend-asset-repository.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/shared/dia-backend/asset/dia-backend-asset-repository.service.ts b/src/app/shared/dia-backend/asset/dia-backend-asset-repository.service.ts index d51afa0b0..766f69af0 100644 --- a/src/app/shared/dia-backend/asset/dia-backend-asset-repository.service.ts +++ b/src/app/shared/dia-backend/asset/dia-backend-asset-repository.service.ts @@ -357,6 +357,7 @@ export interface DiaBackendAsset extends Tuple { readonly caption: string; readonly post_creation_workflow_id: string; readonly mint_workflow_id: string; + readonly uploaded_at: string; } export interface OwnerAddresses extends Tuple { From 41f9cd430379d5aecce608b448ad4538f9529e0d Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 21 Dec 2023 14:29:22 +0800 Subject: [PATCH 11/19] WIP: set proof.uploadedDate after successful upload --- .../asset/uploading/dia-backend-asset-uploading.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/shared/dia-backend/asset/uploading/dia-backend-asset-uploading.service.ts b/src/app/shared/dia-backend/asset/uploading/dia-backend-asset-uploading.service.ts index 2cb55da5d..d4a75a06c 100644 --- a/src/app/shared/dia-backend/asset/uploading/dia-backend-asset-uploading.service.ts +++ b/src/app/shared/dia-backend/asset/uploading/dia-backend-asset-uploading.service.ts @@ -145,6 +145,7 @@ export class DiaBackendAssetUploadingService { }), map(diaBackendAsset => { proof.diaBackendAssetId = diaBackendAsset.id; + proof.uploadedAt = diaBackendAsset.uploaded_at; return proof; }), retryWhen(err$ => From 0e26e4bb2f6c82ccb3f660dbc6c8328c436b382e Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 21 Dec 2023 14:29:47 +0800 Subject: [PATCH 12/19] WIP: set proof.uploadedDate during storeRemoteCapture storeIndexedProof is called from storeRemoteCapture. storeRemoteCapture most of the time is called when user restore (during app start) or reload (triggers pull to refresh) --- .../asset/downloading/dia-backend-downloading.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/shared/dia-backend/asset/downloading/dia-backend-downloading.service.ts b/src/app/shared/dia-backend/asset/downloading/dia-backend-downloading.service.ts index 30ea7f3d7..a86cd830f 100644 --- a/src/app/shared/dia-backend/asset/downloading/dia-backend-downloading.service.ts +++ b/src/app/shared/dia-backend/asset/downloading/dia-backend-downloading.service.ts @@ -80,6 +80,7 @@ export class DiaBackendAssetDownloadingService { }, }); proof.diaBackendAssetId = diaBackendAsset.id; + proof.uploadedAt = diaBackendAsset.uploaded_at; if (diaBackendAsset.signed_metadata) proof.setSignatureVersion(); return this.proofRepository.add(proof, OnConflictStrategy.REPLACE); } From a2d591bb5b7417c5be9762395b7141d9ed0a060f Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Wed, 20 Dec 2023 17:12:40 +0800 Subject: [PATCH 13/19] WIP: sort verified tabs by uploadedAt or timestamp --- src/app/features/home/capture-tab/capture-tab.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/features/home/capture-tab/capture-tab.component.ts b/src/app/features/home/capture-tab/capture-tab.component.ts index 128ab9bd0..389e09937 100644 --- a/src/app/features/home/capture-tab/capture-tab.component.ts +++ b/src/app/features/home/capture-tab/capture-tab.component.ts @@ -94,7 +94,9 @@ export class CaptureTabComponent implements OnInit { ); readonly captures$ = this.proofs$.pipe( - map(proofs => proofs.sort((a, b) => b.timestamp - a.timestamp)) + map(proofs => + proofs.sort((a, b) => b.uploadedAtOrTimestamp - a.uploadedAtOrTimestamp) + ) ); readonly networkConnected$ = this.networkService.connected$; From c4a0040a4852736de2543111c1f555eff2f4b9b3 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 2 Jan 2024 14:14:06 +0800 Subject: [PATCH 14/19] refactor(capture-item): remove unnecessary .trim() on caption --- .../home/capture-tab/capture-item/capture-item.component.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/app/features/home/capture-tab/capture-item/capture-item.component.ts b/src/app/features/home/capture-tab/capture-item/capture-item.component.ts index 3dfd8966e..9ce20305c 100644 --- a/src/app/features/home/capture-tab/capture-item/capture-item.component.ts +++ b/src/app/features/home/capture-tab/capture-item/capture-item.component.ts @@ -71,9 +71,7 @@ export class CaptureItemComponent { ) ); - readonly hasCaption$ = this.proof$.pipe( - map(proof => proof.caption.trim() !== '') - ); + readonly hasCaption$ = this.proof$.pipe(map(proof => proof.caption !== '')); readonly isVideo$ = this.proof$.pipe( concatMap(proof => proof.getFirstAssetMeta()), From dbd03540a065537378552dbb24c99e0b4701f96b Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 2 Jan 2024 14:18:35 +0800 Subject: [PATCH 15/19] refactor(proof.ts): remove unused try catch block --- src/app/shared/repositories/proof/proof.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/app/shared/repositories/proof/proof.ts b/src/app/shared/repositories/proof/proof.ts index 34e8f0578..0fe22169d 100644 --- a/src/app/shared/repositories/proof/proof.ts +++ b/src/app/shared/repositories/proof/proof.ts @@ -61,12 +61,8 @@ export class Proof { ? this.timestamp : this.timestamp * MILLISECONDS_PER_SECOND; - try { - const serverTimestampInMilliseconds = Date.parse(this.uploadedAt ?? ''); - return serverTimestampInMilliseconds || proofTimestampInMilliseconds; - } catch (error) { - return proofTimestampInMilliseconds; - } + const serverTimestampInMilliseconds = Date.parse(this.uploadedAt ?? ''); + return serverTimestampInMilliseconds || proofTimestampInMilliseconds; } /** From b0b3349d4a608d214e1f78e3b3f61cc377a5e50c Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Tue, 2 Jan 2024 14:25:27 +0800 Subject: [PATCH 16/19] feat(urls.ts): redirect nftsearch.site to verify.numbersprotocol.io --- src/app/utils/url.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/utils/url.ts b/src/app/utils/url.ts index 05160ab83..c6b74c7d1 100644 --- a/src/app/utils/url.ts +++ b/src/app/utils/url.ts @@ -15,9 +15,9 @@ export function getAssetProfileUrl(id: string, token?: string) { export function getAssetProfileForNSE(id: string, token?: string) { if (token) { - return `https://nftsearch.site/asset-profile?cid=${id}&tmp_token=${token}`; + return `https://verify.numbersprotocol.io/asset-profile?cid=${id}&tmp_token=${token}`; } - return `https://nftsearch.site/asset-profile?cid=${id}`; + return `https://verify.numbersprotocol.io/asset-profile?cid=${id}`; } export function getAssetProfileForCaptureIframe(cid: string) { From 49e3a131f309a4a41daa8269d56ac74cd5d67bc8 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Fri, 5 Jan 2024 16:49:14 +0800 Subject: [PATCH 17/19] fix(urls.ts): change query param to cid from nid as requested https://app.asana.com/0/0/1206255834643809/1206285527476452/f --- src/app/utils/url.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/utils/url.ts b/src/app/utils/url.ts index c6b74c7d1..197d148d8 100644 --- a/src/app/utils/url.ts +++ b/src/app/utils/url.ts @@ -15,9 +15,9 @@ export function getAssetProfileUrl(id: string, token?: string) { export function getAssetProfileForNSE(id: string, token?: string) { if (token) { - return `https://verify.numbersprotocol.io/asset-profile?cid=${id}&tmp_token=${token}`; + return `https://verify.numbersprotocol.io/asset-profile?nid=${id}&tmp_token=${token}`; } - return `https://verify.numbersprotocol.io/asset-profile?cid=${id}`; + return `https://verify.numbersprotocol.io/asset-profile?nid=${id}`; } export function getAssetProfileForCaptureIframe(cid: string) { From b12b5f535e38e3478f67be1662f7a3006999b061 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 11 Jan 2024 13:58:33 +0800 Subject: [PATCH 18/19] chore: bump app version to 0.88.6 --- 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 c2d30e65b..3b4b003ed 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 871 - versionName "0.87.1" + versionCode 886 + versionName "0.88.6" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildFeatures { diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index d08c49ea4..d91779a43 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 = 871; + CURRENT_PROJECT_VERSION = 886; 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.87.1; + MARKETING_VERSION = 0.88.6; 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 = 871; + CURRENT_PROJECT_VERSION = 886; 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.87.1; + MARKETING_VERSION = 0.88.6; 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 25b71405e..699f08a39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "capture-lite", - "version": "0.87.1", + "version": "0.88.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "capture-lite", - "version": "0.87.1", + "version": "0.88.6", "dependencies": { "packages": "^0.0.8", "@angular/animations": "^14.2.0", diff --git a/package.json b/package.json index aeac9c949..98fe9c22d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "capture-lite", - "version": "0.87.1", + "version": "0.88.6", "author": "numbersprotocol", "homepage": "https://numbersprotocol.io/", "scripts": { From 5922db2cd450e99381fd38d736313029660a08f3 Mon Sep 17 00:00:00 2001 From: sultanmyrza Date: Thu, 11 Jan 2024 14:03:39 +0800 Subject: [PATCH 19/19] chore: update CHANGELOG.md for 0.88.6 --- CHANGELOG.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48cd5d047..08cb07e28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.88.6] - 2024-01-11 + +### Added + +Feature update the NSE domain (#3161) +Feat confirm dialog on pull to refresh (#3171) +Feat sort assets in VERIFIED tab by uploaded_at (#3158) + +### Fixed + +Fix excessive api call (#3157) + ## [0.87.1] - 2023-12-04 ### Fixed @@ -2226,7 +2238,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.87.1...HEAD +[unreleased]: https://github.com/numbersprotocol/capture-lite/compare/0.88.6...HEAD +[0.88.6]: https://github.com/numbersprotocol/capture-lite/compare/0.87.1...0.88.6 [0.87.1]: https://github.com/numbersprotocol/capture-lite/compare/0.87.0...0.87.1 [0.87.0]: https://github.com/numbersprotocol/capture-lite/compare/0.86.4...0.87.0 [0.86.4]: https://github.com/numbersprotocol/capture-lite/compare/0.83.2...0.86.4