mirror of
git://f0xx.org/ac/ac-be-hub
synced 2026-07-29 02:58:26 +03:00
feat(hub): shared nav shell, downloads UI, footer fix
Signed-off-by: Anton Afanasyeu <a.afanasieff@gmail.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/** Orchestration default channel; stable is fallback for promoted releases. */
|
||||
var OTA_CHANNELS = ['staging', 'stable'];
|
||||
|
||||
function setStatus(msg) {
|
||||
var el = document.getElementById('hub-download-apk-status');
|
||||
if (el) {
|
||||
@@ -8,32 +11,79 @@
|
||||
}
|
||||
}
|
||||
|
||||
function resolveStableApkUrl() {
|
||||
return fetch('/v0/ota/channel/stable.json', { cache: 'no-store' })
|
||||
.then(function (r) {
|
||||
if (!r.ok) {
|
||||
throw new Error('stable channel HTTP ' + r.status);
|
||||
}
|
||||
return r.json();
|
||||
})
|
||||
function fetchJson(url) {
|
||||
return fetch(url, { cache: 'no-store' }).then(function (r) {
|
||||
if (!r.ok) {
|
||||
throw new Error('HTTP ' + r.status + ' for ' + url);
|
||||
}
|
||||
return r.json();
|
||||
});
|
||||
}
|
||||
|
||||
function resolveApkFromChannel(channelName) {
|
||||
return fetchJson('/v0/ota/channel/' + encodeURIComponent(channelName) + '.json')
|
||||
.then(function (channel) {
|
||||
var manifestUrl = channel && channel.manifestUrl;
|
||||
if (!manifestUrl) {
|
||||
throw new Error('stable.json missing manifestUrl');
|
||||
throw new Error(channelName + ' missing manifestUrl');
|
||||
}
|
||||
return fetch(manifestUrl, { cache: 'no-store' }).then(function (r) {
|
||||
if (!r.ok) {
|
||||
throw new Error('manifest HTTP ' + r.status);
|
||||
}
|
||||
return r.json();
|
||||
});
|
||||
return fetchJson(manifestUrl);
|
||||
})
|
||||
.then(function (manifest) {
|
||||
var url = manifest && (manifest.apkUrl || manifest.bundleUrl);
|
||||
var url =
|
||||
(manifest && (manifest.browserApkUrl || manifest.apkUrl)) || '';
|
||||
if (!url) {
|
||||
throw new Error('manifest missing apkUrl');
|
||||
throw new Error('manifest missing browserApkUrl/apkUrl');
|
||||
}
|
||||
return url;
|
||||
if (!manifest.browserApkUrl && /\.otapkg(\?|$)/.test(url)) {
|
||||
url = url.replace(/\.otapkg(\?|$)/, '.apk$1');
|
||||
}
|
||||
var nameMatch = url.match(/\/([^/?#]+\.apk)(?:\?|$)/i);
|
||||
var filename = nameMatch ? nameMatch[1] : 'android_cast-latest.apk';
|
||||
return { url: url, filename: filename, channel: channelName };
|
||||
});
|
||||
}
|
||||
|
||||
function resolveApkUrl() {
|
||||
var chain = Promise.reject(new Error('no channel'));
|
||||
OTA_CHANNELS.forEach(function (ch) {
|
||||
chain = chain.catch(function () {
|
||||
return resolveApkFromChannel(ch);
|
||||
});
|
||||
});
|
||||
return chain;
|
||||
}
|
||||
|
||||
function triggerDownload(url, filename) {
|
||||
if (/\.apk(\?|$)/i.test(url)) {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
a.rel = 'noopener';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
return Promise.resolve();
|
||||
}
|
||||
return fetch(url, { cache: 'no-store' })
|
||||
.then(function (r) {
|
||||
if (!r.ok) {
|
||||
throw new Error('download HTTP ' + r.status);
|
||||
}
|
||||
return r.blob();
|
||||
})
|
||||
.then(function (blob) {
|
||||
var blobUrl = URL.createObjectURL(blob);
|
||||
var link = document.createElement('a');
|
||||
link.href = blobUrl;
|
||||
link.download = filename;
|
||||
link.rel = 'noopener';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
setTimeout(function () {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
}, 60000);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,14 +94,17 @@
|
||||
}
|
||||
btn.addEventListener('click', function () {
|
||||
btn.disabled = true;
|
||||
setStatus('Resolving stable OTA channel…');
|
||||
resolveStableApkUrl()
|
||||
.then(function (url) {
|
||||
setStatus('Downloading…');
|
||||
window.location.href = url;
|
||||
setStatus('Resolving OTA channel…');
|
||||
resolveApkUrl()
|
||||
.then(function (resolved) {
|
||||
setStatus('Downloading ' + resolved.filename + ' (' + resolved.channel + ')…');
|
||||
return triggerDownload(resolved.url, resolved.filename);
|
||||
})
|
||||
.catch(function (err) {
|
||||
setStatus('APK unavailable — run builder OTA publish (stable). ' + (err.message || err));
|
||||
setStatus(
|
||||
'APK unavailable — run builder with OTA publish (staging). ' +
|
||||
(err.message || err)
|
||||
);
|
||||
})
|
||||
.finally(function () {
|
||||
btn.disabled = false;
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
document.documentElement.setAttribute('lang', lang);
|
||||
var footerLeft = document.querySelector('.platform-footer__left[data-i18n]');
|
||||
if (footerLeft) {
|
||||
var holder = footerLeft.getAttribute('data-holder') || 'Anton Afanaasyeu';
|
||||
var holder = footerLeft.getAttribute('data-holder') || 'Anton Afanasyeu';
|
||||
var year = footerLeft.getAttribute('data-year') || String(new Date().getFullYear());
|
||||
footerLeft.textContent = t('footer.copyright', { holder: holder, year: year });
|
||||
}
|
||||
|
||||
@@ -60,10 +60,10 @@
|
||||
"page6.btn_docs": "Other documents",
|
||||
"page6.hint": "Wiki opens in Gitea; other documents lists ac-docs drafts, DRs, and specs.",
|
||||
"page7.title": "Downloads",
|
||||
"page7.lead": "Latest stable builds from the orchestration pipeline (next integration).",
|
||||
"page7.lead": "Latest builds from the orchestration pipeline (staging channel, branch next).",
|
||||
"page7.btn_apk": "Android APK",
|
||||
"page7.btn_studio": "Session Studio",
|
||||
"page7.hint": "APK matches the stable OTA channel signature and version.",
|
||||
"page7.hint": "APK is the installable package from the staging OTA channel (falls back to stable).",
|
||||
"page8.title": "Resources",
|
||||
"page8.lead": "Source control, consoles, and integration entry points.",
|
||||
"page8.btn_git": "Git Access",
|
||||
|
||||
@@ -60,10 +60,10 @@
|
||||
"page6.btn_docs": "Прочие документы",
|
||||
"page6.hint": "Wiki в Gitea; каталог — черновики ac-docs, DR и specs.",
|
||||
"page7.title": "Загрузки",
|
||||
"page7.lead": "Последние stable-сборки из orchestration (ветка next).",
|
||||
"page7.lead": "Последние сборки из orchestration (канал staging, ветка next).",
|
||||
"page7.btn_apk": "Android APK",
|
||||
"page7.btn_studio": "Session Studio",
|
||||
"page7.hint": "APK совпадает с stable OTA-каналом.",
|
||||
"page7.hint": "APK — установочный пакет из staging OTA-канала (fallback на stable).",
|
||||
"page8.title": "Ресурсы",
|
||||
"page8.lead": "Исходники, консоли и точки интеграции.",
|
||||
"page8.btn_git": "Git Access",
|
||||
|
||||
Reference in New Issue
Block a user