@@ -9475,6 +9475,17 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the timestamp of a pointer event, in milliseconds.
|
||||||
|
* Extracted as a method so tests can spy on it and return a fixed value,
|
||||||
|
* making the One Euro Filter in the freedraw handler deterministic.
|
||||||
|
*/
|
||||||
|
protected getPointerEventTimestamp(
|
||||||
|
ev: Pick<PointerEvent, "timeStamp">,
|
||||||
|
): number {
|
||||||
|
return ev.timeStamp;
|
||||||
|
}
|
||||||
|
|
||||||
private onPointerMoveFromPointerDownHandler(
|
private onPointerMoveFromPointerDownHandler(
|
||||||
pointerDownState: PointerDownState,
|
pointerDownState: PointerDownState,
|
||||||
) {
|
) {
|
||||||
@@ -10232,8 +10243,9 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
|
|
||||||
if (event.pointerType === "mouse") {
|
if (event.pointerType === "mouse") {
|
||||||
// One Euro Filter: speed-adaptive low-pass for mouse events.
|
// One Euro Filter: speed-adaptive low-pass for mouse events.
|
||||||
|
const evTs = this.getPointerEventTimestamp(ev);
|
||||||
const dt = Math.max(
|
const dt = Math.max(
|
||||||
prevTs !== null ? (ev.timeStamp - prevTs) / 1000 : 1 / 60,
|
prevTs !== null ? (evTs - prevTs) / 1000 : 1 / 60,
|
||||||
0.001,
|
0.001,
|
||||||
); // seconds
|
); // seconds
|
||||||
|
|
||||||
@@ -10261,7 +10273,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
|
|
||||||
prevRawX = rawDx;
|
prevRawX = rawDx;
|
||||||
prevRawY = rawDy;
|
prevRawY = rawDy;
|
||||||
prevTs = ev.timeStamp;
|
prevTs = evTs;
|
||||||
|
|
||||||
dx = emaX;
|
dx = emaX;
|
||||||
dy = emaY;
|
dy = emaY;
|
||||||
|
|||||||
@@ -9084,8 +9084,8 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
|
|||||||
20,
|
20,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"23.68762",
|
"21.04874",
|
||||||
"23.68762",
|
"21.04874",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
50,
|
50,
|
||||||
@@ -9186,8 +9186,8 @@ exports[`history > multiplayer undo/redo > should not let remote changes to inte
|
|||||||
20,
|
20,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"23.68762",
|
"21.04874",
|
||||||
"23.68762",
|
"21.04874",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
50,
|
50,
|
||||||
@@ -11292,489 +11292,6 @@ exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end o
|
|||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end of test] undo stack 1`] = `[]`;
|
exports[`history > multiplayer undo/redo > should redraw arrows on undo > [end of test] undo stack 1`] = `[]`;
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] appState 1`] = `
|
|
||||||
{
|
|
||||||
"activeEmbeddable": null,
|
|
||||||
"activeLockedId": null,
|
|
||||||
"activeTool": {
|
|
||||||
"customType": null,
|
|
||||||
"fromSelection": false,
|
|
||||||
"lastActiveTool": null,
|
|
||||||
"locked": false,
|
|
||||||
"type": "selection",
|
|
||||||
},
|
|
||||||
"bindMode": "orbit",
|
|
||||||
"bindingPreference": "enabled",
|
|
||||||
"collaborators": Map {},
|
|
||||||
"contextMenu": null,
|
|
||||||
"croppingElementId": null,
|
|
||||||
"currentHoveredFontFamily": null,
|
|
||||||
"currentItemArrowType": "round",
|
|
||||||
"currentItemBackgroundColor": "transparent",
|
|
||||||
"currentItemEndArrowhead": "arrow",
|
|
||||||
"currentItemFillStyle": "solid",
|
|
||||||
"currentItemFontFamily": 5,
|
|
||||||
"currentItemFontSize": 20,
|
|
||||||
"currentItemOpacity": 100,
|
|
||||||
"currentItemRoughness": 1,
|
|
||||||
"currentItemRoundness": "sharp",
|
|
||||||
"currentItemStartArrowhead": null,
|
|
||||||
"currentItemStrokeColor": "#1e1e1e",
|
|
||||||
"currentItemStrokeStyle": "solid",
|
|
||||||
"currentItemStrokeWidth": 2,
|
|
||||||
"currentItemTextAlign": "left",
|
|
||||||
"cursorButton": "up",
|
|
||||||
"defaultSidebarDockedPreference": false,
|
|
||||||
"editingFrame": null,
|
|
||||||
"editingGroupId": null,
|
|
||||||
"editingTextElement": null,
|
|
||||||
"elementsToHighlight": null,
|
|
||||||
"errorMessage": null,
|
|
||||||
"exportBackground": true,
|
|
||||||
"exportEmbedScene": false,
|
|
||||||
"exportScale": 1,
|
|
||||||
"exportWithDarkMode": false,
|
|
||||||
"fileHandle": null,
|
|
||||||
"followedBy": Set {},
|
|
||||||
"frameRendering": {
|
|
||||||
"clip": true,
|
|
||||||
"enabled": true,
|
|
||||||
"name": true,
|
|
||||||
"outline": true,
|
|
||||||
},
|
|
||||||
"frameToHighlight": null,
|
|
||||||
"gridModeEnabled": false,
|
|
||||||
"gridSize": 20,
|
|
||||||
"gridStep": 5,
|
|
||||||
"height": 0,
|
|
||||||
"hoveredElementIds": {},
|
|
||||||
"isBindingEnabled": true,
|
|
||||||
"isCropping": false,
|
|
||||||
"isLoading": false,
|
|
||||||
"isMidpointSnappingEnabled": true,
|
|
||||||
"isResizing": false,
|
|
||||||
"isRotating": false,
|
|
||||||
"lastPointerDownWith": "mouse",
|
|
||||||
"lockedMultiSelections": {},
|
|
||||||
"multiElement": null,
|
|
||||||
"newElement": null,
|
|
||||||
"objectsSnapModeEnabled": false,
|
|
||||||
"offsetLeft": 0,
|
|
||||||
"offsetTop": 0,
|
|
||||||
"openDialog": null,
|
|
||||||
"openMenu": null,
|
|
||||||
"openPopup": null,
|
|
||||||
"openSidebar": null,
|
|
||||||
"originSnapOffset": null,
|
|
||||||
"penDetected": false,
|
|
||||||
"penMode": false,
|
|
||||||
"preferredSelectionTool": {
|
|
||||||
"initialized": true,
|
|
||||||
"type": "selection",
|
|
||||||
},
|
|
||||||
"previousSelectedElementIds": {},
|
|
||||||
"resizingElement": null,
|
|
||||||
"scrollX": 0,
|
|
||||||
"scrollY": 0,
|
|
||||||
"searchMatches": null,
|
|
||||||
"selectedElementIds": {},
|
|
||||||
"selectedElementsAreBeingDragged": false,
|
|
||||||
"selectedGroupIds": {},
|
|
||||||
"selectionElement": null,
|
|
||||||
"shouldCacheIgnoreZoom": false,
|
|
||||||
"showHyperlinkPopup": false,
|
|
||||||
"showWelcomeScreen": true,
|
|
||||||
"snapLines": [],
|
|
||||||
"startBoundElement": null,
|
|
||||||
"stats": {
|
|
||||||
"open": false,
|
|
||||||
"panels": 3,
|
|
||||||
},
|
|
||||||
"suggestedBinding": null,
|
|
||||||
"theme": "light",
|
|
||||||
"toast": null,
|
|
||||||
"userToFollow": null,
|
|
||||||
"viewBackgroundColor": "#ffffff",
|
|
||||||
"viewModeEnabled": false,
|
|
||||||
"width": 0,
|
|
||||||
"zenModeEnabled": false,
|
|
||||||
"zoom": {
|
|
||||||
"value": 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] element 0 1`] = `
|
|
||||||
{
|
|
||||||
"angle": 0,
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"boundElements": null,
|
|
||||||
"customData": undefined,
|
|
||||||
"fillStyle": "solid",
|
|
||||||
"frameId": null,
|
|
||||||
"groupIds": [
|
|
||||||
"inner",
|
|
||||||
"outer",
|
|
||||||
],
|
|
||||||
"height": 100,
|
|
||||||
"id": "id0",
|
|
||||||
"index": "a0",
|
|
||||||
"isDeleted": false,
|
|
||||||
"link": null,
|
|
||||||
"locked": false,
|
|
||||||
"opacity": 100,
|
|
||||||
"roughness": 1,
|
|
||||||
"roundness": null,
|
|
||||||
"strokeColor": "#1e1e1e",
|
|
||||||
"strokeStyle": "solid",
|
|
||||||
"strokeWidth": 2,
|
|
||||||
"type": "rectangle",
|
|
||||||
"updated": 1,
|
|
||||||
"version": 2,
|
|
||||||
"width": 100,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] element 1 1`] = `
|
|
||||||
{
|
|
||||||
"angle": 0,
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"boundElements": null,
|
|
||||||
"customData": undefined,
|
|
||||||
"fillStyle": "solid",
|
|
||||||
"frameId": null,
|
|
||||||
"groupIds": [
|
|
||||||
"outer",
|
|
||||||
],
|
|
||||||
"height": 100,
|
|
||||||
"id": "id1",
|
|
||||||
"index": "a1",
|
|
||||||
"isDeleted": false,
|
|
||||||
"link": null,
|
|
||||||
"locked": false,
|
|
||||||
"opacity": 100,
|
|
||||||
"roughness": 1,
|
|
||||||
"roundness": null,
|
|
||||||
"strokeColor": "#1e1e1e",
|
|
||||||
"strokeStyle": "solid",
|
|
||||||
"strokeWidth": 2,
|
|
||||||
"type": "rectangle",
|
|
||||||
"updated": 1,
|
|
||||||
"version": 2,
|
|
||||||
"width": 100,
|
|
||||||
"x": 100,
|
|
||||||
"y": 100,
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] element 2 1`] = `
|
|
||||||
{
|
|
||||||
"angle": 0,
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"boundElements": null,
|
|
||||||
"customData": undefined,
|
|
||||||
"fillStyle": "solid",
|
|
||||||
"frameId": null,
|
|
||||||
"groupIds": [
|
|
||||||
"inner",
|
|
||||||
"outer",
|
|
||||||
],
|
|
||||||
"height": 100,
|
|
||||||
"id": "id2",
|
|
||||||
"index": "a2",
|
|
||||||
"isDeleted": false,
|
|
||||||
"link": null,
|
|
||||||
"locked": false,
|
|
||||||
"opacity": 100,
|
|
||||||
"roughness": 1,
|
|
||||||
"roundness": null,
|
|
||||||
"strokeColor": "#1e1e1e",
|
|
||||||
"strokeStyle": "solid",
|
|
||||||
"strokeWidth": 2,
|
|
||||||
"type": "rectangle",
|
|
||||||
"updated": 1,
|
|
||||||
"version": 2,
|
|
||||||
"width": 100,
|
|
||||||
"x": 200,
|
|
||||||
"y": 200,
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] number of elements 1`] = `3`;
|
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] number of renders 1`] = `16`;
|
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] redo stack 1`] = `[]`;
|
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should support undo and redo when escape unwinds nested group editing > [end of test] undo stack 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"appState": AppStateDelta {
|
|
||||||
"delta": Delta {
|
|
||||||
"deleted": {
|
|
||||||
"selectedElementIds": {
|
|
||||||
"id0": true,
|
|
||||||
"id1": true,
|
|
||||||
"id2": true,
|
|
||||||
},
|
|
||||||
"selectedGroupIds": {
|
|
||||||
"outer": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"selectedElementIds": {},
|
|
||||||
"selectedGroupIds": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"elements": {
|
|
||||||
"added": {},
|
|
||||||
"removed": {
|
|
||||||
"id0": {
|
|
||||||
"deleted": {
|
|
||||||
"angle": 0,
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"boundElements": null,
|
|
||||||
"customData": undefined,
|
|
||||||
"fillStyle": "solid",
|
|
||||||
"frameId": null,
|
|
||||||
"groupIds": [
|
|
||||||
"inner",
|
|
||||||
"outer",
|
|
||||||
],
|
|
||||||
"height": 100,
|
|
||||||
"index": "a0",
|
|
||||||
"isDeleted": false,
|
|
||||||
"link": null,
|
|
||||||
"locked": false,
|
|
||||||
"opacity": 100,
|
|
||||||
"roughness": 1,
|
|
||||||
"roundness": null,
|
|
||||||
"strokeColor": "#1e1e1e",
|
|
||||||
"strokeStyle": "solid",
|
|
||||||
"strokeWidth": 2,
|
|
||||||
"type": "rectangle",
|
|
||||||
"version": 2,
|
|
||||||
"width": 100,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0,
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"isDeleted": true,
|
|
||||||
"version": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"id1": {
|
|
||||||
"deleted": {
|
|
||||||
"angle": 0,
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"boundElements": null,
|
|
||||||
"customData": undefined,
|
|
||||||
"fillStyle": "solid",
|
|
||||||
"frameId": null,
|
|
||||||
"groupIds": [
|
|
||||||
"outer",
|
|
||||||
],
|
|
||||||
"height": 100,
|
|
||||||
"index": "a1",
|
|
||||||
"isDeleted": false,
|
|
||||||
"link": null,
|
|
||||||
"locked": false,
|
|
||||||
"opacity": 100,
|
|
||||||
"roughness": 1,
|
|
||||||
"roundness": null,
|
|
||||||
"strokeColor": "#1e1e1e",
|
|
||||||
"strokeStyle": "solid",
|
|
||||||
"strokeWidth": 2,
|
|
||||||
"type": "rectangle",
|
|
||||||
"version": 2,
|
|
||||||
"width": 100,
|
|
||||||
"x": 100,
|
|
||||||
"y": 100,
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"isDeleted": true,
|
|
||||||
"version": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"id2": {
|
|
||||||
"deleted": {
|
|
||||||
"angle": 0,
|
|
||||||
"backgroundColor": "transparent",
|
|
||||||
"boundElements": null,
|
|
||||||
"customData": undefined,
|
|
||||||
"fillStyle": "solid",
|
|
||||||
"frameId": null,
|
|
||||||
"groupIds": [
|
|
||||||
"inner",
|
|
||||||
"outer",
|
|
||||||
],
|
|
||||||
"height": 100,
|
|
||||||
"index": "a2",
|
|
||||||
"isDeleted": false,
|
|
||||||
"link": null,
|
|
||||||
"locked": false,
|
|
||||||
"opacity": 100,
|
|
||||||
"roughness": 1,
|
|
||||||
"roundness": null,
|
|
||||||
"strokeColor": "#1e1e1e",
|
|
||||||
"strokeStyle": "solid",
|
|
||||||
"strokeWidth": 2,
|
|
||||||
"type": "rectangle",
|
|
||||||
"version": 2,
|
|
||||||
"width": 100,
|
|
||||||
"x": 200,
|
|
||||||
"y": 200,
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"isDeleted": true,
|
|
||||||
"version": 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"updated": {},
|
|
||||||
},
|
|
||||||
"id": "id5",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appState": AppStateDelta {
|
|
||||||
"delta": Delta {
|
|
||||||
"deleted": {
|
|
||||||
"editingGroupId": "outer",
|
|
||||||
"selectedElementIds": {},
|
|
||||||
"selectedGroupIds": {
|
|
||||||
"inner": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"editingGroupId": null,
|
|
||||||
"selectedElementIds": {
|
|
||||||
"id1": true,
|
|
||||||
},
|
|
||||||
"selectedGroupIds": {
|
|
||||||
"outer": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"elements": {
|
|
||||||
"added": {},
|
|
||||||
"removed": {},
|
|
||||||
"updated": {},
|
|
||||||
},
|
|
||||||
"id": "id7",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appState": AppStateDelta {
|
|
||||||
"delta": Delta {
|
|
||||||
"deleted": {
|
|
||||||
"editingGroupId": "inner",
|
|
||||||
"selectedElementIds": {},
|
|
||||||
"selectedGroupIds": {},
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"editingGroupId": "outer",
|
|
||||||
"selectedElementIds": {
|
|
||||||
"id2": true,
|
|
||||||
},
|
|
||||||
"selectedGroupIds": {
|
|
||||||
"inner": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"elements": {
|
|
||||||
"added": {},
|
|
||||||
"removed": {},
|
|
||||||
"updated": {},
|
|
||||||
},
|
|
||||||
"id": "id9",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appState": AppStateDelta {
|
|
||||||
"delta": Delta {
|
|
||||||
"deleted": {
|
|
||||||
"editingGroupId": "outer",
|
|
||||||
"selectedElementIds": {
|
|
||||||
"id2": true,
|
|
||||||
},
|
|
||||||
"selectedGroupIds": {
|
|
||||||
"inner": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"editingGroupId": "inner",
|
|
||||||
"selectedElementIds": {},
|
|
||||||
"selectedGroupIds": {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"elements": {
|
|
||||||
"added": {},
|
|
||||||
"removed": {},
|
|
||||||
"updated": {},
|
|
||||||
},
|
|
||||||
"id": "id19",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appState": AppStateDelta {
|
|
||||||
"delta": Delta {
|
|
||||||
"deleted": {
|
|
||||||
"editingGroupId": null,
|
|
||||||
"selectedElementIds": {
|
|
||||||
"id1": true,
|
|
||||||
},
|
|
||||||
"selectedGroupIds": {
|
|
||||||
"outer": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"editingGroupId": "outer",
|
|
||||||
"selectedElementIds": {},
|
|
||||||
"selectedGroupIds": {
|
|
||||||
"inner": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"elements": {
|
|
||||||
"added": {},
|
|
||||||
"removed": {},
|
|
||||||
"updated": {},
|
|
||||||
},
|
|
||||||
"id": "id20",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"appState": AppStateDelta {
|
|
||||||
"delta": Delta {
|
|
||||||
"deleted": {
|
|
||||||
"selectedElementIds": {},
|
|
||||||
"selectedGroupIds": {},
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"selectedElementIds": {
|
|
||||||
"id0": true,
|
|
||||||
"id1": true,
|
|
||||||
"id2": true,
|
|
||||||
},
|
|
||||||
"selectedGroupIds": {
|
|
||||||
"outer": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"elements": {
|
|
||||||
"added": {},
|
|
||||||
"removed": {},
|
|
||||||
"updated": {},
|
|
||||||
},
|
|
||||||
"id": "id21",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`history > multiplayer undo/redo > should update history entries after remote changes on the same properties > [end of test] appState 1`] = `
|
exports[`history > multiplayer undo/redo > should update history entries after remote changes on the same properties > [end of test] appState 1`] = `
|
||||||
{
|
{
|
||||||
"activeEmbeddable": null,
|
"activeEmbeddable": null,
|
||||||
|
|||||||
@@ -2120,6 +2120,9 @@ describe("history", () => {
|
|||||||
await render(
|
await render(
|
||||||
<Excalidraw handleKeyboardGlobally={true} isCollaborating={true} />,
|
<Excalidraw handleKeyboardGlobally={true} isCollaborating={true} />,
|
||||||
);
|
);
|
||||||
|
// Pin the One Euro Filter clock to a fixed value so the smoothed
|
||||||
|
// intermediate freedraw point is deterministic regardless of system load.
|
||||||
|
vi.spyOn(h.app as any, "getPointerEventTimestamp").mockReturnValue(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not override remote changes on different elements", async () => {
|
it("should not override remote changes on different elements", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user