From b7f8a749374b6c0cc29d6e56f3173c6efee8ca1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Tue, 6 Jun 2017 15:09:30 +0200 Subject: [PATCH] Introduce derived state for when table query states are ready --- .../routing/wp-list/wp-list.controller.ts | 13 +- frontend/app/components/states.service.ts | 22 +++- .../state/wp-table-base.service.ts | 6 +- .../app/components/wp-list/wp-list.service.ts | 73 ++--------- .../wp-states-initialization.service.ts | 119 ++++++++++++++++++ 5 files changed, 158 insertions(+), 75 deletions(-) create mode 100644 frontend/app/components/wp-list/wp-states-initialization.service.ts diff --git a/frontend/app/components/routing/wp-list/wp-list.controller.ts b/frontend/app/components/routing/wp-list/wp-list.controller.ts index b651fb3897..120ce3ed84 100644 --- a/frontend/app/components/routing/wp-list/wp-list.controller.ts +++ b/frontend/app/components/routing/wp-list/wp-list.controller.ts @@ -98,16 +98,11 @@ function WorkPackagesListController($scope:any, function setupQueryObservers() { - scopedObservable($scope, states.table.query.values$()) - .withLatestFrom( - wpTablePagination.observeOnScope($scope) - ).subscribe(([query, pagination]) => { - $scope.tableInformationLoaded = true; - updateTitle(query); - - wpListChecksumService.updateIfDifferent(query, pagination as WorkPackageTablePagination); - }); + scopedObservable($scope, states.tableRendering.onQueryUpdated.changes$()) + .subscribe((val) => { + $scope.tableInformationLoaded = states.tableRendering.onQueryUpdated.hasValue(); + }); wpTablePagination.observeOnScope($scope) .withLatestFrom(scopedObservable($scope, states.table.query.values$())) diff --git a/frontend/app/components/states.service.ts b/frontend/app/components/states.service.ts index 1ac227c451..acd52e22d8 100644 --- a/frontend/app/components/states.service.ts +++ b/frontend/app/components/states.service.ts @@ -1,4 +1,4 @@ -import {createNewContext, input, multiInput, StatesGroup} from "reactivestates"; +import {combine, createNewContext, derive, input, multiInput, StatesGroup} from "reactivestates"; import {Subject} from "rxjs"; import {opServicesModule} from "../angular-modules"; import {QueryFormResource} from "./api/api-v3/hal-resources/query-form-resource.service"; @@ -39,6 +39,8 @@ export class States extends StatesGroup { // Work package table states table = new TableState(); + tableRendering = new TableRenderingStates(this.table); + // Updater states on user input updates = new UserUpdaterStates(this.table); @@ -94,6 +96,24 @@ export class TableState { stopAllSubscriptions = new Subject(); // Fire when table refresh is required refreshRequired = input(); + +} + +export class TableRenderingStates { + constructor(private table:TableState) { + } + + // State when all required input states for the current query are ready + private combinedTableStates = combine( + this.table.rows, + this.table.columns, + this.table.sum, + this.table.groupBy, + this.table.sortBy + ); + + onQueryUpdated = derive(this.combinedTableStates, ($, input) => $.mapTo(null)); + } export class UserUpdaterStates { diff --git a/frontend/app/components/wp-fast-table/state/wp-table-base.service.ts b/frontend/app/components/wp-fast-table/state/wp-table-base.service.ts index 99e213792f..2ebb0de543 100644 --- a/frontend/app/components/wp-fast-table/state/wp-table-base.service.ts +++ b/frontend/app/components/wp-fast-table/state/wp-table-base.service.ts @@ -48,7 +48,11 @@ export abstract class WorkPackageTableBaseService { public get state(): InputState { return this.states.table[this.stateName]; - }; + } + + public clear(reason:string) { + this.state.clear(reason); + } public observeOnScope(scope:ng.IScope) { return scopedObservable(scope, this.state.values$()); diff --git a/frontend/app/components/wp-list/wp-list.service.ts b/frontend/app/components/wp-list/wp-list.service.ts index dd3e5d5cb0..8facfac08f 100644 --- a/frontend/app/components/wp-list/wp-list.service.ts +++ b/frontend/app/components/wp-list/wp-list.service.ts @@ -48,6 +48,7 @@ import {WorkPackageTableTimelineService} from "./../wp-fast-table/state/wp-table import {WorkPackageTableHierarchiesService} from "./../wp-fast-table/state/wp-table-hierarchy.service"; import {SchemaCacheService} from "../schemas/schema-cache.service"; import {Observable} from "rxjs"; +import {WorkPackageStatesInitializationService} from './wp-states-initialization.service'; export class WorkPackagesListService { constructor(protected NotificationsService:any, @@ -58,16 +59,8 @@ export class WorkPackagesListService { protected QueryDm:QueryDmService, protected QueryFormDm:QueryFormDmService, protected states:States, - protected wpCacheService:WorkPackageCacheService, - protected schemaCacheService:SchemaCacheService, - protected wpTableColumns:WorkPackageTableColumnsService, - protected wpTableSortBy:WorkPackageTableSortByService, - protected wpTableGroupBy:WorkPackageTableGroupByService, - protected wpTableFilters:WorkPackageTableFiltersService, - protected wpTableSum:WorkPackageTableSumService, - protected wpTableTimeline:WorkPackageTableTimelineService, - protected wpTableHierarchies:WorkPackageTableHierarchiesService, protected wpTablePagination:WorkPackageTablePaginationService, + protected wpStatesInitialization:WorkPackageStatesInitializationService, protected wpListInvalidQueryService:WorkPackagesListInvalidQueryService, protected I18n:op.I18n, protected queryMenuItemFactory:any) { @@ -126,7 +119,7 @@ export class WorkPackagesListService { public loadResultsList(query:QueryResource, additionalParams:PaginationObject):ng.IPromise { let wpListPromise = this.QueryDm.loadResults(query, additionalParams); - return this.updateStatesFromWPListOnPromise(wpListPromise); + return this.updateStatesFromWPListOnPromise(query, wpListPromise); } /** @@ -153,7 +146,7 @@ export class WorkPackagesListService { public loadForm(query:QueryResource):ng.IPromise { return this.QueryFormDm.load(query).then((form:QueryFormResource) => { - this.updateStatesFromForm(query, form); + this.wpStatesInitialization.updateStatesFromForm(query, form); return form; }); @@ -278,7 +271,7 @@ export class WorkPackagesListService { promise .then(query => { this.states.table.context.doAndTransition('Query loaded', () => { - this.updateStatesFromQuery(query); + this.wpStatesInitialization.initialize(query, query.results); }); return query; @@ -287,64 +280,16 @@ export class WorkPackagesListService { return promise; } - private updateStatesFromWPListOnPromise(promise:ng.IPromise):ng.IPromise { + private updateStatesFromWPListOnPromise(query:QueryResource, promise:ng.IPromise):ng.IPromise { return promise.then((results) => { this.states.table.context.doAndTransition('Query loaded', () => { - this.updateStatesFromWPCollection(results); + this.wpStatesInitialization.initialize(query, results); }); return results; }); } - private updateStatesFromQuery(query:QueryResource) { - this.updateStatesFromWPCollection(query.results); - - this.states.table.query.putValue(query); - - this.wpTableSum.initialize(query); - this.wpTableColumns.initialize(query); - this.wpTableGroupBy.initialize(query); - this.wpTableTimeline.initialize(query); - this.wpTableHierarchies.initialize(query); - - this.AuthorisationService.initModelAuth('query', query.$links); - } - - private updateStatesFromWPCollection(results:WorkPackageCollectionResource) { - if (results.schemas) { - _.each(results.schemas.elements, (schema:SchemaResource) => { - this.states.schemas.get(schema.href as string).putValue(schema); - }); - } - - this.states.table.rows.putValue(results.elements); - - this.wpCacheService.updateWorkPackageList(results.elements); - - this.states.table.results.putValue(results); - - this.states.table.groups.putValue(angular.copy(results.groups)); - - this.wpTablePagination.initialize(results); - - this.AuthorisationService.initModelAuth('work_packages', results.$links); - } - - private updateStatesFromForm(query:QueryResource, form:QueryFormResource) { - let schema = form.schema as QuerySchemaResourceInterface; - - _.each(schema.filtersSchemas.elements, (schema:QueryFilterInstanceSchemaResource) => { - this.states.schemas.get(schema.href as string).putValue(schema); - }); - - this.states.table.form.putValue(form); - this.wpTableSortBy.initialize(query, schema); - this.wpTableFilters.initialize(query, schema); - this.wpTableGroupBy.update(query, schema); - this.wpTableColumns.update(query, schema); - } - private get currentQuery() { return this.states.table.query.value!; } @@ -380,8 +325,8 @@ export class WorkPackagesListService { } this.states.table.context.doAndTransition('Query loaded', () => { - this.updateStatesFromQuery(query); - this.updateStatesFromForm(query, form); + this.wpStatesInitialization.initialize(query, query.results); + this.wpStatesInitialization.updateStatesFromForm(query, form); }); deferred.resolve(query); diff --git a/frontend/app/components/wp-list/wp-states-initialization.service.ts b/frontend/app/components/wp-list/wp-states-initialization.service.ts new file mode 100644 index 0000000000..fd3cb2eab6 --- /dev/null +++ b/frontend/app/components/wp-list/wp-states-initialization.service.ts @@ -0,0 +1,119 @@ +import {States} from '../states.service'; +import {WorkPackagesListInvalidQueryService} from './wp-list-invalid-query.service'; +import {WorkPackageTablePaginationService} from '../wp-fast-table/state/wp-table-pagination.service'; +import {WorkPackageTableHierarchiesService} from '../wp-fast-table/state/wp-table-hierarchy.service'; +import {WorkPackageTableTimelineService} from '../wp-fast-table/state/wp-table-timeline.service'; +import {WorkPackageTableSumService} from '../wp-fast-table/state/wp-table-sum.service'; +import {WorkPackageTableFiltersService} from '../wp-fast-table/state/wp-table-filters.service'; +import {WorkPackageTableGroupByService} from '../wp-fast-table/state/wp-table-group-by.service'; +import {WorkPackageTableSortByService} from '../wp-fast-table/state/wp-table-sort-by.service'; +import {WorkPackageTableColumnsService} from '../wp-fast-table/state/wp-table-columns.service'; +import {QueryResource} from '../api/api-v3/hal-resources/query-resource.service'; +import {WorkPackageCollectionResource} from '../api/api-v3/hal-resources/wp-collection-resource.service'; +import {SchemaResource} from '../api/api-v3/hal-resources/schema-resource.service'; +import {QueryFormResource} from '../api/api-v3/hal-resources/query-form-resource.service'; +import {QuerySchemaResourceInterface} from '../api/api-v3/hal-resources/query-schema-resource.service'; +import {QueryFilterInstanceSchemaResource} from '../api/api-v3/hal-resources/query-filter-instance-schema-resource.service'; +import {WorkPackageCacheService} from '../work-packages/work-package-cache.service'; + +export class WorkPackageStatesInitializationService { + constructor(protected states:States, + protected wpTableColumns:WorkPackageTableColumnsService, + protected wpTableSortBy:WorkPackageTableSortByService, + protected wpTableGroupBy:WorkPackageTableGroupByService, + protected wpTableFilters:WorkPackageTableFiltersService, + protected wpTableSum:WorkPackageTableSumService, + protected wpTableTimeline:WorkPackageTableTimelineService, + protected wpTableHierarchies:WorkPackageTableHierarchiesService, + protected wpTablePagination:WorkPackageTablePaginationService, + protected wpListInvalidQueryService:WorkPackagesListInvalidQueryService, + protected wpCacheService:WorkPackageCacheService, + protected AuthorisationService:any) { + } + + /** + * Initialize the query and table states from the given query and results. + * They may or may not come from the same query source. + * + * @param query + * @param results + */ + public initialize(query:QueryResource, results:WorkPackageCollectionResource) { + this.clearStates(); + + this.initializeFromQuery(query); + this.initializeFromResults(results); + } + + /** + * Insert new information from the query from to the states. + * + * @param query + * @param form + */ + public updateStatesFromForm(query:QueryResource, form:QueryFormResource) { + let schema = form.schema as QuerySchemaResourceInterface; + + _.each(schema.filtersSchemas.elements, (schema:QueryFilterInstanceSchemaResource) => { + this.states.schemas.get(schema.href as string).putValue(schema); + }); + + this.states.table.form.putValue(form); + this.wpTableSortBy.initialize(query, schema); + this.wpTableFilters.initialize(query, schema); + this.wpTableGroupBy.update(query, schema); + this.wpTableColumns.update(query, schema); + } + + private initializeFromResults(results:WorkPackageCollectionResource) { + if (results.schemas) { + _.each(results.schemas.elements, (schema:SchemaResource) => { + this.states.schemas.get(schema.href as string).putValue(schema); + }); + } + + this.states.table.rows.putValue(results.elements); + + this.wpCacheService.updateWorkPackageList(results.elements); + + this.states.table.results.putValue(results); + + this.states.table.groups.putValue(angular.copy(results.groups)); + + this.wpTablePagination.initialize(results); + + this.AuthorisationService.initModelAuth('work_packages', results.$links); + } + + private initializeFromQuery(query:QueryResource) { + this.states.table.query.putValue(query); + + this.wpTableSum.initialize(query); + this.wpTableColumns.initialize(query); + this.wpTableGroupBy.initialize(query); + this.wpTableTimeline.initialize(query); + this.wpTableHierarchies.initialize(query); + + this.AuthorisationService.initModelAuth('query', query.$links); + } + + private clearStates() { + const reason = 'Clearing states before re-initialization.'; + + // Clear table states + this.wpTableSum.clear(reason) + this.wpTableColumns.clear(reason); + this.wpTableGroupBy.clear(reason); + this.wpTableTimeline.clear(reason); + this.wpTableHierarchies.clear(reason); + + // Clear immediate input states + this.states.table.rows.clear(reason); + this.states.table.results.clear(reason); + this.states.table.groups.clear(reason); + } +} + +angular + .module('openproject.workPackages.services') + .service('wpStatesInitialization', WorkPackageStatesInitializationService);