Skip to content

Commit

Permalink
Merge branch 'develop' into dependabot/npm_and_yarn/webpack-dev-middl…
Browse files Browse the repository at this point in the history
…eware-5.3.4
  • Loading branch information
matthiaslehnertum authored Mar 24, 2024
2 parents 0127b2f + fe75ff1 commit d17ac8d
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 4 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ls1intum/apollon",
"version": "3.3.8",
"version": "3.3.9",
"description": "A UML diagram editor.",
"keywords": [],
"homepage": "https://github.com/ls1intum/apollon#readme",
Expand Down
47 changes: 47 additions & 0 deletions src/main/services/patcher/patcher-saga.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { call, debounce, delay, put, select, take } from 'redux-saga/effects';
import { SagaIterator } from 'redux-saga';

import { run } from '../../utils/actions/sagas';
import { PatcherActionTypes } from './patcher-types';
import { ModelState } from '../../components/store/model-state';
import { UMLContainerRepository } from '../uml-container/uml-container-repository';
import { UMLElement } from '../uml-element/uml-element';
import { UMLRelationship } from '../uml-relationship/uml-relationship';
import { recalc } from '../uml-relationship/uml-relationship-saga';
import { render } from '../layouter/layouter';

/**
* Fixes the layout of the diagram after importing a patch.
*/
export function* PatchLayouter(): SagaIterator {
yield run([patchLayout]);
}

export function* patchLayout(): SagaIterator {
yield debounce(100, PatcherActionTypes.PATCH, recalculateLayouts);
}

function* recalculateLayouts(): SagaIterator {
const { elements }: ModelState = yield select();

const ids = Object.values(elements)
.filter((x) => !x.owner)
.map((x) => x.id);

if (!ids.length) {
return;
}

yield put(UMLContainerRepository.append(ids));

for (const id of Object.keys(elements)) {
yield delay(0);
if (UMLElement.isUMLElement(elements[id])) {
yield call(render, id);
}

if (UMLRelationship.isUMLRelationship(elements[id]) && !elements[id].isManuallyLayouted) {
yield call(recalc, id);
}
}
}
3 changes: 2 additions & 1 deletion src/main/services/saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import { UMLContainerSaga } from './uml-container/uml-container-saga';
import { UMLDiagramSaga } from './uml-diagram/uml-diagram-saga';
import { UMLElementSaga } from './uml-element/uml-element-saga';
import { UMLRelationshipSaga } from './uml-relationship/uml-relationship-saga';
import { PatchLayouter } from './patcher/patcher-saga';

export type SagaContext = {
layer: ILayer | null;
};

export function* saga(): SagaIterator {
yield composeSaga([Layouter, UMLElementSaga, UMLContainerSaga, UMLRelationshipSaga, UMLDiagramSaga]);
yield composeSaga([Layouter, UMLElementSaga, UMLContainerSaga, UMLRelationshipSaga, UMLDiagramSaga, PatchLayouter]);
}
70 changes: 70 additions & 0 deletions src/tests/unit/services/patcher/patcher-saga-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { call, debounce, delay, select, take } from 'redux-saga/effects';

import { patchLayout } from '../../../../main/services/patcher/patcher-saga';
import { PatcherActionTypes, PatcherRepository } from '../../../../main/services/patcher';
import { UMLElementState } from '../../../../main/services/uml-element/uml-element-types';
import { IUMLRelationship } from '../../../../main/services/uml-relationship/uml-relationship';
import { render } from '../../../../main/services/layouter/layouter';
import { recalc } from '../../../../main/services/uml-relationship/uml-relationship-saga';

describe('test patcher saga.', () => {
test('it invokes re-renders and re-calcs after a patch.', () => {
const run = patchLayout();
const debounced = run.next().value;
expect(debounced).toEqual(debounce(100, PatcherActionTypes.PATCH, expect.any(Function)));

const fork = debounced['payload']['args'][2]();
expect(fork.next(PatcherRepository.patch([{ op: 'add', path: '/x', value: 42 }])).value).toEqual(select());

const elements: UMLElementState = {
x: {
type: 'Package',
id: 'x',
name: 'package',
owner: null,
bounds: { x: 0, y: 0, width: 100, height: 100 },
},
y: {
type: 'Class',
id: 'y',
name: 'class',
owner: 'x',
bounds: { x: 0, y: 0, width: 100, height: 100 },
},
z: {
type: 'Class',
id: 'z',
name: 'class',
owner: null,
bounds: { x: 0, y: 0, width: 100, height: 100 },
},
w: {
type: 'ClassInheritance',
id: 'w',
name: '...',
owner: null,
source: { element: 'y', direction: 'Up' },
target: { element: 'z', direction: 'Down' },
path: [
{ x: 0, y: 0 },
{ x: 200, y: 100 },
],
bounds: { x: 0, y: 0, width: 200, height: 100 },
} as IUMLRelationship,
};

fork.next({ elements });

expect(fork.next().value).toEqual(delay(0));
expect(fork.next().value).toEqual(call(render, 'x'));

expect(fork.next().value).toEqual(delay(0));
expect(fork.next().value).toEqual(call(render, 'y'));

expect(fork.next().value).toEqual(delay(0));
expect(fork.next().value).toEqual(call(render, 'z'));

expect(fork.next().value).toEqual(delay(0));
expect(fork.next().value).toEqual(call(recalc, 'w'));
});
});

0 comments on commit d17ac8d

Please sign in to comment.