Skip to content

Commit cfeb44f

Browse files
committed
listening for block drop changes and extracting project into localstorage
1 parent 4b1e9b6 commit cfeb44f

File tree

2 files changed

+106
-4
lines changed

2 files changed

+106
-4
lines changed

src/components/Editor/Project/Project.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ const Project = (props) => {
9696
const scratchGuiProps = {
9797
locale: "en",
9898
menuBarHidden: true,
99-
assetHost: "https://editor-scratch.raspberrypi.org/api/assets",
100-
basePath: scratchBasePath,
101-
projectId: scratchProjectId,
102-
projectHost: scratchProjectHost,
99+
// assetHost: "https://editor-scratch.raspberrypi.org/api/assets",
100+
// basePath: scratchBasePath,
101+
// projectId: scratchProjectId,
102+
// projectHost: scratchProjectHost,
103103
};
104104

105105
return (

src/components/Editor/Project/ScratchIntegrationHOC.jsx

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,114 @@ const ScratchIntegrationHOC = function (WrappedComponent) {
2020
"handleUpload",
2121
"handleRemix",
2222
"handleSave",
23+
"handleBlocksChanged",
2324
]);
2425
}
2526
componentDidMount() {
2627
window.addEventListener("message", this.handleMessage);
2728
this.props.setStageSize();
29+
if (this.props.vm) {
30+
console.log("Setting up VM listeners in componentDidMount...");
31+
this.setupVMListeners();
32+
} else {
33+
console.log("VM not available yet in componentDidMount.");
34+
}
35+
}
36+
37+
componentDidUpdate(prevProps) {
38+
// Set up listeners when VM becomes available
39+
if (!prevProps.vm && this.props.vm) {
40+
console.log("Setting up VM listeners in componentDidUpdate...");
41+
this.setupVMListeners();
42+
}
2843
}
44+
2945
componentWillUnmount() {
3046
window.removeEventListener("message", this.handleMessage);
47+
this.removeVMListeners();
48+
}
49+
50+
setupVMListeners() {
51+
const vm = this.props.vm;
52+
if (!vm) return;
53+
54+
console.log("=== Looking for Blockly workspace ===");
55+
56+
// Method 1: Check for global Blockly
57+
if (window.Blockly) {
58+
console.log("Found global Blockly:", window.Blockly);
59+
const workspace = window.Blockly.getMainWorkspace?.();
60+
console.log("Blockly main workspace:", workspace);
61+
62+
if (workspace) {
63+
workspace.addChangeListener((event) => {
64+
console.log("Blockly workspace change event:", event);
65+
console.log("Event type:", event.type);
66+
if (event.type === "endDrag") {
67+
this.handleBlocksChanged();
68+
}
69+
});
70+
console.log("✓ Added Blockly workspace change listener");
71+
return; // Success!
72+
}
73+
}
74+
// const vm = this.props.vm;
75+
// if (!vm) return;
76+
77+
// // if (vm.runtime.getEditingTarget()) {
78+
// // const workspace = vm.runtime.getEditingTarget().blocks;
79+
// console.log(vm);
80+
// console.log(vm.runtime);
81+
// console.log(vm.runtime.constructor.PROJECT_CHANGED);
82+
// vm.runtime.on('BLOCK_DRAG_UPDATE', this.handleBlocksChanged);
83+
// // workspace.on('BLOCK_CREATE', this.handleBlocksChanged);
84+
// // workspace.on('BLOCK_DELETE', this.handleBlocksChanged);
85+
// // }
86+
// console.log("Blocks changed listener set up...")
87+
// // this.startPolling();
88+
}
89+
90+
removeVMListeners() {
91+
// Clean up any listeners set up in setupVMListeners
92+
const vm = this.props.vm;
93+
if (!vm) return;
94+
95+
// const workspace = vm.runtime.getEditingTarget()?.blocks;
96+
vm.runtime.removeListener('BLOCK_DRAG_UPDATE', this.handleBlocksChanged);
3197
}
98+
handleBlocksChanged() {
99+
console.log("Blocks have changed");
100+
101+
// Debounce to avoid saving on every tiny change
102+
if (this.saveTimeout) {
103+
clearTimeout(this.saveTimeout);
104+
}
105+
106+
this.saveTimeout = setTimeout(() => {
107+
if (this.props.saveProjectSb3) {
108+
this.props.saveProjectSb3().then((sb3Content) => {
109+
console.log("Autosaving project...", sb3Content);
110+
111+
// Convert Blob/ArrayBuffer to base64 for localStorage
112+
const reader = new FileReader();
113+
reader.onloadend = () => {
114+
const base64String = reader.result.split(',')[1]; // Remove data:application/octet-stream;base64, prefix
115+
localStorage.setItem("autosavedProject", base64String);
116+
console.log("Project saved to localStorage (base64)");
117+
};
118+
reader.readAsDataURL(sb3Content);
119+
120+
// This sb3Content is what you'd send to your save API
121+
// It's the complete .sb3 file content
122+
});
123+
}
124+
}, 2000); // Wait 2 seconds after last change
125+
};
126+
32127
handleMessage(event) {
128+
// These are events sent from the page telling Scratch GUI to do certain things.
129+
// Here we are telling Scratch GUI how to do those things.
130+
// We want this the other way around in some of these cases.
33131
if (event.origin !== window.location.origin) return;
34132

35133
switch (event.data.type) {
@@ -103,13 +201,17 @@ const ScratchIntegrationHOC = function (WrappedComponent) {
103201
saveProjectSb3: null,
104202
loadProject: null,
105203
vmReady: false,
204+
vm: null,
106205
};
206+
} else {
207+
console.log("Scratch VM is initialized");
107208
}
108209

109210
return {
110211
saveProjectSb3: vm.saveProjectSb3?.bind(vm),
111212
loadProject: vm.loadProject?.bind(vm),
112213
vmReady: true,
214+
vm: vm,
113215
};
114216
};
115217

0 commit comments

Comments
 (0)