Merge pull request #4411 from MaximilianKrambach/sort_alternative

fix sort performance issues for dropdowns
This commit is contained in:
Sean 2019-03-15 09:58:32 +01:00 committed by GitHub
commit 398ffc30ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 326 additions and 231 deletions

View File

@ -2,6 +2,8 @@ import { Injectable } from '@angular/core';
import { tap, map } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import { BaseAgendaContentObjectRepository } from '../base-agenda-content-object-repository';
import { BaseRepository } from '../base-repository';
import { BaseAgendaViewModel } from 'app/site/base/base-agenda-view-model';
@ -13,7 +15,6 @@ import { DataStoreService } from '../../core-services/data-store.service';
import { HttpService } from 'app/core/core-services/http.service';
import { Item } from 'app/shared/models/agenda/item';
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
import { TranslateService } from '@ngx-translate/core';
import { TreeService } from 'app/core/ui-services/tree.service';
import { ViewItem } from 'app/site/agenda/models/view-item';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
@ -39,15 +40,15 @@ export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
translate: TranslateService,
private httpService: HttpService,
private config: ConfigService,
private treeService: TreeService,
private translate: TranslateService
private treeService: TreeService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Item);
super(DS, dataSend, mapperService, viewModelStoreService, translate, Item);
}
public getVerboseName = (plural: boolean = false) => {
@ -85,7 +86,7 @@ export class ItemRepositoryService extends BaseRepository<ViewItem, Item> {
const repo = this.collectionStringMapperService.getRepository(
viewItem.item.content_object.collection
) as BaseAgendaContentObjectRepository<any, any>;
return numberPrefix + repo.getAgendaTitleWithType(viewItem);
return numberPrefix + repo.getAgendaTitleWithType(viewItem.title_information);
}
};
viewItem.getListTitle = viewItem.getTitle;

View File

@ -30,12 +30,12 @@ export class TopicRepositoryService extends BaseAgendaContentObjectRepository<Vi
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private translate: TranslateService
translate: TranslateService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Topic, [Mediafile, Item]);
super(DS, dataSend, mapperService, viewModelStoreService, translate, Topic, [Mediafile, Item]);
}
public getAgendaTitle = (topic: Partial<Topic> | Partial<ViewTopic>) => {

View File

@ -36,9 +36,9 @@ export class AssignmentRepositoryService extends BaseAgendaContentObjectReposito
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
private translate: TranslateService
translate: TranslateService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Assignment, [User, Item, Tag]);
super(DS, dataSend, mapperService, viewModelStoreService, translate, Assignment, [User, Item, Tag]);
}
public getAgendaTitle = (assignment: Partial<Assignment> | Partial<ViewAssignment>) => {

View File

@ -1,3 +1,5 @@
import { TranslateService } from '@ngx-translate/core';
import { BaseViewModel } from '../../site/base/base-view-model';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
import { CollectionStringMapperService } from '../core-services/collectionStringMapper.service';
@ -25,9 +27,18 @@ export abstract class BaseAgendaContentObjectRepository<
dataSend: DataSendService,
collectionStringMapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
baseModelCtor: ModelConstructor<M>,
depsModelCtors?: ModelConstructor<BaseModel>[]
) {
super(DS, dataSend, collectionStringMapperService, viewModelStoreService, baseModelCtor, depsModelCtors);
super(
DS,
dataSend,
collectionStringMapperService,
viewModelStoreService,
translate,
baseModelCtor,
depsModelCtors
);
}
}

View File

