Skip to content

Commit

Permalink
Rebuild Capture - Step D1 (Stateful Capture Items) #452
Browse files Browse the repository at this point in the history
* Add mutex lock when updating table.

* Navigate up after deleting capture item.

* Remove notification when collecting and uploading proof.

* Implement stateful CaptureItem.

* Display an empty small image to avoid border of img without src.

* Remove spinner in inbox and transactions page.
  • Loading branch information
seanwu1105 authored Jan 13, 2021
1 parent fe67d6e commit abfeb60
Show file tree
Hide file tree
Showing 20 changed files with 194 additions and 268 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslocoService } from '@ngneat/transloco';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { concatMap, map, shareReplay, switchMap, tap } from 'rxjs/operators';
import { defer } from 'rxjs';
import {
concatMap,
concatMapTo,
map,
shareReplay,
switchMap,
tap,
} from 'rxjs/operators';
import { BlockingActionService } from '../../../../shared/services/blocking-action/blocking-action.service';
import { ConfirmAlert } from '../../../../shared/services/confirm-alert/confirm-alert.service';
import { DiaBackendAuthService } from '../../../../shared/services/dia-backend/auth/dia-backend-auth.service';
Expand Down Expand Up @@ -96,7 +104,8 @@ export class CaptureDetailsPage {

private async remove() {
const action$ = this.proof$.pipe(
concatMap(proof => this.proofRepository.remove(proof))
concatMap(proof => this.proofRepository.remove(proof)),
concatMapTo(defer(() => this.router.navigate(['..'])))
);
const result = await this.confirmAlert.present();
if (result) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<div *ngIf="willCollectTruth$ | async" class="spinner-container">
<ion-spinner></ion-spinner>
</div>
<ion-icon *ngIf="willCollectTruth$ | async" name="hourglass-outline"></ion-icon>
<!-- Display an empty small image to avoid border of img without src. -->
<img
[src]="
(thumbnailUrl$ | async) ||
'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
"
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
:host {
display: block;
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
border-radius: 4px;
background-color: whitesmoke;
}

ion-icon {
position: absolute;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
z-index: 10;
opacity: 50%;
font-size: 24px;
color: white;
}

.spinner-container {
position: absolute;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
z-index: 9;

ion-spinner {
width: 48px;
height: 48px;
opacity: 50%;

--color: white;
}
}

img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
background-color: whitesmoke;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SharedTestingModule } from '../../../../shared/shared-testing.module';
import { CaptureItemComponent } from './capture-item.component';

describe('CaptureItemComponent', () => {
let component: CaptureItemComponent;
let fixture: ComponentFixture<CaptureItemComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [CaptureItemComponent],
imports: [SharedTestingModule],
}).compileComponents();

fixture = TestBed.createComponent(CaptureItemComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Component, Input } from '@angular/core';
import { UntilDestroy } from '@ngneat/until-destroy';
import { BehaviorSubject } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { getOldProof } from '../../../../shared/services/repositories/proof/old-proof-adapter';
import { Proof } from '../../../../shared/services/repositories/proof/proof';
import { isNonNullable } from '../../../../utils/rx-operators/rx-operators';

@UntilDestroy({ checkProperties: true })
@Component({
selector: 'app-capture-item',
templateUrl: './capture-item.component.html',
styleUrls: ['./capture-item.component.scss'],
})
export class CaptureItemComponent {
// Use setter to make sure the item is updated even if the component is
// recycled and redrawed (e.g. virtual scroll).
@Input()
set item(value: CaptureItem) {
this._item$.next(value);
}

// tslint:disable-next-line: rxjs-no-explicit-generics
private readonly _item$ = new BehaviorSubject<CaptureItem | undefined>(
undefined
);
readonly item$ = this._item$.asObservable().pipe(isNonNullable());
readonly thumbnailUrl$ = this.item$.pipe(
switchMap(item => item.getThumbnailUrl())
);
readonly willCollectTruth$ = this.item$.pipe(
map(item => item.proof?.willCollectTruth === true)
);
}

export class CaptureItem {
proof?: Proof;
private readonly createdTimestamp: number;

get oldProofHash() {
if (this.proof) {
return getOldProof(this.proof).hash;
}
}

get timestamp() {
if (this.proof) {
return this.proof.timestamp;
}
return this.createdTimestamp;
}

constructor({ proof }: { proof?: Proof }) {
this.proof = proof;
this.createdTimestamp = Date.now();
}

async getThumbnailUrl() {
if (this.proof) {
return this.proof.getThumbnailUrl();
}
}
}
21 changes: 8 additions & 13 deletions src/app/features/home/capture-tab/capture-tab.component.html
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
<ng-container
*ngFor="let group of capturesByDate$ | async | keyvalue: keyDescendingOrder"
*ngFor="
let group of capturesByDate$ | async | keyvalue: keyDescendingOrder;
trackBy: trackCaptureGroupByDate
"
>
<div class="mat-title">{{ group.key }}</div>
<mat-grid-list cols="3" gutterSize="8px">
<mat-grid-tile
*ngFor="let capture of group.value"
*ngFor="let captureItem of group.value; trackBy: trackCaptureItem"
[routerLink]="
capture.proof.willCollectTruth
captureItem.proof.willCollectTruth
? undefined
: ['capture-details', { oldProofHash: capture.oldProofHash }]
: ['capture-details', { oldProofHash: captureItem.oldProofHash }]
"
class="capture-item"
>
<div *ngIf="capture.proof.willCollectTruth" class="spinner-container">
<ion-spinner></ion-spinner>
</div>
<ion-icon
*ngIf="capture.proof.willCollectTruth"
name="hourglass-outline"
></ion-icon>
<img [src]="capture.thumbnailUrl" />
<app-capture-item [item]="captureItem"></app-capture-item>
</mat-grid-tile>
</mat-grid-list>
</ng-container>
43 changes: 0 additions & 43 deletions src/app/features/home/capture-tab/capture-tab.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,3 @@ div.mat-title {
margin: 26px 0 0;
font-size: large;
}

.capture-item {
height: 30vw;
overflow: hidden;
object-fit: cover;
border-radius: 4px;

.spinner-container {
position: absolute;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
z-index: 9;

ion-spinner {
width: 48px;
height: 48px;
opacity: 50%;

--color: white;
}
}

ion-icon {
position: absolute;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
z-index: 10;
opacity: 30%;
color: white;
font-size: 24px;
}

img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: inherit;
}
}
29 changes: 18 additions & 11 deletions src/app/features/home/capture-tab/capture-tab.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { formatDate, KeyValue } from '@angular/common';
import { Component } from '@angular/core';
import { groupBy } from 'lodash';
import { concatMap, map } from 'rxjs/operators';
import { getOldProof } from '../../../shared/services/repositories/proof/old-proof-adapter';
import { ProofRepository } from '../../../shared/services/repositories/proof/proof-repository.service';
import { CaptureItem } from './capture-item/capture-item.component';

