diff --git a/.env.development b/.env.development index fea6886..77ae56f 100644 --- a/.env.development +++ b/.env.development @@ -1,12 +1,14 @@ -SERVICE_WORKER_FILE='sw.js' +APP_VERSION="DEV 0.0.10" +SERVICE_WORKER_FILE='service-worker.js' APP_ID='1' -VUE_APP_URL='prova SVILUPPO!' +APP_URL='https://freeplanet.app' PROVA_PAOLO='PROVA SVILUPPO' LANG_DEFAULT='it' -MONGODB_HOST='http://localhost:3000' PAO_APP_ID='KKPPAA5KJK435J3KSS9F9D8S9F8SD98F9SDF' MASTER_KEY='KKPPSS5KJK435J3KSS9F9D8S9F8SD3CR3T' +MONGODB_HOST='http://localhost:3000' LOGO_REG='freeplanet-logo-full.svg' TEST_EMAIL='paolo.arena77@gmail.com' TEST_USERNAME='paoloar77' TEST_PASSWORD='mypassword@1A' +PUBLICKEY_PUSH='BGxRrFWnPoa_ImUaWXmeEOFVI9VNKVKaAPsvsM1XY6wn24yxp9MyOQ4crNYCJKxSXV65Y1GblW5_VLoamedcZ1I' diff --git a/config/envparser.js b/config/envparser.js index a53d29e..5594197 100644 --- a/config/envparser.js +++ b/config/envparser.js @@ -9,7 +9,8 @@ switch (process.env.NODE_ENV) { path = `.env.development` break default: - path = `.env` + path = `.env.production` + break } // console.log("PATH", path) diff --git a/package-lock.json b/package-lock.json index 0c3bf32..2b2bcf6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "freeplanet", - "version": "0.0.1", + "version": "0.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -11193,6 +11193,11 @@ } } }, + "idb-keyval": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-3.1.0.tgz", + "integrity": "sha512-iFwFN5n00KNNnVxlOOK280SJJfXWY7pbMUOQXdIXehvvc/mGCV/6T2Ae+Pk2KwAkkATDTwfMavOiDH5lrJKWXQ==" + }, "ieee754": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", @@ -11216,6 +11221,15 @@ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=" }, + "immortal-db": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/immortal-db/-/immortal-db-1.0.2.tgz", + "integrity": "sha512-7EaVr6vUaaqsl9Jnp+CY4FzA1jIQD+o1tFEY2+O4ibYgmVB+FEWDoyUNN/naq9ZfiYKw4+uly1fpxk0xyE358w==", + "requires": { + "idb-keyval": "3.1.0", + "js-cookie": "2.2.0" + } + }, "import-cwd": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", @@ -21098,7 +21112,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -24014,7 +24028,7 @@ "graceful-fs": "4.1.15", "make-dir": "1.3.0", "unique-string": "1.0.0", - "write-file-atomic": "2.3.0", + "write-file-atomic": "2.4.2", "xdg-basedir": "3.0.0" } }, @@ -24028,9 +24042,9 @@ } }, "write-file-atomic": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", - "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", + "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", "dev": true, "requires": { "graceful-fs": "4.1.15", @@ -25489,9 +25503,9 @@ }, "dependencies": { "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true }, "chardet": { @@ -25502,7 +25516,7 @@ }, "external-editor": { "version": "2.2.0", - "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { @@ -25537,7 +25551,7 @@ "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", "dev": true, "requires": { - "ansi-escapes": "3.1.0", + "ansi-escapes": "3.2.0", "chalk": "2.4.1", "cli-cursor": "2.1.0", "cli-width": "2.2.0", diff --git a/package.json b/package.json index f48f25a..4bdad00 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,13 @@ { "name": "freeplanet", - "version": "0.0.1", + "version": "0.0.4", "private": true, + "keywords": [ + "freeplanet", + "free social" + ], + "author": "Paolo Arena", + "license": "MIT", "scripts": { "lint": "tslint --project tsconfig.json", "lint:fix": "tslint --project tsconfig.json --fix", @@ -10,11 +16,12 @@ "pwa": "NODE_ENV=development NODE_OPTIONS=--max_old_space_size=4096 DEBUG=v8:* quasar dev -m pwa", "test:unit": "jest", "test:cover": "jest --coverage", - "build": "quasar build -m pwa && workbox generateSW workbox-config.js", + "build": "quasar build -m pwa", "build:clean": "quasar clean", "serve": "quasar serve ./dist/pwa-mat", "serve:coverage": "quasar serve test/coverage/lcov-report/ --cache 0 --port 8788", - "deploy": "deploy.sh" + "deploy": "deploy.sh", + "generate-sw": "workbox generateSW workbox-config.js" }, "dependencies": { "@types/vuelidate": "^0.7.0", @@ -29,6 +36,7 @@ "graphql": "^0.13.2", "graphql-tag": "^2.8.0", "gsap": "^2.0.2", + "immortal-db": "^1.0.2", "jquery": "^3.3.1", "js-cookie": "^2.2.0", "localforage": "^1.7.3", diff --git a/quasar.conf.js b/quasar.conf.js index 3e42cce..3dbbc0d 100644 --- a/quasar.conf.js +++ b/quasar.conf.js @@ -59,7 +59,7 @@ module.exports = function (ctx) { store: 'src/store/index.ts' }, // app plugins (/src/plugins) - plugins: ['i18n', 'axios', 'vee-validate', 'myconfig', 'local-storage', 'error-handler', 'indexdb', 'vue-idb'], + plugins: ['i18n', 'axios', 'vee-validate', 'myconfig', 'local-storage', 'error-handler', 'globalroutines', 'vue-idb'], css: [ 'app.styl' ], @@ -88,6 +88,7 @@ module.exports = function (ctx) { .alias .set('~', __dirname) .set('@', path.resolve(__dirname, 'src')) + // .set('env', path.resolve(__dirname, 'config/helpers/env.js')) config.module .rule('template-engine') .test(/\.pug$/) @@ -180,8 +181,18 @@ module.exports = function (ctx) { } }, pwa: { + // runtimeCaching: [ + // { + // urlPattern: '/statics', + // handler: 'networkFirst' + // } + // ], + + // workboxPluginMode: 'GenerateSW', workboxPluginMode: 'InjectManifest', - // workboxOptions: {}, + workboxOptions: { + // swSrc: 'src/sw.js', + }, manifest: { name: 'Free Planet', short_name: 'freeplanet', diff --git a/src-pwa/custom-service-worker.js b/src-pwa/custom-service-worker.js index 81b7637..c1cdaa1 100644 --- a/src-pwa/custom-service-worker.js +++ b/src-pwa/custom-service-worker.js @@ -1,24 +1,419 @@ + /* * This file (which will be your service worker) * is picked up by the build system ONLY if * quasar.conf > pwa > workboxPluginMode is set to "InjectManifest" */ -self.addEventListener('install', function(event) { - console.log('[Service Worker] Installing Service Worker ...', event); -}); +// Questo è il swSrc -self.addEventListener('activate', function(event) { - console.log('[Service Worker] Activating Service Worker ...', event); - return self.clients.claim(); -}); +console.log('SW-06 ___________________________ PAO: this is my custom service worker'); -self.addEventListener('fetch', function(event) { - //console.log('[Service Worker] Fetching something ....', event); - if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') { - console.log('SAME ORIGIN!', event); - return; +importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.0.0/workbox-sw.js'); //++Todo: Replace with local workbox.js +importScripts('../statics/js/idb.js'); +importScripts('../statics/js/storage.js'); + + +console.log('SW-06 1'); +const cfgenv = { + serverweb: self.location.protocol + "//" + self.location.hostname + ':3000', + dbname: 'mydb3', + dbversion: 11, +} + +console.log('SW-06 2'); + +console.log('SERVERWEB=', cfgenv.serverweb) + +// console.log('serverweb', cfgenv.serverweb) + +async function writeData(table, data) { + console.log('writeData', table, data); + await idbKeyval.setdata(table, data); +} + +async function readAllData(table) { + console.log('readAllData', table); + return await idbKeyval.getalldata(table); +} + +async function clearAllData(table) { + console.log('clearAllData', table); + await idbKeyval.clearalldata(table) +} + +async function deleteItemFromData(table, id) { + console.log('deleteItemFromData', table, 'ID:', id); + + await idbKeyval.deletedata(table, id) +} + + + +// self.addEventListener('activate', function(event) { +// event.waitUntil( +// // createDB() +// ); +// }); + +if (!workbox) { + let workbox = new self.WorkboxSW(); + console.log('SW-06 3'); +} + +if (workbox) { + // const url = new URL(location.href); + // const debug = url.searchParams.has('debug'); + const debug = false; + workbox.setConfig({ debug: debug }); + + workbox.core.setCacheNameDetails({ prefix: "freeplanet" }); + + /** + * The workboxSW.precacheAndRoute() method efficiently caches and responds to + * requests for URLs in the manifest. + * See https://goo.gl/S9QRab + */ + self.__precacheManifest = [].concat(self.__precacheManifest || []); + workbox.precaching.suppressWarnings(); + workbox.precaching.precacheAndRoute(self.__precacheManifest, {}); + + // workbox.routing.registerRoute(/^http/, workbox.strategies.networkFirst(), 'GET'); + + workbox.routing.registerRoute( + new RegExp(/\.(?:png|gif|jpg|jpeg|svg)$/), + workbox.strategies.staleWhileRevalidate({ + cacheName: 'images', + plugins: [ + new workbox.expiration.Plugin({ + maxEntries: 60, + maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days + }), + ], + }), + ); + + workbox.routing.registerRoute( + new RegExp(/.*(?:googleapis|gstatic)\.com.*$/), + workbox.strategies.staleWhileRevalidate({ + cacheName: 'google-fonts', + plugins: [ + new workbox.expiration.Plugin({ + maxEntries: 30, + }), + ] + }) + ); + + + workbox.routing.registerRoute( + new RegExp(cfgenv.serverweb + '/todos/'), + function (args) { + console.log('registerRoute!') + return fetch(args.event.request, args.event.headers) + .then(function (res) { + console.log('1° ******* [[[ SERVICE-WORKER ]]] registerRoute fetch: ', args.event) + // LOAD FROM SERVER , AND SAVE INTO INDEXEDDB + console.log('res.status', res.status) + if (res.status === 200) { + var clonedRes = res.clone(); + clearAllData('todos') + .then(function () { + return clonedRes.json(); + }) + .then(function (data) { + if (data.todos) { + console.log('Records TODOS Received from Server [', data.todos.length, 'record]', data.todos) + for (let key in data.todos) { + writeData('todos', data.todos[key]) + } + } + }); + return res + } + }) + } + ); + + + workbox.routing.registerRoute( + new RegExp(/.*\/(?:statics\/icons).*$/), + workbox.strategies.cacheFirst({ + cacheName: 'image-cache', + plugins: [ + new workbox.expiration.Plugin({ + maxAgeSeconds: 30 * 24 * 60 * 60, + }), + ] + }) + ); + + workbox.routing.registerRoute( + new RegExp(/.*\/(?:css|font).*/), + workbox.strategies.cacheFirst({ + cacheName: 'css-fonts', + plugins: [ + new workbox.expiration.Plugin({ + maxAgeSeconds: 30 * 24 * 60 * 60, + }), + ] + }) + ); + + + workbox.routing.registerRoute( + new RegExp('https://cdnjs.coudflare.com/ajax/libs/material-design-lite/1.3.0/material.indigo-pink.min.css'), + workbox.strategies.staleWhileRevalidate({ + cacheName: 'material-css', + plugins: [ + new workbox.expiration.Plugin({ + maxAgeSeconds: 30 * 24 * 60 * 60, + }), + ] + }) + ); + +// Storage + workbox.routing.registerRoute( + new RegExp(/.*(?:storage\.freeplanet)\.app.*$/), + workbox.strategies.staleWhileRevalidate({ + cacheName: 'storage', + plugins: [ + new workbox.expiration.Plugin({ + maxAgeSeconds: 30 * 24 * 60 * 60, + // Only cache 10 requests. + maxEntries: 200, + }), + ] + }) + ); + + workbox.routing.registerRoute( + new RegExp(/.*\/(?:statics).*$/), + workbox.strategies.cacheFirst({ + cacheName: 'statics', + plugins: [ + new workbox.expiration.Plugin({ + maxAgeSeconds: 10 * 24 * 60 * 60, + // Only cache 10 requests. + }), + ] + }) + ); + + workbox.routing.registerRoute( + new RegExp(/^http/), + workbox.strategies.networkFirst({ + cacheName: 'all-stuff', + plugins: [ + new workbox.expiration.Plugin({ + maxAgeSeconds: 10 * 24 * 60 * 60, + // Only cache 10 requests. + }), + ] + }) + ); + + + workbox.routing.registerRoute( + new RegExp('/admin/'), + workbox.strategies.networkOnly() + ); + + +} + +if ('serviceWorker' in navigator) { + + console.log('***************** Entering in custom-service-worker.js:') + +} + +self.addEventListener('fetch', (event) => { + if (event.request.url === '/') { + const staleWhileRevalidate = new workbox.strategies.StaleWhileRevalidate(); + event.respondWith(staleWhileRevalidate.handle({event})); } - event.respondWith(fetch(event.request)); +}); + +// self.addEventListener('fetch', function (event) { +// console.log('[Service Worker] Fetching something ....', event); +// console.log('event.request.cache=', event.request.cache) +// if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') { +// console.log('SAME ORIGIN!', event); +// return; +// } +// event.respondWith(caches.match(event.request)); +// }); + + +// const syncStore = {} +// self.addEventListener('message', event => { +// if (event.data.type === 'sync') { +// // get a unique id to save the data +// const id = uuid() +// syncStore[id] = event.data +// // register a sync and pass the id as tag for it to get the data +// self.registration.sync.register(id) +// } +// console.log(event.data) +// }) + +self.addEventListener('sync', function (event) { + console.log('[Service Worker V5] Background syncing', event.tag); + + let mystrparam = event.tag + let multiparams = mystrparam.split('|') + if (multiparams) { + if (multiparams.length > 3) { + let cmd = multiparams[0] + let table = multiparams[1] + let method = multiparams[2] + let token = multiparams[3] + // let lang = multiparams[3] + + if (cmd === 'sync-todos') { + console.log('[Service Worker] Syncing', cmd, table, method); + + const headers = new Headers() + headers.append('content-Type', 'application/json') + headers.append('Accept', 'application/json') + headers.append('x-auth', token) + + console.log('A1) INIZIO.............................................................'); + + event.waitUntil( + readAllData(table) + .then(function (alldata) { + const myrecs = [...alldata] + console.log('----------------------- LEGGO QUALCOSA DAL WAITUNTIL ') + if (myrecs) { + for (let rec of myrecs) { + //console.log('syncing', table, '', rec.descr) + let link = cfgenv.serverweb + '/todos' + + if (method !== 'POST') + link += '/' + rec._id + + console.log('++++++++++++++++++ SYNCING !!!! ', rec.descr, table, 'FETCH: ', method, link, 'data:') + + // Insert/Delete/Update table to the server + fetch(link, { + method: method, + headers: headers, + mode: 'cors', // 'no-cors', + body: JSON.stringify(rec) + }) + .then(function (resData) { + // console.log('Result CALL ', method, ' OK? =', resData.ok); + + // Anyway Delete this, otherwise in some cases will return error, but it's not a problem. + // for example if I change a record and then I deleted ... + // if (resData.ok) { + deleteItemFromData(table, rec._id); + // } + + console.log('DELETE: ', mystrparam) + deleteItemFromData('swmsg', mystrparam) + + }) + .catch(function (err) { + console.log('!!!!!!!!!!!!!!! Error while sending data', err); + }); + } + } + }) + ); + console.log('A2) ?????????????????????????? ESCO DAL LOOP !!!!!!!!! err=') + } + } + } +}) +; + + +/* + +// send message to serviceWorker +function sync (url, options) { + navigator.serviceWorker.controller.postMessage({type: 'sync', url, options}) +} + + +const syncStore = {} +self.addEventListener('message', event => { + if(event.data.type === 'sync') { + // get a unique id to save the data + const id = uuid() + syncStore[id] = event.data + // register a sync and pass the id as tag for it to get the data + self.registration.sync.register(id) + } + console.log(event.data) +}) + + +self.addEventListener('sync', event => { + // get the data by tag + const {url, options} = syncStore[event.tag] + event.waitUntil(fetch(url, options)) +}) +*/ + +self.addEventListener('notificationclick', function(event) { + var notification = event.notification; + var action = event.action; + + console.log(notification); + + if (action === 'confirm') { + console.log('Confirm was chosen'); + notification.close(); + } else { + console.log(action); + event.waitUntil( + clients.matchAll() + .then(function(clis) { + var client = clis.find(function(c) { + return c.visibilityState === 'visible'; + }); + + if (client !== undefined) { + client.navigate(notification.data.url); + client.focus(); + } else { + clients.openWindow(notification.data.url); + } + notification.close(); + }) + ); + } +}); + +self.addEventListener('notificationclose', function(event) { + console.log('Notification was closed', event); +}); + +self.addEventListener('push', function(event) { + console.log('Push Notification received', event); + + var data = {title: 'New!', content: 'Something new happened!', openUrl: '/'}; + + if (event.data) { + data = JSON.parse(event.data.text()); + } + + var options = { + body: data.content, + icon: '/statics/icons/android-chrome-192x192.png', + badge: '/statics/icons/android-chrome-192x192.png', + data: { + url: data.openUrl + } + }; + + event.waitUntil( + self.registration.showNotification(data.title, options) + ); }); diff --git a/src-pwa/register-service-worker.js b/src-pwa/register-service-worker.js index b7df77d..31d3287 100644 --- a/src-pwa/register-service-worker.js +++ b/src-pwa/register-service-worker.js @@ -3,22 +3,25 @@ * when building for PRODUCTION */ -import {register} from 'register-service-worker' +import { register } from 'register-service-worker' + + register(process.env.SERVICE_WORKER_FILE, { ready() { console.log('READY::: App is being served from cache by a service worker.') }, + registered(registration) { // registration -> a ServiceWorkerRegistration instance - console.log('REGISTERED::: !!!') + console.log('REGISTERED::: !!!', process.env.SERVICE_WORKER_FILE) }, - cached(registration) { // registration -> a ServiceWorkerRegistration instance + cached(registration) { console.log('CACHED::: Content has been cached for offline use.') }, - updatefound(registration) { // registration -> a ServiceWorkerRegistration instance + updatefound(registration) { console.log('UPDATEFOUND::: New content is downloading.') }, - updated(registration) { // registration -> a ServiceWorkerRegistration instance + updated(registration) { console.log('New content is available; please refresh.') }, offline() { @@ -33,3 +36,4 @@ register(process.env.SERVICE_WORKER_FILE, { // ServiceWorkerRegistration: https://developer.mozilla.org/en-uk/docs/Web/API/ServiceWorkerRegistration +// "build": "quasar build -m pwa && workbox generateSW workbox-config.js", diff --git a/src/App.scss b/src/App.scss new file mode 100644 index 0000000..e085d76 --- /dev/null +++ b/src/App.scss @@ -0,0 +1,41 @@ +.fade-enter-active, .fade-leave-active { + transition: opacity .2s; +} + +.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ +{ + opacity: 0; +} + +.slide-enter { +} + +.slide-enter-active { + animation: slide-in 0.2s ease-out forwards; +} + +.slide-leave { +} + +.slide-leave-active { + animation: slide-out 0.5s ease-out forwards; +} + +@keyframes slide-in { + from { + transform: translateX(-500px); + } + to { + transform: translateX(0); + } +} + +@keyframes slide-out { + from { + transform: translateX(0); + } + + to { + transform: translateX(1600px); + } +} diff --git a/src/App.ts b/src/App.ts new file mode 100644 index 0000000..3fa8cf6 --- /dev/null +++ b/src/App.ts @@ -0,0 +1,55 @@ +import Vue from 'vue' +import { Component } from 'vue-property-decorator' +import { UserStore } from '@store' +import { EventBus, RootState, storeBuilder, DebugMode } from '@store' +import router from './router' + +import $ from 'jquery' + +import Header from './components/Header.vue' + +import globalroutines from './globalroutines/index' +import { GlobalStore } from './store/Modules' + + + +@Component({ + components: { + appHeader: Header + }, + router +}) + + +export default class App extends Vue { + public backgroundColor = 'whitesmoke' + public isSubscribed = false + public $q + + + created() { + if (process.env.DEV) { + console.info('SESSIONE IN SVILUPPO ! (DEV)') + console.info(process.env) + } + if (process.env.PROD) { + console.info('SESSIONE IN PRODUZIONE!') + // console.info(process.env) + } + + UserStore.actions.autologin() + .then((loginEseguito) => { + if (loginEseguito) { + globalroutines(this, 'loadapp', '') + // this.$router.replace('/') + + // Create Subscription to Push Notification + GlobalStore.actions.createPushSubscription() + } + }) + + } + + + +} diff --git a/src/App.vue b/src/App.vue index 072e1c5..a43de50 100644 --- a/src/App.vue +++ b/src/App.vue @@ -15,89 +15,8 @@ - - - diff --git a/src/components/Header.vue b/src/components/Header.vue index db65f37..7d2557b 100644 --- a/src/components/Header.vue +++ b/src/components/Header.vue @@ -29,7 +29,7 @@ {{$t('msg.myAppName')}} -
{{$t('msg.myDescriz')}}
+
{{$t('msg.myDescriz')}} {{ getAppVersion() }}
@@ -96,6 +96,10 @@ localStorage.setItem(rescodes.localStorage.leftDrawerOpen, value.toString()) } + getAppVersion() { + // return "AA" + return "[" + process.env.APP_VERSION + "]" + } get lang() { return this.$q.i18n.lang } @@ -106,7 +110,7 @@ // dynamic import, so loading on demand only import(`quasar-framework/i18n/${lang}`).then(lang => { this.$q.i18n.set(lang.default) - import(`src/i18n`).then(function () { + import(`src/statics/i18n`).then(function () { }) }) } diff --git a/src/components/categories/category/category.ts b/src/components/categories/category/category.ts index 0cbfb38..eec08f6 100644 --- a/src/components/categories/category/category.ts +++ b/src/components/categories/category/category.ts @@ -80,7 +80,7 @@ export default class Category extends Vue { } async loadCat() { - await this.$db.categories.toArray().then(ris => this.categories_loc = ris) + // await this.$db.categories.toArray().then(ris => this.categories_loc = ris) this.updatetable() } diff --git a/src/components/categories/category/category.vue b/src/components/categories/category/category.vue index 1d9eff6..6a9c70c 100644 --- a/src/components/categories/category/category.vue +++ b/src/components/categories/category/category.vue @@ -53,7 +53,7 @@ - + diff --git a/src/components/categories/tabledata/tabledata.ts b/src/components/categories/tabledata/tabledata.ts index 7bb46c1..b27f197 100644 --- a/src/components/categories/tabledata/tabledata.ts +++ b/src/components/categories/tabledata/tabledata.ts @@ -100,7 +100,7 @@ export default class Tabledata extends Vue { objcat.descr_it = this.category // Add to Indexdb - await this.$db.categories.add(objcat + await this.$db.categories.put(objcat ).then(ris => { myid = ris }) diff --git a/src/components/logo/logo.ts b/src/components/logo/logo.ts index 17f7d8c..5c43ec4 100644 --- a/src/components/logo/logo.ts +++ b/src/components/logo/logo.ts @@ -13,7 +13,7 @@ export default class Logo extends Vue { logoimg: string = '' created() { - this.logoimg = 'statics/' + process.env.LOGO_REG + this.logoimg = 'statics/images/' + process.env.LOGO_REG this.animate() } diff --git a/src/components/todos/SingleTodo/SingleTodo.scss b/src/components/todos/SingleTodo/SingleTodo.scss index b8247e6..9ea75a7 100644 --- a/src/components/todos/SingleTodo/SingleTodo.scss +++ b/src/components/todos/SingleTodo/SingleTodo.scss @@ -57,15 +57,7 @@ $heightitem: 19px; font-size: 0.75rem; } -.todo-menu { - min-width: 202px; -} - -.item-menu{ - font-size: 1rem; - -} .titleLista-item { max-width: 92px; @@ -144,9 +136,6 @@ $heightitem: 19px; color: #888; } -.menuProgress { - -} .colProgress { } diff --git a/src/components/todos/SingleTodo/SingleTodo.ts b/src/components/todos/SingleTodo/SingleTodo.ts index 436ac1c..84d6b48 100644 --- a/src/components/todos/SingleTodo/SingleTodo.ts +++ b/src/components/todos/SingleTodo/SingleTodo.ts @@ -130,7 +130,7 @@ export default class SingleTodo extends Vue { // this.classDescr += ' show' // } - // this.getinputdescr = 'inputdescr' + this.itemtodo.id + // this.getinputdescr = 'inputdescr' + this.itemtodo._id // console.log('classDescrEdit = ', this.classDescrEdit) // console.log('classDescr', this.classDescr) @@ -234,6 +234,8 @@ export default class SingleTodo extends Vue { exitEdit(singola: boolean = false) { if (this.inEdit) { + if (this.precDescr !== this.itemtodo.descr) + this.updateTodo() // console.log('exitEdit') this.inEdit = false this.updateClasses @@ -243,11 +245,30 @@ export default class SingleTodo extends Vue { keyDownArea(e) { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault() +/* + if ((e.key === 'ArrowUp') && !e.shiftKey) { + e.key = 'Tab' + e.shiftKey = true + } + + if ((e.key === 'ArrowDown') && !e.shiftKey) { + let nextInput = inputs.get(inputs.index(this) + 1) + if (nextInput) { + nextInput.focus() + } + } +*/ + + if (((e.key === 'Enter') || (e.key === 'Tab')) && !e.shiftKey) { this.updateTodo() - this.deselectRiga() - this.faiFocus('insertTask', true) + + if ((e.key === 'Tab') && !e.shiftKey) { + + } else { + e.preventDefault() + this.deselectRiga() + this.faiFocus('insertTask', true) + } } // console.log('keyDownArea', e) @@ -261,6 +282,9 @@ export default class SingleTodo extends Vue { } updateTodo() { + if (this.itemtodo.descr === this.precDescr) + return + this.itemtodo.descr = this.precDescr console.log('updateTodo', this.precDescr, this.itemtodo.descr) this.watchupdate() @@ -284,7 +308,7 @@ export default class SingleTodo extends Vue { } updateicon() { - console.log('updateicon') + // console.log('updateicon') if (this.itemtodo.completed) this.iconCompleted = 'check_circle' else @@ -344,7 +368,7 @@ export default class SingleTodo extends Vue { .then(ris => { console.log('ris', ris) if (ris) - this.removeitem(this.itemtodo.id) + this.removeitem(this.itemtodo._id) }).catch(err => { }) diff --git a/src/components/todos/SingleTodo/SingleTodo.vue b/src/components/todos/SingleTodo/SingleTodo.vue index bbc070f..1c33fb7 100644 --- a/src/components/todos/SingleTodo/SingleTodo.vue +++ b/src/components/todos/SingleTodo/SingleTodo.vue @@ -59,7 +59,7 @@ - + diff --git a/src/components/todos/SubMenus/SubMenus.scss b/src/components/todos/SubMenus/SubMenus.scss index e69de29..6cbbff6 100644 --- a/src/components/todos/SubMenus/SubMenus.scss +++ b/src/components/todos/SubMenus/SubMenus.scss @@ -0,0 +1,16 @@ +.todo-menu { + min-width: 202px; + +} + +.item-menu{ + font-size: 1rem; +} + +.item-menu:hover { + cursor: pointer; +} + +.menuProgress { + +} diff --git a/src/components/todos/SubMenus/SubMenus.vue b/src/components/todos/SubMenus/SubMenus.vue index f6fda08..73c166d 100644 --- a/src/components/todos/SubMenus/SubMenus.vue +++ b/src/components/todos/SubMenus/SubMenus.vue @@ -30,7 +30,7 @@ - {{field.label}} + {{field.label}} @@ -60,3 +60,7 @@ + + diff --git a/src/components/todos/todo/todo.ts b/src/components/todos/todo/todo.ts index 8389b06..cc35885 100644 --- a/src/components/todos/todo/todo.ts +++ b/src/components/todos/todo/todo.ts @@ -1,18 +1,26 @@ import Vue from 'vue' -import { Component, Prop, Watch } from 'vue-property-decorator' +import { Component, Watch } from 'vue-property-decorator' import { SingleTodo } from '../SingleTodo' import { ITodo } from '@src/model' import { rescodes } from '../../../store/Modules/rescodes' -import { UserStore } from '@modules' +import { Todos } from '@store' +import { UserStore } from '@store' + +import objectId from '../../../js/objectId.js' import _ from 'lodash' import draggable from 'vuedraggable' +import VueIdb from 'vue-idb' + +import globalroutines from '../../../globalroutines/index' + import $ from 'jquery' +import Api from "@api" @Component({ @@ -25,6 +33,7 @@ export default class Todo extends Vue { title: string = '' todo: string = '' todos_arr: ITodo[] = [] + prevRecords: ITodo[] = [] drag: boolean = true startpos: number = 0 listPriorityLabel: number[] = [] @@ -33,6 +42,10 @@ export default class Todo extends Vue { itemDragStart: any = null itemDragEnd: any = null selrowid: number = 0 + polling = null + + fieldtochange: String [] = ['descr', 'completed', 'category', 'expiring_at', 'priority', 'id_prev', 'id_next', 'pos', 'enableExpiring', 'progress'] + // @Prop({ required: false }) category: string @@ -44,11 +57,56 @@ export default class Todo extends Vue { console.log('drag = ' + this.drag) } + @Watch('$route', { immediate: true, deep: true }) + onUrlChange(newVal: any) { + // Some action + } + + @Watch('$route.params.category') changecat() { - console.log('changecat') + // console.log('changecat') this.load() } + get todos_changed() { + return Todos.state.todos_changed + } + + get reload_fromServer() { + return Todos.state.reload_fromServer + } + + + @Watch('todos_changed', { immediate: true, deep: true }) + changetodos_changed(value: string, oldValue: string) { + + this.$q.notify('Changed...') + + // console.log('Todos.state.todos_changed CHANGED!', value, oldValue) + this.updatetable(true) + } + + @Watch('reload_fromServer', { immediate: true }) + reload_fromServer_changed(value: string, oldValue: string) { + console.log('reload_fromServer_changed!', value, oldValue) + // if (value) { + Todos.actions.dbLoadTodo(false) + + Todos.actions.updateArrayInMemory() + // } + } + + + get testPao() { + return Todos.state.testpao + } + + @Watch('testPao', { immediate: true, deep: true }) + changedTestpao(value: string, oldValue: string) { + // console.log('testpao CHANGED', value, oldValue) + this.updatetable(true) + } + getCategory() { return this.$route.params.category // return this.category @@ -90,10 +148,10 @@ export default class Todo extends Vue { async updateLinkedList(init: boolean, arr: ITodo[] = this.todos_arr) { - console.log('updateLinkedList', this.todos_arr) + // console.log('updateLinkedList', this.todos_arr) - let idprev = -1 - let idnext = -1 + let idprev = '' + let idnext = '' let pos = 1 if (arr.length > 0) { idprev = arr[0].id_prev @@ -104,32 +162,39 @@ export default class Todo extends Vue { idprev = rescodes.LIST_START } else { const elemprev = this.getelem(index - 1, arr) - idprev = elemprev.id + idprev = elemprev._id } if (index === arr.length - 1) { idnext = rescodes.LIST_END } else { const elemnext = this.getelem(index + 1, arr) - idnext = elemnext.id + idnext = elemnext._id } - elem.modified = ((elem.id_prev !== idprev) || (elem.id_next !== idnext) || (elem.pos !== pos)) ? true : elem.modified + // elem.modified = ((elem.id_prev !== idprev) || (elem.id_next !== idnext) || (elem.pos !== pos)) ? true : elem.modified + // elem.modified = elem.pos !== pos ? true : elem.modified + // if (elem.modified) + // console.log('MODIFICATO QUIIIIIIIIIIIIIIIIIIII', elem.id_prev, idprev, elem.id_next, idnext, elem.pos, pos) + elem.id_prev = idprev elem.id_next = idnext - elem.pos = pos + if (elem.pos !== pos) { + elem.modified = true + elem.pos = pos + } if (init) { elem.modified = false } pos++ - this.logelem('updateLinked', elem) + // this.logelem('updateLinked', elem) }) } logelem(mystr, elem) { - console.log(mystr, 'elem [', elem.id, '] ', elem.descr, ' Pr(', this.getPriorityByInd(elem.priority), ') [', elem.id_prev, '-', elem.id_next, '] modif=', elem.modified) + console.log(mystr, 'elem [', elem._id, '] ', elem.descr, ' Pr(', this.getPriorityByInd(elem.priority), ') [', elem.id_prev, '-', elem.id_next, '] modif=', elem.modified) } getPriorityToSet(ind1, ind2) { @@ -164,7 +229,7 @@ export default class Todo extends Vue { } - getTitlePriority (priority) { + getTitlePriority(priority) { let cl = '' if (priority === rescodes.Todos.PRIORITY_HIGH) @@ -196,7 +261,7 @@ export default class Todo extends Vue { myobj.modified = (myobj.completed !== completed) ? true : myobj.modified myobj.completed = completed changecompleted = true - console.log('Newcompleted: ', completed) + console.log('Newcompleted: ', completed, 'modif', myobj.modified) } if (!changecompleted) { @@ -214,14 +279,18 @@ export default class Todo extends Vue { // Updated only elements modified await this.updateModifyRecords(true) + this.updatetable() + } async updateModifyRecords(refresh: boolean = false) { let update = false await this.todos_arr.forEach((elem: ITodo) => { if (elem.modified) { + console.log('calling MODIFY 3') this.modify(elem, false) update = true + elem.modified = false } }) @@ -240,50 +309,86 @@ export default class Todo extends Vue { arr.forEach(rec => { this.arrPrior.push(rec.value) }) - console.log('Array PRIOR:', this.arrPrior) + // console.log('Array PRIOR:', this.arrPrior) } + beforedestroy() { + clearInterval(this.polling) + } async load() { + + this.todos_arr = [...Todos.state.todos] + // Set last category selected localStorage.setItem(rescodes.localStorage.categorySel, this.getCategory()) for (let todosKey in rescodes.Todos) { this.listPriorityLabel.push(rescodes.Todos[todosKey]) } - console.log('Priority:' + this.listPriorityLabel) + // console.log('Priority:' + this.listPriorityLabel) this.setarrPriority() this.clearArr() await this.updatetable() - this.todos_arr.forEach((elem, index) => { - this.logelem('LOAD ' + index, elem) - }) + + this.checkUpdate_everytime() + + /* + this.todos_arr.forEach((elem, index) => { + this.logelem('LOAD ' + index, elem) + }) + */ } + // Call to check if need to refresh + checkUpdate_everytime() { + this.polling = setInterval(() => { + this.checkUpdate() + }, 10000) + } + + copy(o) { + let output, v, key + output = Array.isArray(o) ? [] : {} + for (key in o) { + v = o[key] + output[key] = (typeof v === 'object') ? this.copy(v) : v + } + return output + } + + initcat() { - const mydateexp = new Date().setDate((new Date()).getDate() + 1) + + let mydatenow = new Date().getDate() + let mydateexp = new Date().getDate() + 10 + + console.log('User:' + UserStore.state.userId) const objtodo: ITodo = { + // _id: new Date().toISOString(), // Create NEW + _id: objectId(), userId: UserStore.state.userId, descr: '', priority: rescodes.Todos.PRIORITY_NORMAL, completed: false, - created_at: new Date(), + created_at: mydatenow, + modify_at: mydatenow, + completed_at: 0, category: '', - modify_at: new Date(), expiring_at: mydateexp, enableExpiring: false, - id_prev: 0, - id_next: 0, + id_prev: '', + id_next: '', pos: 0, - modified: true, + modified: false, progress: 0 } - return objtodo + return this.copy(objtodo) } @@ -296,42 +401,173 @@ export default class Todo extends Vue { return '' } + isRegistered() { + return localStorage.getItem(rescodes.localStorage.userId) !== '' + } + async insertTodo() { if (this.todo.trim() === '') return + if (!this.isRegistered()) { + // Not logged + this.$q.notify(this.$t('user.notregistered')) + return + } + const objtodo = this.initcat() + console.log('insertTodo ', UserStore.state.userId) + objtodo.descr = this.todo objtodo.category = this.getCategory() - const lastelem = this.getLastList() - objtodo.id_prev = (lastelem !== null) ? lastelem.id : rescodes.LIST_START + const lastelem: ITodo = this.getLastList() + objtodo.id_prev = (lastelem !== null) ? lastelem._id : rescodes.LIST_START objtodo.id_next = rescodes.LIST_END objtodo.pos = (lastelem !== null) ? lastelem.pos + 1 : 1 - objtodo.modified = true + objtodo.modified = false + + if (objtodo.userId === undefined) { + this.$q.notify(this.$t('todo.usernotdefined')) + return + } + + await globalroutines(this, 'write', 'todos', objtodo) + .then((id) => { + console.log('*** IDNEW (3) = ', id) + + // update also the last elem + if (lastelem !== null) { + lastelem.id_next = id + // lastelem.modified = true + console.log('calling MODIFY 4', lastelem) + } - // Add to Indexdb - await this.$db.todos.add(objtodo - ).then((id) => { - console.log('*** IDNEW = ', id) - if (lastelem !== null) { - lastelem.id_next = id - lastelem.modified = true this.modify(lastelem, false) - } - this.modify(objtodo, true) - }).catch(err => { - console.log('Errore: ' + err.message) - }) + .then(ris => { + console.log('END calling MODIFY 4') + + this.saveItemToSyncAndDb(rescodes.DB.TABLE_SYNC_TODOS, 'POST', objtodo, true) + this.updatetable(false) + + }) + + + }).catch(err => { + console.log('Errore: ' + err.message) + }) + + // console.log('ESCO.........') // empty the field this.todo = '' } + + async cmdToSyncAndDb(cmd, table, method, item: ITodo, id, msg: String, update: boolean) { + // Send to Server to Sync + + console.log('cmdToSyncAndDb', cmd, table, method, item.descr, id, msg) + + let cmdSw = cmd + if ((cmd === rescodes.DB.CMD_SYNC_NEW_TODOS) || (cmd === rescodes.DB.CMD_DELETE_TODOS)) { + cmdSw = rescodes.DB.CMD_SYNC_TODOS + } + + if (process.env.DEV) { + console.log('serviceWorker ', ('serviceWorker' in navigator) ? 'PRESENT!' : 'DOESN\'T EXIST!') + console.log('SyncManager ', ('SyncManager' in window) ? 'PRESENT!' : 'DOESN\'T EXIST!') + } + + const mythis = this + if ('serviceWorker' in navigator) { + await navigator.serviceWorker.ready + .then(function (sw) { + // _id: new Date().toISOString(), + console.log('---------------------- navigator.serviceWorker.ready') + + // mythis.sendMessageToSW(item, method) + + globalroutines(mythis, 'write', table, item, id) + .then(function (id) { + // console.log('id', id) + + }) + const sep = '|' + + let multiparams = cmdSw + sep + table + sep + method + sep + UserStore.state.idToken + sep + UserStore.state.lang + let mymsgkey = { + _id: multiparams, + value: multiparams + } + globalroutines(mythis, 'write', 'swmsg', mymsgkey, multiparams) + .then(ris => { + if ('SyncManager' in window) { + console.log(' SENDING... sw.sync.register', multiparams) + return sw.sync.register(multiparams) + } else { + // #Todo ++ Alternative 2 to SyncManager + Api.syncAlternative(multiparams) + } + }) + .then(function () { + + let snackbarContainer = document.querySelector('#confirmation-toast') + let data = { message: msg } + // snackbarContainer.MaterialSnackbar.showSnackbar(data) + }) + .catch(function (err) { + console.error('Errore in globalroutines', table, err) + }) + }) + + if (update) { + // // Update the array in memory, from todos table from IndexedDb + Todos.actions.updateArrayInMemory() + .then((ris) => { + return ris + }) + } + + } else { + if (cmd === rescodes.DB.CMD_SYNC_NEW_TODOS) { + if (method === 'POST') + Todos.actions.dbInsertTodo(item) + else if (method === 'PATCH') + Todos.actions.dbSaveTodo(item) + } else if (cmd === rescodes.DB.CMD_DELETE_TODOS) + Todos.actions.dbDeleteTodo(item) + } + } + + async saveItemToSyncAndDb(table: String, method, item: ITodo, update: boolean) { + return await this.cmdToSyncAndDb(rescodes.DB.CMD_SYNC_NEW_TODOS, table, method, item, 0, 'Your Post was saved for syncing!', update) + } + + + deleteItemToSyncAndDb(table: String, item: ITodo, id, update: boolean) { + return this.cmdToSyncAndDb(rescodes.DB.CMD_DELETE_TODOS, table, 'DELETE', item, id, 'Your Post was canceled for syncing!', update) + } + + /* + sendMessageToSW(recdata, method) { + + navigator.serviceWorker.controller.postMessage({ + type: 'sync', + recdata, + method, + cmd: 'sync-new-todos', + token: UserStore.state.idToken, + lang: UserStore.state.lang + }) + } + */ + + getElemById(id, lista = this.todos_arr) { let myobj: ITodo for (myobj of lista) { - if (myobj.id === id) { + if (myobj._id === id) { return myobj } } @@ -351,23 +587,23 @@ export default class Todo extends Vue { if (myobjprev !== null) { myobjprev.id_next = myobjtrov.id_next myobjprev.modified = true + console.log('calling MODIFY 2') this.modify(myobjprev, false) } if (myobjnext !== null) { myobjnext.id_prev = myobjtrov.id_prev myobjnext.modified = true + console.log('calling MODIFY 1') this.modify(myobjnext, false) } - console.log('ENTRATO') + this.deleteItemToSyncAndDb(rescodes.DB.TABLE_DELETE_TODOS, myobjtrov, id, true) + const mythis = this // Delete item - await this.$db.todos - .where('id').equals(id) - .delete() - .then(() => { - console.log('UpdateTable') + await globalroutines(this, 'delete', 'todos', null, id) + .then((ris) => { mythis.updatetable() }).catch((error) => { console.log('err: ', error) @@ -377,12 +613,44 @@ export default class Todo extends Vue { console.log('FINE deleteitem') } + getElem(myarray: ITodo[], id) { + for (let i = 0; i < myarray.length - 1; i++) { + if (myarray[i]._id === id) + return myarray[i] + } + return null + } + + isRecordModifPos(itemnew: ITodo, itemold: ITodo) { + return itemnew.pos !== itemold.pos + } + async updatetable(refresh: boolean = false) { - await this.filtertodos(refresh) + // console.log('updatetable') + + this.prevRecords = [...this.todos_arr] + + return await Todos.actions.getTodosByCategory(this.getCategory()) + .then(arrtemp => { + + arrtemp = _.orderBy(arrtemp, ['completed', 'priority', 'pos'], ['asc', 'desc', 'asc']) + + this.updateLinkedList(true, arrtemp) + + // If changed the position, then set modified + arrtemp.forEach(itemNew => { + const itemOld = this.getElem(this.prevRecords, itemNew._id) + if (itemOld) + itemNew.modified = this.isRecordModifPos(itemNew, itemOld) ? true : false + }) + + this.todos_arr = [...arrtemp] // make copy + + }) } clearArr() { - this.todos_arr = [] + // this.todos_arr = [] } existArr(x) { @@ -428,7 +696,7 @@ export default class Todo extends Vue { current = this.getElemById(current.id_next, arrris) if (current === null) break - if (current.id === currentprec.id) + if (current._id === currentprec._id) break myarr.push(current) currentprec = current @@ -441,48 +709,9 @@ export default class Todo extends Vue { } - async filtertodos(refresh: boolean = false) { - console.log('filtertodos') - - let arrtemp = [] - - if (this.filter) { - // #Todo If need to filter the output database ... - await this.$db.todos - .where('userId').equals(UserStore.state.userId) - .and(todo => todo.category === this.getCategory()) - .toArray() - .then((response) => { - Promise.all(response.map(key => key)) - .then((ristodos) => { - arrtemp = ristodos - }) - }) - } else { - await this.$db.todos - .where('userId').equals(UserStore.state.userId) - .and(todo => todo.category === this.getCategory()) - .toArray().then(ristodos => { - arrtemp = ristodos - }) - - arrtemp = _.orderBy(arrtemp, ['completed', 'priority', 'pos'], ['asc', 'desc', 'asc']) - } - - this.updateLinkedList(true, arrtemp) - - // set array - // arrtemp = this.setArrayFinale(arrtemp) - - this.todos_arr = [...arrtemp] // make copy - - - return [] - } - sortarr(arr, field) { - return arr.slice().sort(function(a, b) { + return arr.slice().sort(function (a, b) { return a[field] - b[field] }) @@ -514,21 +743,21 @@ export default class Todo extends Vue { // } // - deselectAllRows(item, check, onlythis: boolean = false) { - console.log('deselectAllRows : ', item) + deselectAllRows(item: ITodo, check, onlythis: boolean = false) { + // console.log('deselectAllRows : ', item) for (let i = 0; i < this.$refs.single.length; i++) { let contr = this.$refs.single[i] // @ts-ignore - let id = contr.itemtodo.id + let id = contr.itemtodo._id // Don't deselect the actual clicked! let des = false if (onlythis) { - des = item.id === id - }else { - des = ((check && (item.id !== id)) || (!check)) + des = item._id === id + } else { + des = ((check && (item._id !== id)) || (!check)) } if (des) { // @ts-ignore @@ -541,7 +770,7 @@ export default class Todo extends Vue { // let index = -1 // // get index // this.$refs.single.forEach( (singletodo: SingleTodo) => { - // if (singletodo.itemtodo.id === rec.id) + // if (singletodo.itemtodo._id === rec._id) // index = -1 // }) // @@ -549,41 +778,77 @@ export default class Todo extends Vue { modifyField(recOut, recIn, field) { if (recOut[field] !== recIn[field]) { + console.log('*************** CAMPO ', field, 'MODIFICATO!', recOut[field], recIn[field]) recOut.modified = true recOut[field] = recIn[field] + return true } + return false } async modify(myobj: ITodo, update: boolean) { - await this.$db.transaction('rw', [this.$db.todos], async () => { - const miorec = await this.$db.todos.get(myobj.id) + if (myobj === null) + return new Promise(function (resolve, reject) { + resolve() + }) + await globalroutines(this, 'read', 'todos', null, myobj._id) + .then(miorec => { + if (miorec === undefined) { + console.log('~~~~~~~~~~~~~~~~~~~~ !!!!!!!!!!!!!!!!!! Record not Found !!!!!! id=', myobj._id) + return + } - this.modifyField(miorec, myobj, 'descr') - this.modifyField(miorec, myobj, 'completed') - this.modifyField(miorec, myobj, 'category') - this.modifyField(miorec, myobj, 'expiring_at') - this.modifyField(miorec, myobj, 'priority') - this.modifyField(miorec, myobj, 'id_prev') - this.modifyField(miorec, myobj, 'id_next') - this.modifyField(miorec, myobj, 'pos') - this.modifyField(miorec, myobj, 'enableExpiring') - this.modifyField(miorec, myobj, 'progress') + if (this.modifyField(miorec, myobj, 'completed')) + miorec.completed_at = new Date().getDate() + + this.fieldtochange.forEach(field => { + this.modifyField(miorec, myobj, field) + }) - if (miorec.modified) { - miorec.modify_at = new Date() + if (miorec.modified) { + console.log('Todo MODIFICATO! ', miorec.descr, 'SALVALO SULLA IndexedDB todos') + miorec.modify_at = new Date().getDate() + miorec.modified = false - this.logelem('modify', miorec) + // this.logelem('modify', miorec) - await this.$db.todos.put(miorec) + globalroutines(this, 'write', 'todos', miorec) + .then(ris => { - if (update) - await this.updatetable(false) - } - }) + this.saveItemToSyncAndDb(rescodes.DB.TABLE_SYNC_TODOS_PATCH, 'PATCH', miorec, update) + .then(() => { + // console.log('SET MODIFIED FALSE') + if (update) + this.updatetable(false) + + }) + }) + } + }) } + clicktest() { + console.log('clicktest!') + + const objtodo = this.initcat() + objtodo.descr = 'PROVA' + objtodo.category = this.getCategory() + Todos.state.todos.push(objtodo) + Todos.mutations.setTodos_changed() + + console.log('Todos.state.todos', Todos.state.todos) + } + + clicktest2() { + this.updatetable(false) + console.log('Todos.state.todos', Todos.state.todos) + } + + checkUpdate() { + Todos.actions.waitAndcheckPendingMsg() + } } diff --git a/src/components/todos/todo/todo.vue b/src/components/todos/todo/todo.vue index a01f401..eb221c3 100644 --- a/src/components/todos/todo/todo.vue +++ b/src/components/todos/todo/todo.vue @@ -13,7 +13,7 @@ -
+
@@ -35,6 +35,16 @@ :after="[{icon: 'arrow_forward', content: true, handler () {}}]" v-on:keyup.enter="insertTodo"/> + + + + +
+ + + +
+
diff --git a/src/globalroutines/index.js b/src/globalroutines/index.js new file mode 100644 index 0000000..bcde872 --- /dev/null +++ b/src/globalroutines/index.js @@ -0,0 +1,7 @@ +import indexdb from './indexdb' + +export default async (context, cmd, table, data = null, id = '') => { + const descr = data !== null ? data.descr : '' + // console.log('globalroutines', cmd, table, descr, id) + return await indexdb(context, cmd, table, data, id) +} diff --git a/src/globalroutines/indexdb.js b/src/globalroutines/indexdb.js new file mode 100644 index 0000000..00e7d3b --- /dev/null +++ b/src/globalroutines/indexdb.js @@ -0,0 +1,79 @@ +import store from '../store' +import _ from 'lodash' +import { UserStore, Todos } from '@store' +import { i18n } from '../plugins/i18n' + +import {idbKeyval as storage} from '../js/storage.js'; + +function saveConfigIndexDb(context) { + + let data = [] + data['_id'] = 1 + data['lang'] = UserStore.state.lang + data['token'] = UserStore.state.idToken + data['userId'] = UserStore.state.userId + + writeConfigIndexDb('config', data) +} + +function writeConfigIndexDb(context, data) { + // console.log('writeConfigIndexDb', data) + + storage.setdata('config', data) + .then(ris => { + return true + }) + +} + +async function readfromIndexDbToStateTodos(context, table) { + // console.log('*** read from IndexDb to state.todos') + + return await storage.getalldata(table) + .then(records => { + // console.log('&&&&&&& readfromIndexDbToStateTodos OK: Num RECORD: ', records.length) + if (table === 'todos') { + Todos.state.todos = [...records] + Todos.mutations.setTodos_changed() + // console.log('Todos.state.todos_changed:', Todos.state.todos_changed) + // setTimeout(testfunc2, 3000) + } + }).catch((error) => { + console.log('err: ', error) + }) + +} + +function consolelogpao(str, str2 = '', str3 = '') { + console.log(str, str2, str3) + // Todos.mutations.setTestpao(str + str2 + str3) +} + +function testfunc2 () { + consolelogpao('testfunc2') + Todos.mutations.setTodos_changed() + + consolelogpao('testfunc2: Todos.state.todos_changed:', Todos.state.todos_changed) +} + +export default async (context, cmd, table, datakey = null, id = '') => { + if (cmd === 'loadapp') { + // ****** LOAD APP AL CARICAMENTO ! ******* + return saveConfigIndexDb(context, datakey) + + } else if (cmd === 'write') { + return await storage.setdata(table, datakey) + } else if (cmd === 'updateinMemory') { + return await readfromIndexDbToStateTodos(context, table) + } else if (cmd === 'readall') { + return await storage.getalldata(table) + } else if (cmd === 'count') { + return await storage.count(table) + } else if (cmd === 'read') { + return await storage.getdata(table, id) + } else if (cmd === 'delete') { + return await storage.deletedata(table, id) + } else if (cmd === 'log') { + consolelogpao(table) + } +} diff --git a/src/globalroutines/util.js b/src/globalroutines/util.js new file mode 100644 index 0000000..ffb4043 --- /dev/null +++ b/src/globalroutines/util.js @@ -0,0 +1,18 @@ +import { UserStore } from "../store/Modules"; +import messages from "../statics/i18n"; + +function translate(params) { + let msg = params.split('.') + let lang = UserStore.state.lang + + let stringa = messages[lang] + + let ris = stringa + msg.forEach(param => { + ris = ris[param] + }) + + return ris +} + +export default translate diff --git a/src/index.template.html b/src/index.template.html index 4e2e4de..aa2c291 100644 --- a/src/index.template.html +++ b/src/index.template.html @@ -10,15 +10,25 @@ - - - - - - - + + + + + + + + + - + diff --git a/src/js/fetch.js b/src/js/fetch.js index 6bac6b3..30ea634 100644 --- a/src/js/fetch.js +++ b/src/js/fetch.js @@ -295,7 +295,7 @@ } // HTTP methods whose capitalization should be normalized - var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] + var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'PATCH'] function normalizeMethod(method) { var upcased = method.toUpperCase() diff --git a/src/js/idb.js b/src/js/idb.js deleted file mode 100644 index 9835513..0000000 --- a/src/js/idb.js +++ /dev/null @@ -1,311 +0,0 @@ -'use strict'; - -(function() { - function toArray(arr) { - return Array.prototype.slice.call(arr); - } - - function promisifyRequest(request) { - return new Promise(function(resolve, reject) { - request.onsuccess = function() { - resolve(request.result); - }; - - request.onerror = function() { - reject(request.error); - }; - }); - } - - function promisifyRequestCall(obj, method, args) { - var request; - var p = new Promise(function(resolve, reject) { - request = obj[method].apply(obj, args); - promisifyRequest(request).then(resolve, reject); - }); - - p.request = request; - return p; - } - - function promisifyCursorRequestCall(obj, method, args) { - var p = promisifyRequestCall(obj, method, args); - return p.then(function(value) { - if (!value) return; - return new Cursor(value, p.request); - }); - } - - function proxyProperties(ProxyClass, targetProp, properties) { - properties.forEach(function(prop) { - Object.defineProperty(ProxyClass.prototype, prop, { - get: function() { - return this[targetProp][prop]; - }, - set: function(val) { - this[targetProp][prop] = val; - } - }); - }); - } - - function proxyRequestMethods(ProxyClass, targetProp, Constructor, properties) { - properties.forEach(function(prop) { - if (!(prop in Constructor.prototype)) return; - ProxyClass.prototype[prop] = function() { - return promisifyRequestCall(this[targetProp], prop, arguments); - }; - }); - } - - function proxyMethods(ProxyClass, targetProp, Constructor, properties) { - properties.forEach(function(prop) { - if (!(prop in Constructor.prototype)) return; - ProxyClass.prototype[prop] = function() { - return this[targetProp][prop].apply(this[targetProp], arguments); - }; - }); - } - - function proxyCursorRequestMethods(ProxyClass, targetProp, Constructor, properties) { - properties.forEach(function(prop) { - if (!(prop in Constructor.prototype)) return; - ProxyClass.prototype[prop] = function() { - return promisifyCursorRequestCall(this[targetProp], prop, arguments); - }; - }); - } - - function Index(index) { - this._index = index; - } - - proxyProperties(Index, '_index', [ - 'name', - 'keyPath', - 'multiEntry', - 'unique' - ]); - - proxyRequestMethods(Index, '_index', IDBIndex, [ - 'get', - 'getKey', - 'getAll', - 'getAllKeys', - 'count' - ]); - - proxyCursorRequestMethods(Index, '_index', IDBIndex, [ - 'openCursor', - 'openKeyCursor' - ]); - - function Cursor(cursor, request) { - this._cursor = cursor; - this._request = request; - } - - proxyProperties(Cursor, '_cursor', [ - 'direction', - 'key', - 'primaryKey', - 'value' - ]); - - proxyRequestMethods(Cursor, '_cursor', IDBCursor, [ - 'update', - 'delete' - ]); - - // proxy 'next' methods - ['advance', 'continue', 'continuePrimaryKey'].forEach(function(methodName) { - if (!(methodName in IDBCursor.prototype)) return; - Cursor.prototype[methodName] = function() { - var cursor = this; - var args = arguments; - return Promise.resolve().then(function() { - cursor._cursor[methodName].apply(cursor._cursor, args); - return promisifyRequest(cursor._request).then(function(value) { - if (!value) return; - return new Cursor(value, cursor._request); - }); - }); - }; - }); - - function ObjectStore(store) { - this._store = store; - } - - ObjectStore.prototype.createIndex = function() { - return new Index(this._store.createIndex.apply(this._store, arguments)); - }; - - ObjectStore.prototype.index = function() { - return new Index(this._store.index.apply(this._store, arguments)); - }; - - proxyProperties(ObjectStore, '_store', [ - 'name', - 'keyPath', - 'indexNames', - 'autoIncrement' - ]); - - proxyRequestMethods(ObjectStore, '_store', IDBObjectStore, [ - 'put', - 'add', - 'delete', - 'clear', - 'get', - 'getAll', - 'getKey', - 'getAllKeys', - 'count' - ]); - - proxyCursorRequestMethods(ObjectStore, '_store', IDBObjectStore, [ - 'openCursor', - 'openKeyCursor' - ]); - - proxyMethods(ObjectStore, '_store', IDBObjectStore, [ - 'deleteIndex' - ]); - - function Transaction(idbTransaction) { - this._tx = idbTransaction; - this.complete = new Promise(function(resolve, reject) { - idbTransaction.oncomplete = function() { - resolve(); - }; - idbTransaction.onerror = function() { - reject(idbTransaction.error); - }; - idbTransaction.onabort = function() { - reject(idbTransaction.error); - }; - }); - } - - Transaction.prototype.objectStore = function() { - return new ObjectStore(this._tx.objectStore.apply(this._tx, arguments)); - }; - - proxyProperties(Transaction, '_tx', [ - 'objectStoreNames', - 'mode' - ]); - - proxyMethods(Transaction, '_tx', IDBTransaction, [ - 'abort' - ]); - - function UpgradeDB(db, oldVersion, transaction) { - this._db = db; - this.oldVersion = oldVersion; - this.transaction = new Transaction(transaction); - } - - UpgradeDB.prototype.createObjectStore = function() { - return new ObjectStore(this._db.createObjectStore.apply(this._db, arguments)); - }; - - proxyProperties(UpgradeDB, '_db', [ - 'name', - 'version', - 'objectStoreNames' - ]); - - proxyMethods(UpgradeDB, '_db', IDBDatabase, [ - 'deleteObjectStore', - 'close' - ]); - - function DB(db) { - this._db = db; - } - - DB.prototype.transaction = function() { - return new Transaction(this._db.transaction.apply(this._db, arguments)); - }; - - proxyProperties(DB, '_db', [ - 'name', - 'version', - 'objectStoreNames' - ]); - - proxyMethods(DB, '_db', IDBDatabase, [ - 'close' - ]); - - // Add cursor iterators - // TODO: remove this once browsers do the right thing with promises - ['openCursor', 'openKeyCursor'].forEach(function(funcName) { - [ObjectStore, Index].forEach(function(Constructor) { - Constructor.prototype[funcName.replace('open', 'iterate')] = function() { - var args = toArray(arguments); - var callback = args[args.length - 1]; - var nativeObject = this._store || this._index; - var request = nativeObject[funcName].apply(nativeObject, args.slice(0, -1)); - request.onsuccess = function() { - callback(request.result); - }; - }; - }); - }); - - // polyfill getAll - [Index, ObjectStore].forEach(function(Constructor) { - if (Constructor.prototype.getAll) return; - Constructor.prototype.getAll = function(query, count) { - var instance = this; - var items = []; - - return new Promise(function(resolve) { - instance.iterateCursor(query, function(cursor) { - if (!cursor) { - resolve(items); - return; - } - items.push(cursor.value); - - if (count !== undefined && items.length == count) { - resolve(items); - return; - } - cursor.continue(); - }); - }); - }; - }); - - var exp = { - open: function(name, version, upgradeCallback) { - var p = promisifyRequestCall(indexedDB, 'open', [name, version]); - var request = p.request; - - request.onupgradeneeded = function(event) { - if (upgradeCallback) { - upgradeCallback(new UpgradeDB(request.result, event.oldVersion, request.transaction)); - } - }; - - return p.then(function(db) { - return new DB(db); - }); - }, - delete: function(name) { - return promisifyRequestCall(indexedDB, 'deleteDatabase', [name]); - } - }; - - if (typeof module !== 'undefined') { - module.exports = exp; - module.exports.default = module.exports; - } - else { - self.idb = exp; - } -}()); diff --git a/src/js/immortal-db.min.js b/src/js/immortal-db.min.js new file mode 100644 index 0000000..7735d36 --- /dev/null +++ b/src/js/immortal-db.min.js @@ -0,0 +1,8 @@ +var ImmortalDB=function(t){var r={};function n(e){if(r[e])return r[e].exports;var o=r[e]={i:e,l:!1,exports:{}};return t[e].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=r,n.d=function(t,r,e){n.o(t,r)||Object.defineProperty(t,r,{enumerable:!0,get:e})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,r){if(1&r&&(t=n(t)),8&r)return t;if(4&r&&"object"==typeof t&&t&&t.__esModule)return t;var e=Object.create(null);if(n.r(e),Object.defineProperty(e,"default",{enumerable:!0,value:t}),2&r&&"string"!=typeof t)for(var o in t)n.d(e,o,function(r){return t[r]}.bind(null,o));return e},n.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(r,"a",r),r},n.o=function(t,r){return Object.prototype.hasOwnProperty.call(t,r)},n.p="",n(n.s=68)}([function(t,r,n){var e=n(39)("wks"),o=n(19),i=n(1).Symbol,u="function"==typeof i;(t.exports=function(t){return e[t]||(e[t]=u&&i[t]||(u?i:o)("Symbol."+t))}).store=e},function(t,r){var n=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(t,r){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},function(t,r,n){var e=n(1),o=n(13),i=n(10),u=n(12),c=n(7),a=function(t,r,n){var s,f,l,p,h=t&a.F,v=t&a.G,y=t&a.S,d=t&a.P,m=t&a.B,g=v?e:y?e[r]||(e[r]={}):(e[r]||{}).prototype,w=v?o:o[r]||(o[r]={}),x=w.prototype||(w.prototype={});for(s in v&&(n=r),n)l=((f=!h&&g&&void 0!==g[s])?g:n)[s],p=m&&f?c(l,e):d&&"function"==typeof l?c(Function.call,l):l,g&&u(g,s,l,t&a.U),w[s]!=l&&i(w,s,p),d&&x[s]!=l&&(x[s]=l)};e.core=o,a.F=1,a.G=2,a.S=4,a.P=8,a.B=16,a.W=32,a.U=64,a.R=128,t.exports=a},function(t,r,n){var e=n(6),o=n(67),i=n(43),u=Object.defineProperty;r.f=n(5)?Object.defineProperty:function(t,r,n){if(e(t),r=i(r,!0),e(n),o)try{return u(t,r,n)}catch(t){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(t[r]=n.value),t}},function(t,r,n){t.exports=!n(9)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(t,r,n){var e=n(2);t.exports=function(t){if(!e(t))throw TypeError(t+" is not an object!");return t}},function(t,r,n){var e=n(18);t.exports=function(t,r,n){if(e(t),void 0===r)return t;switch(n){case 1:return function(n){return t.call(r,n)};case 2:return function(n,e){return t.call(r,n,e)};case 3:return function(n,e,o){return t.call(r,n,e,o)}}return function(){return t.apply(r,arguments)}}},function(t,r){var n={}.hasOwnProperty;t.exports=function(t,r){return n.call(t,r)}},function(t,r){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,r,n){var e=n(4),o=n(20);t.exports=n(5)?function(t,r,n){return e.f(t,r,o(1,n))}:function(t,r,n){return t[r]=n,t}},function(t,r,n){var e=n(61),o=n(41);t.exports=function(t){return e(o(t))}},function(t,r,n){var e=n(1),o=n(10),i=n(8),u=n(19)("src"),c=Function.toString,a=(""+c).split("toString");n(13).inspectSource=function(t){return c.call(t)},(t.exports=function(t,r,n,c){var s="function"==typeof n;s&&(i(n,"name")||o(n,"name",r)),t[r]!==n&&(s&&(i(n,u)||o(n,u,t[r]?""+t[r]:a.join(String(r)))),t===e?t[r]=n:c?t[r]?t[r]=n:o(t,r,n):(delete t[r],o(t,r,n)))})(Function.prototype,"toString",function(){return"function"==typeof this&&this[u]||c.call(this)})},function(t,r){var n=t.exports={version:"2.6.1"};"number"==typeof __e&&(__e=n)},function(t,r,n){var e=n(4).f,o=n(8),i=n(0)("toStringTag");t.exports=function(t,r,n){t&&!o(t=n?t:t.prototype,i)&&e(t,i,{configurable:!0,value:r})}},function(t,r){t.exports=!1},function(t,r){t.exports={}},function(t,r){var n={}.toString;t.exports=function(t){return n.call(t).slice(8,-1)}},function(t,r){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,r){var n=0,e=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+e).toString(36))}},function(t,r){t.exports=function(t,r){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:r}}},function(t,r,n){var e=n(6),o=n(83),i=n(31),u=n(32)("IE_PROTO"),c=function(){},a=function(){var t,r=n(44)("iframe"),e=i.length;for(r.style.display="none",n(59).appendChild(r),r.src="javascript:",(t=r.contentWindow.document).open(),t.write("