Browse Source

Minor refactorings.

pull/282/head
Sebastian Stehle 9 years ago
parent
commit
72f92f21bc
  1. 2
      src/Squidex/app/features/rules/pages/rules/rules-page.component.html
  2. 4
      src/Squidex/app/features/rules/pages/rules/rules-page.component.ts
  3. 36
      src/Squidex/app/features/settings/pages/clients/clients-page.component.html
  4. 42
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html
  5. 4
      src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts
  6. 38
      src/Squidex/app/features/settings/pages/plans/plans-page.component.html
  7. 74
      src/Squidex/app/features/settings/pages/plans/plans-page.component.ts
  8. 1
      src/Squidex/app/shared/internal.ts
  9. 2
      src/Squidex/app/shared/module.ts
  10. 9
      src/Squidex/app/shared/state/clients.state.spec.ts
  11. 27
      src/Squidex/app/shared/state/clients.state.ts
  12. 11
      src/Squidex/app/shared/state/contributors.state.spec.ts
  13. 24
      src/Squidex/app/shared/state/contributors.state.ts
  14. 10
      src/Squidex/app/shared/state/patterns.state.ts
  15. 131
      src/Squidex/app/shared/state/plans.state.ts

2
src/Squidex/app/features/rules/pages/rules/rules-page.component.html

@ -30,7 +30,7 @@
<table class="table table-items table-fixed" *ngIf="rules.length > 0"> <table class="table table-items table-fixed" *ngIf="rules.length > 0">
<tbody> <tbody>
<ng-template ngFor let-rule [ngForOf]="rules"> <ng-template ngFor let-rule [ngForOf]="rules" [ngForTrackBy]="trackByRule">
<tr> <tr>
<td class="cell-separator"> <td class="cell-separator">
<h3>If</h3> <h3>If</h3>

4
src/Squidex/app/features/rules/pages/rules/rules-page.component.ts

@ -80,4 +80,8 @@ export class RulesPageComponent implements OnInit {
this.addRuleDialog.show(); this.addRuleDialog.show();
} }
public trackByRule(index: number, rule: RuleDto) {
return rule.id;
}
} }

36
src/Squidex/app/features/settings/pages/clients/clients-page.component.html

@ -10,26 +10,26 @@
<div class="table-items-row table-items-row-empty" *ngIf="clients.length === 0"> <div class="table-items-row table-items-row-empty" *ngIf="clients.length === 0">
No client created yet. No client created yet.
</div> </div>
<sqx-client *ngFor="let client of clients; trackBy: trackByClient" [client]="client"></sqx-client> <sqx-client *ngFor="let client of clients; trackBy: trackByClient" [client]="client"></sqx-client>
</ng-container>
<div class="table-items-footer" *ngIf="clientsState.isLoaded | async"> <div class="table-items-footer">
<form [formGroup]="addClientForm.form" (ngSubmit)="attachClient()"> <form [formGroup]="addClientForm.form" (ngSubmit)="attachClient()">
<div class="row no-gutters"> <div class="row no-gutters">
<div class="col"> <div class="col">
<sqx-control-errors for="name" [submitted]="addClientForm.submitted | async"></sqx-control-errors> <sqx-control-errors for="name" [submitted]="addClientForm.submitted | async"></sqx-control-errors>
<input type="text" class="form-control" formControlName="name" maxlength="40" placeholder="Enter client name" autocomplete="off" sqxLowerCaseInput /> <input type="text" class="form-control" formControlName="name" maxlength="40" placeholder="Enter client name" autocomplete="off" sqxLowerCaseInput />
</div> </div>
<div class="col col-auto pl-1"> <div class="col col-auto pl-1">
<button type="submit" class="btn btn-success" [disabled]="addClientForm.hasNoName | async">Add Client</button> <button type="submit" class="btn btn-success" [disabled]="addClientForm.hasNoName | async">Add Client</button>
</div>
<div class="col col-auto pl-1">
<button type="reset" class="btn btn-secondary" (click)="cancelAttachClient()">Cancel</button>
</div>
</div> </div>
<div class="col col-auto pl-1"> </form>
<button type="reset" class="btn btn-secondary" (click)="cancelAttachClient()">Cancel</button> </div>
</div> </ng-container>
</div>
</form>
</div>
</ng-container> </ng-container>
</sqx-panel> </sqx-panel>

