Browse Source

AppsState introduced

pull/271/head
Sebastian Stehle 8 years ago
parent
commit
ede3364496
  1. 22
      src/Squidex/app/features/apps/pages/apps-page.component.html
  2. 13
      src/Squidex/app/features/apps/pages/apps-page.component.ts
  3. 8
      src/Squidex/app/features/schemas/state/schemas.state.ts
  4. 2
      src/Squidex/app/features/settings/pages/more/more-page.component.ts
  5. 40
      src/Squidex/app/framework/angular/http-extensions-impl.spec.ts
  6. 36
      src/Squidex/app/shared/components/app-context.ts
  7. 61
      src/Squidex/app/shared/components/app-form.component.html
  8. 38
      src/Squidex/app/shared/components/app-form.component.ts
  9. 1
      src/Squidex/app/shared/declarations-base.ts
  10. 35
      src/Squidex/app/shared/guards/app-must-exist.guard.spec.ts
  11. 11
      src/Squidex/app/shared/guards/app-must-exist.guard.ts
  12. 14
      src/Squidex/app/shared/guards/unset-app.guard.spec.ts
  13. 6
      src/Squidex/app/shared/guards/unset-app.guard.ts
  14. 4
      src/Squidex/app/shared/module.ts
  15. 79
      src/Squidex/app/shared/services/apps-store.service.ts
  16. 43
      src/Squidex/app/shared/state/apps.state.spec.ts
  17. 20
      src/Squidex/app/shell/pages/internal/apps-menu.component.html
  18. 11
      src/Squidex/app/shell/pages/internal/apps-menu.component.ts
  19. 11
      src/Squidex/app/shell/pages/internal/internal-area.component.ts

22
src/Squidex/app/features/apps/pages/apps-page.component.html

@ -72,26 +72,10 @@
<div class="modal" *sqxModalView="addAppDialog;onRoot:true" @fade>
<div class="modal-backdrop"></div>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" *ngIf="template">Create {{template}} Sample</h4>
<h4 class="modal-title" *ngIf="!template">Create App</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="addAppDialog.hide()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<sqx-app-form
[template]="template"
(created)="addAppDialog.hide()"
(cancelled)="addAppDialog.hide()">
</sqx-app-form>
</div>
</div>
</div>
<sqx-app-form [template]="appTemplate"
(completed)="addAppDialog.hide()">
</sqx-app-form>
</div>
<sqx-onboarding-dialog [modalView]="onboardingModal"></sqx-onboarding-dialog>

13
src/Squidex/app/features/apps/pages/apps-page.component.ts