@ -1,4 +1,5 @@
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import { BaseViewModel } from '../../site/base/base-view-model';
import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model';
@ -24,17 +25,35 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
protected viewModelSubjects: { [modelId: number]: BehaviorSubject<V> } = {};
/**
* Observable subject for the whole list
* Observable subject for the whole list. These entries are unsorted an not piped through
* autodTime. Just use this internally.
*
* It's used to debounce messages on the sortedViewModelListSubject
*/
protected readonly viewModelListSubject: BehaviorSubject<V[]> = new BehaviorSubject<V[]>([]);
private readonly viewModelListSubject: BehaviorSubject<V[]> = new BehaviorSubject<V[]>([]);
protected readonly viewModelListAuditSubject: BehaviorSubject<V[]> = new BehaviorSubject<V[]>([]);
/**
* Observable subject for the sorted view model list.
*
* All data is piped through an auditTime of 1ms. This is to prevent massive
* updates, if e.g. an autoupdate with a lot motions come in. The result is just one
* update of the new list instead of many unnecessary updates.
*/
protected readonly sortedViewModelListSubject: BehaviorSubject<V[]> = new BehaviorSubject<V[]>([]);
/**
* Observable subject for any changes of view models.
*/
protected readonly generalViewModelSubject: Subject<V> = new Subject<V>();
/**
* Can be used by the sort functions.
*/
protected languageCollator: Intl.Collator;
/**
* The collection string of the managed model.
*/
private _collectionString: string;
public get collectionString(): string {
@ -65,16 +84,28 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
protected dataSend: DataSendService,
protected collectionStringMapperService: CollectionStringMapperService,
protected viewModelStoreService: ViewModelStoreService,
protected translate: TranslateService,
protected baseModelCtor: ModelConstructor<M>,
protected depsModelCtors?: ModelConstructor<BaseModel>[]
) {
this._collectionString = baseModelCtor.COLLECTIONSTRING;
this.getViewModelListObservable().subscribe(x => this.viewModelListAuditSubject.next(x));
// All data is piped through an auditTime of 1ms. This is to prevent massive
// updates, if e.g. an autoupdate with a lot motions come in. The result is just one
// update of the new list instead of many unnecessary updates.
this.viewModelListSubject.pipe(auditTime(1)).subscribe(models => {
this.sortedViewModelListSubject.next(models.sort(this.viewModelSortFn));
});
this.languageCollator = new Intl.Collator(this.translate.currentLang);
}
public onAfterAppsLoaded(): void {
this.DS.clearObservable.subscribe(() => this.clear());
this.translate.onLangChange.subscribe(change => {
this.languageCollator = new Intl.Collator(change.lang);
this.updateViewModelListObservable();
});
// Populate the local viewModelStore with ViewModel Objects.
this.DS.getAll(this.baseModelCtor).forEach((model: M) => {
@ -202,6 +233,19 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
*/
protected clear(): void {
this.viewModelStore = {};
}
/**
* The function used for sorting the data of this repository. The defualt sorts by ID.
*/
protected viewModelSortFn: (a: V, b: V) => number = (a: V, b: V) => a.id - b.id;
/**
* Setter for a sort function. Updates the sorting.
*
* @param fn a sort function
*/
public setSortFunction(fn: (a: V, b: V) => number): void {
this.viewModelSortFn = fn;
this.updateViewModelListObservable();
}
@ -213,14 +257,24 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
}
/**
* helper function to return the viewModel as array
* @returns all view models stored in this repository. Sorting is not guaranteed
*/
public getViewModelList(): V[] {
return Object.values(this.viewModelStore);
}
/**
* returns the current observable for one viewModel
* Get a sorted ViewModelList. This passes through a (1ms short) delay,
* thus may not be accurate, especially on application loading.
*
* @returns all sorted view models stored in this repository.
*/
public getSortedViewModelList(): V[] {
return this.sortedViewModelListSubject.getValue();
}
/**
* @returns the current observable for one viewModel
*/
public getViewModelObservable(id: number): Observable<V> {
if (!this.viewModelSubjects[id]) {
@ -230,14 +284,10 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
}
/**
* Return the Observable of the whole store.
*
* All data is piped through an auditTime of 1ms. This is to prevent massive
* updates, if e.g. an autoupdate with a lot motions come in. The result is just one
* update of the new list instead of many unnecessary updates.
* @returns the (sorted) Observable of the whole store.
*/
public getViewModelListObservable(): Observable<V[]> {
return this.viewModelListSubject.asObservable().pipe(auditTime(1));
return this.sortedViewModelListSubject.asObservable();
}
/**
@ -247,7 +297,7 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
* @returns A subject that holds the model list
*/
public getViewModelListBehaviorSubject(): BehaviorSubject<V[]> {
return this.viewModelListAuditSubject;
return this.sortedViewModelListSubject;
}
/**
@ -268,7 +318,7 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
}
/**
* update the observable of the list
* update the observable of the list. Also updates the sorting of the view model list.
*/
protected updateViewModelListObservable(): void {
this.viewModelListSubject.next(this.getViewModelList());

View File

@ -17,9 +17,9 @@ export class ChatMessageRepositoryService extends BaseRepository<ViewChatMessage
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
private translate: TranslateService
translate: TranslateService
) {
super(DS, dataSend, mapperService, viewModelStoreService, ChatMessage);
super(DS, dataSend, mapperService, viewModelStoreService, translate, ChatMessage);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -100,11 +100,11 @@ export class ConfigRepositoryService extends BaseRepository<ViewConfig, Config>
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
private constantsService: ConstantsService,
private http: HttpService,
private translate: TranslateService
private http: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Config);
super(DS, dataSend, mapperService, viewModelStoreService, translate, Config);
this.constantsService.get('OpenSlidesConfigVariables').subscribe(constant => {
this.createConfigStructure(constant);

View File

@ -35,11 +35,11 @@ export class HistoryRepositoryService extends BaseRepository<ViewHistory, Histor
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
private httpService: HttpService,
private timeTravel: TimeTravelService,
private translate: TranslateService
private timeTravel: TimeTravelService
) {
super(DS, dataSend, mapperService, viewModelStoreService, History, [User]);
super(DS, dataSend, mapperService, viewModelStoreService, translate, History, [User]);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -32,11 +32,12 @@ export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Me
DS: DataStoreService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
translate: TranslateService,
protected dataSend: DataSendService,
private httpService: HttpService,
private translate: TranslateService
private httpService: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Mediafile, [User]);
super(DS, dataSend, mapperService, viewModelStoreService, translate, Mediafile, [User]);
this.initSorting();
}
public getVerboseName = (plural: boolean = false) => {
@ -69,4 +70,13 @@ export class MediafileRepositoryService extends BaseRepository<ViewMediafile, Me
const emptyHeader = new HttpHeaders();
return this.httpService.post<Identifiable>(restPath, file, {}, emptyHeader);
}
/**
* Sets the default sorting (e.g. in dropdowns and for new users) to 'title'
*/
private initSorting(): void {
this.setSortFunction((a: ViewMediafile, b: ViewMediafile) => {
return this.languageCollator.compare(a.title, b.title);
});
}
}

View File

@ -1,6 +1,5 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import { BaseRepository } from '../base-repository';
@ -13,6 +12,8 @@ import { HttpService } from '../../core-services/http.service';
import { ViewCategory } from 'app/site/motions/models/view-category';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
type SortProperty = 'prefix' | 'name';
/**
* Repository Services for Categories
*
@ -27,6 +28,8 @@ import { ViewModelStoreService } from 'app/core/core-services/view-model-store.s
providedIn: 'root'
})
export class CategoryRepositoryService extends BaseRepository<ViewCategory, Category> {
private sortProperty: SortProperty;
/**
* Creates a CategoryRepository
* Converts existing and incoming category to ViewCategories
@ -40,15 +43,21 @@ export class CategoryRepositoryService extends BaseRepository<ViewCategory, Cate
* @param translate translationService to get the currently selected locale
*/
public constructor(
protected DS: DataStoreService,
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
translate: TranslateService,
private httpService: HttpService,
private configService: ConfigService,
private translate: TranslateService
private configService: ConfigService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Category);
super(DS, dataSend, mapperService, viewModelStoreService, translate, Category);
this.sortProperty = this.configService.instant('motions_category_sorting');
this.configService.get<SortProperty>('motions_category_sorting').subscribe(conf => {
this.sortProperty = conf;
this.setConfigSortFn();
});
}
public getVerboseName = (plural: boolean = false) => {
@ -66,8 +75,7 @@ export class CategoryRepositoryService extends BaseRepository<ViewCategory, Cate
* @param category_id category ID
*/
public getCategoryByID(category_id: number): Category {
const catList = this.DS.getAll(Category);
return catList.find(category => category.id === category_id);
return this.DS.find<Category>(Category, cat => cat.id === category_id);
}
/**
@ -81,36 +89,19 @@ export class CategoryRepositoryService extends BaseRepository<ViewCategory, Cate
}
/**
* @ returns the observable for categories sorted according to configuration
* Triggers an update for the sort function responsible for the default sorting of data items
*/
public getSortedViewModelListObservable(): Observable<ViewCategory[]> {
const subject = new BehaviorSubject<ViewCategory[]>([]);
this.getViewModelListObservable().subscribe(categories => {
subject.next(this.sortViewCategoriesByConfig(categories));
});
return subject.asObservable();
}
/**
* Sort viewCategories by the configured settings
*
* @param categories
* @returns the categories sorted by prefix or name, according to the config setting
*
* TODO: That operation is HEAVY
*/
public sortViewCategoriesByConfig(categories: ViewCategory[]): ViewCategory[] {
const sort = this.configService.instant<'prefix' | 'name'>('motions_category_sorting') || 'prefix';
return categories.sort((a, b) => {
if (a[sort] && b[sort]) {
return a[sort].localeCompare(b[sort], this.translate.currentLang);
} else if (sort === 'prefix') {
public setConfigSortFn(): void {
this.setSortFunction((a: ViewCategory, b: ViewCategory) => {
if (a[this.sortProperty] && b[this.sortProperty]) {
return this.languageCollator.compare(a[this.sortProperty], b[this.sortProperty]);
} else if (this.sortProperty === 'prefix') {
if (a.prefix) {
return 1;
} else if (b.prefix) {
return -1;
} else {
return a.name.localeCompare(b.name);
return this.languageCollator.compare(a.name, b.name);
}
}
});

View File

@ -45,12 +45,12 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private translate: TranslateService
translate: TranslateService
) {
super(DS, dataSend, mapperService, viewModelStoreService, MotionChangeRecommendation, [
super(DS, dataSend, mapperService, viewModelStoreService, translate, MotionChangeRecommendation, [
Category,
User,
Workflow
@ -85,7 +85,7 @@ export class ChangeRecommendationRepositoryService extends BaseRepository<
* return the Observable of all change recommendations belonging to the given motion
*/
public getChangeRecosOfMotionObservable(motion_id: number): Observable<ViewMotionChangeRecommendation[]> {
return this.viewModelListSubject.asObservable().pipe(
return this.getViewModelListObservable().pipe(
map((recos: ViewMotionChangeRecommendation[]) => {
return recos.filter(reco => reco.motion_id === motion_id);
})

View File

@ -36,14 +36,14 @@ export class MotionBlockRepositoryService extends BaseAgendaContentObjectReposit
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
translate: TranslateService,
private motionRepo: MotionRepositoryService,
private httpService: HttpService,
private translate: TranslateService
private httpService: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, MotionBlock, [Item]);
super(DS, dataSend, mapperService, viewModelStoreService, translate, MotionBlock, [Item]);
}
public getAgendaTitle = (motionBlock: Partial<MotionBlock> | Partial<ViewMotionBlock>) => {

View File

@ -42,13 +42,13 @@ export class MotionCommentSectionRepositoryService extends BaseRepository<
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private http: HttpService,
private translate: TranslateService
translate: TranslateService,
private http: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, MotionCommentSection, [Group]);
super(DS, dataSend, mapperService, viewModelStoreService, translate, MotionCommentSection, [Group]);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -7,6 +7,8 @@ import { tap, map } from 'rxjs/operators';
import { Category } from 'app/shared/models/motions/category';
import { ChangeRecoMode, ViewMotion } from 'app/site/motions/models/view-motion';
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
import { ConfigService } from 'app/core/ui-services/config.service';
import { DataSendService } from '../../core-services/data-send.service';
import { DataStoreService } from '../../core-services/data-store.service';
import { DiffLinesInParagraph, DiffService, LineRange, ModificationType } from '../../ui-services/diff.service';
@ -42,6 +44,8 @@ import { PersonalNote, PersonalNoteContent } from 'app/shared/models/users/perso
import { ViewPersonalNote } from 'app/site/users/models/view-personal-note';
import { OperatorService } from 'app/core/core-services/operator.service';
type SortProperty = 'callListWeight' | 'identifier';
/**
* Repository Services for motions (and potentially categories)
*
@ -56,6 +60,11 @@ import { OperatorService } from 'app/core/core-services/operator.service';
providedIn: 'root'
})
export class MotionRepositoryService extends BaseAgendaContentObjectRepository<ViewMotion, Motion> {
/**
* The property the incoming data is sorted by
*/
protected sortProperty: SortProperty;
/**
* Creates a MotionRepository
*
@ -68,20 +77,23 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
* @param httpService OpenSlides own Http service
* @param lineNumbering Line numbering for motion text
* @param diff Display changes in motion text as diff.
* @param personalNoteService service fo personal notes
* @param config ConfigService (subscribe to sorting config)
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
translate: TranslateService,
config: ConfigService,
private httpService: HttpService,
private readonly lineNumbering: LinenumberingService,
private readonly diff: DiffService,
private treeService: TreeService,
private translate: TranslateService,
private operator: OperatorService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Motion, [
super(DS, dataSend, mapperService, viewModelStoreService, translate, Motion, [
Category,
User,
Workflow,
@ -92,6 +104,10 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
MotionChangeRecommendation,
PersonalNote
]);
config.get<SortProperty>('motions_motions_sorting').subscribe(conf => {
this.sortProperty = conf;
this.setConfigSortFn();
});
}
public getTitle = (motion: Partial<Motion> | Partial<ViewMotion>) => {
@ -839,4 +855,30 @@ export class MotionRepositoryService extends BaseAgendaContentObjectRepository<V
}
});
}
/**
* Triggers an update for the sort function responsible for the default sorting of data items
*/
public setConfigSortFn(): void {
this.setSortFunction((a: ViewMotion, b: ViewMotion) => {
if (a[this.sortProperty] && b[this.sortProperty]) {
if (a[this.sortProperty] === b[this.sortProperty]) {
return this.languageCollator.compare(a.title, b.title);
} else {
if (this.sortProperty === 'callListWeight') {
// handling numerical values
return a.callListWeight - b.callListWeight;
} else {
return this.languageCollator.compare(a[this.sortProperty], b[this.sortProperty]);
}
}
} else if (a[this.sortProperty]) {
return -1;
} else if (b[this.sortProperty]) {
return 1;
} else {
return this.languageCollator.compare(a.title, b.title);
}
});
}
}

View File

@ -31,12 +31,12 @@ export class StatuteParagraphRepositoryService extends BaseRepository<ViewStatut
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private translate: TranslateService
translate: TranslateService
) {
super(DS, dataSend, mapperService, viewModelStoreService, StatuteParagraph);
super(DS, dataSend, mapperService, viewModelStoreService, translate, StatuteParagraph);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -1,5 +1,4 @@
import { Injectable } from '@angular/core';
import { auditTime } from 'rxjs/operators';
import { Workflow } from 'app/shared/models/motions/workflow';
import { ViewWorkflow } from 'app/site/motions/models/view-workflow';
@ -43,14 +42,15 @@ export class WorkflowRepositoryService extends BaseRepository<ViewWorkflow, Work
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private httpService: HttpService,
private translate: TranslateService
translate: TranslateService,
private httpService: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Workflow);
this.viewModelListSubject.pipe(auditTime(1)).subscribe(models => {
super(DS, dataSend, mapperService, viewModelStoreService, translate, Workflow);
this.sortedViewModelListSubject.subscribe(models => {
if (models && models.length > 0) {
this.initSorting(models);
}

View File

@ -15,13 +15,13 @@ import { ServertimeService } from 'app/core/core-services/servertime.service';
export class CountdownRepositoryService extends BaseRepository<ViewCountdown, Countdown> {
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private translate: TranslateService,
translate: TranslateService,
private servertimeService: ServertimeService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Countdown);
super(DS, dataSend, mapperService, viewModelStoreService, translate, Countdown);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -14,12 +14,12 @@ import { DataSendService } from 'app/core/core-services/data-send.service';
export class ProjectorMessageRepositoryService extends BaseRepository<ViewProjectorMessage, ProjectorMessage> {
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private translate: TranslateService
translate: TranslateService
) {
super(DS, dataSend, mapperService, viewModelStoreService, ProjectorMessage);
super(DS, dataSend, mapperService, viewModelStoreService, translate, ProjectorMessage);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -37,13 +37,13 @@ export class ProjectorRepositoryService extends BaseRepository<ViewProjector, Pr
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private http: HttpService,
private translate: TranslateService
translate: TranslateService,
private http: HttpService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Projector, [Projector]);
super(DS, dataSend, mapperService, viewModelStoreService, translate, Projector, [Projector]);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -34,12 +34,12 @@ export class TagRepositoryService extends BaseRepository<ViewTag, Tag> {
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private translate: TranslateService
translate: TranslateService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Tag);
super(DS, dataSend, mapperService, viewModelStoreService, translate, Tag);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -49,13 +49,13 @@ export class GroupRepositoryService extends BaseRepository<ViewGroup, Group> {
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private constants: ConstantsService,
private translate: TranslateService
translate: TranslateService,
private constants: ConstantsService
) {
super(DS, dataSend, mapperService, viewModelStoreService, Group);
super(DS, dataSend, mapperService, viewModelStoreService, translate, Group);
this.sortPermsPerApp();
}

View File

@ -1,13 +1,14 @@
import { Injectable } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service';
import { DataStoreService } from '../../core-services/data-store.service';
import { BaseRepository } from '../base-repository';
import { CollectionStringMapperService } from '../../core-services/collectionStringMapper.service';
import { PersonalNote } from 'app/shared/models/users/personal-note';
import { ViewPersonalNote } from 'app/site/users/models/view-personal-note';
import { ViewModelStoreService } from 'app/core/core-services/view-model-store.service';
import { TranslateService } from '@ngx-translate/core';
import { DataSendService } from 'app/core/core-services/data-send.service';
/**
*/
@ -24,9 +25,9 @@ export class PersonalNoteRepositoryService extends BaseRepository<ViewPersonalNo
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
private translate: TranslateService
translate: TranslateService
) {
super(DS, dataSend, mapperService, viewModelStoreService, PersonalNote);
super(DS, dataSend, mapperService, viewModelStoreService, translate, PersonalNote);
}
public getVerboseName = (plural: boolean = false) => {

View File

@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject } from 'rxjs';
import { TranslateService } from '@ngx-translate/core';
import { BaseRepository } from '../base-repository';
@ -22,6 +22,8 @@ import { ViewModelStoreService } from 'app/core/core-services/view-model-store.s
*/
type StringNamingSchema = 'lastCommaFirst' | 'firstSpaceLast';
type SortProperty = 'first_name' | 'last_name' | 'number';
/**
* Repository service for users
*
@ -31,23 +33,36 @@ type StringNamingSchema = 'lastCommaFirst' | 'firstSpaceLast';
providedIn: 'root'
})
export class UserRepositoryService extends BaseRepository<ViewUser, User> {
/**
* The property the incoming data is sorted by
*/
protected sortProperty: SortProperty;
/**
* Constructor for the user repo
*
* @param DS The DataStore
* @param mapperService Maps collection strings to classes
* @param dataSend sending changed objects
* @param translate
* @param httpService
* @param configService
*/
public constructor(
DS: DataStoreService,
dataSend: DataSendService,
mapperService: CollectionStringMapperService,
viewModelStoreService: ViewModelStoreService,
protected dataSend: DataSendService,
private translate: TranslateService,
translate: TranslateService,
private httpService: HttpService,
private configService: ConfigService
) {
super(DS, dataSend, mapperService, viewModelStoreService, User, [Group]);
super(DS, dataSend, mapperService, viewModelStoreService, translate, User, [Group]);
this.sortProperty = this.configService.instant('users_sort_by');
this.configService.get<SortProperty>('users_sort_by').subscribe(conf => {
this.sortProperty = conf;
this.setConfigSortFn();
});
}
public getVerboseName = (plural: boolean = false) => {
@ -219,7 +234,7 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
* Searches and returns Users by full name
*
* @param name
* @returns all users matching that name
* @returns all users matching that name (unsorted)
*/
public getUsersByName(name: string): ViewUser[] {
return this.getViewModelList().filter(user => {
@ -301,43 +316,22 @@ export class UserRepositoryService extends BaseRepository<ViewUser, User> {
}
/**
* @returns the observable for users sorted according to configuration
*
* TODO: This is leading to heavy operations
* Triggers an update for the sort function responsible for the default sorting of data items
*/
public getSortedViewModelListObservable(): Observable<ViewUser[]> {
const subject = new BehaviorSubject<ViewUser[]>([]);
this.getViewModelListObservable().subscribe(users => {
subject.next(this.sortViewUsersByConfig(users));
});
return subject.asObservable();
}
/**
* Sort viewUsers by the configured settings
*
* @param users
* @returns the users sorted by first name, last name or number, according
* to the config setting. Fallthrough and identical cases will be sorted by
* 'short_name'
*
* TODO: That operation is HEAVY
*/
public sortViewUsersByConfig(users: ViewUser[]): ViewUser[] {
const sort = this.configService.instant<'first_name' | 'last_name' | 'number'>('users_sort_by') || 'last_name';
return users.sort((a, b) => {
if (a[sort] && b[sort]) {
if (a[sort] === b[sort]) {
return a.short_name.localeCompare(b.short_name, this.translate.currentLang);
public setConfigSortFn(): void {
this.setSortFunction((a: ViewUser, b: ViewUser) => {
if (a[this.sortProperty] && b[this.sortProperty]) {
if (a[this.sortProperty] === b[this.sortProperty]) {
return this.languageCollator.compare(a.short_name, b.short_name);
} else {
return a[sort].localeCompare(b[sort], this.translate.currentLang);
return this.languageCollator.compare(a[this.sortProperty], b[this.sortProperty]);
}
} else if (a[sort] && !b[sort]) {
} else if (a[this.sortProperty] && !b[this.sortProperty]) {
return -1;
} else if (b[sort]) {
} else if (b[this.sortProperty]) {
return 1;
} else {
return a.short_name.localeCompare(b.short_name);
return this.languageCollator.compare(a.short_name, b.short_name);
}
});
}

View File

@ -118,7 +118,7 @@ export abstract class BaseFilterListService<M extends BaseModel, V extends BaseV
public filter(): Observable<V[]> {
this.repo
.getViewModelListObservable()
.pipe(auditTime(100))
.pipe(auditTime(10))
.subscribe(data => {
this.currentRawData = data;
this.filteredData = this.filterData(data);

View File

@ -50,6 +50,11 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
*/
private sortFn: (a: V, b: V) => number;
/**
* default sorting to use if the client was not initialized before
*/
protected defaultSorting: keyof V = 'id';
/**
* Constructor. Does nothing. TranslateService is used for localeCompeare.
*/
@ -113,7 +118,7 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
* get the property of the viewModel the sorting is based on.
*/
public get sortProperty(): string {
return this.sortOptions.sortProperty as string;
return this.sortOptions ? (this.sortOptions.sortProperty as string) : '';
}
public get isActive(): boolean {
@ -138,7 +143,7 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
* @param option
*/
public getSortIcon(option: OsSortingItem<V>): string {
if (this.sortProperty !== (option.property as string)) {
if (!this.sortProperty || this.sortProperty !== (option.property as string)) {
return '';
}
return this.ascending ? 'arrow_downward' : 'arrow_upward';
@ -156,20 +161,21 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
* Retrieve the currently saved sorting definition from the borwser's
* store
*/
private loadStorageDefinition(): void {
const me = this;
this.store.get('sorting_' + this.name).then(function(sorting: OsSortingDefinition<V> | null): void {
private async loadStorageDefinition(): Promise<void> {
const sorting: OsSortingDefinition<V> | null = await this.store.get('sorting_' + this.name);
if (sorting) {
if (sorting.sortProperty) {
me.sortOptions.sortProperty = sorting.sortProperty;
this.sortOptions.sortProperty = sorting.sortProperty;
if (sorting.sortAscending !== undefined) {
me.sortOptions.sortAscending = sorting.sortAscending;
this.sortOptions.sortAscending = sorting.sortAscending;
}
}
} else {
this.sortOptions.sortProperty = this.defaultSorting;
this.sortOptions.sortAscending = true;
}
me.updateSortFn();
me.doAsyncSorting();
});
this.updateSortFn();
this.doAsyncSorting();
}
/**
@ -197,10 +203,10 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
* Recreates the sorting function. Is supposed to be called on init and
* every time the sorting (property, ascending/descending) or the language changes
*/
private updateSortFn(): void {
protected updateSortFn(): void {
const property = this.sortProperty as string;
const ascending = this.ascending;
const lang = this.translate.currentLang; // TODO: observe and update sorting on change
const intl = new Intl.Collator(this.translate.currentLang); // TODO: observe and update sorting on language change
this.sortFn = function(itemA: V, itemB: V): number {
const firstProperty = ascending ? itemA[property] : itemB[property];
@ -234,16 +240,16 @@ export abstract class BaseSortListService<V extends BaseViewModel> {
if (!firstProperty) {
return 1;
}
return firstProperty.localeCompare(secondProperty, lang);
return intl.compare(firstProperty, secondProperty);
case 'function':
const a = firstProperty();
const b = secondProperty();
return a.localeCompare(b, lang);
return intl.compare(a, b);
case 'object':
if (firstProperty instanceof Date) {
return firstProperty > secondProperty ? 1 : -1;
} else {
return firstProperty.toString().localeCompare(secondProperty.toString(), lang);
return intl.compare(firstProperty.toString(), secondProperty.toString());
}
case 'undefined':
return 1;

View File

@ -140,7 +140,7 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
this.addSpeakerForm = new FormGroup({ user_id: new FormControl([]) });
if (this.currentListOfSpeakers) {
this.projectors = projectorRepo.getViewModelList();
this.projectors = projectorRepo.getSortedViewModelList();
this.updateClosProjector();
projectorRepo.getViewModelListObservable().subscribe(newProjectors => {
this.projectors = newProjectors;
@ -160,11 +160,6 @@ export class ListOfSpeakersComponent extends BaseViewComponent implements OnInit
public ngOnInit(): void {
// load and observe users
this.users = this.userRepository.getViewModelListBehaviorSubject();
this.userRepository.getViewModelListBehaviorSubject().subscribe(newUsers => {
if (this.viewItem) {
this.setSpeakerList(this.viewItem.id);
}
});
// detect changes in the form
this.addSpeakerForm.valueChanges.subscribe(formResult => {

View File

@ -13,13 +13,13 @@ export class ViewItem extends BaseViewModel {
/**
* virtual weight defined by the order in the agenda tree, representing a shortcut to sorting by
* weight, parent_id and the parents' weight(s)
* TODO will be accurate if the viewMotion is observed via {@link getViewModelListObservable}, else, it will be undefined
* TODO will be accurate if the viewMotion is observed via {@link getSortedViewModelListObservable}, else, it will be undefined
*/
public agendaListWeight: number;
/**
* The amount of parents in the agenda list tree.
* TODO will be accurate if the viewMotion is observed via {@link getViewModelListObservable}, else, it will be undefined
* TODO will be accurate if the viewMotion is observed via {@link getSortedViewModelListObservable}, else, it will be undefined
*/
public agendaListLevel: number;
@ -39,6 +39,10 @@ export class ViewItem extends BaseViewModel {
return this.item.item_number;
}
public get title_information(): object {
return this.item.title_information;
}
public get duration(): number {
return this.item.duration;
}

View File

@ -98,9 +98,13 @@ export abstract class ListViewBaseComponent<V extends BaseViewModel, M extends B
* Standard filtering function. Sufficient for most list views but can be overwritten
*/
protected onFilter(): void {
if (this.sortService) {
this.subscriptions.push(
this.filterService.filter().subscribe(filteredData => (this.sortService.data = filteredData))
);
} else {
this.filterService.filter().subscribe(filteredData => (this.dataSource.data = filteredData));
}
}
/**

View File

@ -101,7 +101,7 @@ export class CategoryListComponent extends BaseViewComponent implements OnInit {
*/
public ngOnInit(): void {
super.setTitle('Category');
this.repo.getSortedViewModelListObservable().subscribe(newViewCategories => {
this.repo.getViewModelListObservable().subscribe(newViewCategories => {
this.categories = newViewCategories;
});
}
@ -187,21 +187,7 @@ export class CategoryListComponent extends BaseViewComponent implements OnInit {
* @returns all motions in the category
*/
public motionsInCategory(category: Category): ViewMotion[] {
const coll = new Intl.Collator(this.translate.currentLang);
return this.motionRepo
.getViewModelList()
.filter(m => m.category_id === category.id)
.sort((motion1, motion2) => {
if (motion1.identifier && motion2.identifier) {
return coll.compare(motion1.identifier, motion2.identifier);
} else if (motion1.identifier) {
return 1;
} else if (motion2.identifier) {
return -1;
} else {
return coll.compare(motion1.getTitle(), motion2.getTitle());
}
});
return this.motionRepo.getSortedViewModelList().filter(m => m.category_id === category.id);
}
/**

View File

@ -107,7 +107,6 @@ export class MotionBlockListComponent extends ListViewBaseComponent<ViewMotionBl
this.items = this.itemRepo.getViewModelListBehaviorSubject();
// TODO: Should fall under generic sorting in PR 4411
this.repo.getViewModelListObservable().subscribe(newMotionblocks => {
newMotionblocks.sort((a, b) => (a > b ? 1 : -1));
this.dataSource.data = newMotionblocks;

View File

@ -18,6 +18,7 @@ import { itemVisibilityChoices } from 'app/shared/models/agenda/item';
import { LinenumberingService } from 'app/core/ui-services/linenumbering.service';
import { LocalPermissionsService } from 'app/site/motions/services/local-permissions.service';
import { Mediafile } from 'app/shared/models/mediafiles/mediafile';
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
import { Motion } from 'app/shared/models/motions/motion';
import {
MotionChangeRecommendationComponentData,
@ -49,7 +50,6 @@ import { ViewStatuteParagraph } from 'app/site/motions/models/view-statute-parag
import { ViewTag } from 'app/site/tags/models/view-tag';
import { ViewUnifiedChange } from 'app/shared/models/motions/view-unified-change';
import { TagRepositoryService } from 'app/core/repositories/tags/tag-repository.service';
import { MediafileRepositoryService } from 'app/core/repositories/mediafiles/mediafile-repository.service';
import { WorkflowRepositoryService } from 'app/core/repositories/motions/workflow-repository.service';
import { MotionBlockRepositoryService } from 'app/core/repositories/motions/motion-block-repository.service';
@ -368,6 +368,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
* @param agendaRepo Read out agenda variables
* @param changeRecoRepo Change Recommendation Repository
* @param statuteRepo: Statute Paragraph Repository
* @param mediafileRepo Mediafile Repository
* @param DS The DataStoreService
* @param configService The configuration provider
* @param sanitizer For making HTML SafeHTML
@ -863,7 +864,7 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
const content = this.motion.getTitle();
if (await this.promptService.open(title, content)) {
await this.repo.delete(this.motion);
this.router.navigate(['./motions/']);
this.router.navigate(['../motions/']);
}
}
@ -1112,18 +1113,6 @@ export class MotionDetailComponent extends BaseViewComponent implements OnInit,
* then appending motion without identifiers sorted by title
*/
public setSurroundingMotions(): void {
// TODO: that operation is HEAVY
this.motionObserver.value.sort((a, b) => {
if (a.identifier && b.identifier) {
return a.identifier.localeCompare(b.identifier, this.translate.currentLang);
} else if (a.identifier) {
return 1;
} else if (b.identifier) {
return -1;
} else {
return a.title.localeCompare(b.title, this.translate.currentLang);
}
});
const indexOfCurrent = this.motionObserver.value.findIndex(motion => {
return motion === this.motion;
});

View File

@ -61,7 +61,7 @@ export class MotionExportDialogComponent implements OnInit {
* @returns a list of availavble commentSections
*/
public get commentsToExport(): ViewMotionCommentSection[] {
return this.commentRepo.getViewModelList();
return this.commentRepo.getSortedViewModelList();
}
/**
* Hold the default lnMode. Will be set by the constructor.

View File

@ -128,7 +128,7 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion, Motio
.get<string>('motions_recommendations_by')
.subscribe(recommender => (this.recomendationEnabled = !!recommender));
this.motionBlockRepo.getViewModelListObservable().subscribe(mBs => (this.motionBlocks = mBs));
this.categoryRepo.getSortedViewModelListObservable().subscribe(cats => (this.categories = cats));
this.categoryRepo.getViewModelListObservable().subscribe(cats => (this.categories = cats));
this.tagRepo.getViewModelListObservable().subscribe(tags => (this.tags = tags));
this.workflowRepo.getViewModelListObservable().subscribe(wfs => (this.workflows = wfs));
this.setFulltextFilter();

View File

@ -182,7 +182,7 @@ export class MotionFilterListService extends BaseFilterListService<Motion, ViewM
* Subscibes to changing Categories, and updates the filter accordingly
*/
private subscribeCategories(): void {
this.categoryRepo.getSortedViewModelListObservable().subscribe(categories => {
this.categoryRepo.getViewModelListObservable().subscribe(categories => {
const categoryOptions: OsFilterOptions = categories.map(cat => ({
condition: cat.id,
label: cat.prefixedName,

View File

@ -74,7 +74,7 @@ export class MotionMultiselectService {
*/
public async moveToItem(motions: ViewMotion[]): Promise<void> {
const title = this.translate.instant('This will move all selected motions as childs to:');
const choices: (Displayable & Identifiable)[] = this.agendaRepo.getViewModelList();
const choices: (Displayable & Identifiable)[] = this.agendaRepo.getSortedViewModelList();
const selectedChoice = await this.choiceService.open(title, choices);
if (selectedChoice) {
const requestData = {
@ -139,7 +139,7 @@ export class MotionMultiselectService {
const clearChoice = this.translate.instant('No category');
const selectedChoice = await this.choiceService.open(
title,
this.categoryRepo.sortViewCategoriesByConfig(this.categoryRepo.getViewModelList()),
this.categoryRepo.getSortedViewModelList(),
false,
null,
clearChoice
@ -164,7 +164,12 @@ export class MotionMultiselectService {
'This will add or remove the following submitters for all selected motions:'
);
const choices = [this.translate.instant('Add'), this.translate.instant('Remove')];
const selectedChoice = await this.choiceService.open(title, this.userRepo.getViewModelList(), true, choices);
const selectedChoice = await this.choiceService.open(
title,
this.userRepo.getSortedViewModelList(),
true,
choices
);
if (selectedChoice && selectedChoice.action === choices[0]) {
const requestData = motions.map(motion => {
let submitterIds = [...motion.sorted_submitters_id, ...(selectedChoice.items as number[])];
@ -200,7 +205,12 @@ export class MotionMultiselectService {
this.translate.instant('Remove'),
this.translate.instant('Clear tags')
];
const selectedChoice = await this.choiceService.open(title, this.tagRepo.getViewModelList(), true, choices);
const selectedChoice = await this.choiceService.open(
title,
this.tagRepo.getSortedViewModelList(),
true,
choices
);
if (selectedChoice && selectedChoice.action === choices[0]) {
const requestData = motions.map(motion => {
let tagIds = [...motion.tags_id, ...(selectedChoice.items as number[])];
@ -242,7 +252,7 @@ export class MotionMultiselectService {
const clearChoice = this.translate.instant('Clear motion block');
const selectedChoice = await this.choiceService.open(
title,
this.motionBlockRepo.getViewModelList(),
this.motionBlockRepo.getSortedViewModelList(),
false,
null,
clearChoice
@ -280,7 +290,7 @@ export class MotionMultiselectService {
// insert after chosen
const olderSibling = this.repo.getViewModel(selectedChoice.items as number);
const parentId = olderSibling ? olderSibling.sort_parent_id : null;
const siblings = this.repo.getViewModelList().filter(motion => motion.sort_parent_id === parentId);
const siblings = allMotions.filter(motion => motion.sort_parent_id === parentId);
const idx = siblings.findIndex(sib => sib.id === olderSibling.id);
const before = siblings.slice(0, idx + 1);
const after = siblings.slice(idx + 1);

View File

@ -48,7 +48,7 @@ export class MotionPdfService {
*
* @param translate handle translations
* @param motionRepo get parent motions
* @param statureRepo To get formated stature paragraphs
* @param statuteRepo To get formated stature paragraphs
* @param changeRecoRepo to get the change recommendations
* @param configService Read config variables
* @param htmlToPdfService To convert HTML text into pdfmake doc def
@ -59,7 +59,7 @@ export class MotionPdfService {
public constructor(
private translate: TranslateService,
private motionRepo: MotionRepositoryService,
private statureRepo: StatuteParagraphRepositoryService,
private statuteRepo: StatuteParagraphRepositoryService,
private changeRecoRepo: ChangeRecommendationRepositoryService,
private configService: ConfigService,
private htmlToPdfService: HtmlToPdfService,
@ -123,7 +123,7 @@ export class MotionPdfService {
}
if (infoToExport && infoToExport.includes('allcomments')) {
commentsToExport = this.commentRepo.getViewModelList().map(vm => vm.id);
commentsToExport = this.commentRepo.getSortedViewModelList().map(vm => vm.id);
}
if (commentsToExport) {
motionPdfContent.push(this.createComments(motion, commentsToExport));
@ -495,7 +495,7 @@ export class MotionPdfService {
}
} else if (motion.isStatuteAmendment()) {
// statute amendments
const statutes = this.statureRepo.getViewModelList();
const statutes = this.statuteRepo.getViewModelList();
motionText = this.motionRepo.formatStatuteAmendment(statutes, motion, lineLength);
} else {
// lead motion or normal amendments

View File

@ -37,6 +37,6 @@ export class MotionSortListService extends BaseSortListService<ViewMotion> {
*/
public constructor(translate: TranslateService, store: StorageService, config: ConfigService) {
super(translate, store);
this.sortOptions.sortProperty = config.instant<keyof ViewMotion>('motions_motions_sorting');
this.defaultSorting = config.instant<keyof ViewMotion>('motions_motions_sorting');
}
}

View File

@ -19,7 +19,7 @@
Reference projector for current list of speakers:
</span>&nbsp;
<mat-form-field>
<mat-select [disabled]="!!editId" [value]="projectors[0].reference_projector_id" (selectionChange)="onSelectReferenceProjector($event)">
<mat-select [disabled]="!!editId" [value]="projectors.length ? projectors[0].reference_projector_id : null" (selectionChange)="onSelectReferenceProjector($event)">
<mat-option *ngFor="let projector of projectors" [value]="projector.id">
{{ projector.getTitle() | translate }}
</mat-option>

View File

@ -120,6 +120,7 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
*/
public ngOnInit(): void {
super.setTitle('Projectors');
this.projectors = this.repo.getViewModelList();
this.repo.getViewModelListObservable().subscribe(projectors => (this.projectors = projectors));
}

View File

@ -64,7 +64,7 @@ export class ProjectorMessageListComponent extends BaseViewComponent implements
*/
public ngOnInit(): void {
super.setTitle('Messages');
this.messages = this.repo.getViewModelList();
this.messages = this.repo.getSortedViewModelList();
this.repo.getViewModelListObservable().subscribe(messages => (this.messages = messages));
}

View File

@ -139,7 +139,7 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit {
}
this.createForm();
this.groups = this.groupRepo.getViewModelList().filter(group => group.id !== 1);
this.groups = this.groupRepo.getSortedViewModelList().filter(group => group.id !== 1);
this.groupRepo
.getViewModelListObservable()
.subscribe(groups => (this.groups = groups.filter(group => group.id !== 1)));

View File

@ -167,7 +167,8 @@ export class UserListComponent extends ListViewBaseComponent<ViewUser, User> imp
this.setFulltextFilter();
// Initialize the groups
this.groups = this.groupRepo.getViewModelList().filter(group => group.id !== 1);
this.groups = this.groupRepo.getSortedViewModelList().filter(group => group.id !== 1);
this.groupRepo.getViewModelListObservable().subscribe(groups => (this.groups = groups));
}
/**

View File

@ -36,6 +36,6 @@ export class UserSortListService extends BaseSortListService<ViewUser> {
*/
public constructor(translate: TranslateService, store: StorageService, config: ConfigService) {
super(translate, store);
this.sortOptions.sortProperty = config.instant<keyof ViewUser>('motions_motions_sorting');
this.defaultSorting = config.instant<keyof ViewUser>('users_sort_by');
}
}