42
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.html

@ -38,27 +38,27 @@
</ng-template> </ng-template>
</tbody> </tbody>
</table> </table>
</ng-container>
<div class="table-items-footer" *ngIf="(contributorsState.isMaxReached | async) === false">
<div class="table-items-footer" *ngIf="(contributorsState.isMaxReached | async) === false"> <form [formGroup]="assignContributorForm.form" (ngSubmit)="assignContributor()">
<form [formGroup]="assignContributorForm.form" (ngSubmit)="assignContributor()"> <div class="row no-gutters">
<div class="row no-gutters"> <div class="col">
<div class="col"> <sqx-autocomplete [source]="usersDataSource" formControlName="user" [inputName]="'contributor'" placeholder="Find existing user" displayProperty="displayName">
<sqx-autocomplete [source]="usersDataSource" formControlName="user" [inputName]="'contributor'" placeholder="Find existing user" displayProperty="displayName"> <ng-template let-user="$implicit">
<ng-template let-user="$implicit"> <span class="autocomplete-user">
<span class="autocomplete-user"> <img class="user-picture autocomplete-user-picture" [attr.src]="user | sqxUserDtoPicture" />
<img class="user-picture autocomplete-user-picture" [attr.src]="user | sqxUserDtoPicture" />
<span class="user-name autocomplete-user-name">{{user.displayName}}</span> <span class="user-name autocomplete-user-name">{{user.displayName}}</span>
</span> </span>
</ng-template> </ng-template>
</sqx-autocomplete> </sqx-autocomplete>
</div> </div>
<div class="col col-auto pl-1"> <div class="col col-auto pl-1">
<button type="submit" class="btn btn-success" [disabled]="assignContributorForm.hasNoUser | async">Add Contributor</button> <button type="submit" class="btn btn-success" [disabled]="assignContributorForm.hasNoUser | async">Add Contributor</button>
</div> </div>
</div> </div>
</form> </form>
</div> </div>
</ng-container>
</ng-container> </ng-container>
</sqx-panel> </sqx-panel>

4
src/Squidex/app/features/settings/pages/contributors/contributors-page.component.ts

