From 343fc1430c138e1c3fa7ac8a0c2fe15f4d75f3d9 Mon Sep 17 00:00:00 2001 From: dvmarinoff Date: Fri, 19 Mar 2021 19:22:25 +0200 Subject: [PATCH] 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` --- .gitattributes | 2 - db.js | 51 ++++++++++-------- functions.js | 5 ++ index.js | 5 -- package-lock.json | 10 +--- package.json | 4 +- storage/idb.js | 14 ++--- storage/local-storage.js | 110 ++++++++++++++------------------------ storage/workout.js | 2 +- values.js | 112 +++++++++++++++++++++++++++++++++++++++ views.js | 35 +++++++++--- workouts/workouts.js | 22 -------- 12 files changed, 222 insertions(+), 150 deletions(-) create mode 100644 values.js diff --git a/.gitattributes b/.gitattributes index dcf8856..ff50978 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1 @@ -index.js merge=ours manifest.webmanifest merge=ours - diff --git a/db.js b/db.js index 210d89c..1bfb29e 100644 --- a/db.js +++ b/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 }; diff --git a/functions.js b/functions.js index bab3b98..b930a21 100644 --- a/functions.js +++ b/functions.js @@ -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, diff --git a/index.js b/index.js index 2728c71..9f239d4 100644 --- a/index.js +++ b/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}); diff --git a/package-lock.json b/package-lock.json index 4cd6676..c497d48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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 } diff --git a/package.json b/package.json index 943e251..6742237 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,5 @@ }, "author": "", "license": "ISC", - "dependencies": { - "regenerator-runtime": "^0.13.7" - } + "dependencies": {} } diff --git a/storage/idb.js b/storage/idb.js index 50c46dc..c5a696d 100644 --- a/storage/idb.js +++ b/storage/idb.js @@ -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'); } diff --git a/storage/local-storage.js b/storage/local-storage.js index 7231a5d..a4c05fd 100644 --- a/storage/local-storage.js +++ b/storage/local-storage.js @@ -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 }; diff --git a/storage/workout.js b/storage/workout.js index e064ba3..3299250 100644 --- a/storage/workout.js +++ b/storage/workout.js @@ -6,4 +6,4 @@ class Workout extends IDBStore { postInit() {} } -export { }; +export { Workout }; diff --git a/values.js b/values.js new file mode 100644 index 0000000..3b43de9 --- /dev/null +++ b/values.js @@ -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 }; diff --git a/views.js b/views.js index 570173a..f6addee 100644 --- a/views.js +++ b/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 = ` @@ -1104,8 +1123,8 @@ function Views() { NavigationWidget(); ActivityView(); - SettingsView(); WorkoutsView(); + SettingsView(); UploadWorkoutView(); diff --git a/workouts/workouts.js b/workouts/workouts.js index cc65a5f..fb7db28 100644 --- a/workouts/workouts.js +++ b/workouts/workouts.js @@ -1,27 +1,5 @@ let workouts = [ -// { name: 'Software Test', -// type: 'Debug', -// description: 'Software Test', -// duration: 60, -// xml: -// ` -// Marinov -// Software Test -// Software Test -// bike -// -// -// -// -// -// -// - -// -// -// ` -// }, { 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.',