diff --git a/src/lib/project-info.js b/src/lib/project-info.js
index ac16d29be..30b82e777 100644
--- a/src/lib/project-info.js
+++ b/src/lib/project-info.js
@@ -20,7 +20,8 @@ module.exports = {
const stage = project.targets[0];
return Object.values(stage.variables)
.some(variable => variable.length === 3); // 3 entries if cloud var
- }
+ },
+ videoSensing: project => (project.extensions || []).includes('videoSensing')
},
2: {
extensions: () => [], // Showing extension chip not implemented for scratch2 projects
@@ -30,6 +31,11 @@ module.exports = {
// Block traversing is complicated in scratch2 projects...
// This check should work even if you have sprites named getUserName, etc.
JSON.stringify(project).indexOf('["getUserName"]') !== -1,
- cloudData: project => project.info.hasCloudData
+ cloudData: project => project.info.hasCloudData,
+ videoSensing: project => {
+ const stringifiedProject = JSON.stringify(project);
+ return ['senseVideoMotion', 'setVideoState', 'setVideoTransparency', 'whenSensorGreaterThan']
+ .some(opcode => stringifiedProject.includes(`["${opcode}"`));
+ }
}
};
diff --git a/src/views/preview/l10n.json b/src/views/preview/l10n.json
index 31271fbf2..e54594781 100644
--- a/src/views/preview/l10n.json
+++ b/src/views/preview/l10n.json
@@ -47,5 +47,6 @@
"project.cloudDataLink": "See Data",
"project.usernameBlockAlert": "This project can detect who is using it, through the \"username\" block. To hide your identity, sign out before using the project.",
"project.inappropriateUpdate": "Hmm...the bad word detector thinks there is a problem with your text. Please change it and remember to be respectful.",
- "project.mutedAddToStudio": "You will be able to add to studios again {inDuration}."
+ "project.mutedAddToStudio": "You will be able to add to studios again {inDuration}.",
+ "project.cloudDataAndVideoAlert": "For privacy reasons, cloud variables have been disabled in this project because it contains video sensing blocks."
}
diff --git a/src/views/preview/presentation.jsx b/src/views/preview/presentation.jsx
index f900fde88..2e56d2b0e 100644
--- a/src/views/preview/presentation.jsx
+++ b/src/views/preview/presentation.jsx
@@ -125,6 +125,7 @@ const PreviewPresentation = ({
originalInfo,
parentInfo,
showCloudDataAlert,
+ showCloudDataAndVideoAlert,
showUsernameBlockAlert,
projectHost,
projectId,
@@ -335,16 +336,23 @@ const PreviewPresentation = ({
{fullscreen: isFullScreen}
)}
>
- {showCloudDataAlert && (
-
-
-
- )}
- {showUsernameBlockAlert && (
-
-
-
- )}
+
+ {showCloudDataAlert && (
+
+
+
+ )}
+ {showCloudDataAndVideoAlert && (
+
+
+
+ )}
+ {showUsernameBlockAlert && (
+
+
+
+ )}
+
{
+ test('videoSensing returns true for a version 3 project with video', () => {
+ const result = projectInfo[videoVersion3.projectVersion].videoSensing(videoVersion3);
+ expect(result).toEqual(true);
+ });
+
+ test('videoSensing returns false for a version 3 project with no video', () => {
+ const result = projectInfo[noVideoVersion3.projectVersion].videoSensing(noVideoVersion3);
+ expect(result).toEqual(false);
+ });
+
+ test('videoSensing returns true for a version 2 project with video', () => {
+ const result = projectInfo[videoVersion2.projectVersion].videoSensing(videoVersion2);
+ expect(result).toEqual(true);
+ });
+});
+
+const videoVersion3 = {
+ targets: [
+ {
+ isStage: true,
+ name: 'Stage',
+ variables: {
+ '`jEk@4|i[#Fk?(8x)AV.-my variable': [
+ 'my variable',
+ '0'
+ ]
+ },
+ lists: {},
+ broadcasts: {},
+ blocks: {
+ 'FJz[,QI8`P^5;FEjdBhc': {
+ opcode: 'event_whenflagclicked',
+ next: 'f8q%j#X8sU#C+E1z|-oF',
+ parent: null,
+ inputs: {},
+ fields: {},
+ shadow: false,
+ topLevel: true,
+ x: 255,
+ y: 171
+ },
+ 'f8q%j#X8sU#C+E1z|-oF': {
+ opcode: 'videoSensing_videoToggle',
+ next: null,
+ parent: 'FJz[,QI8`P^5;FEjdBhc',
+ inputs: {
+ VIDEO_STATE: [
+ 1,
+ 'a2$KXEUlr`=IW!MX8(M7'
+ ]
+ },
+ fields: {},
+ shadow: false,
+ topLevel: false
+ },
+ 'a2$KXEUlr`=IW!MX8(M7': {
+ opcode: 'videoSensing_menu_VIDEO_STATE',
+ next: null,
+ parent: 'f8q%j#X8sU#C+E1z|-oF',
+ inputs: {},
+ fields: {
+ VIDEO_STATE: [
+ 'on',
+ null
+ ]
+ },
+ shadow: true,
+ topLevel: false
+ }
+ },
+ comments: {},
+ currentCostume: 0,
+ costumes: [
+ {
+ name: 'backdrop1',
+ dataFormat: 'svg',
+ assetId: 'cd21514d0531fdffb22204e0ec5ed84a',
+ md5ext: 'cd21514d0531fdffb22204e0ec5ed84a.svg',
+ rotationCenterX: 240,
+ rotationCenterY: 180
+ }
+ ],
+ sounds: [
+ {
+ name: 'pop',
+ assetId: '83a9787d4cb6f3b7632b4ddfebf74367',
+ dataFormat: 'wav',
+ format: '',
+ rate: 48000,
+ sampleCount: 1123,
+ md5ext: '83a9787d4cb6f3b7632b4ddfebf74367.wav'
+ }
+ ],
+ volume: 100,
+ layerOrder: 0,
+ tempo: 60,
+ videoTransparency: 50,
+ videoState: 'on',
+ textToSpeechLanguage: null
+ }
+ ],
+ monitors: [
+ {
+ id: '`jEk@4|i[#Fk?(8x)AV.-my variable',
+ mode: 'default',
+ opcode: 'data_variable',
+ params: {
+ VARIABLE: 'my variable'
+ },
+ spriteName: null,
+ value: '0',
+ width: 0,
+ height: 0,
+ x: 7,
+ y: 17,
+ visible: false,
+ sliderMin: 0,
+ sliderMax: 100,
+ isDiscrete: true
+ }
+ ],
+ extensions: [
+ 'videoSensing'
+ ],
+ meta: {
+ semver: '3.0.0',
+ vm: '1.2.48',
+ // eslint-disable-next-line max-len
+ agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'
+ },
+ projectVersion: 3
+};
+
+const noVideoVersion3 = {
+ targets: [
+ {
+ isStage: true,
+ name: 'Stage',
+ variables: {
+ '`jEk@4|i[#Fk?(8x)AV.-my variable': [
+ 'my variable',
+ '0'
+ ]
+ },
+ lists: {},
+ broadcasts: {},
+ blocks: {
+ 'FJz[,QI8`P^5;FEjdBhc': {
+ opcode: 'event_whenflagclicked',
+ next: '`CA90wtKfX0xa.mK80[|',
+ parent: null,
+ inputs: {},
+ fields: {},
+ shadow: false,
+ topLevel: true,
+ x: 255,
+ y: 171
+ },
+ '`CA90wtKfX0xa.mK80[|': {
+ opcode: 'data_setvariableto',
+ next: null,
+ parent: 'FJz[,QI8`P^5;FEjdBhc',
+ inputs: {
+ VALUE: [
+ 1,
+ [
+ 10,
+ '0'
+ ]
+ ]
+ },
+ fields: {
+ VARIABLE: [
+ 'my variable',
+ '`jEk@4|i[#Fk?(8x)AV.-my variable'
+ ]
+ },
+ shadow: false,
+ topLevel: false
+ }
+ },
+ comments: {},
+ currentCostume: 0,
+ costumes: [
+ {
+ name: 'backdrop1',
+ dataFormat: 'svg',
+ assetId: 'cd21514d0531fdffb22204e0ec5ed84a',
+ md5ext: 'cd21514d0531fdffb22204e0ec5ed84a.svg',
+ rotationCenterX: 240,
+ rotationCenterY: 180
+ }
+ ],
+ sounds: [
+ {
+ name: 'pop',
+ assetId: '83a9787d4cb6f3b7632b4ddfebf74367',
+ dataFormat: 'wav',
+ format: '',
+ rate: 48000,
+ sampleCount: 1123,
+ md5ext: '83a9787d4cb6f3b7632b4ddfebf74367.wav'
+ }
+ ],
+ volume: 100,
+ layerOrder: 0,
+ tempo: 60,
+ videoTransparency: 50,
+ videoState: 'on',
+ textToSpeechLanguage: null
+ }
+ ],
+ monitors: [
+ {
+ id: '`jEk@4|i[#Fk?(8x)AV.-my variable',
+ mode: 'default',
+ opcode: 'data_variable',
+ params: {
+ VARIABLE: 'my variable'
+ },
+ spriteName: null,
+ value: '0',
+ width: 0,
+ height: 0,
+ x: 7,
+ y: 17,
+ visible: false,
+ sliderMin: 0,
+ sliderMax: 100,
+ isDiscrete: true
+ }
+ ],
+ extensions: [],
+ meta: {
+ semver: '3.0.0',
+ vm: '1.2.48',
+ // eslint-disable-next-line max-len
+ agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'
+ },
+ projectVersion: 3
+};
+
+const videoVersion2 = {
+ objName: 'Stage',
+ sounds: [{
+ soundName: 'pop',
+ soundID: 1,
+ md5: '83a9787d4cb6f3b7632b4ddfebf74367.wav',
+ sampleCount: 258,
+ rate: 11025,
+ format: ''
+ }],
+ costumes: [{
+ costumeName: 'backdrop1',
+ baseLayerID: 3,
+ baseLayerMD5: '739b5e2a2435f6e1ec2993791b423146.png',
+ bitmapResolution: 1,
+ rotationCenterX: 240,
+ rotationCenterY: 180
+ }],
+ currentCostumeIndex: 0,
+ penLayerMD5: '5c81a336fab8be57adc039a8a2b33ca9.png',
+ penLayerID: 0,
+ tempoBPM: 60,
+ videoAlpha: 0.5,
+ children: [{
+ objName: 'Sprite1',
+ scripts: [[62,
+ 85,
+ [['whenGreenFlag'], ['doForever', [['say:', ['senseVideoMotion', 'motion', 'this sprite']]]]]],
+ [70, 216, [['setVideoState', 'on']]],
+ [66, 281, [['setVideoTransparency', 50]]]],
+ sounds: [{
+ soundName: 'meow',
+ soundID: 0,
+ md5: '83c36d806dc92327b9e7049a565c6bff.wav',
+ sampleCount: 18688,
+ rate: 22050,
+ format: ''
+ }],
+ costumes: [{
+ costumeName: 'costume1',
+ baseLayerID: 1,
+ baseLayerMD5: 'f9a1c175dbe2e5dee472858dd30d16bb.svg',
+ bitmapResolution: 1,
+ rotationCenterX: 47,
+ rotationCenterY: 55
+ },
+ {
+ costumeName: 'costume2',
+ baseLayerID: 2,
+ baseLayerMD5: '6e8bd9ae68fdb02b7e1e3df656a75635.svg',
+ bitmapResolution: 1,
+ rotationCenterX: 47,
+ rotationCenterY: 55
+ }],
+ currentCostumeIndex: 0,
+ scratchX: 0,
+ scratchY: 0,
+ scale: 1,
+ direction: 90,
+ rotationStyle: 'normal',
+ isDraggable: false,
+ indexInLibrary: 1,
+ visible: true,
+ spriteInfo: {
+ }
+ }],
+ info: {
+ userAgent: 'Scratch 2.0 Offline Editor',
+ flashVersion: 'WIN 33,1,1,743',
+ spriteCount: 1,
+ videoOn: false,
+ scriptCount: 1,
+ swfVersion: 'v461'
+ },
+ projectVersion: 2
+};