Skip to content

Commit 1a23f49

Browse files
authored
Merge pull request #82 from RKBoss6/uiChanges
UI Changes
2 parents 5436722 + ab40d79 commit 1a23f49

File tree

1 file changed

+73
-38
lines changed

1 file changed

+73
-38
lines changed

js/index.js

Lines changed: 73 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const DEFAULTSETTINGS = {
88
settime : false, // Always update time when we connect
99
favourites : ["launch"],
1010
language : "",
11-
appsFavoritedThisSession : [], // list of apps favourited before database was updated
11+
appsfavouritedThisSession : [], // list of apps favourited before database was updated
1212
bleCompat: false, // 20 byte MTU BLE Compatibility mode
1313
sendUsageStats: true, // send usage stats to banglejs.com
1414
alwaysAllowUpdate : false, // Always show "reinstall app" button regardless of the version
@@ -501,19 +501,23 @@ function handleAppInterface(app) {
501501
});
502502
}
503503

504-
function changeAppFavourite(favourite, app) {
504+
function changeAppFavourite(favourite, app,refresh=true) {
505+
506+
505507
if (favourite) {
506-
SETTINGS.appsFavoritedThisSession.push({"id":app.id,"favs":appSortInfo[app.id]&&appSortInfo[app.id].favourites?appSortInfo[app.id].favourites:0});
508+
SETTINGS.appsfavouritedThisSession.push({"id":app.id,"favs":appSortInfo[app.id]&&appSortInfo[app.id].favourites?appSortInfo[app.id].favourites:0});
507509
SETTINGS.favourites = SETTINGS.favourites.concat([app.id]);
508510
} else {
509-
SETTINGS.appsFavoritedThisSession = SETTINGS.appsFavoritedThisSession.filter(obj => obj.id !== app.id);
511+
SETTINGS.appsfavouritedThisSession = SETTINGS.appsfavouritedThisSession.filter(obj => obj.id !== app.id);
510512
SETTINGS.favourites = SETTINGS.favourites.filter(e => e != app.id);
511513
}
514+
512515
saveSettings();
513-
refreshLibrary();
514-
refreshMyApps();
516+
if(refresh) {
517+
refreshLibrary();
518+
refreshMyApps();
519+
}
515520
}
516-
517521
// =========================================== Top Navigation
518522
function showTab(tabname) {
519523
htmlToArray(document.querySelectorAll("#tab-navigate .tab-item")).forEach(tab => {
@@ -543,6 +547,29 @@ librarySearchInput.addEventListener('input', evt => {
543547

544548
// =========================================== App Info
545549

550+
551+
552+
553+
function getAppfavourites(app){
554+
let info = appSortInfo[app.id] || {};
555+
// start with whatever number we have in the database (may be undefined -> treat as 0)
556+
let appFavourites = (typeof info.favourites === 'number') ? info.favourites : 0;
557+
let favsThisSession = SETTINGS.appsfavouritedThisSession.find(obj => obj.id === app.id);
558+
if (favsThisSession) {
559+
// If the database count changed since we recorded the session-favourite, it means
560+
// the server/db has been updated and our optimistic session entry is stale.
561+
if (typeof info.favourites === 'number' && info.favourites !== favsThisSession.favs) {
562+
// remove stale session entry
563+
SETTINGS.appsfavouritedThisSession = SETTINGS.appsfavouritedThisSession.filter(obj => obj.id !== app.id);
564+
} else {
565+
// otherwise include our optimistic +1 so the UI updates immediately
566+
appFavourites += 1;
567+
}
568+
}
569+
return appFavourites;
570+
}
571+
572+
546573
function getAppHTML(app, appInstalled, forInterface) {
547574
let version = getVersionInfo(app, appInstalled);
548575
let versionInfo = version.text;
@@ -559,21 +586,11 @@ function getAppHTML(app, appInstalled, forInterface) {
559586
infoTxt.push(`${info.installs} reported installs (${percentText})`);
560587
}
561588
if (info.favourites) {
562-
let favsThisSession = SETTINGS.appsFavoritedThisSession.find(obj => obj.id === app.id);
563-
let percent=(info.favourites / info.installs * 100).toFixed(0);
589+
appFavourites = getAppfavourites(app);
590+
let percent=(appFavourites / info.installs * 100).toFixed(0);
564591
let percentText=percent>100?"More than 100% of installs":percent+"% of installs";
565-
if(!info.installs||info.installs<1) {infoTxt.push(`${info.favourites} users favourited`)}
566-
else {infoTxt.push(`${info.favourites} users favourited (${percentText})`)}
567-
appFavourites = info.favourites;
568-
if(favsThisSession){
569-
if(info.favourites!=favsThisSession.favs){
570-
//database has been updated, remove app from favsThisSession
571-
SETTINGS.appsFavoritedThisSession = SETTINGS.appsFavoritedThisSession.filter(obj => obj.id !== app.id);
572-
}
573-
else{
574-
appFavourites += 1; //add one to give the illusion of immediate database changes
575-
}
576-
}
592+
if(!info.installs||info.installs<1) {infoTxt.push(`${appFavourites} users favourited`);}
593+
else {infoTxt.push(`${appFavourites} users favourited (${percentText})`);}
577594
}
578595
if (infoTxt.length)
579596
versionTitle = `title="${infoTxt.join("\n")}"`;
@@ -585,12 +602,13 @@ function getAppHTML(app, appInstalled, forInterface) {
585602
let githubLink = Const.APP_SOURCECODE_URL ?
586603
`<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>` : "";
587604
let getAppFavouritesHTML = cnt => {
588-
if (!cnt) return "";
589-
let txt = (cnt > 999) ? Math.round(cnt/1000)+"k" : cnt;
590-
return `<span>${txt}</span>`;
605+
// Always show a count (0 if none) and format large numbers with 'k'
606+
let n = (cnt && typeof cnt === 'number') ? cnt : 0;
607+
let txt = (n > 999) ? Math.round(n/100)/10+"k" : n;
608+
return `<span class="fav-count" style="margin-left:-1em;margin-right:0.5em">${txt}</span>`;
591609
};
592610

593-
let html = `<div class="tile column col-6 col-sm-12 col-xs-12 app-tile">
611+
let html = `<div class="tile column col-6 col-sm-12 col-xs-12 app-tile ${version.canUpdate?'updateTile':''}">
594612
<div class="tile-icon">
595613
<figure class="avatar"><img src="apps/${app.icon?`${app.id}/${app.icon}`:"unknown.png"}" alt="${escapeHtml(app.name)}"></figure>
596614
</div>
@@ -601,20 +619,21 @@ function getAppHTML(app, appInstalled, forInterface) {
601619
<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>
602620
</div>
603621
<div class="tile-action">`;
622+
html += `<div class="pill-container">`;
604623
if (forInterface=="library") html += `
605-
<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>
624+
<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>
606625
<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>
607626
<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>
608627
<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>
609628
<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>
610629
<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>
611630
<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>`;
612631
if (forInterface=="myapps") html += `
613-
<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>
632+
<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>
614633
<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>
615634
<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>
616635
<button class="btn btn-link btn-action btn-lg" appid="${app.id}" title="Remove App"><i class="icon icon-delete"></i></button>`;
617-
html += "</div>";
636+
html += "</div></div>";
618637
if (forInterface=="library") {
619638
let screenshots = (app.screenshots || []).filter(s=>s.url);
620639
if (screenshots.length)
@@ -649,6 +668,23 @@ function refreshSort(){
649668
if(activeSort) sortContainer.querySelector('.chip[sortid="'+activeSort+'"]').classList.add('active');
650669
else sortContainer.querySelector('.chip[sortid]').classList.add('active');
651670
}
671+
function handlefavouriteClick(icon,app,button){
672+
const favAnimMS = 500; // duration of favourite animation in ms
673+
// clicked: animate and toggle favourite state immediately for instant feedback
674+
let favourite = SETTINGS.favourites.find(e => e == app.id);
675+
changeAppFavourite(!favourite, app,false);
676+
if (icon) icon.classList.toggle("icon-favourite-active", !favourite);
677+
if (icon) icon.classList.add("favoriteAnim");
678+
// update visible count optimistically (always update, even if 0)
679+
let cnt = getAppfavourites(app);
680+
let txt = (cnt > 999) ? Math.round(cnt/100)/10+"k" : cnt;
681+
let countEl = button.querySelector('.fav-count');
682+
if (countEl) countEl.textContent = String(txt);
683+
// ensure animation class is removed after the duration so it can be re-triggered
684+
setTimeout(() => {
685+
try { if (icon) icon.classList.remove("favoriteAnim"); } catch (e) { console.error(e); }
686+
}, favAnimMS);
687+
}
652688
// Refill the library with apps
653689
function refreshLibrary(options) {
654690
options = options||{};
@@ -789,7 +825,6 @@ function refreshLibrary(options) {
789825
visibleApps = visibleApps.slice(0, Const.MAX_APPS_SHOWN-1);
790826
}
791827

792-
793828
panelbody.innerHTML = visibleApps.map((app,idx) => {
794829
let appInstalled = device.appsInstalled.find(a=>a.id==app.id);
795830
return getAppHTML(app, appInstalled, "library");
@@ -801,7 +836,7 @@ function refreshLibrary(options) {
801836
htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
802837
button.addEventListener("click",event => {
803838
let button = event.currentTarget;
804-
let icon = button.firstChild;
839+
let icon = (button.querySelector && (button.querySelector('i.icon'))) || button.firstElementChild || button.firstChild;
805840
let appid = button.getAttribute("appid");
806841
let app = appNameToApp(appid);
807842
if (!app) throw new Error("App "+appid+" not found");
@@ -842,8 +877,7 @@ function refreshLibrary(options) {
842877
if (err != "") showToast("Failed, "+err, "error");
843878
});
844879
} else if ( button.classList.contains("btn-favourite")) {
845-
let favourite = SETTINGS.favourites.find(e => e == app.id);
846-
changeAppFavourite(!favourite, app);
880+
handlefavouriteClick(icon,app,button);
847881
}
848882
});
849883
});
@@ -1109,7 +1143,7 @@ function refreshMyApps() {
11091143
htmlToArray(panelbody.getElementsByTagName("button")).forEach(button => {
11101144
button.addEventListener("click",event => {
11111145
let button = event.currentTarget;
1112-
let icon = button.firstChild;
1146+
let icon = (button.querySelector && (button.querySelector('i.icon'))) || button.firstElementChild || button.firstChild;
11131147
let appid = button.getAttribute("appid");
11141148
let app = appNameToApp(appid);
11151149
if (!app) throw new Error("App "+appid+" not found");
@@ -1120,17 +1154,18 @@ function refreshMyApps() {
11201154
handleAppInterface(app).catch( err => {
11211155
if (err != "") showToast("Failed, "+err, "error");
11221156
});
1123-
if (icon.classList.contains("icon-favourite")) {
1124-
let favourite = SETTINGS.favourites.find(e => e == app.id);
1125-
changeAppFavourite(!favourite, app);
1157+
// handle favourites on My Apps page (button has class btn-favourite)
1158+
if (button.classList && button.classList.contains("btn-favourite")) {
1159+
handlefavouriteClick(icon,app,button);
11261160
}
11271161
});
11281162
});
11291163
let nonCustomAppsToUpdate = getAppsToUpdate({excludeCustomApps:true});
11301164
let tab = document.querySelector("#tab-myappscontainer a");
11311165
let updateApps = document.querySelector("#myappscontainer .updateapps");
11321166
if (nonCustomAppsToUpdate.length) {
1133-
updateApps.innerHTML = `Update ${nonCustomAppsToUpdate.length} apps`;
1167+
1168+
updateApps.innerHTML = `Update ${nonCustomAppsToUpdate.length} ${nonCustomAppsToUpdate.length>1?"apps":"app"}`;
11341169
updateApps.classList.remove("hidden");
11351170
updateApps.classList.remove("disabled");
11361171
tab.setAttribute("data-badge", `${device.appsInstalled.length}${nonCustomAppsToUpdate.length}`);
@@ -1364,7 +1399,7 @@ function loadSettings() {
13641399
console.error("Invalid settings");
13651400
}
13661401
// upgrade old settings
1367-
if(!SETTINGS.appsFavoritedThisSession) SETTINGS.appsFavoritedThisSession = [];
1402+
if(!SETTINGS.appsfavouritedThisSession) SETTINGS.appsfavouritedThisSession = [];
13681403
}
13691404
/// Save settings
13701405
function saveSettings() {

0 commit comments

Comments
 (0)