Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 57 additions & 32 deletions js/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//FAV WITH PILL AND ANIM
let appJSON = []; // List of apps and info from apps.json
let appSortInfo = {}; // list of data to sort by, from appdates.csv { created, modified }
let appCounts = {};
Expand Down Expand Up @@ -501,7 +502,7 @@ function handleAppInterface(app) {
});
}

function changeAppFavourite(favourite, app) {
function changeAppFavourite(favourite, app,refresh=true) {
if (favourite) {
SETTINGS.appsFavoritedThisSession.push({"id":app.id,"favs":appSortInfo[app.id]&&appSortInfo[app.id].favourites?appSortInfo[app.id].favourites:0});
SETTINGS.favourites = SETTINGS.favourites.concat([app.id]);
Expand All @@ -510,10 +511,11 @@ function changeAppFavourite(favourite, app) {
SETTINGS.favourites = SETTINGS.favourites.filter(e => e != app.id);
}
saveSettings();
refreshLibrary();
refreshMyApps();
if(refresh) {
refreshLibrary();
refreshMyApps();
}
}

// =========================================== Top Navigation
function showTab(tabname) {
htmlToArray(document.querySelectorAll("#tab-navigate .tab-item")).forEach(tab => {
Expand Down Expand Up @@ -543,6 +545,29 @@ librarySearchInput.addEventListener('input', evt => {

// =========================================== App Info




function getAppFavorites(app){
let info = appSortInfo[app.id] || {};
// start with whatever number we have in the database (may be undefined -> treat as 0)
let appFavourites = (typeof info.favourites === 'number') ? info.favourites : 0;
let favsThisSession = SETTINGS.appsFavoritedThisSession.find(obj => obj.id === app.id);
if (favsThisSession) {
// If the database count changed since we recorded the session-favourite, it means
// the server/db has been updated and our optimistic session entry is stale.
if (typeof info.favourites === 'number' && info.favourites !== favsThisSession.favs) {
// remove stale session entry
SETTINGS.appsFavoritedThisSession = SETTINGS.appsFavoritedThisSession.filter(obj => obj.id !== app.id);
} else {
// otherwise include our optimistic +1 so the UI updates immediately
appFavourites += 1;
}
}
return appFavourites;
}


function getAppHTML(app, appInstalled, forInterface) {
let version = getVersionInfo(app, appInstalled);
let versionInfo = version.text;
Expand All @@ -559,21 +584,11 @@ function getAppHTML(app, appInstalled, forInterface) {
infoTxt.push(`${info.installs} reported installs (${percentText})`);
}
if (info.favourites) {
let favsThisSession = SETTINGS.appsFavoritedThisSession.find(obj => obj.id === app.id);
let percent=(info.favourites / info.installs * 100).toFixed(0);
appFavourites = getAppFavorites(app);
let percent=(appFavourites / info.installs * 100).toFixed(0);
let percentText=percent>100?"More than 100% of installs":percent+"% of installs";
if(!info.installs||info.installs<1) {infoTxt.push(`${info.favourites} users favourited`)}
else {infoTxt.push(`${info.favourites} users favourited (${percentText})`)}
appFavourites = info.favourites;
if(favsThisSession){
if(info.favourites!=favsThisSession.favs){
//database has been updated, remove app from favsThisSession
SETTINGS.appsFavoritedThisSession = SETTINGS.appsFavoritedThisSession.filter(obj => obj.id !== app.id);
}
else{
appFavourites += 1; //add one to give the illusion of immediate database changes
}
}
if(!info.installs||info.installs<1) {infoTxt.push(`${appFavourites} users favourited`)}
else {infoTxt.push(`${appFavourites} users favourited (${percentText})`)}
}
if (infoTxt.length)
versionTitle = `title="${infoTxt.join("\n")}"`;
Expand All @@ -585,9 +600,10 @@ function getAppHTML(app, appInstalled, forInterface) {
let githubLink = Const.APP_SOURCECODE_URL ?
`<a href="${Const.APP_SOURCECODE_URL}/${app.id}" target="_blank" class="link-github"><img src="core/img/github-icon-sml.png" alt="See the code on GitHub"/></a>` : "";
let getAppFavouritesHTML = cnt => {
if (!cnt) return "";
let txt = (cnt > 999) ? Math.round(cnt/1000)+"k" : cnt;
return `<span>${txt}</span>`;
// Always show a count (0 if none) and format large numbers with 'k'
let n = (cnt && typeof cnt === 'number') ? cnt : 0;
let txt = (n > 999) ? Math.round(n/1000)+"k" : n;
return `<span class="fav-count">${txt}</span>`;
};

let html = `<div class="tile column col-6 col-sm-12 col-xs-12 app-tile">
Expand All @@ -601,20 +617,21 @@ function getAppHTML(app, appInstalled, forInterface) {
<a href="${appurl}" class="link-copy-url" appid="${app.id}" title="Copy link to app" style="position:absolute;top: 56px;left: -24px;"><img src="core/img/copy-icon.png" alt="Copy link to app"/></a>
</div>
<div class="tile-action">`;
html += `<div class="pill-container">`;
if (forInterface=="library") html += `
<button class="btn btn-link btn-action btn-lg btn-favourite" appid="${app.id}" title="Favourite"><i class="icon icon-favourite${favourite?" icon-favourite-active":""}">${getAppFavouritesHTML(appFavourites)}</i></button>
<button class="btn btn-link btn-action btn-lg btn-favourite" appid="${app.id}" title="Favourite">${getAppFavouritesHTML(appFavourites)}<i class="icon icon-favourite${favourite?" icon-favourite-active":""}"></i></button>
<button class="btn btn-link btn-action btn-lg ${(appInstalled&&app.interface)?"":"d-hide"}" appid="${app.id}" title="Download data from app"><i class="icon icon-interface"></i></button>
<button class="btn btn-link btn-action btn-lg ${app.allow_emulator?"":"d-hide"}" appid="${app.id}" title="Try in Emulator"><i class="icon icon-emulator"></i></button>
<button class="btn btn-link btn-action btn-lg ${(SETTINGS.alwaysAllowUpdate && appInstalled) || version.canUpdate?"":"d-hide"}" appid="${app.id}" title="Update App"><i class="icon icon-refresh"></i></button>
<button class="btn btn-link btn-action btn-lg ${(!appInstalled && !app.custom)?"":"d-hide"}" appid="${app.id}" title="Upload App"><i class="icon icon-upload"></i></button>
<button class="btn btn-link btn-action btn-lg ${appInstalled?"":"d-hide"}" appid="${app.id}" title="Remove App"><i class="icon icon-delete"></i></button>
<button class="btn btn-link btn-action btn-lg ${app.custom?"":"d-hide"}" appid="${app.id}" title="Customise and Upload App"><i class="icon icon-menu"></i></button>`;
if (forInterface=="myapps") html += `
<button class="btn btn-link btn-action btn-lg btn-favourite" appid="${app.id}" title="Favourite"><i class="icon icon-favourite${favourite?" icon-favourite-active":""}">${getAppFavouritesHTML(appFavourites)}</i></button>
<button class="btn btn-link btn-action btn-lg btn-favourite" appid="${app.id}" title="Favourite">${getAppFavouritesHTML(appFavourites)}<i class="icon icon-favourite${favourite?" icon-favourite-active":""}"></i></button>
<button class="btn btn-link btn-action btn-lg ${(appInstalled&&app.interface)?"":"d-hide"}" appid="${app.id}" title="Download data from app"><i class="icon icon-interface"></i></button>
<button class="btn btn-link btn-action btn-lg ${(SETTINGS.alwaysAllowUpdate && appInstalled) || version.canUpdate?'':'d-hide'}" appid="${app.id}" title="Update App"><i class="icon icon-refresh"></i></button>
<button class="btn btn-link btn-action btn-lg" appid="${app.id}" title="Remove App"><i class="icon icon-delete"></i></button>`;
html += "</div>";
html += "</div></div>";
if (forInterface=="library") {
let screenshots = (app.screenshots || []).filter(s=>s.url);
if (screenshots.length)
Expand Down Expand Up @@ -789,7 +806,6 @@ function refreshLibrary(options) {
visibleApps = visibleApps.slice(0, Const.MAX_APPS_SHOWN-1);
}


panelbody.innerHTML = visibleApps.map((app,idx) => {
let appInstalled = device.appsInstalled.find(a=>a.id==app.id);
return getAppHTML(app, appInstalled, "library");
Expand All @@ -801,7 +817,7 @@ function refreshLibrary(options) {
htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
button.addEventListener("click",event => {
let button = event.currentTarget;
let icon = button.firstChild;
let icon = (button.querySelector && (button.querySelector('i.icon'))) || button.firstElementChild || button.firstChild;
let appid = button.getAttribute("appid");
let app = appNameToApp(appid);
if (!app) throw new Error("App "+appid+" not found");
Expand Down Expand Up @@ -842,8 +858,21 @@ function refreshLibrary(options) {
if (err != "") showToast("Failed, "+err, "error");
});
} else if ( button.classList.contains("btn-favourite")) {
// clicked: animate and toggle favourite state immediately for instant feedback
let favourite = SETTINGS.favourites.find(e => e == app.id);
changeAppFavourite(!favourite, app);
changeAppFavourite(!favourite, app,false);
if (icon) icon.classList.toggle("icon-favourite-active", !favourite);
if (icon) icon.classList.add("favoriteAnim");
// update visible count optimistically (always update, even if 0)
let cnt = getAppFavorites(app);
let txt = (cnt > 999) ? Math.round(cnt/1000)+"k" : cnt;
let countEl = button.querySelector('.fav-count');
if (countEl) countEl.textContent = String(txt);
const ANIM_MS = 500;
// ensure animation class is removed after the duration so it can be re-triggered
setTimeout(() => {
try { if (icon) icon.classList.remove("favoriteAnim"); } catch (e) {}
}, ANIM_MS);
}
});
});
Expand Down Expand Up @@ -1109,7 +1138,7 @@ function refreshMyApps() {
htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
button.addEventListener("click",event => {
let button = event.currentTarget;
let icon = button.firstChild;
let icon = (button.querySelector && (button.querySelector('i.icon'))) || button.firstElementChild || button.firstChild;
let appid = button.getAttribute("appid");
let app = appNameToApp(appid);
if (!app) throw new Error("App "+appid+" not found");
Expand All @@ -1120,10 +1149,6 @@ function refreshMyApps() {
handleAppInterface(app).catch( err => {
if (err != "") showToast("Failed, "+err, "error");
});
if (icon.classList.contains("icon-favourite")) {
let favourite = SETTINGS.favourites.find(e => e == app.id);
changeAppFavourite(!favourite, app);
}
});
});
let nonCustomAppsToUpdate = getAppsToUpdate({excludeCustomApps:true});
Expand Down