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:
Beka Westberg
2024-03-20 19:40:27 +00:00
committed by GitHub
parent fd1a02ff37
commit 407ff44e18
5 changed files with 316 additions and 0 deletions

View File

@@ -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;
}

View File

@@ -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,
};

View File

@@ -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;

View 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(),
);