TouchDevelop/lib/Bazaar.ts

289 строки
12 KiB
TypeScript

///<reference path='refs.ts'/>
module TDev.RT {
//? Browse and review scripts from the bazaar
//@ skill(3)
export module Bazaar
{
//? Returns a user object for a specified user id
//@ cachedAsync returns(User)
export function user_of(id: string, r: ResumeCtx)
{
User.getJsonAsync(id).done(user =>
r.resumeVal((user && user.kind == "user") ? User.mk(id) : undefined),
e => r.resumeVal(undefined));
}
export function userIdAsync(rt:Runtime) : Promise { // : string
if (!rt.requiresAuth()) return Promise.as(rt.getUserId())
return Cloud.authenticateAsync(lf("user identification"))
.then(() => Cloud.getUserId());
}
//? Returns the user object of the current user
//@ returns(User) authAsync
export function current_user(r: ResumeCtx)
{
userIdAsync(r.rt).done(userId => r.resumeVal(userId ? User.mk(userId) : undefined));
}
export function cachedScore(rt: Runtime, score?: number): number {
var currentScore = rt.datas["this"]["leaderboard_score"] || 0;
if (score && score > currentScore) {
rt.datas["this"]["leaderboard_score"] = score;
currentScore = score;
}
return currentScore;
}
//? Gets the current score for the current script
//@ async returns(number)
//@ tandre hasPauseContinue
//@ readsMutable
export function leaderboard_score(r: ResumeCtx) // : number
{
var rt = r.rt;
var currentScore = cachedScore(rt);
var localScore = () => r.resumeVal(currentScore);
if (!rt.currentScriptId || !Cloud.isOnline()) {
localScore();
} else {
Cloud.authenticateAsync(lf("leaderboard"))
.done((authenticated: boolean) => {
if (authenticated) {
var url = Cloud.getPrivateApiUrl('me/leaderboardscored/' + rt.currentScriptId);
var request = WebRequest.mk(url, undefined);
request.sendAsync()
.done((response: WebResponse) => {
var curr = 0;
var json = response.content_as_json();
if (json)
curr = json.number('score');
// max with local score to avoid race in cloud
r.resumeVal(cachedScore(rt, Math.max(currentScore, curr || 0)));
});
} else { localScore(); }
});
}
}
export function postScoreToOfficeMix(score : number, scriptId: string) {
// in office mix, always send a score message
if (Browser.webRunner || Browser.webAppImplicit) {
var msg = JsonObject.wrap({
kind: "leaderboardScore__Send",
data: { score: score, scriptId: scriptId }
});
Web.post_message_to_parent(Cloud.config.rootUrl, msg, null);
Web.post_message_to_parent("http://localhost:15669", msg, null);
}
}
//? Posts the current game score to the script leaderboard
//@ async
//@ writesMutable
export function post_leaderboard_score(score: number, r: ResumeCtx) //: void
{
var rt = r.rt;
var currentScore = cachedScore(rt, score);
if (!rt.currentScriptId || !Cloud.isOnline()) {
r.resume();
} else {
Bazaar.postScoreToOfficeMix(score, rt.currentScriptId);
Cloud.authenticateAsync(lf("leaderboard"))
.done((authenticated: boolean) => {
if (authenticated) {
var url = Cloud.getPrivateApiUrl(rt.currentScriptId + '/leaderboardscores');
var request = WebRequest.mk(url, undefined);
request.set_method('post');
request.set_content_as_json(JsonObject.mk(JSON.stringify({ kind: "leaderboardscore", score: score || 0, userplatform: Browser.platformCaps }), Util.log));
request.sendAsync()
.done((response: WebResponse) => {
var json = response.content_as_json();
if (json)
cachedScore(rt, json.number('score'))
r.resume();
});
}
}, e => {
// something wrong happened, keep moving
r.resume();
});
}
}
export function loadLeaderboardItemsAsync(striptId : string): Promise { // HTMLElement
if (!striptId || !Cloud.isOnline()) return Promise.as([]);
return Cloud.getPublicApiAsync(striptId + '/leaderboardscores?count=250')
.then(leaderboards => {
return leaderboards.items.map(item => {
var userid = item.userid;
var username = item.username;
var userscore = item.score.toString();
var time = Util.timeSince(item.time);
var imgDiv = div('leaderboard-img');
if (item.userhaspicture)
imgDiv.style.backgroundImage = HTML.cssImage(Cloud.getPublicApiUrl(userid + "/picture?type=normal"))
else
imgDiv.innerHTML = TDev.Util.svgGravatar(userid);
var scoreDiv = div('item leaderboard-item', [
imgDiv,
div('leaderboards-score', userscore),
div('leaderboard-center', [
div('item-title', username),
div('item-subtle', time)
])
]);
return scoreDiv;
});
}, e => {
return [];
});
}
//? Posts the current game leaderboard to the wall
//@ cachedAsync
export function post_leaderboard_to_wall(r: ResumeCtx) //: void
{
// TODO this should be cached for page display
var rt = r.rt;
if (!rt.currentScriptId) {
var curr = cachedScore(rt).toString();
var imgDiv = div('leaderboard-img');
imgDiv.innerHTML = TDev.Util.svgGravatar('me');
var leaderboardDiv = div('item leaderboard-item', [
imgDiv,
div('leaderboards-score', curr),
div('leaderboard-center', [
div('item-title', lf("Me")),
div('item-subtle', lf("Publish your script to get a leaderboard available for all your users.")),
])
]);
r.rt.postBoxedHtml(leaderboardDiv, r.rt.current.pc);
r.resume();
} else if (!Cloud.isOnline()) {
var curr = cachedScore(rt).toString();
var imgDiv = div('leaderboard-img');
imgDiv.innerHTML = TDev.Util.svgGravatar('me');
var leaderboardDiv = div('item leaderboard-item', [
imgDiv,
div('leaderboards-score', curr),
div('leaderboard-center', [
div('item-title', lf("Me")),
div('item-subtle', lf("Please connect to internet to load the leaderboards.")),
])
]);
r.rt.postBoxedHtml(leaderboardDiv, r.rt.current.pc);
r.resume();
} else {
r.progress(lf("Loading leaderboards..."));
var leaderboardDiv = div('');
loadLeaderboardItemsAsync(rt.currentScriptId).done(els => {
leaderboardDiv.setChildren(els);
rt.postBoxedHtml(leaderboardDiv, r.rt.current.pc);
r.resume();
}, e => {
rt.postBoxedText(lf("Oops, could not get the leaderboards. Please check your internet connection."), r.rt.current.pc);
r.resume();
});
}
}
//? three-way merge script texts. Debug only: for testing.
//@ dbgOnly
export function merge3(O: string, A: string, B: string): string {
var t1 = (<any>TDev).AST.Parser.parseScript(O);
var t2 = (<any>TDev).AST.Parser.parseScript(A);
var t3 = (<any>TDev).AST.Parser.parseScript(B);
(<any>TDev).AST.TypeChecker.tcApp(t1);
(<any>TDev).AST.TypeChecker.tcApp(t2);
(<any>TDev).AST.TypeChecker.tcApp(t3);
(<any>TDev).TheEditor.initIds(t1);
(<any>TDev).TheEditor.initIds(t2);
(<any>TDev).TheEditor.initIds(t3);
var merged = (<any>TDev).AST.Merge.merge3(t1, t2, t3);
return merged.serialize();
}
//? Launches the bazaar.
//@ obsolete
export function open(): void
{
// obsolete, does nothing
}
//? Opens the review page for the current script
//@ obsolete
export function open_review(): void
{
// obsolete, does nothing
}
//? Opens the leaderboard for the current script
//@ obsolete
export function open_leaderboard(r : ResumeCtx): void
{
post_leaderboard_to_wall(r);
}
//? Returns an identifier of either the top-level script or the current library
//@ [which].deflStrings("top", "current")
export function script_id(which:string, s:IStackFrame) : string
{
if (which == "top") return s.libs.topScriptId;
if (which == "current") return s.libs.scriptId;
return undefined;
}
//? Asks the user to pick a script and return its identifier
//@ [mode].deflStrings("read", "write", "read-write")
//@ returns(string) uiAsync
export function pick_script(mode:string, message:string, r: ResumeCtx)
{
r.rt.host.pickScriptAsync(mode, message).done((id) => {
r.resumeVal(id);
})
}
//? Saves given Abstract Syntax Tree as a script
//@ uiAsync
export function save_ast(id:string, script:JsonObject, r: ResumeCtx)
{
r.rt.host.saveAstAsync(id, script.value()).done((id) => {
r.resume()
})
}
//? Returns the Abstract Syntax Tree JSON object for specified script
//@ [id].deflExpr('bazaar->script_id("current")')
//@ returns(JsonObject) uiAsync
export function ast_of(id:string, r: ResumeCtx)
{
r.rt.host.astOfAsync(id).done((j) => {
if (!j) r.resumeVal(undefined);
else r.resumeVal(JsonObject.wrap(j));
})
}
// returns the app store id if compiled
export var storeidAsync = (): Promise => { // of string
var host : any = (<any>window).touchDevelopHost;
if (host) {
Util.log("using touchdevelop host");
var id = host.storeid()||"";
Util.log("storeid: " + id);
return Promise.as(id);
}
return Promise.as("");
};
}
}