Skip to content
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

Dance captures thumbnails #25748

Merged
merged 13 commits into from Nov 11, 2018
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions apps/src/code-studio/initApp/project.js
Expand Up @@ -87,6 +87,7 @@ var isEditing = false;
let initialSaveComplete = false;
let initialCaptureComplete = false;
let thumbnailChanged = false;
let thumbnailPngBlob = null;

/**
* Current state of our sources API data
Expand Down Expand Up @@ -792,6 +793,11 @@ var projects = module.exports = {

if (preparingRemix) {
return this.sourceHandler.prepareForRemix().then(completeAsyncSave);
} else if (thumbnailPngBlob) {
const blob = thumbnailPngBlob;
thumbnailPngBlob = null;
// Call completeAsyncSave even if thumbnail save fails.
return this.saveThumbnail(blob).then(completeAsyncSave, completeAsyncSave);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

For later: If we can stop depending on the thumbnailURL being saved to the channel row, we'd be able to fire these requests simultaneously! I'm not sure why we do that, since it's always in the same place.

} else {
return completeAsyncSave();
}
Expand Down Expand Up @@ -1267,6 +1273,15 @@ var projects = module.exports = {
return current && current.thumbnailUrl;
},

/**
* Sets the thumbnailPngBlob variable. Caveat: This does not save the thumbnail to the current project.
* Use the saveThumbnail method to do that.
* @param {Blob} pngBlob A Blob in PNG format containing the thumbnail image.
*/
setThumbnailPngBlob(pngBlob) {
thumbnailPngBlob = pngBlob;
},

/**
* Uploads a thumbnail image to the thumbnail path in the files API. If
* successful, stores a URL to access the thumbnail in current.thumbnailUrl.
Expand Down
22 changes: 20 additions & 2 deletions apps/src/dance/Dance.js
Expand Up @@ -16,6 +16,7 @@ import trackEvent from '../util/trackEvent';
import {SignInState} from '../code-studio/progressRedux';
import logToCloud from '../logToCloud';
import {saveReplayLog} from '../code-studio/components/shareDialogRedux';
import {setThumbnailBlobFromCanvas} from '../util/thumbnail';
import SignInOrAgeDialog from "../templates/SignInOrAgeDialog";
import project from "../code-studio/initApp/project";
import {
Expand Down Expand Up @@ -82,10 +83,10 @@ Dance.prototype.init = function (config) {
this.danceReadyPromise = new Promise(resolve => {
this.danceReadyPromiseResolve = resolve;
});

this.studioApp_.labUserId = config.labUserId;

this.level.softButtons = this.level.softButtons || {};
this.tickCount = 0;
this.thumbnailBlob = null;

config.afterClearPuzzle = function () {
this.studioApp_.resetButtonClick();
Expand Down Expand Up @@ -560,6 +561,8 @@ Dance.prototype.updateSongMetadata = function (id) {
*/
Dance.prototype.onHandleEvents = function (currentFrameEvents) {
this.hooks.find(v => v.name === 'runUserEvents').func(currentFrameEvents);
this.tickCount += 1;
this.captureThumbnailImage();
maddiedierker marked this conversation as resolved.
Show resolved Hide resolved
};

/**
Expand All @@ -585,3 +588,18 @@ Dance.prototype.displayFeedback_ = function () {
Dance.prototype.getAppReducers = function () {
return reducers;
};

// Number of ticks after which to capture a thumbnail image of the play space.
const CAPTURE_TICK_INTERVAL = 5;

/**
* Capture a thumbnail image of the play space every CAPTURE_TICK_INTERVAL ticks.
*/
Dance.prototype.captureThumbnailImage = function () {
if (this.tickCount % CAPTURE_TICK_INTERVAL !== 0) {
return;
}

// Set PNG blob on project from current canvas
setThumbnailBlobFromCanvas(document.getElementById('defaultCanvas0'));
};
29 changes: 26 additions & 3 deletions apps/src/util/thumbnail.js
Expand Up @@ -72,21 +72,44 @@ export function captureThumbnailFromSvg(svg) {

/**
* Copies the image from the canvas, shrinks it to a width equal to
* THUMBNAIL_WIDTH preserving aspect ratio, and saves it to the server.
* THUMBNAIL_WIDTH preserving aspect ratio, and returns the thumbnail blob
* to a callback method.
* @param {HTMLCanvasElement} canvas
* @param {func} onComplete
*/
export function captureThumbnailFromCanvas(canvas) {
export function getThumbnailFromCanvas(canvas, onComplete) {
if (!canvas) {
console.warn(`Thumbnail capture failed: canvas element not found.`);
onComplete(null);
return;
}
if (!shouldCapture()) {
onComplete(null);
return;
}
lastCaptureTimeMs = Date.now();

const thumbnailCanvas = createThumbnail(canvas);
canvasToBlob(thumbnailCanvas).then(project.saveThumbnail);
canvasToBlob(thumbnailCanvas).then(onComplete);
}

/**
* Copies the image from the canvas, shrinks it to a width equal to
* THUMBNAIL_WIDTH preserving aspect ratio, and saves it to the server.
* @param {HTMLCanvasElement} canvas
*/
export function captureThumbnailFromCanvas(canvas) {
getThumbnailFromCanvas(canvas, project.saveThumbnail);
}

/**
* Copies the image from the canvas, shrinks it to a width equal to
* THUMBNAIL_WIDTH preserving aspect ratio, and saves it in memory.
* When the project is saved, the thumbnail will be saved as well.
* @param {HTMLCanvasElement} canvas
*/
export function setThumbnailBlobFromCanvas(canvas) {
getThumbnailFromCanvas(canvas, project.setThumbnailPngBlob);
}

/**
Expand Down