Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve view model list equality checks #4491

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions client/src/app/gateways/repositories/base-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,6 @@ export abstract class BaseRepository<V extends BaseViewModel, M extends BaseMode
this.tapViewModels(Object.values(this.viewModelStore));
if (changedModels) {
await this.initChangeBasedResorting(newModels, updatedModels, newViewModels, updatedViewModels);
for (const viewModel of updatedViewModels) {
viewModel.viewModelUpdateTimestamp = Date.now();
}
}
},
type: PipelineActionType.General,
Expand Down
67 changes: 66 additions & 1 deletion client/src/app/infrastructure/utils/functions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ import {
stripHtmlTags,
toBase64,
toBoolean,
toDecimal
toDecimal,
viewModelEqual,
viewModelListEqual
} from './functions';
import { copy } from './transform-functions';

Expand Down Expand Up @@ -756,4 +758,67 @@ describe(`utils: functions`, () => {
expect(findIndexInSortedArray([1, 1, 1, 1, 1, 1, 1], 1, (a, b) => a - b)).toBe(0);
});
});

describe(`viewModelListEqual`, () => {
const els: any[] = [
{ viewModelUpdateTimestamp: 1234, id: 1 },
{ viewModelUpdateTimestamp: 1235, id: 1 },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

els[1] not used in test, please remove it

{ viewModelUpdateTimestamp: 1234, id: 2 },
{ viewModelUpdateTimestamp: 1235, id: 2 },
{ viewModelUpdateTimestamp: 1234, id: 3 }
];

it(`detects change via id switch`, () => {
const l1 = [els[0], els[2]];
const l2 = [els[0], els[4]];

expect(viewModelListEqual(l1, l2)).toBeFalse();
});

it(`detects change via change date switch`, () => {
const l1 = [els[0], els[2]];
const l2 = [els[0], els[3]];

expect(viewModelListEqual(l1, l2)).toBeFalse();
});

it(`detects change via size change`, () => {
const l1 = [els[0], els[2]];
const l2 = [els[0]];

expect(viewModelListEqual(l1, l2)).toBeFalse();
});

it(`detects equality`, () => {
const l1 = [els[0], els[2], els[4]];
const l2 = [els[0], els[2], els[4]];

expect(viewModelListEqual(l1, l2)).toBeTrue();
});
});

describe(`viewModelEqual`, () => {
const els: any[] = [
{ viewModelUpdateTimestamp: 1234, id: 1 },
{ viewModelUpdateTimestamp: 1235, id: 1 },
{ viewModelUpdateTimestamp: 1234, id: 2 },
{ viewModelUpdateTimestamp: 1234, id: 1 }
];

it(`detects change via id switch`, () => {
expect(viewModelEqual(els[0], els[2])).toBeFalse();
});

it(`detects change via change date switch`, () => {
expect(viewModelEqual(els[0], els[1])).toBeFalse();
});

it(`detects change via empty`, () => {
expect(viewModelEqual(els[0], null)).toBeFalse();
});

it(`detects equality`, () => {
expect(viewModelEqual(els[0], els[3])).toBeTrue();
});
});
});
24 changes: 24 additions & 0 deletions client/src/app/infrastructure/utils/functions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Identifiable } from 'src/app/domain/interfaces';
import { BaseModel } from 'src/app/domain/models/base/base-model';
import { BaseViewModel } from 'src/app/site/base/base-view-model';

import { Decimal, Id } from '../../domain/definitions/key-types';

Expand Down Expand Up @@ -521,3 +523,25 @@ export function findIndexInSortedArray<T>(array: T[], toFind: T, compareFn: (a:
}
return -1;
}

export function viewModelListEqual<M extends BaseModel>(l1: BaseViewModel<M>[], l2: BaseViewModel<M>[]): boolean {
if (l1?.length !== l2?.length) {
return false;
}

for (let i = 0; i < l1.length; i++) {
if (!viewModelEqual(l1[i], l2[i])) {
return false;
}
}

return true;
}

