diff --git a/.eslintrc.json b/.eslintrc.json index f6fc534..5d1307c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -46,7 +46,7 @@ "new-parens": "error", "newline-after-var": "off", "newline-before-return": "off", - "newline-per-chained-call": "error", + "newline-per-chained-call": "off", "no-alert": "off", "no-array-constructor": "error", "no-bitwise": "off", @@ -201,6 +201,7 @@ "utils": true, "Promise": true, "Inlet": true, - "db": true + "db": true, + "firebase": true } } diff --git a/src/db.js b/src/db.js index 9575e7c..7b2742b 100644 --- a/src/db.js +++ b/src/db.js @@ -1,15 +1,51 @@ (() => { const FAUX_DELAY = 1; + + var db; + var dbPromise; + + async function getDb() { + if (dbPromise) { + return dbPromise; + } + dbPromise = new Promise((resolve, reject) => { + if (db) { + return resolve(db); + } + firebase + .firestore() + .enablePersistence() + .then(function() { + // Initialize Cloud Firestore through firebase + db = firebase.firestore(); + console.log('firebase db ready', db); + resolve(db); + }) + .catch(function(err) { + reject(err.code); + if (err.code === 'failed-precondition') { + // Multiple tabs open, persistence can only be enabled + // in one tab at a a time. + // ... + } else if (err.code === 'unimplemented') { + // The current browser does not support all of the + // features required to enable persistence + // ... + } + }); + }); + return dbPromise; + } + var local = { get: (obj, cb) => { + const retVal = {}; if (typeof obj === 'string') { - const retVal = {}; retVal[obj] = JSON.parse(window.localStorage.getItem(obj)); setTimeout(() => cb(retVal), FAUX_DELAY); } else { - const retVal = {}; Object.keys(obj).forEach(key => { - let val = window.localStorage.getItem(key); + const val = window.localStorage.getItem(key); retVal[key] = val === undefined || val === null ? obj[key] : JSON.parse(val); }); @@ -21,11 +57,14 @@ window.localStorage.setItem(key, JSON.stringify(obj[key])); }); setTimeout(() => { - if (cb) cb(); + if (cb) { + cb(); + } }, FAUX_DELAY); } }; window.db = { + getDb, local: chrome && chrome.storage ? chrome.storage.local : local, sync: chrome && chrome.storage ? chrome.storage.sync : local }; diff --git a/src/index.html b/src/index.html index 8ebc9fb..5e0e0e6 100644 --- a/src/index.html +++ b/src/index.html @@ -580,10 +580,12 @@ + + - + @@ -592,6 +594,7 @@ + diff --git a/src/itemService.js b/src/itemService.js new file mode 100644 index 0000000..6b90d01 --- /dev/null +++ b/src/itemService.js @@ -0,0 +1,66 @@ +(() => { + window.itemService = { + async getItem(id) { + var db = await window.db.getDb(); + return db.doc(`items/${id}`).get().then(doc => { + return doc.data(); + }); + }, + + async getAllItems() { + var db = await window.db.getDb(); + + return db + .doc(`users/${window.user.uid}`) + .get() + .then(doc => { + return doc.data().items; + }) + .then(async itemIds => { + console.log('itemids', itemIds); + var items = []; + for (var id in itemIds) { + var item = await this.getItem(id); + items.push(item); + } + return items; + }); + }, + + async setUser() { + var db = await window.db.getDb(); + return db.doc(`users/${window.user.uid}`).set({ + items: {} + }); + }, + + async setItem(id, item) { + var db = await window.db.getDb(); + console.log(`Starting to save item ${id}`); + return db + .collection('items') + .doc(id) + .set(item, { + merge: true + }) + .then(arg => { + console.log('Document written', arg); + }) + .catch(error => console.log(error)); + }, + + async setItemForUser(itemId) { + var db = await window.db.getDb(); + return db + .collection('users') + .doc(window.user.uid) + .update({ + [`items.${itemId}`]: true + }) + .then(arg => { + console.log(`Item ${itemId} set for user`, arg); + }) + .catch(error => console.log(error)); + } + }; +})(); diff --git a/src/script.js b/src/script.js index e6130e5..a8a2c21 100644 --- a/src/script.js +++ b/src/script.js @@ -10,7 +10,7 @@ customEditorFontInput, cssSettingsModal, cssSettingsBtn, acssSettingsTextarea, globalConsoleContainerEl, externalLibrarySearchInput, keyboardShortcutsModal */ /* eslint-disable no-extra-semi */ -(function(alertsService) { +(function(alertsService, itemService) { /* eslint-enable no-extra-semi */ var scope = scope || {}; var version = '2.9.6'; @@ -323,6 +323,10 @@ globalConsoleContainerEl, externalLibrarySearchInput, keyboardShortcutsModal }); // Push into the items hash if its a new item being saved if (isNewItem) { + if (!window.IS_EXTENSION) { + itemService.setItemForUser(currentItem.id); + return; + } db.local.get( { items: {} @@ -408,7 +412,8 @@ globalConsoleContainerEl, externalLibrarySearchInput, keyboardShortcutsModal currentItem.mainSizes = getMainPaneSizes(); utils.log('saving key', key || currentItem.id, currentItem); - return saveSetting(key || currentItem.id, currentItem).then(() => { + saveSetting(key || currentItem.id, currentItem); + return itemService.setItem(key || currentItem.id, currentItem).then(() => { alertsService.add('Item saved.'); unsavedEditCount = 0; saveBtn.classList.remove('is-marked'); @@ -472,16 +477,26 @@ globalConsoleContainerEl, externalLibrarySearchInput, keyboardShortcutsModal * @param {boolean} shouldSaveGlobally Whether to store the fetched items in global arr for later use. * @return {promise} Promise. */ - function fetchItems(shouldSaveGlobally) { + async function fetchItems(shouldSaveGlobally) { var d = deferred(); + savedItems = savedItems || {}; + var items = []; + if (!window.IS_EXTENSION) { + items = await itemService.getAllItems(); + if (shouldSaveGlobally) { + items.forEach(item => { + savedItems[item.id] = item; + }); + } + d.resolve(items); + return d.promise; + } db.local.get('items', function(result) { - var itemIds = Object.getOwnPropertyNames(result.items || {}), - items = []; + var itemIds = Object.getOwnPropertyNames(result.items || {}); if (!itemIds.length) { d.resolve([]); } - savedItems = savedItems || {}; trackEvent('fn', 'fetchItems', itemIds.length); for (let i = 0; i < itemIds.length; i++) { /* eslint-disable no-loop-func */ @@ -1996,7 +2011,43 @@ globalConsoleContainerEl, externalLibrarySearchInput, keyboardShortcutsModal e.preventDefault(); }; + scope.login = function(e) { + var config = { + apiKey: 'AIzaSyBl8Dz7ZOE7aP75mipYl2zKdLSRzBU2fFc', + authDomain: 'web-maker-app.firebaseapp.com', + databaseURL: 'https://web-maker-app.firebaseio.com', + projectId: 'web-maker-app', + storageBucket: 'web-maker-app.appspot.com', + messagingSenderId: '560473480645' + }; + firebase.initializeApp(config); + + firebase.auth().onAuthStateChanged(function(user) { + if (user) { + utils.log(user); + scope.user = window.user = user; + // itemService.setUser(); + // ... + } else { + // User is signed out. + // ... + } + // ... + }); + + firebase.auth().signInAnonymously().then().catch(function(error) { + // Handle Errors here. + utils.log(error); + }); + + if (e) { + e.preventDefault(); + } + }; + function init() { + scope.login(); + var lastCode; CodeMirror.modeURL = `lib/codemirror/mode/%N/%N.js`; @@ -2514,4 +2565,4 @@ globalConsoleContainerEl, externalLibrarySearchInput, keyboardShortcutsModal scope.closeAllOverlays = closeAllOverlays; init(); -})(window.alertsService); +})(window.alertsService, window.itemService); diff --git a/src/service-worker-registration.js b/src/service-worker-registration.js index bbcc757..38f6b92 100644 --- a/src/service-worker-registration.js +++ b/src/service-worker-registration.js @@ -15,48 +15,52 @@ */ /* eslint-env browser */ -'use strict'; if ('serviceWorker' in navigator) { - // Delay registration until after the page has loaded, to ensure that our - // precaching requests don't degrade the first visit experience. - // See https://developers.google.com/web/fundamentals/instant-and-offline/service-worker/registration - window.addEventListener('load', function() { - // Your service-worker.js *must* be located at the top-level directory relative to your site. - // It won't be able to control pages unless it's located at the same level or higher than them. - // *Don't* register service worker file in, e.g., a scripts/ sub-directory! - // See https://github.com/slightlyoff/ServiceWorker/issues/468 - navigator.serviceWorker.register('service-worker.js').then(function(reg) { - // updatefound is fired if service-worker.js changes. - reg.onupdatefound = function() { - // The updatefound event implies that reg.installing is set; see - // https://w3c.github.io/ServiceWorker/#service-worker-registration-updatefound-event - var installingWorker = reg.installing; + // Delay registration until after the page has loaded, to ensure that our + // precaching requests don't degrade the first visit experience. + // See https://developers.google.com/web/fundamentals/instant-and-offline/service-worker/registration + window.addEventListener('load', function() { + // Your service-worker.js *must* be located at the top-level directory relative to your site. + // It won't be able to control pages unless it's located at the same level or higher than them. + // *Don't* register service worker file in, e.g., a scripts/ sub-directory! + // See https://github.com/slightlyoff/ServiceWorker/issues/468 + navigator.serviceWorker + .register('service-worker.js') + .then(function(reg) { + // updatefound is fired if service-worker.js changes. + reg.onupdatefound = function() { + // The updatefound event implies that reg.installing is set; see + // https://w3c.github.io/ServiceWorker/#service-worker-registration-updatefound-event + var installingWorker = reg.installing; - installingWorker.onstatechange = function() { - switch (installingWorker.state) { - case 'installed': - if (navigator.serviceWorker.controller) { - // At this point, the old content will have been purged and the fresh content will - // have been added to the cache. - // It's the perfect time to display a "New content is available; please refresh." - // message in the page's interface. - console.log('New or updated content is available.'); - } else { - // At this point, everything has been precached. - // It's the perfect time to display a "Content is cached for offline use." message. - console.log('Content is now available offline!'); - } - break; + installingWorker.onstatechange = function() { + switch (installingWorker.state) { + case 'installed': + if (navigator.serviceWorker.controller) { + // At this point, the old content will have been purged and the fresh content will + // have been added to the cache. + // It's the perfect time to display a "New content is available; please refresh." + // message in the page's interface. + console.log('New or updated content is available.'); + } else { + // At this point, everything has been precached. + // It's the perfect time to display a "Content is cached for offline use." message. + console.log('Content is now available offline!'); + } + break; - case 'redundant': - console.error('The installing service worker became redundant.'); - break; - } - }; - }; - }).catch(function(e) { - console.error('Error during service worker registration:', e); - }); - }); + case 'redundant': + console.error( + 'The installing service worker became redundant.' + ); + break; + } + }; + }; + }) + .catch(function(e) { + console.error('Error during service worker registration:', e); + }); + }); } diff --git a/src/utils.js b/src/utils.js index e51bba9..80db01f 100644 --- a/src/utils.js +++ b/src/utils.js @@ -183,4 +183,6 @@ window.chrome = window.chrome || {}; window.chrome.i18n = { getMessage: () => {} }; + + window.IS_EXTENSION = !!window.chrome.extension; })();