From 1af85afe6e90f07c27f429fd41d84faef34dd7cd Mon Sep 17 00:00:00 2001 From: James-Livesey Date: Mon, 8 Nov 2021 13:36:15 +0000 Subject: [PATCH] Add basic management options --- dashboard/dashboard.js | 2 + dashboard/editor.html | 4 +- dashboard/editor.js | 2 - dashboard/manager.html | 56 +++++++++- dashboard/manager.js | 120 +++++++++++++++++++++ dialogs.js | 2 +- locale/en_GB.json | 30 ++++-- style.css | 231 +++++++++++++++++++++++++++-------------- 8 files changed, 353 insertions(+), 94 deletions(-) diff --git a/dashboard/dashboard.js b/dashboard/dashboard.js index e1d7981..c1b7f47 100644 --- a/dashboard/dashboard.js +++ b/dashboard/dashboard.js @@ -11,10 +11,12 @@ namespace("com.subnodal.nanoplay.website.dashboard", function(exports) { var subElements = require("com.subnodal.subelements"); var resources = require("com.subnodal.nanoplay.website.resources"); + var dialogs = require("com.subnodal.nanoplay.website.dialogs"); var darkThemeEnabled = false; window.dashboard = exports; + window.dialogs = dialogs; exports.toggleTheme = function() { darkThemeEnabled = !darkThemeEnabled; diff --git a/dashboard/editor.html b/dashboard/editor.html index 9c5f1ed..3191166 100644 --- a/dashboard/editor.html +++ b/dashboard/editor.html @@ -57,10 +57,10 @@

{{ editor.getAppName() }} - + - + diff --git a/dashboard/editor.js b/dashboard/editor.js index 8450c2c..b91a9a9 100644 --- a/dashboard/editor.js +++ b/dashboard/editor.js @@ -19,11 +19,9 @@ namespace("com.subnodal.nanoplay.website.editor", function(exports) { var resources = require("com.subnodal.nanoplay.website.resources"); var dialogs = require("com.subnodal.nanoplay.website.dialogs"); var communications = require("com.subnodal.nanoplay.website.communications"); - var dashboard = require("com.subnodal.nanoplay.website.dashboard"); var simulator = require("com.subnodal.nanoplay.website.simulator"); window.editor = exports; - window.dialogs = dialogs; const SUPPORTED_LANGUAGES = ["en_GB", "fr_FR"]; const DEFAULT_APP_ICON = "AAAAAAAAA////AAAf///YAAH///+AABAAAAgAAX/vgIAAEAAACAABe79wgAAQAAAIAAF+94CAABAAAAgAAW+/4IAAEAAACAABfvv4gAAQAAAIAAD///8AAAAAAAAAA=="; diff --git a/dashboard/manager.html b/dashboard/manager.html index e23d93a..57afbab 100644 --- a/dashboard/manager.html +++ b/dashboard/manager.html @@ -52,8 +52,60 @@
-

Manager coming soon!

-

Our NanoPlay manager is still in development! Come back later when we have something to show.

+ + +

{{ manager.getCurrentNanoplay().name }}

+ +

{{ _("manager_storageInfo", {percentage: Math.max(Math.round(((manager.MAXIMUM_FREE_STORAGE - manager.getLoadedFreeStorage()) / manager.MAXIMUM_FREE_STORAGE) * 100), 0), bytesFree: manager.getLoadedFreeStorage()}) }}

+ +

System update

+

Your NanoPlay is up-to-date on version 0.2.6!

+ +

General settings

+ + + +
+ + +

Installed apps

+ +
+ {{ appManifest.name[l10n.getLocaleCode()] || _("untitled") }} +
+ + +
+
+
+ +

manager_noApps

+
+
+
+ +

manager_connect

+

manager_connectDescription

+ + +

+
+ +

manager_noBluetooth

+
+
+
+
diff --git a/dashboard/manager.js b/dashboard/manager.js index 90f2e73..5a0f0bc 100644 --- a/dashboard/manager.js +++ b/dashboard/manager.js @@ -8,5 +8,125 @@ */ namespace("com.subnodal.nanoplay.website.manager", function(exports) { + var subElements = require("com.subnodal.subelements"); + var nanoplay = require("com.subnodal.nanoplay.webapi"); + window.manager = exports; + + exports.MAXIMUM_FREE_STORAGE = 8_712; // Bytes + + exports.pages = { + GENERAL: 0, + APPS: 1 + }; + + var currentPage = exports.pages.GENERAL; + var currentNanoplay = new nanoplay.NanoPlay(); + var loadedAppsList = {}; + var loadedFreeStorage = 0; + + exports.getCurrentPage = function() { + return currentPage; + } + + exports.setCurrentPage = function(page) { + currentPage = page; + + subElements.render(); + }; + + exports.getCurrentNanoplay = function() { + return currentNanoplay; + }; + + exports.showLoading = function(loadingMessage = _("manager_applyingSetting")) { + document.querySelector("#loadingMessage").textContent = loadingMessage; + + document.querySelector("#loading").removeAttribute("hidden"); + }; + + exports.hideLoading = function() { + document.querySelector("#loading").setAttribute("hidden", ""); + }; + + exports.getAppsList = function() { + exports.showLoading(_("manager_gettingApps")); + + return currentNanoplay.getApps().then(function(apps) { + loadedAppsList = apps; + + manager.setCurrentPage(manager.pages.APPS); + + exports.hideLoading(); + + return Promise.resolve(); + }); + }; + + exports.getLoadedAppsList = function() { + return loadedAppsList; + }; + + exports.getFreeStorage = function() { + return currentNanoplay.getFreeStorage().then(function(data) { + loadedFreeStorage = data; + + subElements.render(); + + return Promise.resolve(); + }); + }; + + exports.getLoadedFreeStorage = function() { + return loadedFreeStorage; + }; + + exports.deleteApp = function(appId) { + exports.showLoading(_("manager_gettingApps")); + + return currentNanoplay.removeApp(appId).then(function() { + delete loadedAppsList[appId]; + + return exports.getFreeStorage().then(function() { + exports.hideLoading(); + + return Promise.resolve(); + }); + }); + }; + + exports.connect = function() { + document.querySelector("#connectButton").textContent = _("connecting"); + document.querySelector("#connectButton").disabled = true; + + return currentNanoplay.connect().then(function() { + document.querySelector("#connectButton").textContent = _("connect"); + document.querySelector("#connectButton").disabled = false; + + document.querySelector("#connectError").textContent = ""; + + return exports.getFreeStorage(); + }).catch(function(error) { + document.querySelector("#connectButton").textContent = _("connect"); + document.querySelector("#connectButton").disabled = false; + + document.querySelector("#connectError").textContent = _("manager_connectionError"); + + console.error(error); + + return Promise.resolve(); + }); + }; + + subElements.ready(function() { + document.querySelectorAll("#managerOptions, #managerOptions div button, #managerOptions div label").forEach(function(element) { + element.addEventListener("mouseover", function(event) { + var previewScreen = element.getAttribute("data-preview") || "home"; + + document.querySelector("#managerPreview .device .screen").style.backgroundImage = `url("/media/manager/${previewScreen}.png")`; + + event.stopPropagation(); + }); + }); + }); }); \ No newline at end of file diff --git a/dialogs.js b/dialogs.js index 8ed9624..fdd638d 100644 --- a/dialogs.js +++ b/dialogs.js @@ -13,7 +13,7 @@ namespace("com.subnodal.nanoplay.website.dialogs", function(exports) { exports.open = function(dialogId) { document.getElementById(dialogId).setAttribute("open", ""); - document.querySelector("dialog[open] a, dialog[open] input, dialog[open] button").focus(); + document.querySelector("dialog[open] a, dialog[open] input, dialog[open] button")?.focus(); }; exports.close = function(dialogId) { diff --git a/locale/en_GB.json b/locale/en_GB.json index 1c2abf1..61b7616 100644 --- a/locale/en_GB.json +++ b/locale/en_GB.json @@ -10,6 +10,18 @@ "shop": "Shop", "dashboard": "Dashboard", + "ok": "OK", + "cancel": "Cancel", + "done": "Done", + "open": "Open", + "edit": "Edit", + "delete": "Delete", + "connect": "Connect", + "connecting": "Connecting...", + "upload": "Upload", + "uploading": "Uploading...", + "untitled": "Untitled", + "signIn_title": "Let's go!", "signIn_description": "Sign in with your Subnodal Account to get coding! Your code, apps, settings and more will all be stored with your Subnodal Account for you to access from anywhere.", "signIn_signUpHint": "Don't have a Subnodal Account yet? Signing up is free and easy!", @@ -32,8 +44,6 @@ "editor_noAppsYet": "No apps yet!", "editor_noAppsYetMessage": "Once you've coded some apps, they'll appear here for you to open later.", "editor_sendToNanoplay": "Send to NanoPlay", - "editor_connecting": "Connecting...", - "editor_uploading": "Uploading...", "editor_noErrorsYet": "No errors yet!", "editor_noBluetooth": "No Bluetooth? Don't worry! Visit the NanoPlay editor on your mobile phone to connect to your NanoPlay.", "editor_noBluetoothQrCode": "Point your phone's camera at this QR code to quickly visit the NanoPlay editor", @@ -62,13 +72,17 @@ "editor_logSource_simulator": "Simulator", "manager": "Manager", + "manager_connect": "Connect to your NanoPlay", + "manager_connectDescription": "To access your NanoPlay's settings, you'll need to connect to your NanoPlay using Bluetooth.", + "manager_connectionError": "Looks like we couldsn't connect to your NanoPlay! Try connecting again.", + "manager_noBluetooth": "Unfortunately, this device doesn't have Bluetooth. Try using another device (such as a smartphone) instead.", + "manager_storageInfo": "Storage {percentage}% full · {bytesFree} bytes free", + "manager_noApps": "You have no apps on this NanoPlay.", + "manager_applyingSetting": "Applying setting...", + "manager_gettingApps": "Getting apps...", + "manager_deletingApp": "Deleting app...", "toggleDarkMode": "Toggle dark mode", - "backToDashboard": "Back to dashboard", - - "ok": "OK", - "cancel": "Cancel", - "done": "Done", - "open": "Open" + "backToDashboard": "Back to dashboard" } } \ No newline at end of file diff --git a/style.css b/style.css index 43f0a4c..69b0356 100644 --- a/style.css +++ b/style.css @@ -25,6 +25,7 @@ --simulatorBlueButton: #dceffd; --simulatorOrangeButton: #fdf5e3; --simulatorScreen: #7fc5f7; + --overlayBackground: rgba(255, 255, 255, 0.8); } * { @@ -68,6 +69,7 @@ body.dark { --blueText: white; --orange: #f5b342; --orangeText: white; + --overlayBackground: rgba(12, 36, 53, 0.8); } nav { @@ -254,7 +256,7 @@ button.orange { color: var(--orangeText); } -input { +input, select { padding: 10px; padding-left: 1em; padding-right: 1em; @@ -287,7 +289,7 @@ label span:first-child { vertical-align: middle; } -label input { +label input, label select { width: calc(100% - 210px); vertical-align: middle; } @@ -319,57 +321,43 @@ pre strong.error { color: var(--red); } +progress { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + height: 0.8em; + padding: 0; + overflow: hidden; +} + +progress::-webkit-progress-bar { + background-color: var(--shade); + border-radius: 10px; + overflow: hidden; +} + +progress::-webkit-progress-value { + background-color: var(--blue); + border-radius: 10px; + transition: width 0.5s; +} + +progress::-moz-progress-bar { + background-color: var(--blue); + border-radius: 10px; +} + loader { position: relative; display: block; - width: calc(2em * 2); - height: calc(2em * 2); - margin: calc(0.5em + 5px) auto; + width: calc(2rem * 2); + height: calc(2rem * 2); + margin: calc(0.5rem + 5px) auto; + background-color: inherit; color: var(--blue); border-radius: 50%; box-shadow: inset 0 0 0 10px; - -moz-transform: translateZ(0); - -webkit-transform: translateZ(0); - transform: translateZ(0); -} - -loader::before, loader::after { - position: absolute; - content: ""; -} - -loader::before { - left: -2.5px; - width: calc(2em + 5px); - height: calc(calc(2em * 2) + 5px); - background-color: var(--background); - border-radius: calc(calc(2em * 2) + 5px) 0 0 calc(calc(2em * 2) + 5px); - -moz-transform-origin: calc(2em + 5px) calc(2em + 1px); - -webkit-transform-origin: calc(2em + 5px) calc(2em + 1px); - transform-origin: calc(2em + 5px) calc(2em + 1px); - -moz-transform: rotate(-180deg); - -webkit-transform: rotate(-180deg); - transform: rotate(-180deg); - -moz-animation: spinner 2s infinite ease 1.5s; - -webkit-animation: spinner 2s infinite ease 1.5s; - animation: spinner 2s infinite ease 1.5s; -} - -loader::after { - left: calc(2em - 2.5px); - width: calc(2em + 5px); - height: calc(calc(2em * 2) + 5px); - background-color: var(--background); - border-radius: 0 calc(calc(2em * 2) + 5px) calc(calc(2em * 2) + 5px) 0; - -moz-transform-origin: 0 calc(2em + 1px); - -webkit-transform-origin: 0 calc(2em + 1px); - transform-origin: 0 calc(2em + 1px); - -moz-transform: rotate(-180deg); - -webkit-transform: rotate(-180deg); - transform: rotate(-180deg); - -moz-animation: spinner 2s infinite ease; - -webkit-animation: spinner 2s infinite ease; - animation: spinner 2s infinite ease; + animation: loader 2s infinite linear; } loader.entireScreen { @@ -804,16 +792,118 @@ body.dark .qrCode { #managerOptions { display: flex; + position: relative; + align-items: center; +} + +#managerOptions #loading { + display: flex; + position: absolute; align-items: center; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: 100%; + height: 100%; + max-height: unset; + margin: 0; + padding: 0; + background-color: var(--overlayBackground); + transition: 0.5s opacity; +} + +#managerOptions #loading[hidden] { + opacity: 0; + pointer-events: none; +} + +#managerOptions #loading div { + text-align: center; +} + +#managerOptions #loading div p { + font-size: 1em; } #managerOptions div { width: 100%; - max-height: 60vh; - margin-inline-end: 5vw; + max-height: 80vh; + margin-inline-end: calc(5vw - 1em); + padding-inline-end: 1em; overflow: auto; } +#managerOptions .menu h1 { + font-size: 1.2em; +} + +#managerOptions .menu h2 { + font-size: 1em; +} + +#managerOptions .menu p { + font-size: 0.8em; +} + +#managerOptions .menu button:not(.back):not(.app *), #managerOptions .menu .app { + display: block; + width: 100%; + padding: 10px; + padding-left: 1em; + padding-right: 1em; + margin-top: 5px; + margin-bottom: 5px; + background-color: var(--shade); + color: var(--shadeText); + border-radius: 10px; + text-align: left; +} + +#managerOptions .menu button.back { + display: block; +} + +#managerOptions .menu label { + display: block; + padding: 10px; + background-color: var(--shade); + color: var(--shadeText); + border-radius: 10px; +} + +#managerOptions .menu label span { + display: block; + width: calc(100% - 2rem - 10px); + margin-left: calc(1rem - 5px); + margin-right: calc(1rem - 5px); + margin-bottom: 10px; + font-size: 0.8em; +} + +#managerOptions .menu label input, #managerOptions .menu label select { + width: 100%; + padding-left: 10px; + padding-right: 10px; + background-color: var(--background); + color: var(--foreground); +} + +#managerOptions .menu progress { + width: 100%; +} + +#managerOptions .menu .app strong, #managerOptions .menu .app span { + display: block; +} + +#managerOptions .menu .app .options { + margin-top: 5px; + margin-bottom: 5px; + padding-inline-end: 0; + text-align: end; +} + @media (max-width: 800px) { .simulator { width: 30vw; @@ -884,7 +974,7 @@ body.dark .qrCode { align-items: flex-start; } - #managerOptions div { + #managerOptions .menu { max-height: calc(100vh - 60vw - 5em); margin-inline-end: 0; } @@ -901,45 +991,28 @@ body.dark .qrCode { } } -@-moz-keyframes spinner { +@keyframes loader { 0% { - -moz-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - - 100% { - -moz-transform: rotate(360deg); - -webkit-transform: rotate(360deg); - transform: rotate(360deg); + clip-path: polygon(50% 50%, 50% 0%, 0% 0, 0% 100%, 100% 100%, 100% 0%); + transform: rotateZ(0deg); } -} -@-webkit-keyframes spinner { - 0% { - -moz-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(0deg); + 25% { + clip-path: polygon(50% 50%, 50% 0%, 0% 0, 0% 100%, 100% 100%, 100% 100%); } - 100% { - -moz-transform: rotate(360deg); - -webkit-transform: rotate(360deg); - transform: rotate(360deg); + 50% { + clip-path: polygon(50% 50%, 50% 0%, 0% 0, 0% 100%, 100% 100%, 0% 100%); + transform: rotateZ(360deg); } -} -@keyframes spinner { - 0% { - -moz-transform: rotate(0deg); - -webkit-transform: rotate(0deg); - transform: rotate(-180deg); + 75% { + clip-path: polygon(50% 50%, 50% 0%, 0% 0, 0% 100%, 100% 100%, 100% 100%); } 100% { - -moz-transform: rotate(360deg); - -webkit-transform: rotate(360deg); - transform: rotate(180deg); + clip-path: polygon(50% 50%, 50% 0%, 0% 0, 0% 100%, 100% 100%, 100% 0%); + transform: rotateZ(720deg); } }