export function viewModelEqual<M extends BaseModel>(vm1: BaseViewModel<M>, vm2: BaseViewModel<M>): boolean {
if (!vm1 || !vm2) {
return false;
}

return vm1.viewModelUpdateTimestamp === vm2.viewModelUpdateTimestamp && vm1.id === vm2.id;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { TranslateService } from '@ngx-translate/core';
import { combineLatest, distinctUntilChanged, Subscription } from 'rxjs';
import { Id } from 'src/app/domain/definitions/key-types';
import { Permission } from 'src/app/domain/definitions/permission';
import { viewModelListEqual } from 'src/app/infrastructure/utils';
import { VoteControllerService } from 'src/app/site/pages/meetings/modules/poll/services/vote-controller.service';
import { VotingService } from 'src/app/site/pages/meetings/modules/poll/services/voting.service';
import { ViewPoll } from 'src/app/site/pages/meetings/pages/polls';
Expand Down Expand Up @@ -43,14 +44,7 @@ export class VotingBannerService {
) {
combineLatest([
this.activeMeeting.meetingIdObservable.pipe(distinctUntilChanged()),
this.activePolls.activePollsObservable.pipe(
distinctUntilChanged((previous, current) => {
const prevStarted = previous.map(p => p.id);
const currStarted = current.map(p => p.id);

return prevStarted.length === currStarted.length && currStarted.equals(prevStarted);
})
),
this.activePolls.activePollsObservable.pipe(distinctUntilChanged(viewModelListEqual)),
this.meetingSettingsService.get(`users_enable_vote_delegations`).pipe(distinctUntilChanged()),
this.meetingSettingsService.get(`users_forbid_delegator_to_vote`).pipe(distinctUntilChanged()),
this.operator.userObservable.pipe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Poll } from 'src/app/domain/models/poll/poll';
import { PollState } from 'src/app/domain/models/poll/poll-constants';
import { PollRepositoryService } from 'src/app/gateways/repositories/polls/poll-repository.service';
import { VoteRepositoryService } from 'src/app/gateways/repositories/polls/vote-repository.service';
import { viewModelListEqual } from 'src/app/infrastructure/utils';
import { BaseMeetingControllerService } from 'src/app/site/pages/meetings/base/base-meeting-controller.service';
import { MeetingControllerServiceCollectorService } from 'src/app/site/pages/meetings/services/meeting-controller-service-collector.service';

Expand All @@ -22,12 +23,7 @@ export class PollControllerService extends BaseMeetingControllerService<ViewPoll

this.getViewModelListObservableOfStarted()
.pipe(
distinctUntilChanged((previous, current) => {
const prevStarted = previous.map(p => p.id);
const currStarted = current.map(p => p.id);

return prevStarted.length === currStarted.length && currStarted.equals(prevStarted);
}),
distinctUntilChanged(viewModelListEqual),
map(value => value.map(p => p.id))
)
.subscribe(startedPolls => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Identifiable } from 'src/app/domain/interfaces';
import { MotionChangeRecommendation } from 'src/app/domain/models/motions/motion-change-recommendation';
import { ChangeRecoMode, ModificationType } from 'src/app/domain/models/motions/motions.constants';
import { MotionChangeRecommendationRepositoryService } from 'src/app/gateways/repositories/motions';
import { viewModelListEqual } from 'src/app/infrastructure/utils';
import { BaseMeetingControllerService } from 'src/app/site/pages/meetings/base/base-meeting-controller.service';
import { MeetingControllerServiceCollectorService } from 'src/app/site/pages/meetings/services/meeting-controller-service-collector.service';

Expand Down Expand Up @@ -48,12 +49,7 @@ export class MotionChangeRecommendationControllerService extends BaseMeetingCont
public getChangeRecosOfMotionObservable(motionId: Id): Observable<ViewMotionChangeRecommendation[]> {
return this.getViewModelListObservable().pipe(
map((recos: ViewMotionChangeRecommendation[]) => recos.filter(reco => reco.motion_id === motionId)),
distinctUntilChanged(
(prev, curr) =>
prev?.length === curr?.length &&
Math.max(...prev.map(e => e.viewModelUpdateTimestamp)) ===
Math.max(...curr.map(e => e.viewModelUpdateTimestamp))
)
distinctUntilChanged(viewModelListEqual)
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Identifiable } from 'src/app/domain/interfaces';
import { Motion } from 'src/app/domain/models/motions/motion';
import { CreateResponse } from 'src/app/gateways/repositories/base-repository';
import { MotionRepositoryService } from 'src/app/gateways/repositories/motions';
import { viewModelListEqual } from 'src/app/infrastructure/utils';

import { ViewMotion } from '../../../view-models';
import { MotionControllerService } from '../motion-controller.service';
Expand Down Expand Up @@ -43,13 +44,7 @@ export class AmendmentControllerService {
public getViewModelListObservableFor(motion: Identifiable): Observable<ViewMotion[]> {
return this.getViewModelListObservable().pipe(
map(_motions => _motions.filter(_motion => _motion.lead_motion_id === motion.id)),
distinctUntilChanged(
(prev, curr) =>
prev?.length === curr?.length &&
Math.max(...prev.map(e => e.viewModelUpdateTimestamp)) ===
Math.max(...curr.map(e => e.viewModelUpdateTimestamp)) &&
prev?.map(e => e.state_id).equals(curr?.map(e => e.state_id))
)
distinctUntilChanged(viewModelListEqual)
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, distinctUntilChanged, map, Observable, Subscription } from 'rxjs';
import { Id } from 'src/app/domain/definitions/key-types';
import { viewModelListEqual } from 'src/app/infrastructure/utils';
import { ModelRequestService } from 'src/app/site/services/model-request.service';

import { PollControllerService } from '../modules/poll/services/poll-controller.service';
Expand Down Expand Up @@ -44,12 +45,7 @@ export class ActivePollsService {
.getViewModelListObservable()
.pipe(
map(polls => polls.filter(p => p.isStarted)),
distinctUntilChanged((previous, current) => {
const prevStarted = previous.map(p => p.id);
const currStarted = current.map(p => p.id);

return prevStarted.length === currStarted.length && currStarted.equals(prevStarted);
})
distinctUntilChanged(viewModelListEqual)
)
.subscribe(polls => {
this.pollIds = polls.map(poll => poll.id);
Expand Down
Loading