@Component({
selector: 'app-capture-tab',
Expand All @@ -15,17 +15,11 @@ export class CaptureTabComponent {
readonly capturesByDate$ = this.proofs$.pipe(
map(proofs => proofs.sort((a, b) => b.timestamp - a.timestamp)),
concatMap(proofs =>
Promise.all(
proofs.map(async proof => ({
proof,
thumbnailUrl: await proof.getThumbnailUrl(),
oldProofHash: getOldProof(proof).hash,
}))
)
Promise.all(proofs.map(async proof => new CaptureItem({ proof })))
),
map(captures =>
groupBy(captures, capture =>
formatDate(capture.proof.timestamp, 'yyyy/MM/dd', 'en-US')
map(captureItems =>
groupBy(captureItems, item =>
formatDate(item.timestamp, 'yyyy/MM/dd', 'en-US')
)
)
);
Expand All @@ -39,4 +33,17 @@ export class CaptureTabComponent {
): number {
return a.key > b.key ? -1 : b.key > a.key ? 1 : 0;
}

// tslint:disable-next-line: prefer-function-over-method
trackCaptureGroupByDate(
_: number,
item: { key: string; value: CaptureItem[] }
) {
return item.key;
}

// tslint:disable-next-line: prefer-function-over-method
trackCaptureItem(_: number, item: CaptureItem) {
return item.oldProofHash;
}
}
3 changes: 2 additions & 1 deletion src/app/features/home/home.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { NgModule } from '@angular/core';
import { PostCaptureCardModule } from '../../shared/core/post-capture-card/post-capture-card.module';
import { SharedModule } from '../../shared/shared.module';
import { CaptureItemComponent } from './capture-tab/capture-item/capture-item.component';
import { CaptureTabComponent } from './capture-tab/capture-tab.component';
import { HomePageRoutingModule } from './home-routing.module';
import { HomePage } from './home.page';

@NgModule({
imports: [SharedModule, HomePageRoutingModule, PostCaptureCardModule],
declarations: [HomePage, CaptureTabComponent],
declarations: [HomePage, CaptureTabComponent, CaptureItemComponent],
})
export class HomePageModule {}
4 changes: 0 additions & 4 deletions src/app/features/home/inbox/inbox.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@
</mat-toolbar>

<div *transloco="let t" class="page-content">
<mat-spinner
*ngIf="(receivedTransactions$ | async)?.length === 0 && isFetching$ | async"
diameter="36"
></mat-spinner>
<span
*ngIf="
(receivedTransactions$ | async)?.length === 0 &&
Expand Down
6 changes: 0 additions & 6 deletions src/app/features/home/transaction/transaction.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@
</mat-toolbar>

<div *transloco="let t" class="page-content">
<mat-spinner
*ngIf="
(transactionsWithStatus$ | async)?.length === 0 && isFetching$ | async
"
diameter="36"
></mat-spinner>
<mat-list>
<ng-container
*ngFor="
Expand Down
20 changes: 1 addition & 19 deletions src/app/shared/services/collector/collector.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { Injectable } from '@angular/core';
import { TranslocoService } from '@ngneat/transloco';
import { defer } from 'rxjs';
import { ImageStore } from '../image-store/image-store.service';
import { NotificationService } from '../notification/notification.service';
import {
Assets,
getSerializedSortedSignedTargets,
Expand All @@ -11,7 +8,6 @@ import {
SignedTargets,
Truth,
} from '../repositories/proof/proof';
import { ProofRepository } from '../repositories/proof/proof-repository.service';
import { FactsProvider } from './facts/facts-provider';
import { SignatureProvider } from './signature/signature-provider';

Expand All @@ -22,23 +18,9 @@ export class CollectorService {
private readonly factsProviders = new Set<FactsProvider>();
private readonly signatureProviders = new Set<SignatureProvider>();

constructor(
private readonly notificationService: NotificationService,
private readonly translocoService: TranslocoService,
private readonly proofRepository: ProofRepository,
private readonly imageStore: ImageStore
) {}
constructor(private readonly imageStore: ImageStore) {}

async run(assets: Assets) {
return this.notificationService.notifyOnGoing(
defer(() => this._run(assets)),
this.translocoService.translate('storingAssets'),
this.translocoService.translate('message.storingAssets'),
true
);
}

private async _run(assets: Assets) {
const truth = await this.collectTruth(assets);
const signatures = await this.signTargets({ assets, truth });
return Proof.from(this.imageStore, assets, truth, signatures);
Expand Down
Loading

0 comments on commit abfeb60

Please sign in to comment.