mirror of
https://github.com/google/blockly.git
synced 2026-01-09 01:50:11 +01:00
feat: add JSON serialization for workspace comments (#7927)
* feat: basic comment serializer * bad: temporarily jam new comment classes into array * chore: implement serializer * chore: add serialization tests * chore: JSDoc * chore: unonly tests
This commit is contained in:
@@ -53,6 +53,9 @@ export class WorkspaceComment {
|
||||
) {
|
||||
this.id = id && !workspace.getCommentById(id) ? id : idGenerator.genUid();
|
||||
|
||||
// TODO: File an issue to remove this once everything is migrated.
|
||||
workspace.addTopComment(this as AnyDuringMigration);
|
||||
|
||||
// TODO(7909): Fire events.
|
||||
}
|
||||
|
||||
@@ -162,6 +165,7 @@ export class WorkspaceComment {
|
||||
/** Disposes of this comment. */
|
||||
dispose() {
|
||||
this.disposing = true;
|
||||
this.workspace.removeTopComment(this as AnyDuringMigration);
|
||||
this.disposed = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import * as procedures from './serialization/procedures.js';
|
||||
import * as registry from './serialization/registry.js';
|
||||
import * as variables from './serialization/variables.js';
|
||||
import * as workspaces from './serialization/workspaces.js';
|
||||
import * as workspaceComments from './serialization/workspace_comments.js';
|
||||
import {ISerializer} from './interfaces/i_serializer.js';
|
||||
|
||||
export {
|
||||
@@ -26,5 +27,6 @@ export {
|
||||
registry,
|
||||
variables,
|
||||
workspaces,
|
||||
workspaceComments,
|
||||
ISerializer,
|
||||
};
|
||||
|
||||
@@ -20,3 +20,6 @@ export const PROCEDURES = 75;
|
||||
* The priority for deserializing blocks.
|
||||
*/
|
||||
export const BLOCKS = 50;
|
||||
|
||||
/** The priority for deserializing workspace comments. */
|
||||
export const WORKSPACE_COMMENTS = 25;
|
||||
|
||||
145
core/serialization/workspace_comments.ts
Normal file
145
core/serialization/workspace_comments.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2024 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {ISerializer} from '../interfaces/i_serializer.js';
|
||||
import {Workspace} from '../workspace.js';
|
||||
import {WorkspaceSvg} from '../workspace_svg.js';
|
||||
import * as priorities from './priorities.js';
|
||||
import {WorkspaceComment} from '../comments/workspace_comment.js';
|
||||
import {RenderedWorkspaceComment} from '../comments/rendered_workspace_comment.js';
|
||||
import * as eventUtils from '../events/utils.js';
|
||||
import {Coordinate} from '../utils/coordinate.js';
|
||||
import * as serializationRegistry from './registry.js';
|
||||
import {Size} from '../utils/size.js';
|
||||
|
||||
export interface State {
|
||||
id?: string;
|
||||
text?: string;
|
||||
x?: number;
|
||||
y?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
collapsed?: boolean;
|
||||
editable?: boolean;
|
||||
movable?: boolean;
|
||||
deletable?: boolean;
|
||||
}
|
||||
|
||||
/** Serializes the state of the given comment to JSON. */
|
||||
export function save(
|
||||
comment: WorkspaceComment,
|
||||
{
|
||||
addCoordinates = false,
|
||||
saveIds = true,
|
||||
}: {
|
||||
addCoordinates?: boolean;
|
||||
saveIds?: boolean;
|
||||
} = {},
|
||||
): State {
|
||||
const state: State = Object.create(null);
|
||||
|
||||
state.height = comment.getSize().height;
|
||||
state.width = comment.getSize().width;
|
||||
|
||||
if (saveIds) state.id = comment.id;
|
||||
if (addCoordinates) {
|
||||
state.x = comment.getRelativeToSurfaceXY().x;
|
||||
state.y = comment.getRelativeToSurfaceXY().y;
|
||||
}
|
||||
|
||||
if (comment.getText()) state.text = comment.getText();
|
||||
if (comment.isCollapsed()) state.collapsed = true;
|
||||
if (!comment.isOwnEditable()) state.editable = false;
|
||||
if (!comment.isOwnMovable()) state.movable = false;
|
||||
if (!comment.isOwnDeletable()) state.deletable = false;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/** Appends the comment defined by the given state to the given workspace. */
|
||||
export function append(
|
||||
state: State,
|
||||
workspace: Workspace,
|
||||
{recordUndo = false}: {recordUndo?: boolean} = {},
|
||||
): WorkspaceComment {
|
||||
const prevRecordUndo = eventUtils.getRecordUndo();
|
||||
eventUtils.setRecordUndo(recordUndo);
|
||||
|
||||
const comment =
|
||||
workspace instanceof WorkspaceSvg
|
||||
? new RenderedWorkspaceComment(workspace, state.id)
|
||||
: new WorkspaceComment(workspace, state.id);
|
||||
|
||||
if (state.text !== undefined) comment.setText(state.text);
|
||||
if (state.x !== undefined || state.y !== undefined) {
|
||||
const defaultLoc = comment.getRelativeToSurfaceXY();
|
||||
comment.moveTo(
|
||||
new Coordinate(state.x ?? defaultLoc.x, state.y ?? defaultLoc.y),
|
||||
);
|
||||
}
|
||||
if (state.width !== undefined || state.height) {
|
||||
const defaultSize = comment.getSize();
|
||||
comment.setSize(
|
||||
new Size(
|
||||
state.width ?? defaultSize.width,
|
||||
state.height ?? defaultSize.height,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (state.collapsed !== undefined) comment.setCollapsed(state.collapsed);
|
||||
if (state.editable !== undefined) comment.setEditable(state.editable);
|
||||
if (state.movable !== undefined) comment.setMovable(state.movable);
|
||||
if (state.deletable !== undefined) comment.setDeletable(state.deletable);
|
||||
|
||||
eventUtils.setRecordUndo(prevRecordUndo);
|
||||
|
||||
return comment;
|
||||
}
|
||||
|
||||
// Alias to disambiguate saving within the serializer.
|
||||
const saveComment = save;
|
||||
|
||||
/** Serializer for saving and loading workspace comment state. */
|
||||
export class WorkspaceCommentSerializer implements ISerializer {
|
||||
priority = priorities.WORKSPACE_COMMENTS;
|
||||
|
||||
/**
|
||||
* Returns the state of all workspace comments in the given workspace.
|
||||
*/
|
||||
save(workspace: Workspace): State[] | null {
|
||||
const commentStates = [];
|
||||
for (const comment of workspace.getTopComments()) {
|
||||
const state = saveComment(comment as AnyDuringMigration, {
|
||||
addCoordinates: true,
|
||||
saveIds: true,
|
||||
});
|
||||
if (state) commentStates.push(state);
|
||||
}
|
||||
return commentStates.length ? commentStates : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the comments defined by the given state into the given
|
||||
* workspace.
|
||||
*/
|
||||
load(state: State[], workspace: Workspace) {
|
||||
for (const commentState of state) {
|
||||
append(commentState, workspace, {recordUndo: eventUtils.getRecordUndo()});
|
||||
}
|
||||
}
|
||||
|
||||
/** Disposes of any comments that exist on the given workspace. */
|
||||
clear(workspace: Workspace) {
|
||||
for (const comment of workspace.getTopComments()) {
|
||||
comment.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serializationRegistry.register(
|
||||
'workspaceComments',
|
||||
new WorkspaceCommentSerializer(),
|
||||
);
|
||||
@@ -894,4 +894,166 @@ suite('JSO Serialization', function () {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Workspace comments', function () {
|
||||
suite('IDs', function () {
|
||||
test('IDs are saved by default', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(
|
||||
this.workspace,
|
||||
'testID',
|
||||
);
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertProperty(json, 'id', 'testID');
|
||||
});
|
||||
|
||||
test('saving IDs can be disabled', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(
|
||||
this.workspace,
|
||||
'testID',
|
||||
);
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment, {
|
||||
saveIds: false,
|
||||
});
|
||||
|
||||
assertNoProperty(json, 'id');
|
||||
});
|
||||
});
|
||||
|
||||
suite('Coordinates', function () {
|
||||
test('coordinates are not saved by default', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.moveTo(new Blockly.utils.Coordinate(42, 1337));
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertNoProperty(json, 'x');
|
||||
assertNoProperty(json, 'y');
|
||||
});
|
||||
|
||||
test('saving coordinates can be enabled', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.moveTo(new Blockly.utils.Coordinate(42, 1337));
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment, {
|
||||
addCoordinates: true,
|
||||
});
|
||||
|
||||
assertProperty(json, 'x', 42);
|
||||
assertProperty(json, 'y', 1337);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Text', function () {
|
||||
test('the empty string is not saved', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setText('');
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertNoProperty(json, 'text');
|
||||
});
|
||||
|
||||
test('text is saved', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setText('test text');
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertProperty(json, 'text', 'test text');
|
||||
});
|
||||
});
|
||||
|
||||
test('size is saved', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setSize(new Blockly.utils.Size(42, 1337));
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertProperty(json, 'width', 42);
|
||||
assertProperty(json, 'height', 1337);
|
||||
});
|
||||
|
||||
suite('Collapsed', function () {
|
||||
test('collapsed is not saved if false', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setCollapsed(false);
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertNoProperty(json, 'collapsed');
|
||||
});
|
||||
|
||||
test('collapsed is saved if true', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setCollapsed(true);
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertProperty(json, 'collapsed', true);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Editable', function () {
|
||||
test('editable is not saved if true', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setEditable(true);
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertNoProperty(json, 'editable');
|
||||
});
|
||||
|
||||
test('editable is saved if false', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setEditable(false);
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertProperty(json, 'editable', false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Movable', function () {
|
||||
test('movable is not saved if true', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setMovable(true);
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertNoProperty(json, 'movable');
|
||||
});
|
||||
|
||||
test('movable is saved if false', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setMovable(false);
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertProperty(json, 'movable', false);
|
||||
});
|
||||
});
|
||||
|
||||
suite('Deletable', function () {
|
||||
test('deletable is not saved if true', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setDeletable(true);
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertNoProperty(json, 'deletable');
|
||||
});
|
||||
|
||||
test('deletable is saved if false', function () {
|
||||
const comment = new Blockly.comments.WorkspaceComment(this.workspace);
|
||||
comment.setDeletable(false);
|
||||
|
||||
const json = Blockly.serialization.workspaceComments.save(comment);
|
||||
|
||||
assertProperty(json, 'deletable', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user