- Fix a few dependencies in the Jakefile.
- Add metadata support. Currently only offering script name and script
description.
  * Forward the metadata properly to the backend. Reflect it in the local
    storage.
  * Update the messaging protocol accordingly.
  * Add some basic UI for that purpose in the Blockly editor.
  * Merge that data too.
This commit is contained in:
Jonathan Protzenko 2015-04-01 15:32:15 -07:00
Родитель 64424a0758
Коммит 924cc93a00
9 изменённых файлов: 114 добавлений и 23 удалений

Просмотреть файл

@ -241,8 +241,14 @@ mkSimpleTask('build/runner.d.ts', [
'build/libcordova.d.ts',
'runner'
], "runner/refs.ts");
mkSimpleTask('build/ace.js', [ "www/ace/ace-main.ts" ], "www/ace/refs.ts");
mkSimpleTask('build/blockly.js', [ "www/blockly/blockly-main.ts" ], "www/blockly/refs.ts");
mkSimpleTask('build/ace.js', [
"www/ace/ace-main.ts",
"editor/messages.ts"
], "www/ace/refs.ts");
mkSimpleTask('build/blockly.js', [
"www/blockly/blockly-main.ts",
"editor/messages.ts"
], "www/blockly/refs.ts");
// Now come the rules for files that are obtained by concatenating multiple
// _js_ files into another one. The sequence exactly reproduces what happened

Просмотреть файл

@ -2984,7 +2984,7 @@ module TDev
}
else return resp;
})
.then(resp => {
.then(resp => {
header = resp;
if (!header) Util.oops(lf("script not installed"));
return this.saveStateAsync({ forReal: true });
@ -3058,6 +3058,7 @@ module TDev
scriptVersionInCloud: scriptVersionInCloud,
editorState: editorState,
baseSnapshot: header.scriptVersion.baseSnapshot,
metadata: header.meta,
});
ProgressOverlay.hide();
return new PromiseInv();

Просмотреть файл

@ -79,6 +79,18 @@ module TDev {
var scriptText = message.script.scriptText;
var editorState = message.script.editorState;
header.scriptVersion.baseSnapshot = message.script.baseSnapshot;
var metadata = message.script.metadata;
Object.keys(metadata).forEach(k => {
var v = metadata[k];
if (k == "name")
v = v || "unnamed";
header.meta[k] = v;
});
// [name] deserves a special treatment because it
// appears both on the header and in the metadata.
header.name = metadata.name;
// Writes into local storage.
World.updateInstalledScriptAsync(header, scriptText, editorState, false, "").then(() => {
console.log("[external] script saved properly");
@ -88,6 +100,7 @@ module TDev {
status: Status.Ok,
});
});
// Schedules a cloud sync; set the right state so
// that [scheduleSaveToCloudAsync] writes the
// baseSnapshot where we can read it back.
@ -175,6 +188,7 @@ module TDev {
editorState: string;
scriptVersionInCloud: string;
baseSnapshot: string;
metadata: Metadata;
};
// The [scriptVersionInCloud] name is the one that's used by [world.ts];
@ -207,11 +221,7 @@ module TDev {
var extra = JSON.parse(data.scriptVersionInCloud || "{}");
TheChannel.post(<Message_Init>{
type: MessageType.Init,
script: {
scriptText: data.scriptText,
editorState: data.editorState,
baseSnapshot: data.baseSnapshot,
},
script: data,
merge: ("theirs" in extra) ? extra : null
});
});

Просмотреть файл

@ -68,6 +68,12 @@ module TDev {
scriptText: string;
editorState: string;
baseSnapshot: string;
metadata: Metadata; // Must be set to the correct value every time.
}
export interface Metadata {
name: string;
description: string;
}
// In case local and remote modifications have been posted on top of the same cloud

Просмотреть файл

@ -80,8 +80,9 @@ module TDev {
if (script && !header.editor && (!header.meta || header.meta.comment === undefined))
header.meta = getScriptMeta(script);
if (header.editor && (!header.meta || !header.meta.name)) {
Util.log("ERROR pre-condition not met for [setInstalledAsync]");
Util.log("ERROR pre-condition not met for [setInstalledAsync]; bailing");
debugger;
return Promise.as();
}
headerItem[header.guid] = JSON.stringify(header);
var bodyItem = {}
@ -164,12 +165,14 @@ module TDev {
theirs: {
scriptText: theirs.script,
editorState: theirs.editorState,
baseSnapshot: header.scriptVersion.baseSnapshot
baseSnapshot: header.scriptVersion.baseSnapshot,
metadata: header.meta,
},
base: {
scriptText: baseVer.script,
editorState: baseVer.editorState,
baseSnapshot: hd.scriptVersion.baseSnapshot
baseSnapshot: hd.scriptVersion.baseSnapshot,
metadata: hd.meta,
},
}
};

Просмотреть файл

@ -22,6 +22,7 @@ module TDev {
// Also written once at initialization-time.
var editor: AceAjax.Editor = null;
var scriptName: string = null;
// A global that remembers the current version we're editing
var currentVersion: string;
@ -91,6 +92,7 @@ module TDev {
editor.setValue(message.script.scriptText);
editor.clearSelection();
scriptName = message.script.metadata.name;
log("[loaded] version from " + state.lastSave);
}
@ -105,6 +107,10 @@ module TDev {
lastSave: new Date()
}),
baseSnapshot: currentVersion,
metadata: {
name: scriptName,
description: ""
}
},
};
post(message);

