Skip to content

Refactor comments #2502

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

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
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
8 changes: 8 additions & 0 deletions src/engine/blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,14 @@ class Blocks {
}
}

// Delete comments attached to the block.
if (block.comment) {
const editingTarget = this.runtime.getEditingTarget();
if (editingTarget && editingTarget.comments.hasOwnProperty(block.comment)) {
delete editingTarget.comments[block.comment];
}
}

// Delete any script starting with this block.
this._deleteScript(blockId);

Expand Down
5 changes: 3 additions & 2 deletions src/engine/target.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ class Target extends EventEmitter {
/**
* @param {Runtime} runtime Reference to the runtime.
* @param {?Blocks} blocks Blocks instance for the blocks owned by this target.
* @param {?Object.<string, Comment>} comments Array of comments owned by this target.
* @constructor
*/
constructor (runtime, blocks) {
constructor (runtime, blocks, comments) {
super();

if (!blocks) {
Expand Down Expand Up @@ -55,7 +56,7 @@ class Target extends EventEmitter {
* Key is the comment id.
* @type {Object.<string,*>}
*/
this.comments = {};
this.comments = comments || {};
/**
* Dictionary of custom state for this target.
* This can be used to store target-specific custom state for blocks which need it.
Expand Down
2 changes: 1 addition & 1 deletion src/sprites/rendered-target.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class RenderedTarget extends Target {
* @constructor
*/
constructor (sprite, runtime) {
super(runtime, sprite.blocks);
super(runtime, sprite.blocks, sprite.comments);

/**
* Reference to the sprite that this is a render of.
Expand Down
48 changes: 47 additions & 1 deletion src/sprites/sprite.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const RenderedTarget = require('./rendered-target');
const Blocks = require('../engine/blocks');
const Comment = require('../engine/comment');
const {loadSoundFromAsset} = require('../import/load-sound');
const {loadCostumeFromAsset} = require('../import/load-costume');
const newBlockIds = require('../util/new-block-ids');
const StringUtil = require('../util/string-util');
const StageLayering = require('../engine/stage-layering');
const log = require('../util/log');

class Sprite {
/**
Expand Down Expand Up @@ -54,6 +56,13 @@ class Sprite {
if (this.runtime && this.runtime.audioEngine) {
this.soundBank = this.runtime.audioEngine.createBank();
}

/**
* Dictionary of comments for this target.
* Key is the comment id.
* @type {Object.<string, Comment>}
*/
this.comments = {};
}

/**
Expand Down Expand Up @@ -140,11 +149,48 @@ class Sprite {
const blocksContainer = this.blocks._blocks;
const originalBlocks = Object.keys(blocksContainer).map(key => blocksContainer[key]);
const copiedBlocks = JSON.parse(JSON.stringify(originalBlocks));
newBlockIds(copiedBlocks);
const oldToNew = newBlockIds(copiedBlocks);
copiedBlocks.forEach(block => {
newSprite.blocks.createBlock(block);
});

Object.keys(this.comments).forEach(commentId => {
const comment = this.comments[commentId];
const newComment = new Comment(
null, // generate new comment id
comment.text,
comment.x,
comment.y,
comment.width,
comment.height,
comment.minimized
);
// If the comment is attached to a block, it has a blockId property for the block it's attached to.
// Because we generate new block IDs for all the duplicated blocks, change all the comments' attached-block
// IDs from the old block IDs to the new ones.
if (comment.blockId) {
const newBlockId = oldToNew[comment.blockId];
if (newBlockId) {
newComment.blockId = newBlockId;
const newBlock = newSprite.blocks.getBlock(newBlockId);
if (newBlock) {
newBlock.comment = newComment.id;
} else {
log.warn(`Could not find block with id ${newBlockId} associated with comment ${newComment.id}`);
}
} else {
// Comments did not get deleted when the block got deleted
// Such comments have blockId, but because the block is deleted
// oldToNew mapping does not include that blockId
// Handle it as workspace comment
// TODO do not load such comments when deserializing
log.warn(`Could not find block with id ${comment.blockId
} associated with comment ${comment.id} in the block ID mapping`);
}
}
newSprite.comments[newComment.id] = newComment;
});


const allNames = this.runtime.targets.map(t => t.sprite.name);
newSprite.name = StringUtil.unusedName(this.name, allNames);
Expand Down
4 changes: 4 additions & 0 deletions src/util/new-block-ids.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const uid = require('./uid');
* Mutate the given blocks to have new IDs and update all internal ID references.
* Does not return anything to make it clear that the blocks are updated in-place.
* @param {array} blocks - blocks to be mutated.
* @returns {object.<string, string>} - mapping of old block ID to new block ID
*/
module.exports = blocks => {
const oldToNew = {};
Expand All @@ -30,4 +31,7 @@ module.exports = blocks => {
blocks[i].next = oldToNew[blocks[i].next];
}
}

// There are other things that need this mapping e.g. comments
return oldToNew;
};
19 changes: 19 additions & 0 deletions test/unit/engine_blocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,25 @@ test('delete inputs', t => {
t.end();
});

test('delete block with comment', t => {
const b = new Blocks(new Runtime());
const fakeTarget = {
comments: {
bar: {
blockId: 'foo'
}
}
};
b.runtime.getEditingTarget = () => fakeTarget;
b.createBlock({
id: 'foo',
comment: 'bar'
});
b.deleteBlock('foo');
t.notOk(fakeTarget.comments.hasOwnProperty('bar'));
t.end();
});

test('updateAssetName function updates name in sound field', t => {
const b = new Blocks(new Runtime());
b.createBlock({
Expand Down
15 changes: 15 additions & 0 deletions test/unit/sprites_rendered-target.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ test('blocks get new id on duplicate', t => {
});
});

test('comments are duplicated when duplicating target', t => {
const r = new Runtime();
const s = new Sprite(null, r);
const rt = new RenderedTarget(s, r);
rt.createComment('commentid', null, 'testcomment', 0, 0, 100, 100, false);
t.ok(s.comments.hasOwnProperty('commentid'));
return rt.duplicate().then(duplicate => {
// Not ok because comment id should be re-generated
t.notOk(duplicate.comments.hasOwnProperty('commentid'));
t.ok(Object.keys(duplicate.comments).length === 1);
t.end();
});
});


test('direction', t => {
const r = new Runtime();
const s = new Sprite(null, r);
Expand Down