/* * Copyright (C) 2020-2021 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ import {Injectable} from '@angular/core'; import { SCFacet, SCSearchFilter, SCSearchSort, SCThingType, } from '@openstapps/core'; import {Subject} from 'rxjs'; import { FilterBucket, FilterContext, FilterFacet, SortContext, } from './context-type'; /** * ContextMenuService provides bidirectional communication of context menu options and search queries */ @Injectable() export class ContextMenuService { /** * Local filter context object */ contextFilter: FilterContext; /** * Container for the filter context */ filterOptions = new Subject(); /** * Observable filterContext streams */ filterContextChanged$ = this.filterOptions.asObservable(); /** * Container for the filter query (SCSearchFilter) */ filterQuery = new Subject(); /** * Observable filterContext streams */ filterQueryChanged$ = this.filterQuery.asObservable(); /** * Forced SCThingTypeFilter */ forcedType?: SCThingType; /** * Container for the sort context */ sortOptions = new Subject(); /** * Observable SortContext streams */ sortContextChanged$ = this.sortOptions.asObservable(); /** * Container for the sort query */ sortQuery = new Subject(); /** * Observable SortContext streams */ sortQueryChanged$ = this.sortQuery.asObservable(); /** * Returns SCSearchFilter if filterContext value is set, undefined otherwise * * @param filterContext FilterContext to build SCSearchFilter from */ buildFilterQuery = ( filterContext: FilterContext, ): SCSearchFilter | undefined => { const filters: SCSearchFilter[] = []; if (typeof this.forcedType !== 'undefined') { filters.push({ arguments: { field: 'type', value: this.forcedType, }, type: 'value', }); } for (const filterFacet of filterContext.options) { const optionFilters: SCSearchFilter[] = []; for (const filterBucket of filterFacet.buckets) { if (filterBucket.checked) { optionFilters.push({ arguments: { field: filterFacet.field, value: filterBucket.key, }, type: 'value', }); } } if (optionFilters.length > 0) { filters.push({ arguments: { filters: optionFilters, operation: 'or', }, type: 'boolean', }); } } if (filters.length > 0) { return { arguments: { filters: filters, operation: 'and', }, type: 'boolean', }; } return; }; /** * Returns SCSearchSort if sorting value is set, undefined otherwise * * @param sortContext SortContext to build SCSearchSort from */ buildSortQuery = (sortContext: SortContext): SCSearchSort[] | undefined => { if ( sortContext.value && sortContext.value.length > 0 && (sortContext.value === 'name' || sortContext.value === 'type') ) { return [ { arguments: { field: sortContext.value, position: 0, }, order: sortContext.reversed ? 'desc' : 'asc', type: 'ducet', }, ]; } return; }; /** * Updates filter query from filterContext */ contextFilterChanged(filterContext: FilterContext) { this.filterQuery.next(this.buildFilterQuery(filterContext)); } /** * Updates sort query from sortContext */ contextSortChanged(sortContext: SortContext) { this.sortQuery.next(this.buildSortQuery(sortContext)); } /** * Sets sort context */ setContextSort(sortContext: SortContext) { this.sortOptions.next(sortContext); } /** * Updates the filter context options from given facets */ updateContextFilter(facets: SCFacet[]) { // arrange facet field "type" to first position facets.sort((a: SCFacet, b: SCFacet) => { if (a.field === 'type') { return -1; } if (b.field === 'type') { return 1; } return 0; }); if (!this.contextFilter) { this.contextFilter = { name: 'filter', options: [], }; } this.updateContextFilterOptions(this.contextFilter, facets); } /** * Updates context filter with new facets. * It preserves the checked status of existing filter options */ updateContextFilterOptions = ( contextFilter: FilterContext, facets: SCFacet[], ) => { const newFilterOptions: FilterFacet[] = []; // iterate new facets for (const facet of facets) { if (facet.buckets.length > 0) { const newFilterFacet: FilterFacet = { buckets: [], field: facet.field, onlyOnType: facet.onlyOnType, }; newFilterOptions.push(newFilterFacet); // search existing filterOption const filterOption = contextFilter.options.find( (contextFacet: FilterFacet) => contextFacet.field === facet.field && contextFacet.onlyOnType === facet.onlyOnType, ); for (const bucket of facet.buckets) { // search existing bucket to preserve checked status const existingFilterBucket = filterOption ? filterOption.buckets.find( (contextBucket: FilterBucket) => contextBucket.key === bucket.key, ) : undefined; const filterBucket: FilterBucket = { checked: existingFilterBucket ? existingFilterBucket.checked : false, count: bucket.count, key: bucket.key, }; newFilterFacet.buckets.push(filterBucket); } } } // update filter options contextFilter.options = newFilterOptions; this.contextFilter = contextFilter; this.filterOptions.next(contextFilter); }; }