Skip to content
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
52 changes: 50 additions & 2 deletions src/app/models/operation.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ abstract class Operation {
}
}

class TrainrunOperation extends Operation {
abstract class TrainrunOperation extends Operation {
readonly trainrun: TrainrunDto;

constructor(operationType: OperationType, trainrun: Trainrun) {
Expand All @@ -41,6 +41,45 @@ class TrainrunOperation extends Operation {
}
}

type TrainrunUpdateTag =
| "nodes"
| "times"
| "numberOfStops"
| "name"
| "categoryId"
| "frequencyId"
| "timeCategoryId"
| "labelIds"
| "direction";

class TrainrunUpdateOperation extends TrainrunOperation {
readonly tags: TrainrunUpdateTag[];
readonly oneWayDirection?: "forward" | "backward";
constructor(
trainrun: Trainrun,
tags: TrainrunUpdateTag[],
oneWayDirection?: "forward" | "backward",
) {
super(OperationType.update, trainrun);
this.tags = tags;
this.oneWayDirection = oneWayDirection;
}
}

class TrainrunCreateOperation extends TrainrunOperation {
readonly duplicatedTrainrunId?: number;
constructor(trainrun: Trainrun, duplicatedTrainrunId?: number) {
super(OperationType.create, trainrun);
this.duplicatedTrainrunId = duplicatedTrainrunId;
}
}

class TrainrunDeleteOperation extends TrainrunOperation {
constructor(trainrun: Trainrun) {
super(OperationType.delete, trainrun);
}
}

class NodeOperation extends Operation {
readonly node: NodeDto;

Expand Down Expand Up @@ -68,4 +107,13 @@ class NoteOperation extends Operation {
}
}

export {OperationType, Operation, TrainrunOperation, NodeOperation, LabelOperation, NoteOperation};
export {
OperationType,
Operation,
TrainrunUpdateOperation,
TrainrunCreateOperation,
TrainrunDeleteOperation,
NodeOperation,
LabelOperation,
NoteOperation,
};
14 changes: 12 additions & 2 deletions src/app/services/data/node.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import {
NodeOperation,
Operation,
OperationType,
TrainrunOperation,
TrainrunUpdateOperation,
} from "../../models/operation.model";

@Injectable({
Expand Down Expand Up @@ -488,6 +488,13 @@ export class NodeService implements OnDestroy {
this.transitionsUpdated();
this.nodesUpdated();
}
this.operation.emit(
new TrainrunUpdateOperation(trainrunSection1.getTrainrun(), [
"nodes",
"times",
"numberOfStops",
]),
);

return trainrunSection1;
}
Expand Down Expand Up @@ -571,7 +578,10 @@ export class NodeService implements OnDestroy {
this.transitionsUpdated();
this.nodesUpdated();
this.operation.emit(
new TrainrunOperation(OperationType.update, trainrunSections.trainrunSection1.getTrainrun()),
new TrainrunUpdateOperation(trainrunSections.trainrunSection1.getTrainrun(), [
"times",
"numberOfStops",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think numberOfStops changes when toggling non-stop?

Copy link
Copy Markdown
Contributor Author

@Synar Synar Mar 19, 2026

Choose a reason for hiding this comment

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

I will trust you on this.

I am relatively confident about the other tags, but for numberOfStops I only have a general idea of its meaning. So I added it to anything that seemed to touch stops (better to update for nothing than miss an update). But yeah, if you do know which functions actually touch it, that's better =p

]),
);
}

Expand Down
39 changes: 28 additions & 11 deletions src/app/services/data/trainrun.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ import {FilterService} from "../ui/filter.service";
import {Transition} from "../../models/transition.model";
import {Port} from "../../models/port.model";
import {Connection} from "../../models/connection.model";
import {Operation, OperationType, TrainrunOperation} from "../../models/operation.model";
import {
Operation,
OperationType,
TrainrunCreateOperation,
TrainrunDeleteOperation,
TrainrunUpdateOperation,
} from "../../models/operation.model";
import {TrainrunsectionHelper} from "../util/trainrunsection.helper";

@Injectable({
Expand Down Expand Up @@ -158,7 +164,7 @@ export class TrainrunService {
if (enforceUpdate) {
this.trainrunsUpdated();
}
this.operation.emit(new TrainrunOperation(OperationType.delete, trainrun));
this.operation.emit(new TrainrunDeleteOperation(trainrun));
}

getSelectedTrainrun(): Trainrun {
Expand Down Expand Up @@ -230,7 +236,7 @@ export class TrainrunService {
this.nodeService.reorderPortsOnNodesForTrainrun(trainrun, false);
this.propagateTrainrunInitialConsecutiveTimes(trainrun);
this.trainrunsUpdated();
this.operation.emit(new TrainrunOperation(OperationType.update, trainrun));
this.operation.emit(new TrainrunUpdateOperation(trainrun, ["times", "frequencyId"]));
return freqOffset;
}

Expand All @@ -242,7 +248,7 @@ export class TrainrunService {
this.getTrainrunFromId(trainrun.getId()).setTrainrunCategory(category);
this.nodeService.reorderPortsOnNodesForTrainrun(trainrun, false);
this.trainrunsUpdated();
this.operation.emit(new TrainrunOperation(OperationType.update, trainrun));
this.operation.emit(new TrainrunUpdateOperation(trainrun, ["categoryId"]));
}

updateTrainrunTimeCategory(trainrun: Trainrun, timeCategory: TrainrunTimeCategory) {
Expand All @@ -254,21 +260,25 @@ export class TrainrunService {
this.getTrainrunFromId(trainrun.getId()).setTrainrunTimeCategory(timeCategory);
this.nodeService.reorderPortsOnNodesForTrainrun(trainrun, false);
this.trainrunsUpdated();
this.operation.emit(new TrainrunOperation(OperationType.update, trainrun));
this.operation.emit(new TrainrunUpdateOperation(trainrun, ["timeCategoryId"]));
}

updateTrainrunTitle(trainrun: Trainrun, title: string) {
this.getTrainrunFromId(trainrun.getId()).setTitle(title);
this.nodeService.reorderPortsOnNodesForTrainrun(trainrun, false);
this.trainrunsUpdated();
this.operation.emit(new TrainrunOperation(OperationType.update, trainrun));
this.operation.emit(new TrainrunUpdateOperation(trainrun, ["name"]));
}

updateDirection(trainrun: Trainrun, direction: Direction) {
updateDirection(trainrun: Trainrun, direction: Direction, isTrainInverted?: boolean) {
Copy link
Copy Markdown
Member

@emersion emersion Mar 17, 2026

Choose a reason for hiding this comment

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

Could we pass "forward" | "backward" directly as a function argument to avoid the boolean?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure!

const trainrunSection = this.getTrainrunFromId(trainrun.getId());
trainrunSection.setDirection(direction);
this.trainrunsUpdated();
this.operation.emit(new TrainrunOperation(OperationType.update, trainrun));
let oneWayDirection = undefined;
if (direction === Direction.ONE_WAY) oneWayDirection = isTrainInverted ? "backward" : "forward";
this.operation.emit(
new TrainrunUpdateOperation(trainrun, ["direction", "nodes", "times"], oneWayDirection),
);
}

getTrainruns(): Trainrun[] {
Expand Down Expand Up @@ -300,6 +310,7 @@ export class TrainrunService {
if (this.filterService.filterTrainrun(t)) {
this.filterService.clearDeletetFilterTrainrunLabel(labelObject.getId());
t.setLabelIds(t.getLabelIds().filter((labelId: number) => labelId !== labelObject.getId()));
this.operation.emit(new TrainrunUpdateOperation(t, ["labelIds"]));
}
});

Expand Down Expand Up @@ -385,7 +396,10 @@ export class TrainrunService {
newTrainrun.select();
this.nodeService.transitionsUpdated();
this.trainrunsUpdated();
this.operation.emit(new TrainrunOperation(OperationType.create, newTrainrun));
this.operation.emit(new TrainrunCreateOperation(newTrainrun));
this.operation.emit(
new TrainrunUpdateOperation(trainrun2split, ["nodes", "times", "numberOfStops"]),
);
}

combineTwoTrainruns(node: Node, port1: Port, port2: Port) {
Expand Down Expand Up @@ -498,6 +512,9 @@ export class TrainrunService {

// update
this.trainrunsUpdated();
this.operation.emit(
new TrainrunUpdateOperation(trainrun1, ["nodes", "times", "numberOfStops"]),
);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Aren't we missing a TrainrunDeleteOperation here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hmm I don't think so, there is a call to this.deleteTrainrun earlier which I believe already emits the event

this.nodeService.nodesUpdated();
this.nodeService.connectionsUpdated();
this.nodeService.transitionsUpdated();
Expand Down Expand Up @@ -532,7 +549,7 @@ export class TrainrunService {
this.nodeService.nodesUpdated();
this.trainrunsUpdated();
}
this.operation.emit(new TrainrunOperation(OperationType.create, copiedtrainrun));
this.operation.emit(new TrainrunCreateOperation(copiedtrainrun, trainrunId));
return copiedtrainrun;
}

Expand All @@ -552,7 +569,7 @@ export class TrainrunService {
trainrun.setLabelIds(labelIds);
this.trainrunsUpdated();
if (uniqueLabels.length === labels.length) {
this.operation.emit(new TrainrunOperation(OperationType.update, trainrun));
this.operation.emit(new TrainrunUpdateOperation(trainrun, ["labelIds"]));
}
}

Expand Down
46 changes: 36 additions & 10 deletions src/app/services/data/trainrunsection.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ import {Transition} from "../../models/transition.model";
import {takeUntil} from "rxjs/operators";
import {FilterService} from "../ui/filter.service";
import {DirectedTrainrunSectionProxy} from "../util/trainrun.iterator";
import {Operation, OperationType, TrainrunOperation} from "../../models/operation.model";
import {
Operation,
OperationType,
TrainrunCreateOperation,
TrainrunUpdateOperation,
} from "../../models/operation.model";

interface DepartureAndArrivalTimes {
nodeFromDepartureTime: number;
Expand Down Expand Up @@ -309,7 +314,9 @@ export class TrainrunSectionService implements OnDestroy {
const trainrunSection = this.getTrainrunSectionFromId(trs.getId());
trainrunSection.setNumberOfStops(numberOfStops);
this.trainrunSectionsUpdated();
this.operation.emit(new TrainrunOperation(OperationType.update, trainrunSection.getTrainrun()));
this.operation.emit(
new TrainrunUpdateOperation(trainrunSection.getTrainrun(), ["numberOfStops"]),
);
}

updateTrainrunSectionTime(
Expand Down Expand Up @@ -583,12 +590,14 @@ export class TrainrunSectionService implements OnDestroy {
this.trainrunService.trainrunsUpdated();

if (initialTrainrunsLength !== this.trainrunService.trainrunsStore.trainruns.length) {
this.operation.emit(
new TrainrunOperation(OperationType.create, trainrunSection.getTrainrun()),
);
this.operation.emit(new TrainrunCreateOperation(trainrunSection.getTrainrun()));
} else {
this.operation.emit(
new TrainrunOperation(OperationType.update, trainrunSection.getTrainrun()),
new TrainrunUpdateOperation(trainrunSection.getTrainrun(), [
"nodes",
"times",
"numberOfStops",
]),
);
}

Expand Down Expand Up @@ -667,7 +676,13 @@ export class TrainrunSectionService implements OnDestroy {
this.nodeService.transitionsUpdated();
this.trainrunSectionsUpdated();
}
this.operation.emit(new TrainrunOperation(OperationType.update, trainrunSection.getTrainrun()));
this.operation.emit(
new TrainrunUpdateOperation(trainrunSection.getTrainrun(), [
"nodes",
"numberOfStops",
"times",
]),
);
}

deleteListOfTrainrunSections(trainrunSections: TrainrunSection[], enforceUpdate = true) {
Expand Down Expand Up @@ -740,7 +755,11 @@ export class TrainrunSectionService implements OnDestroy {
}
if (this.getTrainrunSections().length) {
this.operation.emit(
new TrainrunOperation(OperationType.update, trainrunSection.getTrainrun()),
new TrainrunUpdateOperation(trainrunSection.getTrainrun(), [
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hm, do you recall why we fire a TrainrunUpdateOperation here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I honestly don't remember :/

Copy link
Copy Markdown
Contributor Author

@Synar Synar Mar 19, 2026

Choose a reason for hiding this comment

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

My understanding is as follow:

  • either we deleted the last section of the trainrun, in which case the deletion of the section will trigger the deletion of the trainrun which will fire a delete event
  • or we still have some sections left in the trainrun. In that case we need to update it as we have changed its path, and I don't believe any update event is fired prior.

"nodes",
"numberOfStops",
"times",
]),
);
}
}
Expand Down Expand Up @@ -785,7 +804,7 @@ export class TrainrunSectionService implements OnDestroy {
) {
this.updateTrainrunSectionLeftAndRightTimes(section, timeStructure);
this.operation.emit(
new TrainrunOperation(OperationType.update, section.trainrunSection.getTrainrun()),
new TrainrunUpdateOperation(section.trainrunSection.getTrainrun(), ["times"]),
);
}

Expand Down Expand Up @@ -849,7 +868,7 @@ export class TrainrunSectionService implements OnDestroy {

this.trainrunSectionsUpdated();
this.nodeService.connectionsUpdated();
this.operation.emit(new TrainrunOperation(OperationType.update, trainrunSection.getTrainrun()));
this.operation.emit(new TrainrunUpdateOperation(trainrunSection.getTrainrun(), ["times"]));
}

trainrunSectionsUpdated() {
Expand Down Expand Up @@ -976,6 +995,13 @@ export class TrainrunSectionService implements OnDestroy {
this.nodeService.connectionsUpdated();
this.nodeService.nodesUpdated();
this.trainrunSectionsUpdated();
this.operation.emit(
new TrainrunUpdateOperation(trainrunSection1.getTrainrun(), [
"nodes",
"times",
"numberOfStops",
]),
);
}

setWarningOnNode(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,15 @@ export class TrainrunSectionCardComponent implements OnInit, AfterViewInit, OnDe
}

const referenceNode = position === "top" ? this.leftNode : this.rightNode;
let isTrainInverted = false;
if (referenceNode !== trainrunSection.getSourceNode()) {
isTrainInverted = true;
this.trainrunSectionService.invertTrainrunSectionsSourceAndTarget(
trainrunSection.getTrainrunId(),
);
}
this.chosenCard = position;
this.trainrunService.updateDirection(selectedTrainrun, Direction.ONE_WAY);
this.trainrunService.updateDirection(selectedTrainrun, Direction.ONE_WAY, isTrainInverted);
}

getTrainrunTimeStructure(): Omit<LeftAndRightTimeStructure, "travelTime"> {
Expand Down