diff --git a/ast/help.ts b/ast/help.ts index cf0b778a..fa4bea43 100644 --- a/ast/help.ts +++ b/ast/help.ts @@ -228,6 +228,20 @@ module TDev { ispositive: boolean; } + export interface JsonRelease extends JsonPublication + { + releaseid:string; + labels:JsonReleaseLabel[]; + } + + export interface JsonReleaseLabel + { + name: string; + userid: string; + time: number; + releaseid: string; + } + export interface JsonEtag { id:string; diff --git a/editor/editor.ts b/editor/editor.ts index 17cdb093..f3144b8a 100644 --- a/editor/editor.ts +++ b/editor/editor.ts @@ -4193,6 +4193,7 @@ module TDev (dbg ? HTML.mkButtonTick(lf("manage showcase"), Ticks.hubShowcaseMgmt, () => { this.hide(); Browser.TheHost.showList("showcase-mgmt", null); }) : null), (Util.localTranslationTracking ? HTML.mkButtonTick(lf("translations"), Ticks.hubShowcaseMgmt, () => { ModalDialog.showText(Util.dumpTranslationFreqs()) }) : null), (dbg ? HTML.mkButton(lf("show internal icons"), () => { ScriptProperties.showIcons(); }) : null), + (Cloud.lite && dbg ? HTML.mkButton(lf("show releases"), () => { Util.setHash("#list:releases") }) : null), ]); } m.add(div("wall-dialog-buttons", HTML.mkButton(lf("close"), () => m.dismiss()))); diff --git a/editor/scriptList.ts b/editor/scriptList.ts index 81b09db1..1ba3c1e2 100644 --- a/editor/scriptList.ts +++ b/editor/scriptList.ts @@ -1210,6 +1210,7 @@ module TDev { export module Browser { else if (e.kind == "art") return this.getArtInfoById(e.id); else if (e.kind == "group") return this.getGroupInfoById(e.id); else if (e.kind == "document") return this.getDocumentInfo(e); + else if (e.kind == "release") return this.getReleaseInfoById(e.id); else return null; } @@ -1373,6 +1374,10 @@ module TDev { export module Browser { if (Cloud.getUserId()) path = Cloud.getUserId() + "/groups"; else path = null; break; + case "releases": + tick(Ticks.browseListReleases) + header = lf("releases"); + break; case "showcase-mgmt": header = "show-mgmt"; break; @@ -1727,6 +1732,17 @@ module TDev { export module Browser { return si; } + public getReleaseInfoById(id:string) + { + var si = this.getLocation(id); + if (!si) { + si = new ReleaseInfo(this); + si.loadFromWeb(id); + this.saveLocation(si); + } + return si; + } + public getArtInfoById(id:string) { var si = this.getLocation(id); @@ -2303,11 +2319,18 @@ module TDev { export module Browser { }); } - public invalidate(path:string) + public invalidate(path:string, rec = false) { this.forEachEntry((e) => { - if (Util.startsWith(e.path, path)) + if (Util.startsWith(e.path, path)) { + if (rec && e.currentData && e.currentData.etags) { + e.currentData.etags.forEach(i => { + var ee = this.entriesByPath[i.id] + if (ee) ee.invalidate() + }) + } e.invalidate() + } }) } @@ -8478,5 +8501,104 @@ module TDev { export module Browser { } } + export class ReleaseInfo + extends BrowserPage { + private name: string; + public description: string; + public userid:string; + + constructor(par: Host) { + super(par) + } + public persistentId() { return "release:" + this.publicId; } + public getTitle() { return this.name || super.getTitle(); } + + public getName() { return lf("settings"); } + public getId() { return "settings"; } + public isMine() { return this.userid == Cloud.getUserId(); } + + public loadFromWeb(id: string) { + Util.assert(!!id) + this.publicId = id; + } + + public mkBoxCore(big: boolean) { + var icon = div("sdIcon", HTML.mkImg("svg:fa-upload,white")); + icon.style.background = "#1731B8"; + var nameBlock = div("sdName"); + var hd = div("sdNameBlock", nameBlock); + + var numbers = div("sdNumbers"); + var author = div("sdAuthorInner"); + + var addInfoInner = div("sdAddInfoInner", "/" + this.publicId); + var pubId = div("sdAddInfoOuter", addInfoInner); + + var res = div("sdHeaderOuter", + div("sdHeader", icon, + div("sdHeaderInner", hd, pubId, div("sdAuthor", author), numbers + ))); + + if (big) + res.className += " sdBigHeader"; + + + return this.withUpdate(res, (u:JsonRelease) => { + var nm = u.releaseid.replace(/^\d\d\d\d\d\d\d+-[^\-]+-/, "") + var labs = u.labels.map(l => l.name).join(", ") + if (labs) nm += " (" + labs + ")" + nameBlock.setChildren([ nm ]) + author.setChildren([u.username]); + addInfoInner.setChildren(["/" + this.publicId + ", " + Util.timeSince(u.time)]); + }); + } + + public mkTabsCore(): BrowserTab[] { + var tabs: BrowserTab[] = [ + this + ]; + return tabs; + } + + + public initTab() { + this.withUpdate(this.tabContent, (u:JsonRelease) => { + var ch = ["current", "beta", "cloud"].map(n => HTML.mkButton("make " + n, () => { + var doit = () => + Cloud.postPrivateApiAsync(this.publicId + "/label", { name: n }) + .done(r => this.reload()) + if (n == "current") ModalDialog.ask(lf("are you sure?"), lf("move {0} label", n), doit) + else doit() + })) + + ch.unshift(div(null, + HTML.mkButtonElt("sdBigButton sdBigButtonFull", div("sdBigButtonIcon", HTML.mkImg("svg:fa-rocket,white")), + div("sdBigButtonDesc", lf("launch"))) + .withClick(() => + Util.navigateInWindow(Cloud.getServiceUrl() + "/app/?r=" + this.publicId)) + )); + + ch.push(div("sdHeading", u.labels.length ? "labels" : "no labels")) + u.labels.forEach(l => { + ch.push(div(null, "label: " + l.name + " by /" + l.userid + " on " + Util.timeSince(l.time))) + }) + + this.tabContent.setChildren(ch) + }); + } + + private reload() + { + this.invalidateCaches(); + TheEditor.historyMgr.reload(HistoryMgr.windowHash()); + } + + private invalidateCaches() { + TheApiCacheMgr.invalidate("releases", true); + TheApiCacheMgr.invalidate(Cloud.getUserId() + "/releases"); + TheApiCacheMgr.invalidate(this.publicId); + } + } + } } diff --git a/rt/ticker.ts b/rt/ticker.ts index 3f25c86c..d5e9fdb9 100644 --- a/rt/ticker.ts +++ b/rt/ticker.ts @@ -423,6 +423,7 @@ module TDev { browseSendPullRequest, browsePublicationNotes, browseListBugs, + browseListReleases, browseShare, browseListMyScripts, browseListNew, diff --git a/tools/client.ts b/tools/client.ts index 1f93c169..ff2b58ad 100644 --- a/tools/client.ts +++ b/tools/client.ts @@ -3577,6 +3577,23 @@ function tdupload(args:string[]) } } +function setlabel(args:string[]) +{ + var mm = /^(http.*)(\?access_token=.*)/.exec(args[0]) + + if (!mm || !args[2]) return + + var liteUrl = mm[1] + var key = mm[2] + + var liteId = args[1] + var channel = args[2] + + tdevGet(liteUrl + "api/" + liteId + "/label" + key, resp => { + console.log("channel: " + resp) + }, 1, { name: channel }) +} + var cmds = { "deps": { f: deps, a: "", h: "compute and output script dependencies" }, "parse": { f: parse, a: "", h: "parse given file; will look for deps in the same directory" }, @@ -3627,6 +3644,7 @@ var cmds = { "tdupload": { f:tdupload, a:'KEY LABEL FILE...', h:'upload a release'}, "uploadfile": { f:uploadfile, a:'KEY FILE...', h:'upload a file'}, "dlpubs": { f:dlpubs, a:'FILE...', h:'download based on tdpublogger output'}, + "setlabel": { f:setlabel, a:'KEY RELID LABEL', h:'set release label'}, } export interface ScriptTemplate {