Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat sort assets in verified tab by uploaded at #3158

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/app/features/home/capture-tab/capture-tab.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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$;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export class DiaBackendAssetUploadingService {
}),
map(diaBackendAsset => {
proof.diaBackendAssetId = diaBackendAsset.id;
proof.uploadedAt = diaBackendAsset.uploaded_at;
return proof;
}),
retryWhen(err$ =>
Expand Down
37 changes: 37 additions & 0 deletions src/app/shared/repositories/proof/proof.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
36 changes: 36 additions & 0 deletions src/app/shared/repositories/proof/proof.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
Expand All @@ -41,6 +48,32 @@ 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;

const serverTimestampInMilliseconds = Date.parse(this.uploadedAt ?? '');
return serverTimestampInMilliseconds || 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;
}
Expand Down Expand Up @@ -120,6 +153,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;
Expand Down Expand Up @@ -290,6 +324,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,
Expand Down Expand Up @@ -424,6 +459,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;
Expand Down
Loading