FE-1041: show more webhooks (#197)

* FE-1041: show more webhooks
This commit is contained in:
Aleksandra Usacheva 2020-04-17 18:10:48 +03:00 committed by GitHub
parent fba331d92e
commit 64e0676661
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 112 additions and 36 deletions

View File

@ -1,4 +1,4 @@
.dsh-integrations { .dsh-integrations {
max-width: 720px; max-width: 720px;
margin: auto; margin: auto auto 20px;
} }

View File

@ -1,43 +1,71 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar'; import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslocoService } from '@ngneat/transloco'; import { TranslocoService } from '@ngneat/transloco';
import sortBy from 'lodash.sortby'; import sortBy from 'lodash.sortby';
import { BehaviorSubject, Observable, of, Subject } from 'rxjs'; import { BehaviorSubject, combineLatest, Observable, of, Subject } from 'rxjs';
import { catchError, filter, map, shareReplay, switchMap } from 'rxjs/operators'; import { catchError, filter, first, map, pluck, shareReplay, switchMap } from 'rxjs/operators';
import { Webhook } from '../../../../api-codegen/capi/swagger-codegen'; import { Webhook } from '../../../../api-codegen/capi/swagger-codegen';
import { WebhooksService } from '../../../../api/webhooks'; import { WebhooksService } from '../../../../api/webhooks';
import { booleanDebounceTime, progress, SHARE_REPLAY_CONF } from '../../../../custom-operators'; import { booleanDebounceTime, progress, SHARE_REPLAY_CONF } from '../../../../custom-operators';
const WEBHOOK_LIMIT = 10;
@Injectable() @Injectable()
export class ReceiveWebhooksService { export class ReceiveWebhooksService {
private webhooksState$ = new BehaviorSubject(null); private webhooksOffset$: BehaviorSubject<number> = new BehaviorSubject(WEBHOOK_LIMIT);
private receiveWebhooks$ = new Subject(); private webhooksState$: BehaviorSubject<Webhook[]> = new BehaviorSubject(null);
private receiveWebhooks$: Subject<void> = new Subject();
webhooks$: Observable<Webhook[]> = this.webhooksState$.pipe( webhooks$: Observable<Webhook[]> = this.webhooksState$.pipe(
filter(s => !!s), filter(s => !!s),
map(w => sortBy(w, i => !i.active)), map(w => sortBy(w, i => !i.active)),
switchMap(webhooks => combineLatest([this.webhooksOffset$, of(webhooks)])),
map(([offset, webhooks]) => webhooks.slice(0, offset)),
shareReplay(SHARE_REPLAY_CONF) shareReplay(SHARE_REPLAY_CONF)
); );
webhooksReceived$ = this.webhooks$.pipe( isLoading$: Observable<boolean> = progress(this.receiveWebhooks$, this.webhooks$).pipe(
map(s => !!s),
shareReplay(SHARE_REPLAY_CONF)
);
isLoading$ = progress(this.receiveWebhooks$, this.webhooksState$).pipe(
booleanDebounceTime(), booleanDebounceTime(),
shareReplay(SHARE_REPLAY_CONF) shareReplay(SHARE_REPLAY_CONF)
); );
hasMore$: Observable<boolean> = combineLatest([this.webhooksState$, this.webhooksOffset$]).pipe(
map(([webhooks, offset]) => webhooks?.length > offset),
shareReplay(SHARE_REPLAY_CONF)
);
constructor( constructor(
private webhooksService: WebhooksService, private webhooksService: WebhooksService,
private snackBar: MatSnackBar, private snackBar: MatSnackBar,
private transloco: TranslocoService private transloco: TranslocoService,
private route: ActivatedRoute,
private router: Router
) { ) {
this.isLoading$.subscribe();
this.route.queryParams
.pipe(
first(),
pluck('offset'),
filter(l => !!l),
map(offset => parseInt(offset, 10))
)
.subscribe(offset => {
this.webhooksOffset$.next(offset);
});
this.webhooksOffset$.subscribe(offset => {
this.router.navigate([location.pathname], {
queryParams: {
offset
}
});
});
this.receiveWebhooks$ this.receiveWebhooks$
.pipe( .pipe(
switchMap(_ => switchMap(() =>
this.webhooksService.getWebhooks().pipe( this.webhooksService.getWebhooks().pipe(
catchError(err => { catchError(err => {
console.error(err); console.error(err);
@ -47,10 +75,16 @@ export class ReceiveWebhooksService {
) )
) )
) )
.subscribe(webhooks => this.webhooksState$.next(webhooks)); .subscribe(webhooks => {
this.webhooksState$.next(webhooks);
});
} }
receiveWebhooks() { receiveWebhooks() {
this.receiveWebhooks$.next(); this.receiveWebhooks$.next();
} }
getMoreWebhooks() {
this.webhooksOffset$.next(this.webhooksOffset$.value + WEBHOOK_LIMIT);
}
} }

View File

@ -1,4 +1,4 @@
import { Component, Inject, Input, OnChanges, SimpleChanges } from '@angular/core'; import { ChangeDetectionStrategy, Component, Inject, Input, OnChanges, SimpleChanges } from '@angular/core';
import isEqual from 'lodash.isequal'; import isEqual from 'lodash.isequal';
import { pluck } from 'rxjs/operators'; import { pluck } from 'rxjs/operators';
@ -11,7 +11,8 @@ type InvoicesEventTypesEnum = InvoicesTopic.EventTypesEnum;
@Component({ @Component({
selector: 'dsh-webhook-panel', selector: 'dsh-webhook-panel',
templateUrl: 'webhook-panel.component.html', templateUrl: 'webhook-panel.component.html',
providers: [WebhookPanelService] providers: [WebhookPanelService],
changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class WebhookPanelComponent implements OnChanges { export class WebhookPanelComponent implements OnChanges {
@Input() @Input()

View File

@ -16,12 +16,10 @@
<dsh-spinner></dsh-spinner> <dsh-spinner></dsh-spinner>
</div> </div>
<dsh-empty-search-result <dsh-empty-search-result *ngIf="(webhooks$ | async)?.length === 0" [text]="t.emptyResult"></dsh-empty-search-result>
*ngIf="!(webhooks$ | async)?.length && (webhooksReceived$ | async)"
[text]="t.emptyResult"
></dsh-empty-search-result>
<div fxLayout="column" [fxLayoutGap]="layoutGap" *ngIf="!(isLoading$ | async)"> <div fxLayout="column" [fxLayoutGap]="layoutGap" *ngIf="!(isLoading$ | async)">
<dsh-webhook-panel *ngFor="let webhook of webhooks$ | async" [webhook]="webhook"></dsh-webhook-panel> <dsh-webhook-panel *ngFor="let webhook of webhooks$ | async" [webhook]="webhook"></dsh-webhook-panel>
<dsh-show-more-panel *ngIf="hasMore$ | async" (showMore)="getMoreWebhooks()"></dsh-show-more-panel>
</div> </div>
</div> </div>

View File

@ -5,12 +5,13 @@ import { CreateWebhookService } from './create-webhook.service';
import { ReceiveWebhooksService } from './receive-webhooks.service'; import { ReceiveWebhooksService } from './receive-webhooks.service';
@Component({ @Component({
templateUrl: 'webhooks.component.html' templateUrl: 'webhooks.component.html',
providers: [ReceiveWebhooksService, CreateWebhookService]
}) })
export class WebhooksComponent implements OnInit { export class WebhooksComponent implements OnInit {
webhooks$ = this.receiveWebhooksService.webhooks$; webhooks$ = this.receiveWebhooksService.webhooks$;
isLoading$ = this.receiveWebhooksService.isLoading$; isLoading$ = this.receiveWebhooksService.isLoading$;
webhooksReceived$ = this.receiveWebhooksService.webhooksReceived$; hasMore$ = this.receiveWebhooksService.hasMore$;
constructor( constructor(
private receiveWebhooksService: ReceiveWebhooksService, private receiveWebhooksService: ReceiveWebhooksService,
@ -25,4 +26,8 @@ export class WebhooksComponent implements OnInit {
createWebhook() { createWebhook() {
this.createWebhookService.createWebhook(); this.createWebhookService.createWebhook();
} }
getMoreWebhooks() {
this.receiveWebhooksService.getMoreWebhooks();
}
} }

View File

@ -12,12 +12,10 @@ import { TranslocoModule } from '@ngneat/transloco';
import { ButtonModule } from '@dsh/components/buttons'; import { ButtonModule } from '@dsh/components/buttons';
import { EmptySearchResultModule } from '@dsh/components/empty-search-result'; import { EmptySearchResultModule } from '@dsh/components/empty-search-result';
import { SpinnerModule } from '@dsh/components/indicators'; import { SpinnerModule } from '@dsh/components/indicators';
import { ShowMorePanelModule } from '@dsh/components/show-more-panel';
import { ShopService } from '../../../../api/shop';
import { WebhooksModule as ApiWebhooksModule } from '../../../../api/webhooks'; import { WebhooksModule as ApiWebhooksModule } from '../../../../api/webhooks';
import { CreateWebhookService } from './create-webhook.service';
import { CreateWebhookComponent } from './create-webhook/create-webhook.component'; import { CreateWebhookComponent } from './create-webhook/create-webhook.component';
import { ReceiveWebhooksService } from './receive-webhooks.service';
import { WebhookPanelModule } from './webhook-panel/webhook-panel.module'; import { WebhookPanelModule } from './webhook-panel/webhook-panel.module';
import { WebhooksRoutingModule } from './webhooks-routing.module'; import { WebhooksRoutingModule } from './webhooks-routing.module';
import { WebhooksComponent } from './webhooks.component'; import { WebhooksComponent } from './webhooks.component';
@ -38,10 +36,10 @@ import { WebhooksComponent } from './webhooks.component';
TranslocoModule, TranslocoModule,
WebhookPanelModule, WebhookPanelModule,
SpinnerModule, SpinnerModule,
EmptySearchResultModule EmptySearchResultModule,
ShowMorePanelModule
], ],
declarations: [WebhooksComponent, CreateWebhookComponent], declarations: [WebhooksComponent, CreateWebhookComponent],
entryComponents: [CreateWebhookComponent], entryComponents: [CreateWebhookComponent]
providers: [ReceiveWebhooksService, CreateWebhookService, ShopService]
}) })
export class WebhooksModule {} export class WebhooksModule {}

View File

@ -66,7 +66,7 @@ export class SearchFormService {
} }
private initForm(defaultLimit = 20): FormGroup { private initForm(defaultLimit = 20): FormGroup {
const form = this.fb.group({ return this.fb.group({
date: { date: {
begin: moment().startOf('month'), begin: moment().startOf('month'),
end: moment().endOf('month') end: moment().endOf('month')
@ -88,6 +88,5 @@ export class SearchFormService {
paymentAmountTo: '', paymentAmountTo: '',
rrn: '' rrn: ''
}); });
return form;
} }
} }

View File

@ -12,11 +12,8 @@
<dsh-payouts-search-form></dsh-payouts-search-form> <dsh-payouts-search-form></dsh-payouts-search-form>
<ng-container *ngIf="!(isInit$ | async)"> <ng-container *ngIf="!(isInit$ | async)">
<dsh-payouts-panels-list></dsh-payouts-panels-list> <dsh-payouts-panels-list></dsh-payouts-panels-list>
<dsh-card *ngIf="hasMore$ | async" class="dsh-payouts-show-more" fxLayout> <dsh-show-more-panel *ngIf="hasMore$ | async" (showMore)="fetchMore()" [isLoading]="isLoading$ | async">
<button dsh-button (click)="fetchMore()" [disabled]="isLoading$ | async" fxFlex *transloco="let t"> </dsh-show-more-panel>
{{ (isLoading$ | async) ? t.loading : t.showMore }}
</button>
</dsh-card>
<div *ngIf="!(payouts$ | async)?.length && !(doAction$ | async)" class="mat-headline dsh-payouts-empty"> <div *ngIf="!(payouts$ | async)?.length && !(doAction$ | async)" class="mat-headline dsh-payouts-empty">
<ng-container *transloco="let t"> <ng-container *transloco="let t">
{{ t.emptySearchResult }} {{ t.emptySearchResult }}

View File

@ -13,6 +13,7 @@ import { RangeDatepickerModule } from '@dsh/components/form-controls';
import { SpinnerModule } from '@dsh/components/indicators'; import { SpinnerModule } from '@dsh/components/indicators';
import { LayoutModule } from '@dsh/components/layout'; import { LayoutModule } from '@dsh/components/layout';
import { ScrollUpModule } from '@dsh/components/navigation'; import { ScrollUpModule } from '@dsh/components/navigation';
import { ShowMorePanelModule } from '@dsh/components/show-more-panel';
import { SearchModule } from '../../../api'; import { SearchModule } from '../../../api';
import { PayoutPanelModule } from './payouts-panels-list'; import { PayoutPanelModule } from './payouts-panels-list';
@ -39,7 +40,8 @@ import { SearchFormComponent } from './search-form';
MatInputModule, MatInputModule,
SpinnerModule, SpinnerModule,
ScrollUpModule, ScrollUpModule,
RangeDatepickerModule RangeDatepickerModule,
ShowMorePanelModule
], ],
declarations: [PayoutsComponent, SearchFormComponent], declarations: [PayoutsComponent, SearchFormComponent],
exports: [PayoutsComponent] exports: [PayoutsComponent]

View File

@ -0,0 +1 @@
export * from './show-more-panel.module';

View File

@ -0,0 +1,5 @@
<dsh-card class="dsh-show-more-panel" fxLayout>
<button dsh-button (click)="getMore()" [disabled]="isLoading" fxFlex *transloco="let t">
{{ isLoading ? t.loading : t.showMore }}
</button>
</dsh-card>

View File

@ -0,0 +1,3 @@
.dsh-show-more-panel {
padding: 10px 20px;
}

View File

@ -0,0 +1,18 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'dsh-show-more-panel',
templateUrl: 'show-more-panel.component.html',
styleUrls: ['show-more-panel.component.scss']
})
export class ShowMorePanelComponent {
@Input()
isLoading = false;
@Output()
showMore: EventEmitter<void> = new EventEmitter();
getMore() {
this.showMore.emit();
}
}

View File

@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { FlexModule } from '@angular/flex-layout';
import { TranslocoModule } from '@ngneat/transloco';
import { ButtonModule } from '@dsh/components/buttons';
import { CardModule } from '@dsh/components/layout';
import { ShowMorePanelComponent } from './show-more-panel.component';
@NgModule({
declarations: [ShowMorePanelComponent],
imports: [CardModule, ButtonModule, FlexModule, TranslocoModule],
exports: [ShowMorePanelComponent]
})
export class ShowMorePanelModule {}