Просмотреть файл

@ -12,6 +12,8 @@ module TDev {
"https://mbitmain.azurewebsites.net": null
};
var $ = (s: string) => document.querySelector(s);
// Both of these are written once when we receive the first (trusted)
// message.
var outer: Window = null;
@ -124,15 +126,29 @@ module TDev {
b.addEventListener("click", f);
return b;
};
var box = document.querySelector("#merge-commands");
var box = $("#merge-commands");
var clearMerge = () => {
while (box.firstChild)
box.removeChild(box.firstChild);
};
var mineText = saveBlockly();
var mineButton = mkButton("🔍", "see mine", () => loadBlockly(mineText));
var theirsButton = mkButton("🔍", "see theirs", () => loadBlockly(merge.theirs.scriptText));
var baseButton = mkButton("🔍", "see base", () => loadBlockly(merge.base.scriptText));
var mineName = getName();
var mineDescription = getDescription();
var mineButton = mkButton("🔍", "see mine", () => {
loadBlockly(mineText);
setName(mineName);
setDescription(mineDescription);
});
var theirsButton = mkButton("🔍", "see theirs", () => {
loadBlockly(merge.theirs.scriptText);
setName(merge.theirs.metadata.name);
setDescription(merge.theirs.metadata.description);
});
var baseButton = mkButton("🔍", "see base", () => {
loadBlockly(merge.base.scriptText);
setName(merge.base.metadata.name);
setDescription(merge.base.metadata.description);
});
var mergeButton = mkButton("👍", "finish merge", () => {
inMerge = false;
currentVersion = merge.theirs.baseSnapshot;
@ -162,7 +178,7 @@ module TDev {
}
function statusMsg(s: string, st: External.Status) {
var box = <HTMLElement> document.querySelector("#log");
var box = <HTMLElement> $("#log");
var elt = document.createElement("div");
elt.classList.add("status");
if (st == External.Status.Error)
@ -200,14 +216,30 @@ module TDev {
return text;
}
function setDescription(x: string) {
(<HTMLInputElement> $("#script-description")).value = (x || "");
}
function setName(x: string) {
(<HTMLInputElement> $("#script-name")).value = x;
}
function getDescription() {
return (<HTMLInputElement> $("#script-description")).value;
}
function getName() {
return (<HTMLInputElement> $("#script-name")).value;
}
var dirty = false;
// Called once at startup
function setupEditor(message: External.Message_Init) {
var state = loadEditorState(message.script.editorState);
Blockly.inject(document.querySelector("#editor"), {
toolbox: document.querySelector("#blockly-toolbox")
Blockly.inject($("#editor"), {
toolbox: $("#blockly-toolbox")
});
loadBlockly(message.script.scriptText);
// Hack alert! Blockly's [fireUiEvent] function [setTimeout]'s (with a 0 delay) the actual
@ -220,6 +252,17 @@ module TDev {
dirty = true;
});
}, 1);
$("#script-name").addEventListener("input", () => {
statusMsg("✎ local changes", External.Status.Ok);
dirty = true;
});
$("#script-description").addEventListener("input", () => {
statusMsg("✎ local changes", External.Status.Ok);
dirty = true;
});
setName(message.script.metadata.name);
setDescription(message.script.metadata.description);
// That's triggered when the user closes or reloads the whole page, but
// doesn't help if the user hits the "back" button in our UI.
@ -240,7 +283,7 @@ module TDev {
}
function doSave() {
if (!dirty || inMerge)
if (!dirty)
return;
var text = saveBlockly();
@ -253,27 +296,31 @@ module TDev {
lastSave: new Date()
}),
baseSnapshot: currentVersion,
metadata: {
name: getName(),
description: getDescription()
}
},
});
dirty = false;
}
function setupButtons() {
document.querySelector("#command-quit").addEventListener("click", () => {
$("#command-quit").addEventListener("click", () => {
doSave();
post({ type: External.MessageType.Quit });
});
document.querySelector("#command-save").addEventListener("click", () => {
$("#command-save").addEventListener("click", () => {
doSave();
});
document.querySelector("#command-compile").addEventListener("click", () => {
$("#command-compile").addEventListener("click", () => {
post(<External.Message_Compile> {
type: External.MessageType.Compile,
text: "", // TODO
language: External.Language.TouchDevelop
});
});
document.querySelector("#command-run").addEventListener("click", () => {
$("#command-run").addEventListener("click", () => {
});
}
}

Просмотреть файл

@ -36,6 +36,10 @@
</div>
<div id="merge-commands" class="hbox">
</div>
<div class="vbox" id="metadata">
<div><input id="script-name" placeholder="script name" /></div>
<div><input id="script-description" placeholder="script description" /></div>
</div>
<div id="log">
<div id="status">✔ loaded</div>
</div>

Просмотреть файл

@ -40,6 +40,14 @@ body {
margin-right: 1em;
}
#metadata {
padding-right: 2em;
}
#metadata > div:first-child {
padding-bottom: 1ex;
}
#toolbox {
padding: .5em;
}