Merge pull request #2720 from bobrippling/apps/popcon

New launcher/boot app: popcon
pull/2735/head^2
Gordon Williams 2023-05-05 08:21:11 +01:00 committed by GitHub
commit 2defa08b86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 225 additions and 0 deletions

View File

@ -0,0 +1 @@
0.01: New App!

View File

@ -0,0 +1,8 @@
Popcon
======
Display apps sorted by regular use. No config - install the app and all your launchers will sort apps by most popular, based off launch counts within the last month, and then sort them by the most recently launched app.
:warning: Warning: this app overrides [`Storage.readJSON`], so may slow down your watch when using apps that perform I/O.
[`Storage.readJSON`]: https://www.espruino.com/ReferenceBANGLEJS2#l_Storage_readJSON

BIN
apps/popconlaunch/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

81
apps/popconlaunch/boot.js Normal file
View File

@ -0,0 +1,81 @@
{
var oldRead_1 = require("Storage").readJSON;
var monthAgo_1 = Date.now() - 1000 * 86400 * 28;
var cache_1;
var ensureCache_1 = function () {
if (!cache_1) {
cache_1 = oldRead_1("popcon.cache.json", true);
if (!cache_1)
cache_1 = {};
}
return cache_1;
};
var saveCache_1 = function (orderChanged) {
require("Storage").writeJSON("popcon.cache.json", cache_1);
if (orderChanged) {
var info = oldRead_1("popcon.info", true);
info.cacheBuster = !info.cacheBuster;
require("Storage").writeJSON("popcon.info", info);
}
};
var sortCache_1 = function () {
var ents = Object.values(cache_1);
ents.sort(function (a, b) {
var n;
var am = (a.last > monthAgo_1);
var bm = (b.last > monthAgo_1);
n = bm - am;
if (n)
return n;
n = b.pop - a.pop;
if (n)
return n;
n = b.last - a.last;
if (n)
return n;
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
});
var i = 0;
var orderChanged = false;
for (var _i = 0, ents_1 = ents; _i < ents_1.length; _i++) {
var ent = ents_1[_i];
if (ent.sortorder !== i)
orderChanged = true;
ent.sortorder = i++;
}
return orderChanged;
};
require("Storage").readJSON = (function (fname, skipExceptions) {
var _a;
var j = oldRead_1(fname, skipExceptions);
if (/\.info$/.test(fname)) {
var cache_2 = ensureCache_1();
var so = void 0;
if (j.src && (so = (_a = cache_2[j.src]) === null || _a === void 0 ? void 0 : _a.sortorder) != null)
j.sortorder = so;
else
j.sortorder = 99;
}
return j;
});
var oldLoad_1 = load;
global.load = function (src) {
if (src) {
var cache_3 = ensureCache_1();
var ent = cache_3[src] || (cache_3[src] = {
pop: 0,
last: 0,
sortorder: -10,
});
ent.pop++;
ent.last = Date.now();
var orderChanged = sortCache_1();
saveCache_1(orderChanged);
}
return oldLoad_1(src);
};
}

102
apps/popconlaunch/boot.ts Normal file
View File

@ -0,0 +1,102 @@
{
type Timestamp = number;
const oldRead = require("Storage").readJSON;
const monthAgo = Date.now() - 1000 * 86400 * 28;
let cache: undefined | {
[key: string]: {
sortorder: number,
pop: number, // amount of launches
last: Timestamp,
}
};
const ensureCache = (): NonNull<typeof cache> => {
if(!cache){
cache = oldRead("popcon.cache.json", true);
if(!cache)
cache = {};
}
return cache;
};
const saveCache = (orderChanged: boolean) => {
require("Storage").writeJSON("popcon.cache.json", cache);
if(orderChanged){
// ensure launchers reload their caches:
const info: AppInfo & { cacheBuster?: boolean } = oldRead("popcon.info", true);
info.cacheBuster = !info.cacheBuster;
require("Storage").writeJSON("popcon.info", info);
}
};
const sortCache = () => {
const ents = Object.values(cache);
ents.sort((a, b) => {
// group the most recently launched apps in the last month,
// then sort by launch count
// then by name
let n;
const am = (a.last > monthAgo) as unknown as number;
const bm = (b.last > monthAgo) as unknown as number;
n = bm - am;
if(n) return n;
n = b.pop - a.pop;
if(n) return n;
// pops are the same, sort by most recent
n = b.last - a.last;
if(n) return n;
if(a.name<b.name) return -1;
if(a.name>b.name) return 1;
return 0;
});
let i = 0;
let orderChanged = false;
for(const ent of ents){
if(ent.sortorder !== i) orderChanged = true;
ent.sortorder = i++;
}
return orderChanged;
};
require("Storage").readJSON = ((fname, skipExceptions) => {
const j: AppInfo = oldRead(fname, skipExceptions);
// ^ technically only AppInfo if we're "*.info"
if(/\.info$/.test(fname)){
const cache = ensureCache();
let so;
if(j.src && (so = cache[j.src]?.sortorder) != null)
j.sortorder = so;
else
j.sortorder = 99;
}
return j;
}) satisfies typeof oldRead;
const oldLoad = load;
global.load = (src: string) => {
if(src){
const cache = ensureCache();
const ent = cache[src] ||= {
pop: 0,
last: 0,
sortorder: -10,
};
ent.pop++;
ent.last = Date.now();
const orderChanged = sortCache();
saveCache(orderChanged);
}
return oldLoad(src);
};
}

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+AH4A/AEvNAAItq6YAFGoYEBF9I1GQ7vTAIIAMS7YqIRAQADBYwunGAyTYFyAyJLzIlJGBYvYXJowcRRaaHCAYveAoYuIF4/NdzQATF6f+45KD6vV6gfD44ME6gNBBgguU3YAB4/U64AD6fHBYQMB6YME6gMDFyoAB6wiE6wLEBhgvWEInX6AvF6ANFF7fREInRF4oMLF6xSLNhgv/GA3JF6PJFywv/F8nCF6PCF8XBBYfBF/4vSQZabLF/4wI6IvP6IuYF4nQF5/QF/4vN5IvP5Iv/F6fIBQfIF8ZUKNRQvVGAYvUFywvz3YvPRzQvE6IvN6IvfACYv/AH4A/AH4Ar"))

View File

@ -0,0 +1,20 @@
{
"id": "popconlaunch",
"name": "Popcon Launcher",
"shortName": "Popcon",
"version": "0.01",
"description": "Launcher modification - your launchers will display your favourite (popular) apps first. Overrides `readJSON`, may slow down your watch",
"readme": "README.md",
"icon": "app.png",
"type": "bootloader",
"tags": "tool,system,launcher",
"supports": ["BANGLEJS2"],
"storage": [
{"name":"popcon.boot.js","url":"boot.js"},
{"name":"popcon.img","url":"icon.js","evaluate":true}
],
"data": [
{"name":"popcon.cache.json"}
],
"sortorder": -10
}

12
typescript/types/info.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
type AppInfo = {
src: string,
img: string,
icon: string,
name: string,
type: AppType,
sortorder?: number,
};
type AppType = "app" | "clock" | "widget" | "module" | "bootloader" |
"settings" | "clkinfo" | "RAM" | "launch" | "textinput" | "scheduler" |
"notify" | "locale";