From 652b0a727d95182d6bbc6b3d2bdae75a63abe8a1 Mon Sep 17 00:00:00 2001 From: Kushagra Gour Date: Sat, 2 Jun 2018 10:57:35 +0530 Subject: [PATCH] Firebase is here people! --- webmaker/package.json | 1 + webmaker/src/analytics.js | 49 ++++++++++++++ webmaker/src/auth.js | 43 +++++++++++++ webmaker/src/components/Login.jsx | 74 +++++++++++++++++++++ webmaker/src/components/MainHeader.jsx | 10 ++- webmaker/src/components/Profile.jsx | 35 ++++++++++ webmaker/src/components/app.jsx | 89 ++++++++++++++++++++++++++ webmaker/src/db.js | 16 ++++- webmaker/src/firebaseInit.js | 11 ++++ webmaker/src/notifications.js | 20 ++++++ 10 files changed, 343 insertions(+), 5 deletions(-) create mode 100644 webmaker/src/analytics.js create mode 100644 webmaker/src/auth.js create mode 100644 webmaker/src/components/Login.jsx create mode 100644 webmaker/src/components/Profile.jsx create mode 100644 webmaker/src/firebaseInit.js create mode 100644 webmaker/src/notifications.js diff --git a/webmaker/package.json b/webmaker/package.json index 5102a6b..7ec727b 100644 --- a/webmaker/package.json +++ b/webmaker/package.json @@ -28,6 +28,7 @@ }, "dependencies": { "codemirror": "^5.37.0", + "firebase": "^5.0.4", "preact": "^8.2.6", "preact-compat": "^3.17.0", "preact-router": "^2.5.7" diff --git a/webmaker/src/analytics.js b/webmaker/src/analytics.js new file mode 100644 index 0000000..1616a3b --- /dev/null +++ b/webmaker/src/analytics.js @@ -0,0 +1,49 @@ +import { + log +} from "./utils"; + +/* global ga */ + +// eslint-disable-next-line max-params +export function trackEvent(category, action, label, value) { + if (window.DEBUG) { + log('trackevent', category, action, label, value); + return; + } + if (window.ga) { + ga('send', 'event', category, action, label, value); + } +}; + +// if online, load after sometime +if (navigator.onLine && !window.DEBUG) { + /* eslint-disable */ + + // prettier-ignore + setTimeout(function () { + (function (i, s, o, g, r, a, m) { + i['GoogleAnalyticsObject'] = r; + i[r] = i[r] || function () { + (i[r].q = i[r].q || []).push(arguments) + }, i[r].l = 1 * new Date(); + a = s.createElement(o), + m = s.getElementsByTagName(o)[0]; + a.async = 1; + a.src = g; + m.parentNode.insertBefore(a, m) + })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga'); + + if (location.href.indexOf('chrome-extension://') === -1) { + ga('create', 'UA-87786708-1'); + } else { + ga('create', 'UA-87786708-1', { + 'cookieDomain': 'none' + }); + // required for chrome extension protocol + ga('set', 'checkProtocolTask', function () { /* nothing */ }); + } + ga('send', 'pageview'); + }, 100); + + /* eslint-enable */ +} diff --git a/webmaker/src/auth.js b/webmaker/src/auth.js new file mode 100644 index 0000000..bd4fa28 --- /dev/null +++ b/webmaker/src/auth.js @@ -0,0 +1,43 @@ +import { + trackEvent +} from './analytics'; + +import firebase from 'firebase/app' + +export const auth = { + logout() { + firebase.auth().signOut(); + }, + login(providerName) { + var provider; + if (providerName === 'facebook') { + provider = new firebase.auth.FacebookAuthProvider(); + } else if (providerName === 'twitter') { + provider = new firebase.auth.TwitterAuthProvider(); + } else if (providerName === 'google') { + provider = new firebase.auth.GoogleAuthProvider(); + provider.addScope('https://www.googleapis.com/auth/userinfo.profile'); + } else { + provider = new firebase.auth.GithubAuthProvider(); + } + + return firebase + .auth() + .signInWithPopup(provider) + .then(function () { + trackEvent('fn', 'loggedIn', providerName); + // Save to recommend next time + window.db.local.set({ + lastAuthProvider: providerName + }); + }) + .catch(function (error) { + utils.log(error); + if (error.code === 'auth/account-exists-with-different-credential') { + alert( + 'You have already signed up with the same email using different social login' + ); + } + }); + } +} diff --git a/webmaker/src/components/Login.jsx b/webmaker/src/components/Login.jsx new file mode 100644 index 0000000..5b73786 --- /dev/null +++ b/webmaker/src/components/Login.jsx @@ -0,0 +1,74 @@ +import { h, Component } from 'preact'; +import { jsLibs, cssLibs } from '../libraryList'; +import { trackEvent } from '../analytics'; +import { auth } from '../auth'; + +export default class Login extends Component { + login(e) { + const provider = e.target.dataset.authProvider; + trackEvent('ui', 'loginProviderClick', provider); + auth.login(provider); + } + logout(e) { + if (this.unsavedEditCount) { + var shouldDiscard = confirm( + 'You have unsaved changes. Do you still want to logout?' + ); + if (!shouldDiscard) { + return; + } + } + trackEvent('fn', 'loggedOut'); + auth.logout(); + } + render() { + return ( +
+

Login / Signup

+ +
+

+ +

+

+ +

+

+ +

+

Join a community of 50,000+ Developers

+
+
+ ); + } +} diff --git a/webmaker/src/components/MainHeader.jsx b/webmaker/src/components/MainHeader.jsx index 5c09d21..8dae8cb 100644 --- a/webmaker/src/components/MainHeader.jsx +++ b/webmaker/src/components/MainHeader.jsx @@ -94,7 +94,7 @@ export default class Header extends Component { Open diff --git a/webmaker/src/components/Profile.jsx b/webmaker/src/components/Profile.jsx new file mode 100644 index 0000000..5b7777d --- /dev/null +++ b/webmaker/src/components/Profile.jsx @@ -0,0 +1,35 @@ +import { h, Component } from 'preact'; + +export default class Profile extends Component { + render() { + return ( +
+ Profile image +

+ {this.props.user && this.props.user.displayName + ? this.props.user.displayName + : 'Anonymous Creator'} +

+

+ +

+
+ ); + } +} diff --git a/webmaker/src/components/app.jsx b/webmaker/src/components/app.jsx index dd83473..c4d71bb 100644 --- a/webmaker/src/components/app.jsx +++ b/webmaker/src/components/app.jsx @@ -8,6 +8,7 @@ import SavedItemPane from './SavedItemPane.jsx'; import AddLibrary from './AddLibrary.jsx'; import Modal from './Modal.jsx'; import HelpModal from './HelpModal.jsx'; +import Login from './Login.jsx'; import { log, generateRandomId } from '../utils'; import { itemService } from '../itemService'; import '../db'; @@ -17,6 +18,9 @@ import { modes, cssModes } from '../codeModes'; import { trackEvent } from '../analytics'; import { deferred } from '../deferred'; import { alertsService } from '../notifications'; +import firebase from 'firebase/app'; +import 'firebase/auth'; +import Profile from './Profile'; if (module.hot) { require('preact/debug'); @@ -38,6 +42,8 @@ export default class App extends Component { isAddLibraryModalOpen: false, isHelpModalOpen: false, isNotificationsModalOpen: false, + isLoginModalOpen: false, + isProfileModalOpen: false, prefs: {}, currentItem: { title: '', @@ -68,6 +74,45 @@ export default class App extends Component { infiniteLoopTimeout: 1000 }; this.prefs = {}; + + firebase.auth().onAuthStateChanged(user => { + this.setState({ isLoginModalOpen: false }); + if (user) { + log('You are -> ', user); + alertsService.add('You are now logged in!'); + this.setState({ user }); + window.user = user; + if ( + !window.localStorage[LocalStorageKeys.ASKED_TO_IMPORT_CREATIONS] && + window.oldSavedCreationsCountEl + ) { + this.fetchItems(false, true).then(items => { + if (!items.length) { + return; + } + this.oldSavedItems = items; + // window.oldSavedCreationsCountEl.textContent = items.length; + this.setState({ + isAskToImportModalOpen: true + }); + trackEvent('ui', 'askToImportModalSeen'); + }); + } + window.db.getUser(user.uid).then(customUser => { + if (customUser) { + const prefs = { ...this.state.prefs }; + Object.assign(prefs, user.settings); + this.setState({ prefs: prefs }); + this.updateSetting(); + } + }); + } else { + this.setState({ user: undefined }); + delete window.user; + // User is signed out. + } + this.updateProfileUi(); + }); } componentWillMount() { @@ -121,6 +166,14 @@ export default class App extends Component { this.updateSetting(); }); } + updateProfileUi() { + if (this.state.user) { + document.body.classList.add('is-logged-in'); + } else { + document.body.classList.remove('is-logged-in'); + } + } + refreshEditor() {} createNewItem() { var d = new Date(); @@ -561,6 +614,27 @@ export default class App extends Component { } autoSaveLoop() {} + loginBtnClickHandler() { + this.setState({ isLoginModalOpen: true }); + } + profileBtnClickHandler() { + this.setState({ isProfileModalOpen: true }); + } + + logout(e) { + e.preventDefault(); + if (unsavedEditCount) { + var shouldDiscard = confirm( + 'You have unsaved changes. Do you still want to logout?' + ); + if (!shouldDiscard) { + return; + } + } + trackEvent('fn', 'loggedOut'); + window.logout(); + } + render() { return (
@@ -569,10 +643,13 @@ export default class App extends Component { externalLibCount={this.state.externalLibCount} openBtnHandler={this.openSavedItemsPane.bind(this)} saveBtnHandler={this.saveBtnClickHandler.bind(this)} + loginBtnHandler={this.loginBtnClickHandler.bind(this)} + profileBtnHandler={this.profileBtnClickHandler.bind(this)} addLibraryBtnHandler={this.openAddLibrary.bind(this)} isFetchingItems={this.state.isFetchingItems} isSaving={this.state.isSaving} titleInputBlurHandler={this.titleInputBlurHandler.bind(this)} + user={this.state.user} /> + this.setState({ isLoginModalOpen: false })} + > + + + this.setState({ isProfileModalOpen: false })} + > + + this.setState({ isHelpModalOpen: false })} diff --git a/webmaker/src/db.js b/webmaker/src/db.js index 3d81231..2340874 100644 --- a/webmaker/src/db.js +++ b/webmaker/src/db.js @@ -1,6 +1,14 @@ +import './firebaseInit'; +import firebase from 'firebase/app'; +import 'firebase/firestore'; import { deferred -} from './deferred' +} from './deferred'; +import { + trackEvent +} from './analytics'; + + (() => { const FAUX_DELAY = 1; @@ -53,6 +61,10 @@ import { .then(function () { // Initialize Cloud Firestore through firebase db = firebase.firestore(); + // const settings = { + // timestampsInSnapshots: true + // }; + // db.settings(settings); utils.log('firebase db ready', db); resolve(db); }) @@ -64,7 +76,7 @@ import { alert( "Opening Web Maker web app in multiple tabs isn't supported at present and it seems like you already have it opened in another tab. Please use in one tab." ); - window.trackEvent('fn', 'multiTabError'); + trackEvent('fn', 'multiTabError'); } else if (err.code === 'unimplemented') { // The current browser does not support all of the // features required to enable persistence diff --git a/webmaker/src/firebaseInit.js b/webmaker/src/firebaseInit.js new file mode 100644 index 0000000..2374ce8 --- /dev/null +++ b/webmaker/src/firebaseInit.js @@ -0,0 +1,11 @@ +import firebase from 'firebase/app'; +// import 'firebase/firestore'; +const 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); diff --git a/webmaker/src/notifications.js b/webmaker/src/notifications.js new file mode 100644 index 0000000..b41f30e --- /dev/null +++ b/webmaker/src/notifications.js @@ -0,0 +1,20 @@ +var hideTimeout; + +function addNotification(msg) { + const noticationContainerEL = $('#js-alerts-container'); + + // var n = document.createElement('div'); + // div.textContent = msg; + // noticationContainerEL.appendChild(n); + noticationContainerEL.textContent = msg; + noticationContainerEL.classList.add('is-active'); + + clearTimeout(hideTimeout); + hideTimeout = setTimeout(function () { + noticationContainerEL.classList.remove('is-active'); + }, 2000); +} + +export const alertsService = { + add: addNotification +};