@ -11,8 +11,9 @@ import { Subscription } from 'rxjs';
import {
AppContext,
AppDto,
AppsStoreService,
AppsState,
fadeAnimation,
ImmutableArray,
ModalView,
OnboardingService
} from 'shared';
@ -32,15 +33,15 @@ export class AppsPageComponent implements OnDestroy, OnInit {
private appsSubscription: Subscription;
public addAppDialog = new ModalView();
public apps: AppDto[];
public template = '';
public apps: ImmutableArray<AppDto>;
public apptemplate = '';
public onboardingModal = new ModalView();
constructor(
public readonly ctx: AppContext,
private readonly appsStore: AppsStoreService,
private readonly appsState: AppsState,
private readonly onboardingService: OnboardingService
) {
}
@ -51,7 +52,7 @@ export class AppsPageComponent implements OnDestroy, OnInit {
public ngOnInit() {
this.appsSubscription =
this.appsStore.apps
this.appsState.apps
.subscribe(apps => {
if (apps.length === 0 && this.onboardingService.shouldShow('dialog')) {
this.onboardingService.disable('dialog');
@ -63,7 +64,7 @@ export class AppsPageComponent implements OnDestroy, OnInit {
}
public createNewApp(template: string) {
this.template = template;
this.apptemplate = template;
this.addAppDialog.show();
}

8
src/Squidex/app/features/schemas/state/schemas.state.ts

@ -12,7 +12,7 @@ import 'framework/utils/rxjs-extensions';
import {
AddFieldDto,
AppsStoreService,
AppsState,
AuthService,
CreateSchemaDto,
DateTime,
@ -34,7 +34,7 @@ export class SchemasState {
public selectedSchema = new BehaviorSubject<SchemaDetailsDto | null>(null);
private get app() {
return this.appsState.app$.value!.name;
return this.appsState.selectedApp.value!.name;
}
private get user() {
@ -45,7 +45,7 @@ export class SchemasState {
private readonly schemasService: SchemasService,
private readonly dialogs: DialogService,
private readonly authState: AuthService,
private readonly appsState: AppsStoreService
private readonly appsState: AppsState
) {
}
@ -56,7 +56,7 @@ export class SchemasState {
Observable.of(<SchemaDetailsDto>this.schemasItems.value.find(x => x.id === id && x instanceof SchemaDetailsDto))
.switchMap(schema => {
if (!schema) {
return this.schemasService.getSchema(this.appsState.app$.value!.name, id).catch(() => Observable.of(null));
return this.schemasService.getSchema(this.app, id).catch(() => Observable.of(null));
} else {
return Observable.of(schema);
}

2
src/Squidex/app/features/settings/pages/more/more-page.component.ts

@ -25,7 +25,7 @@ export class MorePageComponent {
}
public archiveApp() {
this.ctx.appsStore.deleteApp(this.ctx.appName)
this.ctx.appsState.deleteApp(this.ctx.appName)
.subscribe(() => {
this.router.navigate(['/app']);
}, error => {

40
src/Squidex/app/framework/angular/http-extensions-impl.spec.ts

@ -1,40 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { ErrorDto } from './http-extensions-impl';
describe('ErrorDto', () => {
it('Should create simple message when no details are specified.', () => {
const error = new ErrorDto(500, 'Error Message.');
expect(error.displayMessage).toBe('Error Message.');
});
it('Should append dot to message', () => {
const error = new ErrorDto(500, 'Error Message');
expect(error.displayMessage).toBe('Error Message.');
});
it('Should create simple message when detail has one item', () => {
const error = new ErrorDto(500, 'Error Message.', ['Detail Message.']);
expect(error.displayMessage).toBe('Error Message: Detail Message.');
});
it('Should create append do to simple message when detail has one item', () => {
const error = new ErrorDto(500, 'Error Message', ['Detail Message']);
expect(error.displayMessage).toBe('Error Message: Detail Message.');
});
it('Should create html list when error has more items.', () => {
const error = new ErrorDto(500, 'Error Message', ['Detail1.', 'Detail2.']);
expect(error.displayMessage).toBe('Error Message.<ul><li>Detail1.</li><li>Detail2.</li></ul>');
});
});

36
src/Squidex/app/shared/components/app-context.ts

@ -5,37 +5,33 @@
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Injectable, OnDestroy } from '@angular/core';
import { Observable, Subscription } from 'rxjs';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
import { MessageBus } from 'framework';
import {
AppDto,
AppsStoreService,
AppsState,
AuthService,
DialogService,
ErrorDto,
Notification,
Profile
} from './../declarations-base';
@Injectable()
export class AppContext implements OnDestroy {
private readonly appSubscription: Subscription;
private appField: AppDto;
export class AppContext {
public get app(): AppDto {
return this.appField;
return this.appsState.selectedApp.value!;
}
public get appChanges(): Observable<AppDto | null> {
return this.appsStore.selectedApp;
return this.appsState.selectedApp;
}
public get appName(): string {
return this.appField ? this.appField.name : '';
return this.app.name;
}
public get userToken(): string {
@ -53,18 +49,10 @@ export class AppContext implements OnDestroy {
constructor(
public readonly dialogs: DialogService,
public readonly authService: AuthService,
public readonly appsStore: AppsStoreService,
public readonly appsState: AppsState,
public readonly route: ActivatedRoute,
public readonly bus: MessageBus
) {
this.appSubscription =
this.appsStore.selectedApp.take(1).subscribe(app => {
this.appField = app!;
});
}
public ngOnDestroy() {
this.appSubscription.unsubscribe();
}
public confirmUnsavedChanges(): Observable<boolean> {
@ -72,14 +60,10 @@ export class AppContext implements OnDestroy {
}
public notifyInfo(error: string) {
this.dialogs.notify(Notification.info(error));
this.dialogs.notifyInfo(error);
}
public notifyError(error: string | ErrorDto) {
if (error instanceof ErrorDto) {
this.dialogs.notify(Notification.error(error.displayMessage));
} else {
this.dialogs.notify(Notification.error(error));
}
this.dialogs.notifyError(error);
}
}

61
src/Squidex/app/shared/components/app-form.component.html

@ -1,26 +1,43 @@
<form [formGroup]="createForm" (ngSubmit)="createApp()">
<div *ngIf="createFormError">
<div class="form-alert form-alert-error" [innerHTML]="createFormError"></div>
</div>
<div class="modal-dialog">
<div class="modal-content">
<form [formGroup]="createForm" (ngSubmit)="createApp()">
<div class="modal-header">
<h4 class="modal-title" *ngIf="template">Create {{template}} Sample</h4>
<h4 class="modal-title" *ngIf="!template">Create App</h4>
<div class="form-group">
<label for="appName">Name</label>
<sqx-control-errors for="name" submitOnly="true" [submitted]="createFormSubmitted"></sqx-control-errors>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="completed()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<input type="text" class="form-control" id="appName" formControlName="name" autocomplete="off" sqxLowerCaseInput sqxFocusOnInit />
<small class="form-text text-muted">
The app name becomes part of the api url,<br /> e.g {{apiUrl.buildUrl("api/content/")}}<b>{{appName | async}}</b>/.
</small>
<small class="form-text text-muted">
It must contain lower case letters (a-z), numbers and dashes only, and cannot be longer than 40 characters. The name cannot be changed later.
</small>
</div>
<div class="modal-body">
<div *ngIf="createFormError">
<div class="form-alert form-alert-error" [innerHTML]="createFormError"></div>
</div>
<div class="form-group">
<label for="appName">Name</label>
<sqx-control-errors for="name" submitOnly="true" [submitted]="createFormSubmitted"></sqx-control-errors>
<input type="text" class="form-control" id="appName" formControlName="name" autocomplete="off" sqxLowerCaseInput sqxFocusOnInit />
<small class="form-text text-muted">
The app name becomes part of the api url,<br /> e.g {{apiUrl.buildUrl("api/content/")}}<b>{{appName | async}}</b>/.
</small>
<small class="form-text text-muted">
It must contain lower case letters (a-z), numbers and dashes only, and cannot be longer than 40 characters. The name cannot be changed later.
</small>
</div>
</div>
<div class="form-group clearfix">
<button type="reset" class="float-left btn btn-secondary" (click)="cancel()" [disabled]="createFormSubmitted">Cancel</button>
<button type="submit" class="float-right btn btn-success">Create</button>
<div class="modal-footer">
<div class="clearfix">
<button type="reset" class="float-left btn btn-secondary" (click)="cancel()" [disabled]="createFormSubmitted">Cancel</button>
<button type="submit" class="float-right btn btn-success">Create</button>
</div>
</div>
</form>
</div>
</form>
</div>

38
src/Squidex/app/shared/components/app-form.component.ts

@ -10,11 +10,7 @@ import { FormBuilder, Validators } from '@angular/forms';
import { ApiUrlConfig, ValidatorsEx } from 'framework';
import {
AppDto,
AppsStoreService,
CreateAppDto
} from './../declarations-base';
import { AppsState, CreateAppDto } from './../declarations-base';
const FALLBACK_NAME = 'my-app';
@ -25,10 +21,7 @@ const FALLBACK_NAME = 'my-app';
})
export class AppFormComponent {
@Output()
public created = new EventEmitter<AppDto>();
@Output()
public cancelled = new EventEmitter();
public completed = new EventEmitter();
@Input()
public template = '';
@ -49,16 +42,14 @@ export class AppFormComponent {
this.createForm.controls['name'].valueChanges.map(n => n || FALLBACK_NAME)
.startWith(FALLBACK_NAME);
constructor(
public readonly apiUrl: ApiUrlConfig,
private readonly appsStore: AppsStoreService,
constructor(public readonly apiUrl: ApiUrlConfig,
private readonly appsStore: AppsState,
private readonly formBuilder: FormBuilder
) {
}
public cancel() {
this.emitCancelled();
this.resetCreateForm();
public complete() {
this.completed.emit();
}
public createApp() {
@ -71,31 +62,16 @@ export class AppFormComponent {
this.appsStore.createApp(request)
.subscribe(dto => {
this.resetCreateForm();
this.emitCreated(dto);
this.complete();
}, error => {
this.enableCreateForm(error.displayMessage);
});
}
}
private emitCancelled() {
this.cancelled.emit();
}
private emitCreated(app: AppDto) {
this.created.emit(app);
}
private enableCreateForm(message: string) {
this.createForm.enable();
this.createFormSubmitted = false;
this.createFormError = message;
}
private resetCreateForm() {
this.createFormError = '';
this.createForm.enable();
this.createFormSubmitted = false;
}
}

1
src/Squidex/app/shared/declarations-base.ts

@ -20,7 +20,6 @@ export * from './services/app-contributors.service';
export * from './services/app-clients.service';
export * from './services/app-languages.service';
export * from './services/app-patterns.service';
export * from './services/apps-store.service';
export * from './services/apps.service';
export * from './services/assets.service';
export * from './services/auth.service';

35
src/Squidex/app/shared/guards/app-must-exist.guard.spec.ts

@ -8,42 +8,26 @@
import { IMock, Mock } from 'typemoq';
import { Observable } from 'rxjs';
import { AppsStoreService } from 'shared';
import { AppsState } from 'shared';
import { AppMustExistGuard } from './app-must-exist.guard';
import { RouterMockup } from './router-mockup';
describe('AppMustExistGuard', () => {
let appsStore: IMock<AppsStoreService>;
let appsState: IMock<AppsState>;
beforeEach(() => {
appsStore = Mock.ofType(AppsStoreService);
appsState = Mock.ofType(AppsState);
});
it('should navigate to 404 page if app is not found', (done) => {
appsStore.setup(x => x.selectApp('my-app'))
.returns(() => Observable.of(false));
const router = new RouterMockup();
const route = <any> { params: { appName: 'my-app' } };
appsState.setup(x => x.selectApp('my-app'))
.returns(() => Observable.of(null));
const guard = new AppMustExistGuard(appsStore.object, <any>router);
guard.canActivate(route, <any>{})
.subscribe(result => {
expect(result).toBeFalsy();
expect(router.lastNavigation).toEqual(['/404']);
done();
});
});
it('should navigate to 404 page if app loading fails', (done) => {
appsStore.setup(x => x.selectApp('my-app'))
.returns(() => Observable.throw('error'));
const router = new RouterMockup();
const route = <any> { params: { appName: 'my-app' } };
const guard = new AppMustExistGuard(appsStore.object, <any>router);
const guard = new AppMustExistGuard(appsState.object, <any>router);
guard.canActivate(route, <any>{})
.subscribe(result => {
@ -55,12 +39,13 @@ describe('AppMustExistGuard', () => {
});
it('should return true if app is found', (done) => {
appsStore.setup(x => x.selectApp('my-app'))
.returns(() => Observable.of(true));
appsState.setup(x => x.selectApp('my-app'))
.returns(() => Observable.of(<any>{}));
const router = new RouterMockup();
const route = <any> { params: { appName: 'my-app' } };
const guard = new AppMustExistGuard(appsStore.object, <any>router);
const guard = new AppMustExistGuard(appsState.object, <any>router);
guard.canActivate(route, <any>{})
.subscribe(result => {

11
src/Squidex/app/shared/guards/app-must-exist.guard.ts

@ -9,12 +9,12 @@ import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { AppsStoreService } from './../services/apps-store.service';
import { AppsState } from './../state/apps.state';
@Injectable()
export class AppMustExistGuard implements CanActivate {
constructor(
private readonly appsStore: AppsStoreService,
private readonly appsStore: AppsState,
private readonly router: Router
) {
}
@ -28,12 +28,7 @@ export class AppMustExistGuard implements CanActivate {
if (!dto) {
this.router.navigate(['/404']);
}
})
.catch(error => {
this.router.navigate(['/404']);
return Observable.of(false);
});
}).map(a => a !== null);
return result;
}

14
src/Squidex/app/shared/guards/unset-app.guard.spec.ts

@ -8,22 +8,22 @@
import { IMock, Mock, Times } from 'typemoq';
import { Observable } from 'rxjs';
import { AppsStoreService } from 'shared';
import { AppsState } from 'shared';
import { UnsetAppGuard } from './unset-app.guard';
describe('UnsetAppGuard', () => {
let appStoreService: IMock<AppsStoreService>;
let appsState: IMock<AppsState>;
beforeEach(() => {
appStoreService = Mock.ofType(AppsStoreService);
appsState = Mock.ofType(AppsState);
});
it('should unselect app', () => {
appStoreService.setup(x => x.selectApp(null))
.returns(() => Observable.of(false));
appsState.setup(x => x.selectApp(null))
.returns(() => Observable.of(null));
const guard = new UnsetAppGuard(appStoreService.object);
const guard = new UnsetAppGuard(appsState.object);
let result = false;
@ -34,6 +34,6 @@ describe('UnsetAppGuard', () => {
expect(result).toBeTruthy();
appStoreService.verify(x => x.selectApp(null), Times.once());
appsState.verify(x => x.selectApp(null), Times.once());
});
});

6
src/Squidex/app/shared/guards/unset-app.guard.ts

@ -9,16 +9,16 @@ import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { AppsStoreService } from './../services/apps-store.service';
import { AppsState } from './../state/apps.state';
@Injectable()
export class UnsetAppGuard implements CanActivate {
constructor(
private readonly appsStore: AppsStoreService
private readonly appsState: AppsState
) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.appsStore.selectApp(null).map(a => !a);
return this.appsState.selectApp(null).map(a => a === null);
}
}

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

@ -19,7 +19,7 @@ import {
AppLanguagesService,
AppMustExistGuard,
AppPatternsService,
AppsStoreService,
AppsState,
AppsService,
AssetComponent,
AssetPreviewUrlPipe,
@ -125,7 +125,7 @@ export class SqxSharedModule {
AppMustExistGuard,
AppPatternsService,
AppsService,
AppsStoreService,
AppsState,
AssetsService,
AuthService,
BackupsService,

79
src/Squidex/app/shared/services/apps-store.service.ts

@ -1,79 +0,0 @@
/*
* Squidex Headless CMS
*
* @license
* Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
*/
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, Observer, ReplaySubject } from 'rxjs';
import { DateTime } from 'framework';
import {
AppDto,
AppsService,
CreateAppDto
} from './apps.service';
@Injectable()
export class AppsStoreService {
public readonly apps$ = new ReplaySubject<AppDto[]>(1);
public readonly app$ = new BehaviorSubject<AppDto | null>(null);
public get apps(): Observable<AppDto[]> {
return this.apps$;
}
public get selectedApp(): Observable<AppDto | null> {
return this.app$;
}
constructor(
private readonly appsService: AppsService
) {
if (!appsService) {
return;
}
this.appsService.getApps()
.subscribe(apps => {
this.apps$.next(apps);
}, error => {
this.apps$.next([]);
});
}
public selectApp(name: string | null): Observable<boolean> {
return Observable.create((observer: Observer<boolean>) => {
this.apps$.subscribe(apps => {
const app = apps.find(x => x.name === name) || null;
this.app$.next(app);
observer.next(app !== null);
observer.complete();
}, error => {
observer.error(error);
});
});
}
public createApp(dto: CreateAppDto, now?: DateTime): Observable<AppDto> {
return this.appsService.postApp(dto)
.do(app => {
this.apps$.take(1).subscribe(apps => {
this.apps$.next(apps.concat([app]));
});
});
}
public deleteApp(appName: string): Observable<any> {
return this.appsService.deleteApp(appName)
.do(app => {
this.apps$.take(1).subscribe(apps => {
this.apps$.next(apps.filter(a => a.name !== appName));
});
});
}
}

43
src/Squidex/app/shared/services/apps-store.service.spec.ts → src/Squidex/app/shared/state/apps.state.spec.ts

@ -11,12 +11,13 @@ import { IMock, Mock, Times } from 'typemoq';
import {
AppDto,
AppsService,
AppsStoreService,
AppsState,
CreateAppDto,
DateTime
DateTime,
ImmutableArray
} from './../';
describe('AppsStoreService', () => {
describe('AppsState', () => {
const now = DateTime.now();
const oldApps = [
@ -36,10 +37,10 @@ describe('AppsStoreService', () => {
});
it('should load automatically', () => {
const store = new AppsStoreService(appsService.object);
const store = new AppsState(appsService.object);
let result1: AppDto[] | null = null;
let result2: AppDto[] | null = null;
let result1: ImmutableArray<AppDto>;
let result2: ImmutableArray<AppDto>;
store.apps.subscribe(x => {
result1 = x;
@ -49,8 +50,8 @@ describe('AppsStoreService', () => {
result2 = x;
}).unsubscribe();
expect(result1).toEqual(oldApps);
expect(result2).toEqual(oldApps);
expect(result1!.values).toEqual(oldApps);
expect(result2!.values).toEqual(oldApps);
appsService.verifyAll();
});
@ -62,10 +63,10 @@ describe('AppsStoreService', () => {
.returns(() => Observable.of(newApp))
.verifiable(Times.once());
const store = new AppsStoreService(appsService.object);
const store = new AppsState(appsService.object);
let result1: AppDto[] | null = null;
let result2: AppDto[] | null = null;
let result1: ImmutableArray<AppDto>;
let result2: ImmutableArray<AppDto>;
store.apps.subscribe(x => {
result1 = x;
@ -77,8 +78,8 @@ describe('AppsStoreService', () => {
result2 = x;
}).unsubscribe();
expect(result1).toEqual(oldApps);
expect(result2).toEqual(oldApps.concat([newApp]));
expect(result1!.values).toEqual(oldApps);
expect(result2!.values).toEqual(oldApps.concat([newApp]));
appsService.verifyAll();
});
@ -94,11 +95,11 @@ describe('AppsStoreService', () => {
.returns(() => Observable.of({}))
.verifiable(Times.once());
const store = new AppsStoreService(appsService.object);
const store = new AppsState(appsService.object);
let result1: AppDto[] | null = null;
let result2: AppDto[] | null = null;
let result3: AppDto[] | null = null;
let result1: ImmutableArray<AppDto>;
let result2: ImmutableArray<AppDto>;
let result3: ImmutableArray<AppDto>;
store.apps.subscribe(x => {
result1 = x;
@ -116,15 +117,15 @@ describe('AppsStoreService', () => {
result3 = x;
}).unsubscribe();
expect(result1).toEqual(oldApps);
expect(result2).toEqual(oldApps.concat([newApp]));
expect(result3).toEqual(oldApps);
expect(result1!.values).toEqual(oldApps);
expect(result2!.values).toEqual(oldApps.concat([newApp]));
expect(result3!.values).toEqual(oldApps);
appsService.verifyAll();
});
it('should select app', (done) => {
const store = new AppsStoreService(appsService.object);
const store = new AppsState(appsService.object);
store.selectApp(oldApps[0].name).subscribe(isSelected => {
expect(isSelected).toBeTruthy();

20
src/Squidex/app/shell/pages/internal/apps-menu.component.html

@ -36,22 +36,8 @@
<div class="modal" *sqxModalView="addAppDialog;onRoot:true" @fade>
<div class="modal-backdrop"></div>
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Create App</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" (click)="addAppDialog.hide()">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<sqx-app-form
(created)="addAppDialog.hide()"
(cancelled)="addAppDialog.hide()">
</sqx-app-form>
</div>
</div>
</div>
<sqx-app-form
(completed)="addAppDialog.hide()">
</sqx-app-form>
</div>

11
src/Squidex/app/shell/pages/internal/apps-menu.component.ts

@ -9,9 +9,9 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import {
AppContext,
AppDto,
AppsStoreService,
AppsState,
ImmutableArray,
fadeAnimation,
ModalView
} from 'shared';
@ -20,9 +20,6 @@ import {
selector: 'sqx-apps-menu',
styleUrls: ['./apps-menu.component.scss'],
templateUrl: './apps-menu.component.html',
providers: [
AppContext
],
animations: [
fadeAnimation
]
@ -34,12 +31,12 @@ export class AppsMenuComponent implements OnDestroy, OnInit {
public addAppDialog = new ModalView();
public appsMenu = new ModalView(false, true);
public apps: AppDto[] = [];
public apps = ImmutableArray.of<AppDto>([]);
public selectedApp: AppDto | null;
constructor(
private readonly appsStore: AppsStoreService
private readonly appsStore: AppsState
) {
}

11
src/Squidex/app/shell/pages/internal/internal-area.component.ts

@ -9,7 +9,11 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import { DialogService, Notification } from 'shared';
import {
AppsState,
DialogService,
Notification
} from 'shared';
@Component({
selector: 'sqx-internal-area',
@ -23,7 +27,8 @@ export class InternalAreaComponent implements OnDestroy, OnInit {
constructor(
private readonly dialogs: DialogService,
private readonly route: ActivatedRoute
private readonly route: ActivatedRoute,
private readonly appState: AppsState
) {
}
@ -32,6 +37,8 @@ export class InternalAreaComponent implements OnDestroy, OnInit {
}
public ngOnInit() {
this.appState.loadApps().subscribe();
this.queryParamsSubscription =
this.route.queryParams.subscribe(params => {
const successMessage = params['successMessage'];

Loading…
Cancel
Save