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

Diagram schema migration #306

Merged
merged 11 commits into from
Oct 15, 2023
10 changes: 5 additions & 5 deletions docs/source/user/api/typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export type ApollonOptions = {
scale?: number;
};
export type Selection = {
elements: string[];
relationships: string[];
elements: {[id: string]: boolean};
relationships: {[id: string]: boolean};
};
export type UMLModel = {
version: string;
Expand All @@ -33,10 +33,10 @@ export type UMLModel = {
width: number;
height: number;
};
elements: UMLElement[];
elements: {[id: string]: UMLElement};
interactive: Selection;
relationships: UMLRelationship[];
assessments: Assessment[];
relationships: {[id: string]: UMLRelationship};
assessments: {[id: string]: Assessment};
};
export type UMLModelElementType = UMLElementType | UMLRelationshipType | UMLDiagramType;
export type UMLModelElement = {
Expand Down
5 changes: 4 additions & 1 deletion public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ export const setTheming = (theming: string) => {
export const draw = async (mode?: 'include' | 'exclude') => {
if (!editor) return;

const filter: string[] = [...editor.model.interactive.elements, ...editor.model.interactive.relationships];
const filter: string[] = [
...Object.entries(editor.model.interactive.elements).filter(([, value]) => value).map(([key]) => key),
...Object.entries(editor.model.interactive.relationships).filter(([, value]) => value).map(([key]) => key),
];

const exportParam = mode ? { [mode]: filter, scale: editor.getScaleFactor() } : { scale: editor.getScaleFactor() };

Expand Down
21 changes: 17 additions & 4 deletions src/main/apollon-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class ApollonEditor {
};
}

selection: Apollon.Selection = { elements: [], relationships: [] };
selection: Apollon.Selection = { elements: {}, relationships: {} };
private root?: Root;
private currentModelState?: ModelState;
private assessments: Apollon.Assessment[] = [];
Expand Down Expand Up @@ -194,7 +194,16 @@ export class ApollonEditor {
if (!this.store) return;
const dispatch = this.store.dispatch as Dispatch;
dispatch(UMLElementRepository.deselect());
dispatch(UMLElementRepository.select([...selection.elements, ...selection.relationships]));
dispatch(
UMLElementRepository.select([
...Object.entries(selection.elements)
.filter(([, selected]) => selected)
.map(([id]) => id),
...Object.entries(selection.relationships)
.filter(([, selected]) => selected)
.map(([id]) => id),
]),
);
}

_getNewSubscriptionId(subscribers: { [key: number]: any }): number {
Expand Down Expand Up @@ -327,8 +336,12 @@ export class ApollonEditor {
if (!this.store) return;
const { elements, selected, assessments } = this.store.getState();
const selection: Apollon.Selection = {
elements: selected.filter((id) => elements[id].type in UMLElementType),
relationships: selected.filter((id) => elements[id].type in UMLRelationshipType),
elements: selected
.filter((id) => elements[id].type in UMLElementType)
.reduce((acc, id) => ({ ...acc, [id]: true }), {}),
relationships: selected
.filter((id) => elements[id].type in UMLRelationshipType)
.reduce((acc, id) => ({ ...acc, [id]: true }), {}),
};

// check if previous selection differs from current selection, if yes -> notify subscribers
Expand Down
37 changes: 37 additions & 0 deletions src/main/compat/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { UMLModel } from '../typings';
import { isV2, v2ModeltoV3Model, UMLModelCompat as UMLModelCompatV2 } from './v2';

/**
*
* Represents all model versions that can be converted to the latest version.
*
*/
/*
*
* HINT for future maintainers:
*
* this should always be the union of compatible model versions, i.e.
* if the model version is moved to V4, while support for V2 is still required,
* this should look like the following:
*
* ```ts
* export type UMLModelCompat = UMLModelCompatV2 | UMLModelCompatV3;
* ```
*/
export type UMLModelCompat = UMLModelCompatV2;

/**
*
* Converts a model to the latest version.
*
* @param {UMLModelCompat} model model to convert
* @returns {UMLModel} the converted model
*
*/
export function backwardsCompatibleModel(model: UMLModelCompat): UMLModel {
if (isV2(model)) {
return v2ModeltoV3Model(model);
} else {
return model;
}
}
2 changes: 2 additions & 0 deletions src/main/compat/v2/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './typings';
export * from './transform';
24 changes: 24 additions & 0 deletions src/main/compat/v2/transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { UMLModel } from '../../typings';
import { UMLModelV2 } from './typings';

/**
*
* Converts a v2 model to a v3 model.
*
* @param {UMLModelV2} model model to convert
* @returns {UMLModel} the converted model
*
*/
export function v2ModeltoV3Model(model: UMLModelV2): UMLModel {
return {
...model,
version: '3.0.0',
elements: model.elements.reduce((acc, val) => ({ ...acc, [val.id]: val }), {}),
relationships: model.relationships.reduce((acc, val) => ({ ...acc, [val.id]: val }), {}),
assessments: model.assessments.reduce((acc, val) => ({ ...acc, [val.modelElementId]: val }), {}),
interactive: {
elements: model.interactive.elements.reduce((acc, val) => ({ ...acc, [val]: true }), {}),
relationships: model.interactive.relationships.reduce((acc, val) => ({ ...acc, [val]: true }), {}),
},
};
}
51 changes: 51 additions & 0 deletions src/main/compat/v2/typings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { UMLModel, UMLDiagramType, UMLElement, UMLRelationship, Assessment } from '../../typings';

/**
*
* Represents the selection type in V2 schema.
*
*/
export type SelectionV2 = {
elements: string[];
relationships: string[];
};

/**
*
* Represents the V2 model.
*
* @todo This type definition reuses unchanged elements from latest model version.
* This is not ideal, as future maintainers modifying the latest model might make
* changes that would then need to be reflected here. Ideally, model definitions for
* different versions should be completely separate, though that results in a lot of
* code duplication.
*
*/
export type UMLModelV2 = {
version: `2.${number}.${number}`;
type: UMLDiagramType;
size: { width: number; height: number };
elements: UMLElement[];
interactive: SelectionV2;
relationships: UMLRelationship[];
assessments: Assessment[];
};

/**
*
* Represents a model compatible with either V2 or latest version.
*
*/
export type UMLModelCompat = UMLModel | UMLModelV2;

/**
*
* Returns whether the given model is a V2 model.
*
* @param {UMLModelCompat} model model to check
* @returns {boolean} `true` if the model is a V2 model, `false` otherwise
*
*/
export function isV2(model: UMLModelCompat): model is UMLModelV2 {
return model.version.startsWith('2.');
}
7 changes: 3 additions & 4 deletions src/main/components/connectable/connection-preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,9 @@ class Preview extends Component<Props, State> {
if (event instanceof PointerEvent) {
position = new Point(event.clientX - offset.x, event.clientY - offset.y).scale(1 / zoomFactor);
} else {
position = new Point(
event.targetTouches[0].clientX - offset.x,
event.targetTouches[0].clientY - offset.y,
).scale(1 / zoomFactor);
position = new Point(event.targetTouches[0].clientX - offset.x, event.targetTouches[0].clientY - offset.y).scale(
1 / zoomFactor,
);
}

this.setState({ position });
Expand Down
Loading
Loading