mirror of https://github.com/espruino/BangleApps
settings: remember previous menu's scroll position
parent
d7d60b4bca
commit
9503c0fc6b
|
@ -5,12 +5,38 @@ Bangle.drawWidgets();
|
|||
const BANGLEJS2 = process.env.HWVERSION==2;
|
||||
const storage = require('Storage');
|
||||
let settings;
|
||||
const scrolls = [];
|
||||
let menuScroller;
|
||||
|
||||
function updateSettings() {
|
||||
//storage.erase('setting.json'); // - not needed, just causes extra writes if settings were the same
|
||||
storage.write('setting.json', settings);
|
||||
}
|
||||
|
||||
function pushMenu(menu) {
|
||||
if(menuScroller) scrolls.push(menuScroller.scroll);
|
||||
|
||||
// if !menu, we're just pushing and something else takes
|
||||
// care of E.showMenu()
|
||||
if(menu) {
|
||||
const m = E.showMenu(menu);
|
||||
menuScroller = m.scroller;
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
function restoreMenu(menu) {
|
||||
if(!menu[""]) menu[""] = {};
|
||||
menu[""].scroll = scrolls[scrolls.length - 1] | 0;
|
||||
menuScroller = E.showMenu(menu).scroller;
|
||||
}
|
||||
|
||||
function popMenu(menu) {
|
||||
if(!menu[""]) menu[""] = {};
|
||||
menu[""].scroll = scrolls.pop() | 0;
|
||||
menuScroller = E.showMenu(menu).scroller;
|
||||
}
|
||||
|
||||
function updateOptions() {
|
||||
var o = settings.options;
|
||||
// Check to make sure nobody disabled all wakeups and locked themselves out!
|
||||
|
@ -65,38 +91,38 @@ if (("object" != typeof settings) ||
|
|||
("object" != typeof settings.options))
|
||||
resetSettings();
|
||||
|
||||
function showMainMenu() {
|
||||
function mainMenu() {
|
||||
|
||||
const mainmenu = {
|
||||
'': { 'title': /*LANG*/'Settings' },
|
||||
'< Back': ()=>load(),
|
||||
/*LANG*/'Apps': ()=>showAppSettingsMenu(),
|
||||
/*LANG*/'System': ()=>showSystemMenu(),
|
||||
/*LANG*/'Bluetooth': ()=>showBLEMenu(),
|
||||
/*LANG*/'Alerts': ()=>showAlertsMenu(),
|
||||
/*LANG*/'Utils': ()=>showUtilMenu()
|
||||
/*LANG*/'Apps': ()=>pushMenu(appSettingsMenu()),
|
||||
/*LANG*/'System': ()=>pushMenu(systemMenu()),
|
||||
/*LANG*/'Bluetooth': ()=>pushMenu(BLEMenu()),
|
||||
/*LANG*/'Alerts': ()=>pushMenu(alertsMenu()),
|
||||
/*LANG*/'Utils': ()=>pushMenu(utilMenu())
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
return mainmenu;
|
||||
}
|
||||
|
||||
function showSystemMenu() {
|
||||
function systemMenu() {
|
||||
|
||||
const mainmenu = {
|
||||
'': { 'title': /*LANG*/'System' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
/*LANG*/'Theme': ()=>showThemeMenu(),
|
||||
/*LANG*/'LCD': ()=>showLCDMenu(),
|
||||
/*LANG*/'Locale': ()=>showLocaleMenu(),
|
||||
/*LANG*/'Clock': ()=>showClockMenu(),
|
||||
/*LANG*/'Launcher': ()=>showLauncherMenu(),
|
||||
/*LANG*/'Date & Time': ()=>showSetTimeMenu()
|
||||
'< Back': ()=>popMenu(mainMenu()),
|
||||
/*LANG*/'Theme': ()=>pushMenu(themeMenu()),
|
||||
/*LANG*/'LCD': ()=>pushMenu(LCDMenu()),
|
||||
/*LANG*/'Locale': ()=>pushMenu(localeMenu()),
|
||||
/*LANG*/'Clock': ()=>pushMenu(clockMenu()),
|
||||
/*LANG*/'Launcher': ()=>pushMenu(launcherMenu()),
|
||||
/*LANG*/'Date & Time': ()=>pushMenu(setTimeMenu())
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
return mainmenu;
|
||||
}
|
||||
|
||||
function showAlertsMenu() {
|
||||
function alertsMenu() {
|
||||
var beepMenuItem;
|
||||
if (BANGLEJS2) {
|
||||
beepMenuItem = {
|
||||
|
@ -128,7 +154,7 @@ function showAlertsMenu() {
|
|||
|
||||
const mainmenu = {
|
||||
'': { 'title': /*LANG*/'Alerts' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>popMenu(mainMenu()),
|
||||
/*LANG*/'Beep': beepMenuItem,
|
||||
/*LANG*/'Vibration': {
|
||||
value: settings.vibrate,
|
||||
|
@ -153,18 +179,18 @@ function showAlertsMenu() {
|
|||
}
|
||||
};
|
||||
|
||||
return E.showMenu(mainmenu);
|
||||
return mainmenu;
|
||||
}
|
||||
|
||||
|
||||
function showBLEMenu() {
|
||||
function BLEMenu() {
|
||||
var hidV = [false, "kbmedia", "kb", "com", "joy"];
|
||||
var hidN = [/*LANG*/"Off", /*LANG*/"Kbrd & Media", /*LANG*/"Kbrd", /*LANG*/"Kbrd & Mouse", /*LANG*/"Joystick"];
|
||||
var privacy = [/*LANG*/"Off", /*LANG*/"Show name", /*LANG*/"Hide name"];
|
||||
|
||||
E.showMenu({
|
||||
return {
|
||||
'': { 'title': /*LANG*/'Bluetooth' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>popMenu(mainMenu()),
|
||||
/*LANG*/'Make Connectable': ()=>makeConnectable(),
|
||||
/*LANG*/'BLE': {
|
||||
value: settings.ble,
|
||||
|
@ -217,7 +243,7 @@ function showBLEMenu() {
|
|||
},
|
||||
/*LANG*/'Passkey': {
|
||||
value: settings.passkey?settings.passkey:/*LANG*/"none",
|
||||
onchange: () => setTimeout(showPasskeyMenu) // graphical_menu redraws after the call
|
||||
onchange: () => setTimeout(() => pushMenu(passkeyMenu())) // graphical_menu redraws after the call
|
||||
},
|
||||
/*LANG*/'Whitelist': {
|
||||
value:
|
||||
|
@ -228,12 +254,12 @@ function showBLEMenu() {
|
|||
? " (" + settings.whitelist.length + ")"
|
||||
: ""
|
||||
),
|
||||
onchange: () => setTimeout(showWhitelistMenu) // graphical_menu redraws after the call
|
||||
onchange: () => setTimeout(() => pushMenu(whitelistMenu())) // graphical_menu redraws after the call
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function showThemeMenu() {
|
||||
function themeMenu() {
|
||||
function cl(x) { return g.setColor(x).getColor(); }
|
||||
function upd(th) {
|
||||
g.theme = th;
|
||||
|
@ -250,7 +276,7 @@ function showThemeMenu() {
|
|||
|
||||
var themesMenu = {
|
||||
'':{title:/*LANG*/'Theme'},
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'< Back': ()=>popMenu(systemMenu()),
|
||||
/*LANG*/'Dark BW': ()=>{
|
||||
upd({
|
||||
fg:cl("#fff"), bg:cl("#000"),
|
||||
|
@ -283,11 +309,11 @@ function showThemeMenu() {
|
|||
}
|
||||
);
|
||||
|
||||
themesMenu[/*LANG*/'Customize'] = () => showCustomThemeMenu();
|
||||
themesMenu[/*LANG*/'Customize'] = () => pushMenu(customThemeMenu());
|
||||
|
||||
var m = E.showMenu(themesMenu);
|
||||
var m = pushMenu(themesMenu);
|
||||
|
||||
function showCustomThemeMenu() {
|
||||
function customThemeMenu() {
|
||||
function setT(t, v) {
|
||||
let th = g.theme;
|
||||
th[t] = v;
|
||||
|
@ -318,7 +344,7 @@ function showThemeMenu() {
|
|||
}
|
||||
let menu = {
|
||||
'':{title:/*LANG*/'Custom Theme'},
|
||||
"< Back": () => showThemeMenu()
|
||||
"< Back": () => popMenu(themeMenu())
|
||||
};
|
||||
const labels = {
|
||||
fg: /*LANG*/'Foreground', bg: /*LANG*/'Background',
|
||||
|
@ -341,17 +367,17 @@ function showThemeMenu() {
|
|||
},
|
||||
};
|
||||
});
|
||||
m = E.showMenu(menu);
|
||||
m = pushMenu(menu);
|
||||
}
|
||||
}
|
||||
|
||||
function showPasskeyMenu() {
|
||||
function passkeyMenu() {
|
||||
var menu = {
|
||||
"< Back" : ()=>showBLEMenu(),
|
||||
"< Back" : ()=>popMenu(BLEMenu()),
|
||||
/*LANG*/"Disable" : () => {
|
||||
settings.passkey = undefined;
|
||||
updateSettings();
|
||||
showBLEMenu();
|
||||
popMenu(BLEMenu());
|
||||
}
|
||||
};
|
||||
if (!settings.passkey || settings.passkey.length!=6) {
|
||||
|
@ -370,24 +396,24 @@ function showPasskeyMenu() {
|
|||
}
|
||||
};
|
||||
})(i);
|
||||
E.showMenu(menu);
|
||||
return menu;
|
||||
}
|
||||
|
||||
function showWhitelistMenu() {
|
||||
function whitelistMenu() {
|
||||
var menu = {
|
||||
"< Back" : ()=>showBLEMenu(),
|
||||
"< Back" : ()=>popMenu(BLEMenu()),
|
||||
};
|
||||
if (settings.whitelist_disabled) {
|
||||
menu[/*LANG*/"Enable"] = () => {
|
||||
delete settings.whitelist_disabled;
|
||||
updateSettings();
|
||||
showBLEMenu();
|
||||
popMenu(BLEMenu());
|
||||
};
|
||||
} else {
|
||||
menu[/*LANG*/"Disable"] = () => {
|
||||
settings.whitelist_disabled = true;
|
||||
updateSettings();
|
||||
showBLEMenu();
|
||||
popMenu(BLEMenu());
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -398,14 +424,14 @@ function showWhitelistMenu() {
|
|||
settings.whitelist.splice(settings.whitelist.indexOf(d),1);
|
||||
updateSettings();
|
||||
}
|
||||
setTimeout(showWhitelistMenu, 50);
|
||||
setTimeout(() => pushMenu(whitelistMenu()), 50);
|
||||
});
|
||||
}
|
||||
});
|
||||
menu[/*LANG*/'Add Device']=function() {
|
||||
E.showAlert(/*LANG*/"Connect device\nto add to\nwhitelist",/*LANG*/"Whitelist").then(function() {
|
||||
NRF.removeAllListeners('connect');
|
||||
showWhitelistMenu();
|
||||
pushMenu(whitelistMenu());
|
||||
});
|
||||
NRF.removeAllListeners('connect');
|
||||
NRF.on('connect', function(addr) {
|
||||
|
@ -420,13 +446,13 @@ function showWhitelistMenu() {
|
|||
settings.whitelist.push(addr);
|
||||
updateSettings();
|
||||
NRF.removeAllListeners('connect');
|
||||
showWhitelistMenu();
|
||||
pushMenu(whitelistMenu());
|
||||
});
|
||||
};
|
||||
E.showMenu(menu);
|
||||
return menu;
|
||||
}
|
||||
|
||||
function showLCDMenu() {
|
||||
function LCDMenu() {
|
||||
// converts g to Espruino internal unit
|
||||
function gToInternal(g) { return g * 8192; }
|
||||
// converts Espruino internal unit to g
|
||||
|
@ -436,7 +462,7 @@ function showLCDMenu() {
|
|||
|
||||
const lcdMenu = {
|
||||
'': { 'title': 'LCD' },
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'< Back': ()=>popMenu(systemMenu()),
|
||||
};
|
||||
if (BANGLEJS2)
|
||||
Object.assign(lcdMenu, {
|
||||
|
@ -583,13 +609,13 @@ function showLCDMenu() {
|
|||
}
|
||||
});
|
||||
|
||||
return E.showMenu(lcdMenu)
|
||||
return lcdMenu
|
||||
}
|
||||
|
||||
function showLocaleMenu() {
|
||||
function localeMenu() {
|
||||
const localemenu = {
|
||||
'': { 'title': /*LANG*/'Locale' },
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'< Back': ()=>popMenu(systemMenu()),
|
||||
/*LANG*/'Time Zone': {
|
||||
value: settings.timezone,
|
||||
format: v => (v > 0 ? "+" : "") + v,
|
||||
|
@ -620,13 +646,13 @@ function showLocaleMenu() {
|
|||
},
|
||||
}
|
||||
};
|
||||
return E.showMenu(localemenu);
|
||||
return localemenu;
|
||||
}
|
||||
|
||||
function showUtilMenu() {
|
||||
function utilMenu() {
|
||||
var menu = {
|
||||
'': { 'title': /*LANG*/'Utilities' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>popMenu(mainMenu()),
|
||||
/*LANG*/'Debug': {
|
||||
value: E.clip(0|settings.log,0,3),
|
||||
min: 0,
|
||||
|
@ -640,7 +666,7 @@ function showUtilMenu() {
|
|||
/*LANG*/'Compact Storage': () => {
|
||||
E.showMessage(/*LANG*/"Compacting...\nTakes approx\n1 minute",{title:/*LANG*/"Storage"});
|
||||
storage.compact();
|
||||
showUtilMenu();
|
||||
restoreMenu(utilMenu());
|
||||
},
|
||||
/*LANG*/'Rewrite Settings': () => {
|
||||
storage.write(".boot0","eval(require('Storage').read('bootupdate.js'));");
|
||||
|
@ -660,9 +686,13 @@ function showUtilMenu() {
|
|||
}, 1);
|
||||
}
|
||||
};
|
||||
const back = () => {
|
||||
restoreMenu(utilMenu());
|
||||
};
|
||||
|
||||
if (BANGLEJS2)
|
||||
menu[/*LANG*/'Calibrate Battery'] = () => {
|
||||
E.showPrompt(/*LANG*/"Is the battery fully charged?",{title:/*LANG*/"Calibrate",back:showUtilMenu}).then(ok => {
|
||||
E.showPrompt(/*LANG*/"Is the battery fully charged?",{title:/*LANG*/"Calibrate",back}).then(ok => {
|
||||
if (ok) {
|
||||
var s=storage.readJSON("setting.json");
|
||||
s.batFullVoltage = (analogRead(D3)+analogRead(D3)+analogRead(D3)+analogRead(D3))/4;
|
||||
|
@ -674,17 +704,17 @@ function showUtilMenu() {
|
|||
});
|
||||
};
|
||||
menu[/*LANG*/'Reset Settings'] = () => {
|
||||
E.showPrompt(/*LANG*/'Reset to Defaults?',{title:/*LANG*/"Settings",back:showUtilMenu}).then((v) => {
|
||||
E.showPrompt(/*LANG*/'Reset to Defaults?',{title:/*LANG*/"Settings",back}).then((v) => {
|
||||
if (v) {
|
||||
E.showMessage(/*LANG*/'Resetting');
|
||||
resetSettings();
|
||||
setTimeout(showMainMenu, 50);
|
||||
} else showUtilMenu();
|
||||
setTimeout(() => popMenu(mainMenu), 50);
|
||||
} else restoreMenu(utilMenu());
|
||||
});
|
||||
};
|
||||
menu[/*LANG*/"Turn Off"] = () => {
|
||||
E.showPrompt(/*LANG*/"Are you sure? Alarms and timers won't fire", {
|
||||
title:/*LANG*/"Turn Off",back:showUtilMenu
|
||||
title:/*LANG*/"Turn Off",back
|
||||
}).then((confirmed) => {
|
||||
if (confirmed) {
|
||||
E.showMessage(/*LANG*/"See you\nlater!", /*LANG*/"Goodbye");
|
||||
|
@ -697,34 +727,34 @@ function showUtilMenu() {
|
|||
Bangle.softOff ? Bangle.softOff() : Bangle.off();
|
||||
}, 2500);
|
||||
} else {
|
||||
showUtilMenu();
|
||||
restoreMenu(utilMenu());
|
||||
}
|
||||
});
|
||||
};
|
||||
if (Bangle.factoryReset) {
|
||||
menu[/*LANG*/'Factory Reset'] = ()=>{
|
||||
E.showPrompt(/*LANG*/'This will remove everything!',{title:/*LANG*/"Factory Reset",back:showUtilMenu}).then((v) => {
|
||||
E.showPrompt(/*LANG*/'This will remove everything!',{title:/*LANG*/"Factory Reset",back}).then((v) => {
|
||||
if (v) {
|
||||
var n = ((Math.random()*4)&3) + 1;
|
||||
E.showPrompt(/*LANG*/"To confirm, please press "+n,{
|
||||
title:/*LANG*/"Factory Reset",
|
||||
buttons : {"1":1,"2":2,"3":3,"4":4},
|
||||
back:showUtilMenu
|
||||
back
|
||||
}).then(function(v) {
|
||||
if (v==n) {
|
||||
E.showMessage();
|
||||
Terminal.setConsole();
|
||||
Bangle.factoryReset();
|
||||
} else {
|
||||
showUtilMenu();
|
||||
back();
|
||||
}
|
||||
});
|
||||
} else showUtilMenu();
|
||||
} else back();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return E.showMenu(menu);
|
||||
return menu;
|
||||
}
|
||||
|
||||
function makeConnectable() {
|
||||
|
@ -739,19 +769,20 @@ function makeConnectable() {
|
|||
}
|
||||
if (!r) try { NRF.sleep(); } catch (e) { }
|
||||
delete NRF.ignoreWhitelist;
|
||||
showMainMenu();
|
||||
popMenu(mainMenu());
|
||||
});
|
||||
}
|
||||
function showClockMenu() {
|
||||
function clockMenu() {
|
||||
var clockApps = storage.list(/\.info$/)
|
||||
.map(app => {var a=storage.readJSON(app, 1);return (a&&a.type == "clock")?a:undefined})
|
||||
.filter(app => app) // filter out any undefined apps
|
||||
.sort((a, b) => a.sortorder - b.sortorder);
|
||||
const back = ()=>popMenu(systemMenu());
|
||||
const clockMenu = {
|
||||
'': {
|
||||
'title': /*LANG*/'Select Clock',
|
||||
},
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'< Back': back,
|
||||
};
|
||||
clockApps.forEach((app, index) => {
|
||||
var label = app.name;
|
||||
|
@ -762,24 +793,25 @@ function showClockMenu() {
|
|||
settings.clock = app.src;
|
||||
settings.clockHasWidgets = storage.read(app.src).includes("Bangle.loadWidgets");
|
||||
updateSettings();
|
||||
showMainMenu();
|
||||
back();
|
||||
};
|
||||
});
|
||||
if (clockApps.length === 0) {
|
||||
clockMenu[/*LANG*/"No Clocks Found"] = () => { };
|
||||
}
|
||||
return E.showMenu(clockMenu);
|
||||
return clockMenu;
|
||||
}
|
||||
function showLauncherMenu() {
|
||||
function launcherMenu() {
|
||||
var launcherApps = storage.list(/\.info$/)
|
||||
.map(app => {var a=storage.readJSON(app, 1);return (a&&a.type == "launch")?a:undefined})
|
||||
.filter(app => app) // filter out any undefined apps
|
||||
.sort((a, b) => a.sortorder - b.sortorder);
|
||||
const back = ()=>popMenu(systemMenu());
|
||||
const launcherMenu = {
|
||||
'': {
|
||||
'title': /*LANG*/'Select Launcher',
|
||||
},
|
||||
'< Back': ()=>showSystemMenu(),
|
||||
'< Back': back,
|
||||
};
|
||||
launcherApps.forEach((app, index) => {
|
||||
var label = app.name;
|
||||
|
@ -789,22 +821,22 @@ function showLauncherMenu() {
|
|||
launcherMenu[label] = () => {
|
||||
settings.launcher = app.src;
|
||||
updateSettings();
|
||||
showMainMenu();
|
||||
back();
|
||||
};
|
||||
});
|
||||
if (launcherApps.length === 0) {
|
||||
launcherMenu[/*LANG*/"No Launchers Found"] = () => { };
|
||||
}
|
||||
return E.showMenu(launcherMenu);
|
||||
return launcherMenu;
|
||||
}
|
||||
|
||||
function showSetTimeMenu() {
|
||||
function setTimeMenu() {
|
||||
let d = new Date();
|
||||
const timemenu = {
|
||||
'': { 'title': /*LANG*/'Date & Time' },
|
||||
'< Back': function () {
|
||||
setTime(d.getTime() / 1000);
|
||||
showSystemMenu();
|
||||
popMenu(systemMenu());
|
||||
},
|
||||
/*LANG*/'Day': {
|
||||
value: d.getDate(),
|
||||
|
@ -851,13 +883,13 @@ function showSetTimeMenu() {
|
|||
}
|
||||
}
|
||||
};
|
||||
return E.showMenu(timemenu);
|
||||
return timemenu;
|
||||
}
|
||||
|
||||
function showAppSettingsMenu() {
|
||||
function appSettingsMenu() {
|
||||
let appmenu = {
|
||||
'': { 'title': /*LANG*/'App Settings' },
|
||||
'< Back': ()=>showMainMenu(),
|
||||
'< Back': ()=>popMenu(mainMenu()),
|
||||
}
|
||||
const apps = storage.list(/\.settings\.js$/)
|
||||
.map(s => s.substr(0, s.length-12))
|
||||
|
@ -878,12 +910,13 @@ function showAppSettingsMenu() {
|
|||
apps.forEach(function (app) {
|
||||
appmenu[app.name] = () => { showAppSettings(app) };
|
||||
})
|
||||
E.showMenu(appmenu)
|
||||
return appmenu;
|
||||
}
|
||||
function showAppSettings(app) {
|
||||
const back = () => popMenu(appSettingsMenu());
|
||||
const showError = msg => {
|
||||
E.showMessage(`${app.name}:\n${msg}!\n\nBTN1 to go back`);
|
||||
setWatch(showAppSettingsMenu, BTN1, { repeat: false });
|
||||
setWatch(back, BTN1, { repeat: false });
|
||||
}
|
||||
let appSettings = storage.read(app.id+'.settings.js');
|
||||
try {
|
||||
|
@ -896,8 +929,9 @@ function showAppSettings(app) {
|
|||
return showError(/*LANG*/'Invalid settings');
|
||||
}
|
||||
try {
|
||||
// pass showAppSettingsMenu as "back" argument
|
||||
appSettings(()=>showAppSettingsMenu());
|
||||
// pass appSettingsMenu as "back" argument
|
||||
pushMenu();
|
||||
appSettings(back);
|
||||
} catch (e) {
|
||||
console.log(`${app.name} settings error:`, e);
|
||||
return showError(/*LANG*/'Error in settings');
|
||||
|
@ -961,7 +995,7 @@ function showTouchscreenCalibration() {
|
|||
g.setFont("6x8:2").setFontAlign(0,0).drawString(/*LANG*/"Calibrated!", g.getWidth()/2, g.getHeight()/2);
|
||||
}
|
||||
// now load the main menu again
|
||||
setTimeout(showLCDMenu, 500);
|
||||
setTimeout(() => restoreMenu(LCDMenu), 500);
|
||||
}
|
||||
|
||||
function touchHandler(_,e) {
|
||||
|
@ -993,5 +1027,5 @@ function showTouchscreenCalibration() {
|
|||
showTapSpot();
|
||||
}
|
||||
|
||||
showMainMenu();
|
||||
pushMenu(mainMenu());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue