-
Notifications
You must be signed in to change notification settings - Fork 21
UI Changes #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UI Changes #82
Changes from 12 commits
64048b1
c15cfda
b10e2e5
d81eca2
55d985b
e357a34
78448fc
4e01766
a55bf86
c854943
3bac7f5
e19af60
c41881d
f98de42
ab40d79
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,7 @@ let device = { | |
| } | ||
| };*/ | ||
| let LANGUAGE = undefined; | ||
| const favAnimMS = 500; | ||
|
|
||
| /** Ensure we run transfers one after the other rather that potentially having them overlap if the user clicks around | ||
| https://github.com/espruino/EspruinoAppLoaderCore/issues/67 */ | ||
|
|
@@ -501,19 +502,23 @@ 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]); | ||
| } else { | ||
| SETTINGS.appsFavoritedThisSession = SETTINGS.appsFavoritedThisSession.filter(obj => obj.id !== app.id); | ||
| 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 => { | ||
|
|
@@ -543,6 +548,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; | ||
|
|
@@ -559,21 +587,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")}"`; | ||
|
|
@@ -585,12 +603,13 @@ 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/100)/10+"k" : n; | ||
| return `<span class="fav-count" style="margin-left:-1em;margin-right:0.5em">${txt}</span>`; | ||
RKBoss6 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| let html = `<div class="tile column col-6 col-sm-12 col-xs-12 app-tile"> | ||
| let html = `<div class="tile column col-6 col-sm-12 col-xs-12 app-tile ${version.canUpdate?'updateTile':''}"> | ||
| <div class="tile-icon"> | ||
| <figure class="avatar"><img src="apps/${app.icon?`${app.id}/${app.icon}`:"unknown.png"}" alt="${escapeHtml(app.name)}"></figure> | ||
| </div> | ||
|
|
@@ -601,20 +620,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) | ||
|
|
@@ -789,7 +809,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"); | ||
|
|
@@ -801,7 +820,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"); | ||
|
|
@@ -842,8 +861,20 @@ 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/100)/10+"k" : cnt; | ||
| let countEl = button.querySelector('.fav-count'); | ||
| if (countEl) countEl.textContent = String(txt); | ||
| // ensure animation class is removed after the duration so it can be re-triggered | ||
| setTimeout(() => { | ||
| try { if (icon) icon.classList.remove("favoriteAnim"); } catch (e) { console.error(e); } | ||
| }, favAnimMS); | ||
| } | ||
| }); | ||
| }); | ||
|
|
@@ -1109,7 +1140,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"); | ||
|
|
@@ -1120,17 +1151,29 @@ function refreshMyApps() { | |
| handleAppInterface(app).catch( err => { | ||
| if (err != "") showToast("Failed, "+err, "error"); | ||
| }); | ||
RKBoss6 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (icon.classList.contains("icon-favourite")) { | ||
| // handle favourites on My Apps page (button has class btn-favourite) | ||
| if (button.classList && button.classList.contains("btn-favourite")) { | ||
| 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/100)/10+"k" : cnt; | ||
| let countEl = button.querySelector('.fav-count'); | ||
| if (countEl) countEl.textContent = String(txt); | ||
| setTimeout(() => { | ||
| try { if (icon) icon.classList.remove("favoriteAnim"); } catch (e) {} | ||
| }, favAnimMS); | ||
| } | ||
| }); | ||
| }); | ||
| let nonCustomAppsToUpdate = getAppsToUpdate({excludeCustomApps:true}); | ||
| let tab = document.querySelector("#tab-myappscontainer a"); | ||
| let updateApps = document.querySelector("#myappscontainer .updateapps"); | ||
| if (nonCustomAppsToUpdate.length) { | ||
| updateApps.innerHTML = `Update ${nonCustomAppsToUpdate.length} apps`; | ||
|
|
||
| updateApps.innerHTML = `Update ${nonCustomAppsToUpdate.length} ${nonCustomAppsToUpdate.length>1?"apps":"app"}`; | ||
| updateApps.classList.remove("hidden"); | ||
| updateApps.classList.remove("disabled"); | ||
| tab.setAttribute("data-badge", `${device.appsInstalled.length} ⬆${nonCustomAppsToUpdate.length}`); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.