@ -29,11 +29,11 @@ export class UsersDataSource implements AutocompleteSource {
public find(query: string): Observable<any[]> { public find(query: string): Observable<any[]> {
return this.usersService.getUsers(query) return this.usersService.getUsers(query)
.withLatestFrom(this.contributorsState.contributors, (users, contributors) => { .withLatestFrom(this.contributorsState.contributors.filter(x => !!x), (users, contributors) => {
const results: any[] = []; const results: any[] = [];
for (let user of users) { for (let user of users) {
if (!contributors.find(t => t.contributor.contributorId === user.id)) { if (!contributors!.find(t => t.contributor.contributorId === user.id)) {
results.push(user); results.push(user);
} }
} }

38
src/Squidex/app/features/settings/pages/plans/plans-page.component.html

@ -6,71 +6,71 @@
</ng-container> </ng-container>
<ng-container menu> <ng-container menu>
<button class="btn btn-link btn-secondary" (click)="load(true)" title="Refresh Plans (CTRL + SHIFT + R)"> <button class="btn btn-link btn-secondary" (click)="reload()" title="Refresh Plans (CTRL + SHIFT + R)">
<i class="icon-reset"></i> Refresh <i class="icon-reset"></i> Refresh
</button> </button>
<sqx-shortcut keys="ctrl+shift+r" (trigger)="load(true)"></sqx-shortcut> <sqx-shortcut keys="ctrl+shift+r" (trigger)="reload()"></sqx-shortcut>
</ng-container> </ng-container>
<ng-container content> <ng-container content>
<ng-container *ngIf="plans"> <ng-container *ngIf="plansState.plans | async; let plans">
<div class="panel-alert panel-alert-danger" *ngIf="!planOwned"> <div class="panel-alert panel-alert-danger" *ngIf="(plansState.isOwner | async) === false">
You have not created the subscription. Therefore you cannot change the plan. You have not created the subscription. Therefore you cannot change the planInfo.plan.
</div> </div>
<div class="text-muted text-center empty" *ngIf="plans.plans.length === 0"> <div class="text-muted text-center empty" *ngIf="plans.length === 0">
No plan configured, this app has unlimited usage. No plan configured, this app has unlimited usage.
</div> </div>
<div class="clearfix"> <div class="clearfix">
<div class="card plan float-left" *ngFor="let plan of plans.plans"> <div class="card plan float-left" *ngFor="let planInfo of plans; trackBy: trackByPlan">
<div class="card-header text-center"> <div class="card-header text-center">
<h4 class="card-title">{{plan.name}}</h4> <h4 class="card-title">{{planInfo.plan.name}}</h4>
<h5 class="plan-price">{{plan.costs}}</h5> <h5 class="plan-price">{{planInfo.plan.costs}}</h5>
<small class="text-muted">Per Month</small> <small class="text-muted">Per Month</small>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="plan-fact text-center"> <div class="plan-fact text-center">
<div> <div>
<strong>{{plan.maxApiCalls | sqxKNumber}}</strong> API Calls <strong>{{planInfo.plan.maxApiCalls | sqxKNumber}}</strong> API Calls
</div> </div>
<div> <div>
{{plan.maxAssetSize | sqxFileSize}} Storage {{planInfo.plan.maxAssetSize | sqxFileSize}} Storage
</div> </div>
<div> <div>
{{plan.maxContributors}} Contributors {{planInfo.plan.maxContributors}} Contributors
</div> </div>
</div> </div>
<button *ngIf="plan.id === plans.currentPlanId" class="btn btn-block btn-link btn-success plan-selected"> <button *ngIf="planInfo.isSelected" class="btn btn-block btn-link btn-success plan-selected">
&#10003; Selected &#10003; Selected
</button> </button>
<button *ngIf="plan.id !== plans.currentPlanId" class="btn btn-block btn-success" [disabled]="isDisabled || !planOwned" (click)="changePlan(plan.id)"> <button *ngIf="!planInfo.isSelected" class="btn btn-block btn-success" [disabled]="plansState.isDisabled | async" (click)="change(planInfo.plan.id)">
Change Change
</button> </button>
</div> </div>
<div class="card-footer" *ngIf="plan.yearlyId"> <div class="card-footer" *ngIf="planInfo.plan.yearlyId">
<div class="text-center"> <div class="text-center">
<h5 class="plan-price">{{plan.yearlyCosts}}</h5> <h5 class="plan-price">{{planInfo.plan.yearlyCosts}}</h5>
<small class="text-muted">Per Year</small> <small class="text-muted">Per Year</small>
</div> </div>
<button *ngIf="plan.yearlyId === plans.currentPlanId" class="btn btn-block btn-link btn-success plan-selected"> <button *ngIf="planInfo.isYearlySelected" class="btn btn-block btn-link btn-success plan-selected">
&#10003; Selected &#10003; Selected
</button> </button>
<button *ngIf="plan.yearlyId !== plans.currentPlanId" class="btn btn-block btn-success" [disabled]="isDisabled || !planOwned" (click)="changePlan(plan.yearlyId)"> <button *ngIf="!planInfo.isYearlySelected" class="btn btn-block btn-success" [disabled]="plansState.isDisabled | async" (click)="change(planInfo.plan.yearlyId)">
Change Change
</button> </button>
</div> </div>
</div> </div>
</div> </div>
<div *ngIf="plans.hasPortal" class="billing-portal-link"> <div *ngIf="plansState.hasPortal | async" class="billing-portal-link">
Go to <a target="_blank" href="{{portalUrl}}">Billing Portal</a> for payment history and subscription overview. Go to <a target="_blank" href="{{portalUrl}}">Billing Portal</a> for payment history and subscription overview.
</div> </div>
</ng-container> </ng-container>

74
src/Squidex/app/features/settings/pages/plans/plans-page.component.ts

@ -5,18 +5,14 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved. * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/ */
import { Component, OnDestroy, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import { import {
ApiUrlConfig, ApiUrlConfig,
AppPlansDto,
AppsState, AppsState,
AuthService, PlansState,
ChangePlanDto, PlanDto
DialogService,
PlansService
} from '@app/shared'; } from '@app/shared';
@Component({ @Component({
@ -24,75 +20,37 @@ import {
styleUrls: ['./plans-page.component.scss'], styleUrls: ['./plans-page.component.scss'],
templateUrl: './plans-page.component.html' templateUrl: './plans-page.component.html'
}) })
export class PlansPageComponent implements OnDestroy, OnInit { export class PlansPageComponent implements OnInit {
private queryParamsSubscription: Subscription;
private overridePlanId: string; private overridePlanId: string;
public portalUrl = this.apiUrl.buildUrl('/portal/'); public portalUrl = this.apiUrl.buildUrl('/portal/');
public plans: AppPlansDto;
public planOwned = false;
public isDisabled = false;
constructor( constructor(
public readonly appsState: AppsState, public readonly appsState: AppsState,
public readonly plansState: PlansState,
private readonly apiUrl: ApiUrlConfig, private readonly apiUrl: ApiUrlConfig,
private readonly authState: AuthService,
private readonly dialogs: DialogService,
private readonly plansService: PlansService,
private readonly route: ActivatedRoute private readonly route: ActivatedRoute
) { ) {
} }
public ngOnDestroy() {
this.queryParamsSubscription.unsubscribe();
}
public ngOnInit() { public ngOnInit() {
this.queryParamsSubscription = this.route.queryParams.subscribe(params => {
this.route.queryParams.subscribe(params => { this.overridePlanId = params['planId'];
this.overridePlanId = params['planId']; }).unsubscribe();
});
this.load(); this.plansState.load(false, this.overridePlanId).onErrorResumeNext().subscribe();
} }
public load(notifyLoad = false) { public reload() {
this.plansService.getPlans(this.appsState.appName) this.plansState.load(true, this.overridePlanId).onErrorResumeNext().subscribe();
.subscribe(dto => {
if (this.overridePlanId) {
this.plans = dto.changePlanId(this.overridePlanId);
} else {
this.plans = dto;
}
this.planOwned = !dto.planOwner || (dto.planOwner === this.authState.user!.id);
if (notifyLoad) {
this.dialogs.notifyInfo('Plans reloaded.');
}
}, error => {
this.dialogs.notifyError(error);
});
} }
public changePlan(planId: string) { public change(planId: string) {
this.isDisabled = true; this.plansState.change(planId).onErrorResumeNext().subscribe();
}
this.plansService.putPlan(this.appsState.appName, new ChangePlanDto(planId), this.plans.version) public trackByPlan(index: number, plan: PlanDto) {
.do(() => { return plan.id;
this.isDisabled = false;
})
.subscribe(dto => {
if (dto.payload.redirectUri && dto.payload.redirectUri.length > 0) {
window.location.href = dto.payload.redirectUri;
} else {
this.plans = this.plans.changePlanId(planId, dto.version);
}
}, error => {
this.dialogs.notifyError(error);
});
} }
} }

1
src/Squidex/app/shared/internal.ts

@ -44,6 +44,7 @@ export * from './state/backups.state';
export * from './state/clients.state'; export * from './state/clients.state';
export * from './state/contributors.state'; export * from './state/contributors.state';
export * from './state/patterns.state'; export * from './state/patterns.state';
export * from './state/plans.state';
export * from './state/rules.state'; export * from './state/rules.state';
export * from './state/schemas.state'; export * from './state/schemas.state';

2
src/Squidex/app/shared/module.ts

@ -51,6 +51,7 @@ import {
MustBeNotAuthenticatedGuard, MustBeNotAuthenticatedGuard,
PatternsState, PatternsState,
PlansService, PlansService,
PlansState,
ResolveAppLanguagesGuard, ResolveAppLanguagesGuard,
ResolveContentGuard, ResolveContentGuard,
RichEditorComponent, RichEditorComponent,
@ -155,6 +156,7 @@ export class SqxSharedModule {
MustBeNotAuthenticatedGuard, MustBeNotAuthenticatedGuard,
PatternsState, PatternsState,
PlansService, PlansService,
PlansState,
ResolveAppLanguagesGuard, ResolveAppLanguagesGuard,
ResolveContentGuard, ResolveContentGuard,
RulesService, RulesService,

9
src/Squidex/app/shared/state/clients.state.spec.ts

@ -54,8 +54,7 @@ describe('ClientsState', () => {
}); });
it('should load clients', () => { it('should load clients', () => {
expect(clientsState.snapshot.clients.values).toEqual(oldClients); expect(clientsState.snapshot.clients!.values).toEqual(oldClients);
expect(clientsState.snapshot.isLoaded).toBeTruthy();
expect(clientsState.snapshot.version).toEqual(version); expect(clientsState.snapshot.version).toEqual(version);
}); });
@ -69,7 +68,7 @@ describe('ClientsState', () => {
clientsState.attach(request).subscribe(); clientsState.attach(request).subscribe();
expect(clientsState.snapshot.clients.values).toEqual([...oldClients, newClient]); expect(clientsState.snapshot.clients!.values).toEqual([...oldClients, newClient]);
expect(clientsState.snapshot.version).toEqual(newVersion); expect(clientsState.snapshot.version).toEqual(newVersion);
}); });
@ -81,7 +80,7 @@ describe('ClientsState', () => {
clientsState.update(oldClients[0], request).subscribe(); clientsState.update(oldClients[0], request).subscribe();
const client_1 = clientsState.snapshot.clients.at(0); const client_1 = clientsState.snapshot.clients!.at(0);
expect(client_1.name).toBe('NewName'); expect(client_1.name).toBe('NewName');
expect(client_1.permission).toBe('NewPermission'); expect(client_1.permission).toBe('NewPermission');
@ -94,7 +93,7 @@ describe('ClientsState', () => {
clientsState.revoke(oldClients[0]).subscribe(); clientsState.revoke(oldClients[0]).subscribe();
expect(clientsState.snapshot.clients.values).toEqual([oldClients[1]]); expect(clientsState.snapshot.clients!.values).toEqual([oldClients[1]]);
expect(clientsState.snapshot.version).toEqual(newVersion); expect(clientsState.snapshot.version).toEqual(newVersion);
}); });
}); });

27
src/Squidex/app/shared/state/clients.state.ts

@ -58,11 +58,9 @@ export class AttachClientForm extends Form<FormGroup> {
} }
interface Snapshot { interface Snapshot {
clients: ImmutableArray<AppClientDto>; clients?: ImmutableArray<AppClientDto>;
isLoaded: boolean; version?: Version;
version: Version;
} }
@Injectable() @Injectable()
@ -70,15 +68,12 @@ export class ClientsState extends State<Snapshot> {
public clients = public clients =
this.changes.map(x => x.clients); this.changes.map(x => x.clients);
public isLoaded =
this.changes.map(x => x.isLoaded);
constructor( constructor(
private readonly appClientsService: AppClientsService, private readonly appClientsService: AppClientsService,
private readonly appsState: AppsState, private readonly appsState: AppsState,
private readonly dialogs: DialogService private readonly dialogs: DialogService
) { ) {
super({ clients: ImmutableArray.empty(), version: new Version(''), isLoaded: false }); super({});
} }
public load(): Observable<any> { public load(): Observable<any> {
@ -94,10 +89,10 @@ export class ClientsState extends State<Snapshot> {
} }
public attach(request: CreateAppClientDto): Observable<any> { public attach(request: CreateAppClientDto): Observable<any> {
return this.appClientsService.postClient(this.appName, request, this.snapshot.version) return this.appClientsService.postClient(this.appName, request, this.version)
.do(dto => { .do(dto => {
this.next(s => { this.next(s => {
const clients = s.clients.push(dto.payload); const clients = s.clients!.push(dto.payload);
return { ...s, clients, version: dto.version }; return { ...s, clients, version: dto.version };
}); });
@ -106,10 +101,10 @@ export class ClientsState extends State<Snapshot> {
} }
public revoke(client: AppClientDto): Observable<any> { public revoke(client: AppClientDto): Observable<any> {
return this.appClientsService.deleteClient(this.appName, client.id, this.snapshot.version) return this.appClientsService.deleteClient(this.appName, client.id, this.version)
.do(dto => { .do(dto => {
this.next(s => { this.next(s => {
const clients = s.clients.filter(c => c.id !== client.id); const clients = s.clients!.filter(c => c.id !== client.id);
return { ...s, clients, version: dto.version }; return { ...s, clients, version: dto.version };
}); });
@ -118,10 +113,10 @@ export class ClientsState extends State<Snapshot> {
} }
public update(client: AppClientDto, request: UpdateAppClientDto): Observable<any> { public update(client: AppClientDto, request: UpdateAppClientDto): Observable<any> {
return this.appClientsService.putClient(this.appName, client.id, request, this.snapshot.version) return this.appClientsService.putClient(this.appName, client.id, request, this.version)
.do(dto => { .do(dto => {
this.next(s => { this.next(s => {
const clients = s.clients.replaceBy('id', update(client, request)); const clients = s.clients!.replaceBy('id', update(client, request));
return { ...s, clients, version: dto.version }; return { ...s, clients, version: dto.version };
}); });
@ -132,6 +127,10 @@ export class ClientsState extends State<Snapshot> {
private get appName() { private get appName() {
return this.appsState.appName; return this.appsState.appName;
} }
private get version() {
return this.snapshot.version!;
}
} }
const update = (client: AppClientDto, request: UpdateAppClientDto) => const update = (client: AppClientDto, request: UpdateAppClientDto) =>

11
src/Squidex/app/shared/state/contributors.state.spec.ts

@ -59,8 +59,7 @@ describe('ContributorsState', () => {
}); });
it('should load contributors', () => { it('should load contributors', () => {
expect(contributorsState.snapshot.contributors.values).toEqual(oldContributors.map(x => c(x))); expect(contributorsState.snapshot.contributors!.values).toEqual(oldContributors.map(x => c(x)));
expect(contributorsState.snapshot.isLoaded).toBeTruthy();
expect(contributorsState.snapshot.isMaxReached).toBeFalsy(); expect(contributorsState.snapshot.isMaxReached).toBeFalsy();
expect(contributorsState.snapshot.maxContributors).toBe(3); expect(contributorsState.snapshot.maxContributors).toBe(3);
expect(contributorsState.snapshot.version).toEqual(version); expect(contributorsState.snapshot.version).toEqual(version);
@ -76,8 +75,7 @@ describe('ContributorsState', () => {
contributorsState.assign(request).subscribe(); contributorsState.assign(request).subscribe();
expect(contributorsState.snapshot.contributors.values).toEqual([...oldContributors.map(x => c(x)), c(newContributor)]); expect(contributorsState.snapshot.contributors!.values).toEqual([...oldContributors.map(x => c(x)), c(newContributor)]);
expect(contributorsState.snapshot.isLoaded).toBeTruthy();
expect(contributorsState.snapshot.isMaxReached).toBeTruthy(); expect(contributorsState.snapshot.isMaxReached).toBeTruthy();
expect(contributorsState.snapshot.maxContributors).toBe(3); expect(contributorsState.snapshot.maxContributors).toBe(3);
expect(contributorsState.snapshot.version).toEqual(newVersion); expect(contributorsState.snapshot.version).toEqual(newVersion);
@ -93,8 +91,7 @@ describe('ContributorsState', () => {
contributorsState.assign(request).subscribe(); contributorsState.assign(request).subscribe();
expect(contributorsState.snapshot.contributors.values).toEqual([c(oldContributors[0]), c(newContributor)]); expect(contributorsState.snapshot.contributors!.values).toEqual([c(oldContributors[0]), c(newContributor)]);
expect(contributorsState.snapshot.isLoaded).toBeTruthy();
expect(contributorsState.snapshot.isMaxReached).toBeFalsy(); expect(contributorsState.snapshot.isMaxReached).toBeFalsy();
expect(contributorsState.snapshot.maxContributors).toBe(3); expect(contributorsState.snapshot.maxContributors).toBe(3);
expect(contributorsState.snapshot.version).toEqual(newVersion); expect(contributorsState.snapshot.version).toEqual(newVersion);
@ -106,7 +103,7 @@ describe('ContributorsState', () => {
contributorsState.revoke(oldContributors[0]).subscribe(); contributorsState.revoke(oldContributors[0]).subscribe();
expect(contributorsState.snapshot.contributors.values).toEqual([c(oldContributors[1])]); expect(contributorsState.snapshot.contributors!.values).toEqual([c(oldContributors[1])]);
expect(contributorsState.snapshot.version).toEqual(newVersion); expect(contributorsState.snapshot.version).toEqual(newVersion);
}); });

24
src/Squidex/app/shared/state/contributors.state.ts

@ -45,14 +45,13 @@ interface SnapshotContributor {
} }
interface Snapshot { interface Snapshot {
contributors: ImmutableArray<SnapshotContributor>; contributors?: ImmutableArray<SnapshotContributor>;
isLoaded: boolean; isMaxReached?: boolean;
isMaxReached: boolean;
maxContributors: number; maxContributors: number;
version: Version; version?: Version;
} }
@Injectable() @Injectable()
@ -60,9 +59,6 @@ export class ContributorsState extends State<Snapshot> {
public contributors = public contributors =
this.changes.map(x => x.contributors); this.changes.map(x => x.contributors);
public isLoaded =
this.changes.map(x => x.isLoaded);
public isMaxReached = public isMaxReached =
this.changes.map(x => x.isMaxReached); this.changes.map(x => x.isMaxReached);
@ -75,7 +71,7 @@ export class ContributorsState extends State<Snapshot> {
private readonly authState: AuthService, private readonly authState: AuthService,
private readonly dialogs: DialogService private readonly dialogs: DialogService
) { ) {
super({ contributors: ImmutableArray.empty(), version: new Version(''), isLoaded: false, isMaxReached: true, maxContributors: -1 }); super({ maxContributors: -1 });
} }
public load(): Observable<any> { public load(): Observable<any> {
@ -89,9 +85,9 @@ export class ContributorsState extends State<Snapshot> {
} }
public revoke(contributor: AppContributorDto): Observable<any> { public revoke(contributor: AppContributorDto): Observable<any> {
return this.appContributorsService.deleteContributor(this.appName, contributor.contributorId, this.snapshot.version) return this.appContributorsService.deleteContributor(this.appName, contributor.contributorId, this.version)
.do(dto => { .do(dto => {
const contributors = this.snapshot.contributors.filter(x => x.contributor.contributorId !== contributor.contributorId); const contributors = this.snapshot.contributors!.filter(x => x.contributor.contributorId !== contributor.contributorId);
this.replaceContributors(contributors, dto.version); this.replaceContributors(contributors, dto.version);
}) })
@ -99,11 +95,11 @@ export class ContributorsState extends State<Snapshot> {
} }
public assign(request: AppContributorDto): Observable<any> { public assign(request: AppContributorDto): Observable<any> {
return this.appContributorsService.postContributor(this.appName, request, this.snapshot.version) return this.appContributorsService.postContributor(this.appName, request, this.version)
.do(dto => { .do(dto => {
const contributor = this.createContributor(new AppContributorDto(dto.payload.contributorId, request.permission)); const contributor = this.createContributor(new AppContributorDto(dto.payload.contributorId, request.permission));
let contributors = this.snapshot.contributors; let contributors = this.snapshot.contributors!;
if (contributors.find(x => x.contributor.contributorId === dto.payload.contributorId)) { if (contributors.find(x => x.contributor.contributorId === dto.payload.contributorId)) {
contributors = contributors.map(c => c.contributor.contributorId === dto.payload.contributorId ? contributor : c); contributors = contributors.map(c => c.contributor.contributorId === dto.payload.contributorId ? contributor : c);
@ -131,6 +127,10 @@ export class ContributorsState extends State<Snapshot> {
return this.appsState.appName; return this.appsState.appName;
} }
private get version() {
return this.snapshot.version!;
}
private createContributor(contributor: AppContributorDto): SnapshotContributor { private createContributor(contributor: AppContributorDto): SnapshotContributor {
return { contributor, isCurrentUser: contributor.contributorId === this.authState.user!.id }; return { contributor, isCurrentUser: contributor.contributorId === this.authState.user!.id };
} }

10
src/Squidex/app/shared/state/patterns.state.ts

@ -89,7 +89,7 @@ export class PatternsState extends State<Snapshot> {
} }
public create(request: EditAppPatternDto): Observable<any> { public create(request: EditAppPatternDto): Observable<any> {
return this.appPatternsService.postPattern(this.appName, request, this.snapshot.version) return this.appPatternsService.postPattern(this.appName, request, this.version)
.do(dto => { .do(dto => {
this.next(s => { this.next(s => {
const patterns = s.patterns.push(dto.payload).sortByStringAsc(x => x.name); const patterns = s.patterns.push(dto.payload).sortByStringAsc(x => x.name);
@ -101,7 +101,7 @@ export class PatternsState extends State<Snapshot> {
} }
public update(pattern: AppPatternDto, request: EditAppPatternDto): Observable<any> { public update(pattern: AppPatternDto, request: EditAppPatternDto): Observable<any> {
return this.appPatternsService.putPattern(this.appName, pattern.id, request, this.snapshot.version) return this.appPatternsService.putPattern(this.appName, pattern.id, request, this.version)
.do(dto => { .do(dto => {
this.next(s => { this.next(s => {
const patterns = s.patterns.replaceBy('id', update(pattern, request)).sortByStringAsc(x => x.name); const patterns = s.patterns.replaceBy('id', update(pattern, request)).sortByStringAsc(x => x.name);
@ -113,7 +113,7 @@ export class PatternsState extends State<Snapshot> {
} }
public delete(pattern: AppPatternDto): Observable<any> { public delete(pattern: AppPatternDto): Observable<any> {
return this.appPatternsService.deletePattern(this.appName, pattern.id, this.snapshot.version) return this.appPatternsService.deletePattern(this.appName, pattern.id, this.version)
.do(dto => { .do(dto => {
this.next(s => { this.next(s => {
const patterns = s.patterns.filter(c => c.id !== pattern.id); const patterns = s.patterns.filter(c => c.id !== pattern.id);
@ -127,6 +127,10 @@ export class PatternsState extends State<Snapshot> {
private get appName() { private get appName() {
return this.appsState.appName; return this.appsState.appName;
} }
private get version() {
return this.snapshot.version;
}
} }
const update = (pattern: AppPatternDto, request: EditAppPatternDto) => const update = (pattern: AppPatternDto, request: EditAppPatternDto) =>

131
src/Squidex/app/shared/state/plans.state.ts

@ -0,0 +1,131 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import '@app/framework/utils/rxjs-extensions';
import {
DialogService,
ImmutableArray,
State,
Version
} from '@app/framework';
import { AppsState } from './apps.state';
import { AuthService } from './../services/auth.service';
import {
ChangePlanDto,
PlanDto,
PlansService
} from './../services/plans.service';
interface PlanInfo {
plan: PlanDto;
isYearlySelected?: boolean;
isSelected?: boolean;
}
interface Snapshot {
plans?: ImmutableArray<PlanInfo>;
version?: Version;
isOwner?: boolean;
isDisabled?: boolean;
hasPortal?: boolean;
}
@Injectable()
export class PlansState extends State<Snapshot> {
public plans =
this.changes.map(x => x.plans);
public isOwner =
this.changes.map(x => x.isOwner);
public isDisabled =
this.changes.map(x => x.isDisabled || !x.isOwner);
public hasPortal =
this.changes.map(x => x.hasPortal);
public window = window;
constructor(
private readonly appsState: AppsState,
private readonly authState: AuthService,
private readonly dialogs: DialogService,
private readonly plansService: PlansService
) {
super({});
}
public load(notifyLoad = false, overridePlanId?: string): Observable<any> {
return this.plansService.getPlans(this.appName)
.do(dto => {
if (notifyLoad) {
this.dialogs.notifyInfo('Plans reloaded.');
}
this.next(s => {
const planId = overridePlanId || dto.currentPlanId;
return {
...s,
plans: ImmutableArray.of(dto.plans.map(x => this.createPlan(x, planId))),
isOwner: !dto.planOwner || dto.planOwner === this.userId,
isDisabled: false,
hasPortal: dto.hasPortal
};
});
})
.notify(this.dialogs);
}
public change(planId: string): Observable<any> {
this.next(s => ({ ...s, isDisabled: true }));
return this.plansService.putPlan(this.appName, new ChangePlanDto(planId), this.version)
.do(dto => {
if (dto.payload.redirectUri && dto.payload.redirectUri.length > 0) {
this.window.location.href = dto.payload.redirectUri;
} else {
this.next(s => {
return {
...s,
plans: s.plans!.map(x => this.createPlan(x.plan, planId)),
isOwner: true,
isDisabled: false
};
});
}
})
.notify(this.dialogs);
}
private createPlan(plan: PlanDto, id: string) {
return { plan, isYearlySelected: plan.yearlyId === id, isSelected: plan.id === id };
}
private get appName() {
return this.appsState.appName;
}
private get userId() {
return this.authState.user!.id;
}
private get version() {
return this.snapshot.version!;
}
}
Loading…
Cancel
Save