mirror of
https://github.com/chinchang/web-maker.git
synced 2025-07-13 01:56:24 +02:00
Firebase is here people!
This commit is contained in:
@ -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"
|
||||
|
49
webmaker/src/analytics.js
Normal file
49
webmaker/src/analytics.js
Normal file
@ -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 */
|
||||
}
|
43
webmaker/src/auth.js
Normal file
43
webmaker/src/auth.js
Normal file
@ -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'
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
74
webmaker/src/components/Login.jsx
Normal file
74
webmaker/src/components/Login.jsx
Normal file
@ -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 (
|
||||
<div>
|
||||
<h2>Login / Signup</h2>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={this.login.bind(this)}
|
||||
class="social-login-btn social-login-btn--github btn btn-icon btn--big full-width hint--right hint--always"
|
||||
data-auth-provider="github"
|
||||
data-hint="You logged in with Github last time"
|
||||
>
|
||||
<svg>
|
||||
<use xlinkHref="#github-icon" />
|
||||
</svg>Login with Github
|
||||
</button>
|
||||
</p>
|
||||
<p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={this.login.bind(this)}
|
||||
class="social-login-btn social-login-btn--google btn btn-icon btn--big full-width hint--right hint--always"
|
||||
data-auth-provider="google"
|
||||
data-hint="You logged in with Google last time"
|
||||
>
|
||||
<svg>
|
||||
<use xlinkHref="#google-icon" />
|
||||
</svg>Login with Google
|
||||
</button>
|
||||
</p>
|
||||
<p class="mb-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={this.login.bind(this)}
|
||||
class="social-login-btn social-login-btn--facebook btn btn-icon btn--big full-width hint--right hint--always"
|
||||
data-auth-provider="facebook"
|
||||
data-hint="You logged in with Facebook last time"
|
||||
>
|
||||
<svg>
|
||||
<use xlinkHref="#fb-icon" />
|
||||
</svg>Login with Facebook
|
||||
</button>
|
||||
</p>
|
||||
<p>Join a community of 50,000+ Developers</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -94,7 +94,7 @@ export default class Header extends Component {
|
||||
Open
|
||||
</a>
|
||||
<a
|
||||
d-open-modal="loginModal"
|
||||
onClick={this.props.loginBtnHandler}
|
||||
data-event-category="ui"
|
||||
data-event-action="loginButtonClick"
|
||||
class="hide-on-login flex flex-v-center hint--rounded hint--bottom-left"
|
||||
@ -103,7 +103,7 @@ export default class Header extends Component {
|
||||
Login/Signup
|
||||
</a>
|
||||
<a
|
||||
d-open-modal="profileModal"
|
||||
onClick={this.props.profileBtnHandler}
|
||||
data-event-category="ui"
|
||||
data-event-action="headerAvatarClick"
|
||||
aria-label="See profile or Logout"
|
||||
@ -112,7 +112,11 @@ export default class Header extends Component {
|
||||
<img
|
||||
id="headerAvatarImg"
|
||||
width="20"
|
||||
src=""
|
||||
src={
|
||||
this.props.user
|
||||
? this.props.user.photoURL || DEFAULT_PROFILE_IMG
|
||||
: ''
|
||||
}
|
||||
class="main-header__avatar-img"
|
||||
/>
|
||||
</a>
|
||||
|
35
webmaker/src/components/Profile.jsx
Normal file
35
webmaker/src/components/Profile.jsx
Normal file
@ -0,0 +1,35 @@
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
export default class Profile extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div class="tac">
|
||||
<img
|
||||
height="80"
|
||||
class="profile-modal__avatar-img"
|
||||
src={
|
||||
this.props.user
|
||||
? this.props.user.photoURL || DEFAULT_PROFILE_IMG
|
||||
: ''
|
||||
}
|
||||
id="profileAvatarImg"
|
||||
alt="Profile image"
|
||||
/>
|
||||
<h3 id="profileUserName" class="mb-2">
|
||||
{this.props.user && this.props.user.displayName
|
||||
? this.props.user.displayName
|
||||
: 'Anonymous Creator'}
|
||||
</h3>
|
||||
<p>
|
||||
<button
|
||||
class="btn"
|
||||
aria-label="Logout from your account"
|
||||
onClick={this.props.logoutBtnHandler}
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -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 (
|
||||
<div>
|
||||
@ -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}
|
||||
/>
|
||||
<ContentWrap
|
||||
currentItem={this.state.currentItem}
|
||||
@ -647,6 +724,18 @@ export default class App extends Component {
|
||||
onChange={this.updateSetting.bind(this)}
|
||||
/>
|
||||
</Modal>
|
||||
<Modal
|
||||
show={this.state.isLoginModalOpen}
|
||||
closeHandler={() => this.setState({ isLoginModalOpen: false })}
|
||||
>
|
||||
<Login />
|
||||
</Modal>
|
||||
<Modal
|
||||
show={this.state.isProfileModalOpen}
|
||||
closeHandler={() => this.setState({ isProfileModalOpen: false })}
|
||||
>
|
||||
<Profile user={this.state.user} />
|
||||
</Modal>
|
||||
<HelpModal
|
||||
show={this.state.isHelpModalOpen}
|
||||
closeHandler={() => this.setState({ isHelpModalOpen: false })}
|
||||
|
@ -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
|
||||
|
11
webmaker/src/firebaseInit.js
Normal file
11
webmaker/src/firebaseInit.js
Normal file
@ -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);
|
20
webmaker/src/notifications.js
Normal file
20
webmaker/src/notifications.js
Normal file
@ -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
|
||||
};
|
Reference in New Issue
Block a user