mirror of
https://github.com/dvmarinoff/Auuki.git
synced 2026-02-17 15:37:39 +01:00
refactor Local Storage and its values
- LocalStorageItem class will take care of operations on Local Storage, on key, value base - the functions that work on values like ftp, weight, measurement, page, etc will be moved to a corresponding value class to facilitate code reuse. - fixis Issue #33 `Rider weight value in UI goes to default on page refresh`
This commit is contained in:
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,3 +1 @@
|
||||
index.js merge=ours
|
||||
manifest.webmanifest merge=ours
|
||||
|
||||
|
||||
51
db.js
51
db.js
@@ -4,10 +4,11 @@ import { FileHandler } from './file.js';
|
||||
import { IDB } from './storage/idb.js';
|
||||
import { Session } from './storage/session.js';
|
||||
import { Workout } from './storage/workout.js';
|
||||
import { values } from './values.js';
|
||||
|
||||
import { avgOfArray, maxOfArray, sum,
|
||||
first, last, round, mps, kph,
|
||||
timeDiff, fixInRange } from './functions.js';
|
||||
timeDiff, fixInRange, memberOf } from './functions.js';
|
||||
|
||||
let db = {
|
||||
pwr: 0, // controllable
|
||||
@@ -49,11 +50,11 @@ let db = {
|
||||
timestamp: Date.now(),
|
||||
inProgress: false,
|
||||
|
||||
ftp: 0,
|
||||
weight: 0,
|
||||
measurement: 'metric',
|
||||
theme: 'dark',
|
||||
page: 'home',
|
||||
ftp: values.ftp.defaultValue(),
|
||||
weight: values.weight.defaultValue(),
|
||||
theme: values.theme.defaultValue(),
|
||||
measurement: values.measurement.defaultValue(),
|
||||
page: values.page.defaultValue(),
|
||||
|
||||
workout: [],
|
||||
workoutFile: '',
|
||||
@@ -73,6 +74,7 @@ let db = {
|
||||
|
||||
xf.initDB(db);
|
||||
|
||||
|
||||
xf.reg('ui:ant:device:selected', (x, db) => {
|
||||
db.antDeviceId = db.antSearchList.filter(d => {
|
||||
return d.deviceNumber === parseInt(x);
|
||||
@@ -94,25 +96,25 @@ xf.reg('device:cad', (x, db) => db.cad = x);
|
||||
xf.reg('device:dist', (x, db) => db.distance = x);
|
||||
xf.reg('pm:power', (x, db) => db.power = x);
|
||||
xf.reg('ant:hr', (x, db) => db.hrAnt = x);
|
||||
xf.reg('ant:fec:power', (x, db)=> db.pwr = x);
|
||||
xf.reg('ant:fec:speed', (x, db)=> db.spd = x);
|
||||
xf.reg('ant:fec:cadence', (x, db)=> db.cadence = x);
|
||||
xf.reg('ant:fec:power', (x, db) => db.pwr = x);
|
||||
xf.reg('ant:fec:speed', (x, db) => db.spd = x);
|
||||
xf.reg('ant:fec:cadence', (x, db) => db.cadence = x);
|
||||
|
||||
xf.reg('ui:page', (x, db) => db.page = x);
|
||||
xf.reg('ui:ftp', (x, db) => db.ftp = x);
|
||||
xf.reg('ui:weight', (x, db) => db.weight = x);
|
||||
xf.reg('ui:theme', (x, db) => {
|
||||
if(db.theme === 'dark') { db.theme = 'white'; return; }
|
||||
if(db.theme === 'white') { db.theme = 'dark'; return; }
|
||||
xf.reg('ui:theme', (_, db) => {
|
||||
db.theme = values.theme.switch(db.theme);
|
||||
});
|
||||
xf.reg('ui:measurement', (x, db) => {
|
||||
if(db.measurement === 'metric') { db.measurement = 'imperial'; return; }
|
||||
if(db.measurement === 'imperial') { db.measurement = 'metric'; return; }
|
||||
xf.reg('ui:measurement', (_, db) => {
|
||||
db.measurement = values.measurement.switch(db.measurement);
|
||||
});
|
||||
xf.reg('storage:ftp', (x, db) => db.ftp = x);
|
||||
xf.reg('storage:weight', (x, db) => db.weight = x);
|
||||
xf.reg('storage:theme', (x, db) => db.theme = x);
|
||||
xf.reg('storage:measurement', (x, db) => db.measurement = x);
|
||||
xf.reg('storage:set:ftp', (x, db) => {
|
||||
db.ftp = x;
|
||||
});
|
||||
xf.reg('storage:set:weight', (x, db) => db.weight = x);
|
||||
xf.reg('storage:set:theme', (x, db) => db.theme = x);
|
||||
xf.reg('storage:set:measurement', (x, db) => db.measurement = x);
|
||||
|
||||
xf.reg('ui:workoutFile', (x, db) => {
|
||||
db.workoutFile = x;
|
||||
@@ -121,7 +123,6 @@ xf.reg('ui:workoutFile', (x, db) => {
|
||||
});
|
||||
xf.reg('ui:workout:set', (x, db) => {
|
||||
db.workout = db.workouts[x];
|
||||
// console.log(db.workout.intervals);
|
||||
});
|
||||
xf.reg('workout:add', (x, db) => db.workouts.push(x));
|
||||
|
||||
@@ -260,9 +261,11 @@ function dbToSession(db) {
|
||||
workout: db.workout,
|
||||
|
||||
records: db.records,
|
||||
theme: db.theme,
|
||||
|
||||
page: db.page,
|
||||
measurement: db.measurement,
|
||||
// theme: db.theme,
|
||||
// weight: db.weight,
|
||||
// measurement: db.measurement,
|
||||
};
|
||||
return session;
|
||||
}
|
||||
@@ -271,7 +274,7 @@ xf.reg('app:start', async function (x, db) {
|
||||
await idb.open('store', 1, 'session');
|
||||
session = new Session({idb: idb, name: 'session'});
|
||||
await session.restore();
|
||||
|
||||
xf.dispatch('db:ready');
|
||||
});
|
||||
|
||||
xf.reg('lock:beforeunload', (e, db) => {
|
||||
@@ -306,4 +309,6 @@ xf.reg('file:download:activity', (e, db) => {
|
||||
});
|
||||
// Session end
|
||||
|
||||
// values.theme.init();
|
||||
|
||||
export { db };
|
||||
|
||||
@@ -29,6 +29,10 @@ let first = xs => xs[0];
|
||||
let second = xs => xs[1];
|
||||
let third = xs => xs[2];
|
||||
|
||||
function memberOf(xs, y) {
|
||||
return xs.filter(x => x === y).length > 0;
|
||||
}
|
||||
|
||||
function isArray(x) {
|
||||
return Array.isArray(x);
|
||||
}
|
||||
@@ -310,6 +314,7 @@ export {
|
||||
maxOfArray,
|
||||
sum,
|
||||
splitAt,
|
||||
memberOf,
|
||||
delay,
|
||||
parseNumber,
|
||||
toDecimalPoint,
|
||||
|
||||
5
index.js
5
index.js
@@ -1,5 +1,3 @@
|
||||
import 'regenerator-runtime/runtime';
|
||||
|
||||
import { xf } from './xf.js';
|
||||
import { db } from './db.js';
|
||||
import { Controllable } from './ble/controllable.js';
|
||||
@@ -13,7 +11,6 @@ import { DeviceController,
|
||||
FileController,
|
||||
WorkoutController } from './controllers.js';
|
||||
import { FileHandler } from './file.js';
|
||||
import { LocalStorage } from './storage/local-storage.js';
|
||||
import { Vibrate } from './vibrate.js';
|
||||
import { DataMock } from './test/mock.js';
|
||||
|
||||
@@ -54,8 +51,6 @@ async function start() {
|
||||
antFec: antFec,
|
||||
});
|
||||
|
||||
let localStorage = new LocalStorage();
|
||||
|
||||
xf.dispatch('app:start');
|
||||
|
||||
Vibrate({turnOn: true});
|
||||
|
||||
10
package-lock.json
generated
10
package-lock.json
generated
@@ -1,13 +1,5 @@
|
||||
{
|
||||
"name": "flux",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.7",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
|
||||
}
|
||||
}
|
||||
"lockfileVersion": 1
|
||||
}
|
||||
|
||||
@@ -9,7 +9,5 @@
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.7"
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class IDB {
|
||||
};
|
||||
});
|
||||
}
|
||||
delete(idb, name) {
|
||||
deleteStore(idb, name) {
|
||||
const self = this;
|
||||
let deleteReq = idb.deleteDatabase(name);
|
||||
|
||||
@@ -56,11 +56,6 @@ class IDB {
|
||||
return {};
|
||||
});
|
||||
}
|
||||
update(idb) {
|
||||
const self = this;
|
||||
xf.dispatch('idb:update-success', idb);
|
||||
return idb;
|
||||
}
|
||||
createStore(idb, name) {
|
||||
const self = this;
|
||||
if (!idb.objectStoreNames.contains(name)) {
|
||||
@@ -70,6 +65,11 @@ class IDB {
|
||||
console.error(`idb trying to create store with existing name: ${name}`);
|
||||
}
|
||||
}
|
||||
update(idb) {
|
||||
const self = this;
|
||||
xf.dispatch('idb:update-success', idb);
|
||||
return idb;
|
||||
}
|
||||
add(idb, storeName, item) {
|
||||
const self = this;
|
||||
return self.transaction(idb, storeName, 'add', item, 'readwrite');
|
||||
@@ -86,7 +86,7 @@ class IDB {
|
||||
const self = this;
|
||||
return self.transaction(idb, storeName, 'getAll', undefined, 'readonly');
|
||||
}
|
||||
deleteEntry(idb, storeName, id) {
|
||||
delete(idb, storeName, id) {
|
||||
const self = this;
|
||||
return self.transaction(idb, storeName, 'delete', id, 'readwrite');
|
||||
}
|
||||
|
||||
@@ -1,81 +1,51 @@
|
||||
import { xf } from '../xf.js';
|
||||
import { memberOf } from '../functions.js';
|
||||
|
||||
class LocalStorage {
|
||||
constructor(){
|
||||
class LocalStorageItem {
|
||||
constructor(args) {
|
||||
this.key = args.key;
|
||||
this.default = args.default;
|
||||
this.validate = args.validate;
|
||||
this.init();
|
||||
}
|
||||
init() {
|
||||
let self = this;
|
||||
let ftp = window.localStorage.getItem('ftp');
|
||||
let weight = window.localStorage.getItem('rkg');
|
||||
let theme = window.localStorage.getItem('theme');
|
||||
let measurement = window.localStorage.getItem('measurement');
|
||||
|
||||
if(ftp === null || ftp === undefined) {
|
||||
ftp = 250;
|
||||
self.setFtp(ftp);
|
||||
}
|
||||
if(weight === null || weight === undefined) {
|
||||
weight = 75;
|
||||
self.setWeight(weight);
|
||||
}
|
||||
if(measurement === null || measurement === undefined) {
|
||||
measurement = 'metric';
|
||||
self.setMeasurement(measurement);
|
||||
}
|
||||
if(theme === null || theme === undefined) {
|
||||
theme = 'dark';
|
||||
self.setTheme(theme);
|
||||
}
|
||||
|
||||
xf.dispatch('storage:ftp', parseInt(ftp));
|
||||
xf.dispatch('storage:weight', parseInt(weight));
|
||||
xf.dispatch('storage:theme', theme);
|
||||
xf.dispatch('storage:measurement', measurement);
|
||||
|
||||
xf.sub('ui:ftp', ftp => {
|
||||
self.setFtp(parseInt(ftp));
|
||||
});
|
||||
xf.sub('ui:weight', weight => {
|
||||
self.setWeight(parseInt(weight));
|
||||
});
|
||||
xf.sub('db:theme', theme => {
|
||||
self.setTheme(theme);
|
||||
});
|
||||
xf.sub('db:measurement', measurement => {
|
||||
self.setMeasurement(measurement);
|
||||
});
|
||||
const self = this;
|
||||
self.restore();
|
||||
}
|
||||
setFtp(ftp) {
|
||||
if(isNaN(ftp) || ftp > 600 || ftp < 30) {
|
||||
console.warn(`Trying to enter Invalid FTP value in Storage: ${ftp}`);
|
||||
restore() {
|
||||
const self = this;
|
||||
const initialValue = self.get();
|
||||
if(initialValue === null) {
|
||||
self.set(self.default);
|
||||
}
|
||||
const key = self.key;
|
||||
const value = self.get();
|
||||
xf.dispatch(`storage:set:${key}`, value);
|
||||
}
|
||||
get() {
|
||||
const self = this;
|
||||
const key = self.key;
|
||||
const value = window.localStorage.getItem(`${key}`);
|
||||
if(value === undefined || value === null) {
|
||||
console.warn(`Trying to get non-existing value from Local Storage at key ${key}!`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
set(value) {
|
||||
const self = this;
|
||||
const key = self.key;
|
||||
const isValid = self.validate(value);
|
||||
if(isValid) {
|
||||
window.localStorage.setItem(`${key}`, value);
|
||||
} else {
|
||||
window.localStorage.setItem('ftp', Math.round(ftp));
|
||||
xf.dispatch('storage:ftp', ftp);
|
||||
console.warn(`Trying to enter invalid ${key} value in Local Storage: ${value}`);
|
||||
window.localStorage.setItem(`${key}`, self.default);
|
||||
}
|
||||
}
|
||||
setWeight(weight) {
|
||||
if(isNaN(weight) || weight > 400 || weight < 20) {
|
||||
console.warn(`Trying to enter Invalid FTP value in Storage: ${weight}`);
|
||||
|
||||
} else {
|
||||
window.localStorage.setItem('rkg', Math.round(weight));
|
||||
xf.dispatch('storage:weight', weight);
|
||||
}
|
||||
}
|
||||
setTheme(theme) {
|
||||
if(theme === 'dark' || theme === 'white') {
|
||||
window.localStorage.setItem('theme', theme);
|
||||
} else {
|
||||
console.warn(`Trying to enter Invalid Theme system value in Storage: ${theme}`);
|
||||
}
|
||||
}
|
||||
setMeasurement(measurement) {
|
||||
if(measurement === 'metric' || measurement === 'imperial') {
|
||||
window.localStorage.setItem('measurement', measurement);
|
||||
} else {
|
||||
console.warn(`Trying to enter Invalid Measurement system value in Storage: ${measurement}`);
|
||||
}
|
||||
delete() {
|
||||
const self = this;
|
||||
window.localStorage.removeItem(`${self.key}`);
|
||||
}
|
||||
}
|
||||
export { LocalStorage };
|
||||
|
||||
export { LocalStorageItem };
|
||||
|
||||
@@ -6,4 +6,4 @@ class Workout extends IDBStore {
|
||||
postInit() {}
|
||||
}
|
||||
|
||||
export { };
|
||||
export { Workout };
|
||||
|
||||
112
values.js
Normal file
112
values.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { xf } from './xf.js';
|
||||
import { memberOf } from './functions.js';
|
||||
import { LocalStorageItem } from './storage/local-storage.js';
|
||||
|
||||
class Value {
|
||||
constructor(args) {
|
||||
this.name = args.name || undefined;
|
||||
this.init();
|
||||
this.postInit();
|
||||
}
|
||||
init() {
|
||||
const self = this;
|
||||
xf.sub('app:start', _ => { self.localStorage(); });
|
||||
}
|
||||
postInit() { return; }
|
||||
localStorage() {
|
||||
const self = this;
|
||||
self.local = new LocalStorageItem({key: self.name, default: self.defaultValue(), validate: self.isValid.bind(self)});
|
||||
|
||||
xf.sub(`db:${self.name}`, value => {
|
||||
self.local.set(value);
|
||||
});
|
||||
}
|
||||
defaultValue() { return undefined; }
|
||||
isValid(value) { return false; }
|
||||
}
|
||||
|
||||
class Ftp extends Value {
|
||||
defaultValue() { return 200; }
|
||||
isValid(value) {
|
||||
value = parseInt(value);
|
||||
if(isNaN(value) || value > 600 || value < 30) { return false; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Weight extends Value {
|
||||
defaultValue() { return 75; }
|
||||
isValid(value) {
|
||||
value = parseInt(value);
|
||||
if(isNaN(value) || value > 400 || value < 20) { return false; }
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Theme extends Value {
|
||||
defaultValue() { return 'dark'; }
|
||||
collection() { return ['dark', 'white']; }
|
||||
isValid(value) {
|
||||
const self = this;
|
||||
value = String(value);
|
||||
if(memberOf(self.collection(), value)) { return true; }
|
||||
return false;
|
||||
}
|
||||
switch(value) {
|
||||
const self = this;
|
||||
if(value === 'dark') { return 'white'; }
|
||||
if(value === 'white') { return 'dark'; }
|
||||
return self.defaultValue();
|
||||
}
|
||||
}
|
||||
|
||||
class Measurement extends Value {
|
||||
defaultValue() { return 'metric'; }
|
||||
collection() { return ['metric', 'imperial']; }
|
||||
isValid(value) {
|
||||
const self = this;
|
||||
value = String(value);
|
||||
if(memberOf(self.collection(), value)) { return true; }
|
||||
return false;
|
||||
}
|
||||
switch(value) {
|
||||
const self = this;
|
||||
if(value === 'metric') { return 'imperial';}
|
||||
if(value === 'imperial') { return 'metric'; }
|
||||
return self.defaultValue();
|
||||
}
|
||||
}
|
||||
|
||||
class Page extends Value {
|
||||
defaultValue() { return 'home'; }
|
||||
collection() { return ['settings', 'home', 'workouts']; }
|
||||
isValid(value) {
|
||||
const self = this;
|
||||
value = String(value);
|
||||
if(memberOf(self.collection(), value)) { return true; }
|
||||
return false;
|
||||
}
|
||||
switch(value) {
|
||||
const self = this;
|
||||
return self.defaultValue();
|
||||
}
|
||||
}
|
||||
|
||||
const ftp = new Ftp({name: 'ftp'});
|
||||
const weight = new Weight({name: 'weight'});
|
||||
const theme = new Theme({name: 'theme'});
|
||||
const measurement = new Measurement({name: 'measurement'});
|
||||
|
||||
const page = new Page({name: 'page'});
|
||||
|
||||
|
||||
|
||||
const values = {
|
||||
ftp: ftp,
|
||||
weight: weight,
|
||||
theme: theme,
|
||||
measurement: measurement,
|
||||
page: page
|
||||
};
|
||||
|
||||
export { values };
|
||||
35
views.js
35
views.js
@@ -12,9 +12,10 @@ import { avgOfArray,
|
||||
lbsToKg,
|
||||
kmhToMph,
|
||||
fixInRange,
|
||||
fisrt,
|
||||
first,
|
||||
last,
|
||||
} from './functions.js';
|
||||
import { values } from './values.js';
|
||||
|
||||
function ConnectionView(args) {
|
||||
let dom = args.dom;
|
||||
@@ -521,15 +522,18 @@ function SettingsView(args) {
|
||||
measurementBtn: q.get('#measurement-btn'),
|
||||
};
|
||||
|
||||
let ftp = 100;
|
||||
let weight = 75;
|
||||
let measurement = 'metric';
|
||||
let ftp = values.ftp.defaultValue();
|
||||
let weight = values.weight.defaultValue();
|
||||
let theme = values.theme.defaultValue();
|
||||
let measurement = values.measurement.defaultValue();
|
||||
|
||||
xf.sub('db:ftp', ftp => {
|
||||
xf.sub('db:ftp', value => {
|
||||
ftp = value;
|
||||
dom.ftp.value = ftp;
|
||||
});
|
||||
|
||||
xf.sub('db:weight', weight => {
|
||||
xf.sub('db:weight', value => {
|
||||
weight = value;
|
||||
dom.weight.value = formatWeight(weight, measurement);
|
||||
});
|
||||
|
||||
@@ -545,6 +549,7 @@ function SettingsView(args) {
|
||||
dom.theme.classList.remove(`dark-theme`);
|
||||
dom.theme.classList.remove(`white-theme`);
|
||||
dom.theme.classList.add(`${name}-theme`);
|
||||
dom.themeBtn.textContent = `${values.theme.switch(name)}`;
|
||||
});
|
||||
|
||||
xf.sub('change', e => {
|
||||
@@ -559,10 +564,24 @@ function SettingsView(args) {
|
||||
xf.dispatch('ui:ftp', ftp);
|
||||
}, dom.ftpBtn);
|
||||
|
||||
xf.sub('keyup', e => {
|
||||
if(e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
xf.dispatch('ui:ftp', ftp);
|
||||
}
|
||||
}, dom.ftp);
|
||||
|
||||
xf.sub('pointerup', e => {
|
||||
xf.dispatch('ui:weight', weight);
|
||||
}, dom.weightBtn);
|
||||
|
||||
xf.sub('keyup', e => {
|
||||
if(e.keyCode === 13) {
|
||||
e.preventDefault();
|
||||
xf.dispatch('ui:weight', weight);
|
||||
}
|
||||
}, dom.weight);
|
||||
|
||||
xf.sub('pointerup', e => {
|
||||
xf.dispatch('ui:theme');
|
||||
}, dom.themeBtn);
|
||||
@@ -915,7 +934,7 @@ function WorkoutsView() {
|
||||
descriptions: [],
|
||||
};
|
||||
|
||||
let id = 0;
|
||||
let id = 0;
|
||||
|
||||
const off = `
|
||||
<svg class="radio radio-off" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24">
|
||||
@@ -1104,8 +1123,8 @@ function Views() {
|
||||
|
||||
NavigationWidget();
|
||||
ActivityView();
|
||||
SettingsView();
|
||||
WorkoutsView();
|
||||
SettingsView();
|
||||
|
||||
UploadWorkoutView();
|
||||
|
||||
|
||||
@@ -1,27 +1,5 @@
|
||||
let workouts =
|
||||
[
|
||||
// { name: 'Software Test',
|
||||
// type: 'Debug',
|
||||
// description: 'Software Test',
|
||||
// duration: 60,
|
||||
// xml:
|
||||
// `<workout_file>
|
||||
// <author>Marinov</author>
|
||||
// <name>Software Test</name>
|
||||
// <description>Software Test</description>
|
||||
// <sporttype>bike</sporttype>
|
||||
// <tags></tags>
|
||||
// <workout>
|
||||
// <SteadyState Duration="60" Power="0.50"/>
|
||||
// <SteadyState Duration="60" Power="200"/>
|
||||
// <SteadyState Duration="60" Power="0.60" SlopeTarget="3" />
|
||||
// <SteadyState Duration="60" Power="1.21" SlopeTarget="9.5" />
|
||||
// <IntervalsT Repeat="2" OnDuration="60" OffDuration="30" OnPower="1.06" OffPower="0.95" OnSlopeTarget="6.5" OffSlopeTarget="6" />
|
||||
|
||||
// <SteadyState Duration="60" Power="0.50"/>
|
||||
// </workout>
|
||||
// </workout_file>`
|
||||
// },
|
||||
{ name: 'Dijon',
|
||||
type: 'VO2 Max',
|
||||
description: '60/60s or 60 sec ON at 121% of FTP followed by 60 sec OFF. In 2 groups by 8 reps each.',
|
||||
|
||||
Reference in New Issue
Block a user