Merge pull request #4033 from FinnStutzenstein/motion_virtual_sort
OpenSlides ♥ Trees
This commit is contained in:
commit
fed085c319
17
client/src/app/core/services/tree.service.spec.ts
Normal file
17
client/src/app/core/services/tree.service.spec.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { TestBed, inject } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { TreeService } from './tree.service';
|
||||||
|
import { E2EImportsModule } from '../../../e2e-imports.module';
|
||||||
|
|
||||||
|
describe('TreeService', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [E2EImportsModule],
|
||||||
|
providers: [TreeService]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', inject([TreeService], (service: TreeService) => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
}));
|
||||||
|
});
|
142
client/src/app/core/services/tree.service.ts
Normal file
142
client/src/app/core/services/tree.service.ts
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { OpenSlidesComponent } from 'app/openslides.component';
|
||||||
|
import { Displayable } from 'app/shared/models/base/displayable';
|
||||||
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of nodes in our tree. Saves the displayed name, the id, the element and children to build a full tree.
|
||||||
|
*/
|
||||||
|
export interface OSTreeNode<T> {
|
||||||
|
name: string;
|
||||||
|
id: number;
|
||||||
|
item: T;
|
||||||
|
children?: OSTreeNode<T>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This services handles all operations belonging to trees. It can build trees of plain lists (giving the weight
|
||||||
|
* and parentId property) and traverse the trees in pre-order.
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class TreeService extends OpenSlidesComponent {
|
||||||
|
/**
|
||||||
|
* Yes, a constructor.
|
||||||
|
*/
|
||||||
|
public constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the weight casted to a number from a given model.
|
||||||
|
*
|
||||||
|
* @param item The model to get the weight from.
|
||||||
|
* @param key
|
||||||
|
* @returns the weight of the model
|
||||||
|
*/
|
||||||
|
private getAttributeAsNumber<T extends Identifiable & Displayable>(item: T, key: keyof T): number {
|
||||||
|
return (<any>item[key]) as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build our representation of a tree node given the model and optional children
|
||||||
|
* to append to this node.
|
||||||
|
*
|
||||||
|
* @param item The model to create a node of.
|
||||||
|
* @param children Optional children to append to this node.
|
||||||
|
* @returns The created node.
|
||||||
|
*/
|
||||||
|
private buildTreeNode<T extends Identifiable & Displayable>(item: T, children?: OSTreeNode<T>[]): OSTreeNode<T> {
|
||||||
|
return {
|
||||||
|
name: item.getTitle(),
|
||||||
|
id: item.id,
|
||||||
|
item: item,
|
||||||
|
children: children
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a tree from the given items on the relations between items with weight and parentId
|
||||||
|
*
|
||||||
|
* @param items All items to traverse
|
||||||
|
* @param weightKey The key giving access to the weight property
|
||||||
|
* @param parentIdKey The key giving access to the parentId property
|
||||||
|
* @returns An iterator for all items in the right order.
|
||||||
|
*/
|
||||||
|
public makeTree<T extends Identifiable & Displayable>(
|
||||||
|
items: T[],
|
||||||
|
weightKey: keyof T,
|
||||||
|
parentIdKey: keyof T
|
||||||
|
): OSTreeNode<T>[] {
|
||||||
|
// Sort items after their weight
|
||||||
|
items.sort((a, b) => this.getAttributeAsNumber(a, weightKey) - this.getAttributeAsNumber(b, weightKey));
|
||||||
|
// Build a dict with all children (dict-value) to a specific
|
||||||
|
// item id (dict-key).
|
||||||
|
const children: { [parendId: number]: T[] } = {};
|
||||||
|
items.forEach(model => {
|
||||||
|
if (model[parentIdKey]) {
|
||||||
|
const parentId = this.getAttributeAsNumber(model, parentIdKey);
|
||||||
|
if (children[parentId]) {
|
||||||
|
children[parentId].push(model);
|
||||||
|
} else {
|
||||||
|
children[parentId] = [model];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Recursive function that generates a nested list with all
|
||||||
|
// items with there children
|
||||||
|
const getChildren: (_models?: T[]) => OSTreeNode<T>[] = _models => {
|
||||||
|
if (!_models) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nodes: OSTreeNode<T>[] = [];
|
||||||
|
_models.forEach(_model => {
|
||||||
|
nodes.push(this.buildTreeNode(_model, getChildren(children[_model.id])));
|
||||||
|
});
|
||||||
|
return nodes;
|
||||||
|
};
|
||||||
|
// Generates the list of root items (with no parents)
|
||||||
|
const parentItems = items.filter(model => !this.getAttributeAsNumber(model, parentIdKey));
|
||||||
|
return getChildren(parentItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses the given tree in pre order.
|
||||||
|
*
|
||||||
|
* @param tree The tree to traverse
|
||||||
|
* @returns An iterator for all items in the right order.
|
||||||
|
*/
|
||||||
|
public *traverseTree<T>(tree: OSTreeNode<T>[]): Iterator<T> {
|
||||||
|
const nodesToVisit = tree.reverse();
|
||||||
|
while (nodesToVisit.length > 0) {
|
||||||
|
const node = nodesToVisit.pop();
|
||||||
|
if (node.children) {
|
||||||
|
node.children.reverse().forEach(n => {
|
||||||
|
nodesToVisit.push(n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
yield node.item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses items in pre-order givem (implicit) by the weight and parentId.
|
||||||
|
*
|
||||||
|
* Just builds the tree with `makeTree` and get the iterator from `traverseTree`.
|
||||||
|
*
|
||||||
|
* @param items All items to traverse
|
||||||
|
* @param weightKey The key giving access to the weight property
|
||||||
|
* @param parentIdKey The key giving access to the parentId property
|
||||||
|
* @returns An iterator for all items in the right order.
|
||||||
|
*/
|
||||||
|
public traverseItems<T extends Identifiable & Displayable>(
|
||||||
|
items: T[],
|
||||||
|
weightKey: keyof T,
|
||||||
|
parentIdKey: keyof T
|
||||||
|
): Iterator<T> {
|
||||||
|
const tree = this.makeTree(items, weightKey, parentIdKey);
|
||||||
|
return this.traverseTree(tree);
|
||||||
|
}
|
||||||
|
}
|
@ -7,24 +7,16 @@ import { Subscription, Observable } from 'rxjs';
|
|||||||
|
|
||||||
import { Identifiable } from 'app/shared/models/base/identifiable';
|
import { Identifiable } from 'app/shared/models/base/identifiable';
|
||||||
import { Displayable } from 'app/shared/models/base/displayable';
|
import { Displayable } from 'app/shared/models/base/displayable';
|
||||||
|
import { OSTreeNode, TreeService } from 'app/core/services/tree.service';
|
||||||
/**
|
|
||||||
* An representation of our nodes. Saves the displayed name, the id and children to build a full tree.
|
|
||||||
*/
|
|
||||||
interface OSTreeNode {
|
|
||||||
name: string;
|
|
||||||
id: number;
|
|
||||||
children?: OSTreeNode[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The data representation for the sort event.
|
* The data representation for the sort event.
|
||||||
*/
|
*/
|
||||||
export interface OSTreeSortEvent {
|
export interface OSTreeSortEvent<T> {
|
||||||
/**
|
/**
|
||||||
* Gives all nodes to be inserted below the parent_id.
|
* Gives all nodes to be inserted below the parent_id.
|
||||||
*/
|
*/
|
||||||
nodes: OSTreeNode[];
|
nodes: OSTreeNode<T>[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the parent id for the nodes array. Do not provide it, if it's the
|
* Provides the parent id for the nodes array. Do not provide it, if it's the
|
||||||
@ -62,8 +54,8 @@ export class SortingTreeComponent<T extends Identifiable & Displayable> implemen
|
|||||||
if (this.modelSubscription) {
|
if (this.modelSubscription) {
|
||||||
this.modelSubscription.unsubscribe();
|
this.modelSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
this.modelSubscription = models.pipe(auditTime(100)).subscribe(m => {
|
this.modelSubscription = models.pipe(auditTime(10)).subscribe(items => {
|
||||||
this.nodes = this.makeTree(m);
|
this.nodes = this.treeService.makeTree(items, this.weightKey, this.parentIdKey);
|
||||||
setTimeout(() => this.tree.treeModel.expandAll());
|
setTimeout(() => this.tree.treeModel.expandAll());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -93,7 +85,7 @@ export class SortingTreeComponent<T extends Identifiable & Displayable> implemen
|
|||||||
* sorted part of the tree.
|
* sorted part of the tree.
|
||||||
*/
|
*/
|
||||||
@Output()
|
@Output()
|
||||||
public readonly sort = new EventEmitter<OSTreeSortEvent>();
|
public readonly sort = new EventEmitter<OSTreeSortEvent<T>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for the tree. As a default drag and drop is allowed.
|
* Options for the tree. As a default drag and drop is allowed.
|
||||||
@ -112,12 +104,12 @@ export class SortingTreeComponent<T extends Identifiable & Displayable> implemen
|
|||||||
/**
|
/**
|
||||||
* This is our actual tree represented by our own nodes.
|
* This is our actual tree represented by our own nodes.
|
||||||
*/
|
*/
|
||||||
public nodes: OSTreeNode[] = [];
|
public nodes: OSTreeNode<T>[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor. Adds the eventhandler for the drop event to the tree.
|
* Constructor. Adds the eventhandler for the drop event to the tree.
|
||||||
*/
|
*/
|
||||||
public constructor() {
|
public constructor(private treeService: TreeService) {
|
||||||
this.treeOptions.actionMapping = {
|
this.treeOptions.actionMapping = {
|
||||||
mouse: {
|
mouse: {
|
||||||
drop: this.drop.bind(this)
|
drop: this.drop.bind(this)
|
||||||
@ -149,10 +141,14 @@ export class SortingTreeComponent<T extends Identifiable & Displayable> implemen
|
|||||||
* @param param3 The previous and new position os the node
|
* @param param3 The previous and new position os the node
|
||||||
*/
|
*/
|
||||||
private drop(tree: TreeModel, node: TreeNode, $event: any, { from, to }: { from: any; to: any }): void {
|
private drop(tree: TreeModel, node: TreeNode, $event: any, { from, to }: { from: any; to: any }): void {
|
||||||
// check if dropped itself
|
// check if dropped itself by going the tree upwards and check, if one of them is the "from"-node.
|
||||||
if (from.id === to.parent.id) {
|
let parent = to.parent;
|
||||||
|
while (parent !== null) {
|
||||||
|
if (from.id === parent.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
parent = parent.parent;
|
||||||
|
}
|
||||||
|
|
||||||
let parentId;
|
let parentId;
|
||||||
const fromArray = from.parent.data.children;
|
const fromArray = from.parent.data.children;
|
||||||
@ -165,86 +161,4 @@ export class SortingTreeComponent<T extends Identifiable & Displayable> implemen
|
|||||||
transferArrayItem(fromArray, to.parent.data.children, from.index, to.index);
|
transferArrayItem(fromArray, to.parent.data.children, from.index, to.index);
|
||||||
this.sort.emit({ nodes: to.parent.data.children, parent_id: parentId });
|
this.sort.emit({ nodes: to.parent.data.children, parent_id: parentId });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the weight casted to a number from a given model.
|
|
||||||
*
|
|
||||||
* @param model The model to get the weight from.
|
|
||||||
* @returns the weight of the model
|
|
||||||
*/
|
|
||||||
private getWeight(model: T): number {
|
|
||||||
return (<any>model[this.weightKey]) as number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the parent id casted to a number from a given model.
|
|
||||||
*
|
|
||||||
* @param model The model to get the parent id from.
|
|
||||||
* @returns the parent id of the model
|
|
||||||
*/
|
|
||||||
private getParentId(model: T): number {
|
|
||||||
return (<any>model[this.parentIdKey]) as number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build our representation of a tree node given the model and optional children
|
|
||||||
* to append to this node.
|
|
||||||
*
|
|
||||||
* @param model The model to create a node of.
|
|
||||||
* @param children Optional children to append to this node.
|
|
||||||
* @returns The created node.
|
|
||||||
*/
|
|
||||||
private buildTreeNode(model: T, children?: OSTreeNode[]): OSTreeNode {
|
|
||||||
return {
|
|
||||||
name: model.getTitle(),
|
|
||||||
id: model.id,
|
|
||||||
children: children
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a tree from the given models with their parent and weight properties.
|
|
||||||
*
|
|
||||||
* @param models All models to build the tree of
|
|
||||||
* @returns The first layer of the tree given as an array of nodes, because this tree may not have a single root.
|
|
||||||
*/
|
|
||||||
private makeTree(models: T[]): OSTreeNode[] {
|
|
||||||
// copy references to avoid side effects:
|
|
||||||
models = models.map(x => x);
|
|
||||||
|
|
||||||
// Sort items after there weight
|
|
||||||
models.sort((a, b) => this.getWeight(a) - this.getWeight(b));
|
|
||||||
|
|
||||||
// Build a dict with all children (dict-value) to a specific
|
|
||||||
// item id (dict-key).
|
|
||||||
const children: { [parendId: number]: T[] } = {};
|
|
||||||
|
|
||||||
models.forEach(model => {
|
|
||||||
if (model[this.parentIdKey]) {
|
|
||||||
const parentId = this.getParentId(model);
|
|
||||||
if (children[parentId]) {
|
|
||||||
children[parentId].push(model);
|
|
||||||
} else {
|
|
||||||
children[parentId] = [model];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Recursive function that generates a nested list with all
|
|
||||||
// items with there children
|
|
||||||
const getChildren: (_models?: T[]) => OSTreeNode[] = _models => {
|
|
||||||
if (!_models) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const nodes: OSTreeNode[] = [];
|
|
||||||
_models.forEach(_model => {
|
|
||||||
nodes.push(this.buildTreeNode(_model, getChildren(children[_model.id])));
|
|
||||||
});
|
|
||||||
return nodes;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generates the list of root items (with no parents)
|
|
||||||
const parentItems = models.filter(model => !this.getParentId(model));
|
|
||||||
return getChildren(parentItems);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { BaseModel, ModelConstructor } from '../../shared/models/base/base-model
|
|||||||
import { CollectionStringModelMapperService } from '../../core/services/collectionStringModelMapper.service';
|
import { CollectionStringModelMapperService } from '../../core/services/collectionStringModelMapper.service';
|
||||||
import { DataStoreService } from '../../core/services/data-store.service';
|
import { DataStoreService } from '../../core/services/data-store.service';
|
||||||
import { Identifiable } from '../../shared/models/base/identifiable';
|
import { Identifiable } from '../../shared/models/base/identifiable';
|
||||||
|
import { auditTime } from 'rxjs/operators';
|
||||||
|
|
||||||
export abstract class BaseRepository<V extends BaseViewModel, M extends BaseModel> extends OpenSlidesComponent {
|
export abstract class BaseRepository<V extends BaseViewModel, M extends BaseModel> extends OpenSlidesComponent {
|
||||||
/**
|
/**
|
||||||
@ -20,7 +21,7 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
|||||||
/**
|
/**
|
||||||
* Observable subject for the whole list
|
* Observable subject for the whole list
|
||||||
*/
|
*/
|
||||||
protected viewModelListSubject: BehaviorSubject<V[]> = new BehaviorSubject<V[]>(null);
|
protected readonly viewModelListSubject: BehaviorSubject<V[]> = new BehaviorSubject<V[]>([]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -42,9 +43,12 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
|||||||
// Populate the local viewModelStore with ViewModel Objects.
|
// Populate the local viewModelStore with ViewModel Objects.
|
||||||
this.DS.getAll(this.baseModelCtor).forEach((model: M) => {
|
this.DS.getAll(this.baseModelCtor).forEach((model: M) => {
|
||||||
this.viewModelStore[model.id] = this.createViewModel(model);
|
this.viewModelStore[model.id] = this.createViewModel(model);
|
||||||
|
});
|
||||||
|
// Update the list and then all models on their own
|
||||||
|
this.updateViewModelListObservable();
|
||||||
|
this.DS.getAll(this.baseModelCtor).forEach((model: M) => {
|
||||||
this.updateViewModelObservable(model.id);
|
this.updateViewModelObservable(model.id);
|
||||||
});
|
});
|
||||||
this.updateViewModelListObservable();
|
|
||||||
|
|
||||||
// Could be raise in error if the root injector is not known
|
// Could be raise in error if the root injector is not known
|
||||||
this.DS.changeObservable.subscribe(model => {
|
this.DS.changeObservable.subscribe(model => {
|
||||||
@ -131,10 +135,14 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* return the Observable of the whole store
|
* 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.
|
||||||
*/
|
*/
|
||||||
public getViewModelListObservable(): Observable<V[]> {
|
public getViewModelListObservable(): Observable<V[]> {
|
||||||
return this.viewModelListSubject.asObservable();
|
return this.viewModelListSubject.asObservable().pipe(auditTime(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,8 +56,11 @@ export class CallListComponent extends BaseViewComponent {
|
|||||||
/**
|
/**
|
||||||
* Handler for the sort event. The data to change is given to
|
* Handler for the sort event. The data to change is given to
|
||||||
* the repo, sending it to the server.
|
* the repo, sending it to the server.
|
||||||
|
*
|
||||||
|
* @param data The event data. The representation fits the servers requirements, so it can directly
|
||||||
|
* be send to the server via the repository.
|
||||||
*/
|
*/
|
||||||
public sort(data: OSTreeSortEvent): void {
|
public sort(data: OSTreeSortEvent<ViewMotion>): void {
|
||||||
this.motionRepo.sortMotions(data).then(null, this.raiseError);
|
this.motionRepo.sortMotions(data).then(null, this.raiseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,8 +75,8 @@ export class MotionListComponent extends ListViewBaseComponent<ViewMotion> imple
|
|||||||
this.repo.getViewModelListObservable().subscribe(newMotions => {
|
this.repo.getViewModelListObservable().subscribe(newMotions => {
|
||||||
// TODO: This is for testing purposes. Can be removed with #3963
|
// TODO: This is for testing purposes. Can be removed with #3963
|
||||||
this.dataSource.data = newMotions.sort((a, b) => {
|
this.dataSource.data = newMotions.sort((a, b) => {
|
||||||
if (a.weight !== b.weight) {
|
if (a.callListWeight !== b.callListWeight) {
|
||||||
return a.weight - b.weight;
|
return a.callListWeight - b.callListWeight;
|
||||||
} else {
|
} else {
|
||||||
return a.id - b.id;
|
return a.id - b.id;
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,12 @@ export class ViewMotion extends BaseViewModel {
|
|||||||
*/
|
*/
|
||||||
public highlightedLine: number;
|
public highlightedLine: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is set by the repository; this is the order of the flat call list given by
|
||||||
|
* the properties weight and sort_parent_id
|
||||||
|
*/
|
||||||
|
public callListWeight: number;
|
||||||
|
|
||||||
public get motion(): Motion {
|
public get motion(): Motion {
|
||||||
return this._motion;
|
return this._motion;
|
||||||
}
|
}
|
||||||
@ -179,7 +185,7 @@ export class ViewMotion extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get agendaSpeakerAmount(): number {
|
public get agendaSpeakerAmount(): number {
|
||||||
return this.item ? this.item.speakerAmount : null
|
return this.item ? this.item.speakerAmount : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
@ -189,7 +195,7 @@ export class ViewMotion extends BaseViewModel {
|
|||||||
supporters?: User[],
|
supporters?: User[],
|
||||||
workflow?: Workflow,
|
workflow?: Workflow,
|
||||||
state?: WorkflowState,
|
state?: WorkflowState,
|
||||||
item?: Item,
|
item?: Item
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { DataSendService } from '../../../core/services/data-send.service';
|
import { DataSendService } from '../../../core/services/data-send.service';
|
||||||
import { Motion } from '../../../shared/models/motions/motion';
|
import { Motion } from '../../../shared/models/motions/motion';
|
||||||
import { User } from '../../../shared/models/users/user';
|
import { User } from '../../../shared/models/users/user';
|
||||||
@ -20,6 +23,7 @@ import { CollectionStringModelMapperService } from '../../../core/services/colle
|
|||||||
import { HttpService } from 'app/core/services/http.service';
|
import { HttpService } from 'app/core/services/http.service';
|
||||||
import { Item } from 'app/shared/models/agenda/item';
|
import { Item } from 'app/shared/models/agenda/item';
|
||||||
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
import { OSTreeSortEvent } from 'app/shared/components/sorting-tree/sorting-tree.component';
|
||||||
|
import { TreeService } from 'app/core/services/tree.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Repository Services for motions (and potentially categories)
|
* Repository Services for motions (and potentially categories)
|
||||||
@ -51,7 +55,8 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
private dataSend: DataSendService,
|
private dataSend: DataSendService,
|
||||||
private httpService: HttpService,
|
private httpService: HttpService,
|
||||||
private readonly lineNumbering: LinenumberingService,
|
private readonly lineNumbering: LinenumberingService,
|
||||||
private readonly diff: DiffService
|
private readonly diff: DiffService,
|
||||||
|
private treeService: TreeService
|
||||||
) {
|
) {
|
||||||
super(DS, mapperService, Motion, [Category, User, Workflow, Item]);
|
super(DS, mapperService, Motion, [Category, User, Workflow, Item]);
|
||||||
}
|
}
|
||||||
@ -77,6 +82,19 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
return new ViewMotion(motion, category, submitters, supporters, workflow, state, item);
|
return new ViewMotion(motion, category, submitters, supporters, workflow, state, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getViewModelListObservable(): Observable<ViewMotion[]> {
|
||||||
|
return super.getViewModelListObservable().pipe(
|
||||||
|
tap(motions => {
|
||||||
|
const iterator = this.treeService.traverseItems(motions, 'weight', 'sort_parent_id');
|
||||||
|
let m: IteratorResult<ViewMotion>;
|
||||||
|
let virtualWeightCounter = 0;
|
||||||
|
while (!(m = iterator.next()).done) {
|
||||||
|
m.value.callListWeight = virtualWeightCounter++;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a motion
|
* Creates a motion
|
||||||
* Creates a (real) motion with patched data and delegate it
|
* Creates a (real) motion with patched data and delegate it
|
||||||
@ -146,7 +164,7 @@ export class MotionRepositoryService extends BaseRepository<ViewMotion, Motion>
|
|||||||
*
|
*
|
||||||
* @param data The reordered data from the sorting
|
* @param data The reordered data from the sorting
|
||||||
*/
|
*/
|
||||||
public async sortMotions(data: OSTreeSortEvent): Promise<void> {
|
public async sortMotions(data: OSTreeSortEvent<ViewMotion>): Promise<void> {
|
||||||
const url = '/rest/motions/motion/sort/';
|
const url = '/rest/motions/motion/sort/';
|
||||||
await this.httpService.post(url, data);
|
await this.httpService.post(url, data);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user