Last active
January 22, 2020 00:18
-
-
Save DavidFrahm/9c71fa2061640b9cb43b76582cd55d49 to your computer and use it in GitHub Desktop.
FirebaseUI with Stencil ionic-pwa starter (workaround for Rollup issues)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, h } from '@stencil/core'; | |
import { initFirebase } from '../../helpers/firebase'; | |
@Component({ | |
tag: 'app-root', | |
styleUrl: 'app-root.css' | |
}) | |
export class AppRoot { | |
componentWillLoad() { | |
initFirebase(); | |
} | |
render() { | |
return ( | |
<ion-app> | |
<ion-router useHash={false}> | |
<ion-route url="/" component="app-home" /> | |
<ion-route url="/profile/:name" component="app-profile" /> | |
<ion-route url="/sign-up" component="signup-page" /> | |
</ion-router> | |
<ion-nav /> | |
</ion-app> | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This import loads the firebase namespace along with all its type information. | |
* Firebase App (the core Firebase SDK) is always required and must be listed first. | |
*/ | |
import fb from 'firebase/app'; | |
/** | |
* Add the Firebase products that you want to use. | |
* These imports load individual services into the firebase namespace. | |
*/ | |
import 'firebase/auth'; | |
import 'firebase/firestore'; | |
import 'firebase/functions'; | |
import 'firebase/performance'; | |
import 'firebase/analytics'; | |
import { injectJS, injectCSS } from './utils'; | |
/** | |
* Initialize Firebase app here, so it happens early and only once. | |
*/ | |
import { firebaseConfig } from './constants'; | |
fb.initializeApp(firebaseConfig); | |
console.info("Firebase app initialized"); | |
export const firebase = fb; | |
/** | |
* Convenience, so other scripts can use these exports instead of (correctly) importing firebase libraries | |
*/ | |
export const firestore = firebase.firestore(); | |
export const functions = firebase.functions(); | |
export const performance = firebase.performance(); | |
export const auth = firebase.auth(); | |
export const analytics = firebase.analytics(); | |
/** | |
* Needed for firebaseui | |
*/ | |
(window as any).firebase = firebase; | |
/** | |
* Keep TypeScript happy with window/global variables. | |
*/ | |
declare var firebaseui; // Set via initFirebaseUI function below | |
// TODO: Enable this or remove it. | |
// firestore.enablePersistence().catch(err => { | |
// 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 | |
// // ... | |
// } | |
// }); | |
/** | |
* Actual Firebase app initilization happens when this script is first imported and ran. | |
* This is just a call to control when and from where that occurs. | |
* Also a good place to verify Firebase app. | |
* NB: I'm not thrilled about this basically empty function, so might move the app init into app-root before its @Component. | |
*/ | |
export function initFirebase() { | |
console.info("initFirebase()"); | |
try { | |
console.log("firebase", firebase); | |
} catch (error) { | |
console.error("Firebase app is not available"); | |
} | |
} | |
/** | |
* Setup and initialize FirebaseUI on the page. | |
* Must be called after DOM exists for it to find the FirebaseUI widget element on the page. | |
* | |
* As of Dec 2019, FirebaseUI didn't work with Rolllup builds, so we have to add it separately. | |
* Option A: Standard <script ...> and <link ...> tags in index.html | |
* Option B: Programatically inject JS and CSS in the DOM | |
* I went with Option B, because then the scripts are only loaded when FirebaseUI is used, i.e., sign up and sign in. | |
*/ | |
export async function initFirebaseUI(widgetElementId: string) { | |
console.info("initFirebaseUI()"); | |
async function injectResources() { | |
await injectJS( | |
'firebase-ui-script', | |
'https://www.gstatic.com/firebasejs/ui/4.4.0/firebase-ui-auth.js' | |
); | |
await injectCSS( | |
'firebase-ui-css', | |
'https://www.gstatic.com/firebasejs/ui/4.4.0/firebase-ui-auth.css' | |
); | |
console.log("JS and CSS Injected"); | |
} | |
function startFirebaseUI() { | |
console.info("startFirebaseUI()"); | |
console.log("firebase", firebase); | |
console.log("firebaseui", firebaseui); | |
var uiConfig = { | |
signInSuccessUrl: '/my-post-login-route', | |
signInOptions: [ | |
// Leave the lines as is for the providers you want to offer your users. | |
// firebase.auth.GoogleAuthProvider.PROVIDER_ID, | |
// firebase.auth.FacebookAuthProvider.PROVIDER_ID, | |
// firebase.auth.TwitterAuthProvider.PROVIDER_ID, | |
// firebase.auth.GithubAuthProvider.PROVIDER_ID, | |
firebase.auth.EmailAuthProvider.PROVIDER_ID | |
// firebase.auth.PhoneAuthProvider.PROVIDER_ID, | |
// firebaseui.auth.AnonymousAuthProvider.PROVIDER_ID | |
], | |
tosUrl: '/my-terms-of-service-route', | |
privacyPolicyUrl: '/my-privacy-policy-route', | |
credentialHelper: firebaseui.auth.CredentialHelper.NONE, | |
callbacks: { | |
signInSuccessWithAuthResult: authResult => { | |
if (authResult.additionalUserInfo.isNewUser) { | |
authResult.user.sendEmailVerification(); | |
} | |
return false; | |
} | |
} | |
}; | |
// Initialize the FirebaseUI widget using Firebase. | |
const ui = | |
firebaseui.auth.AuthUI.getInstance() || | |
new firebaseui.auth.AuthUI(firebase.auth()); | |
// The start method will wait until the DOM is loaded. | |
ui.start(widgetElementId, uiConfig); | |
}; | |
await injectResources(); | |
startFirebaseUI(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
... | |
"dependencies": { | |
"@ionic/core": "^4.11.7", | |
"firebase": "^7.6.1", | |
"firebaseui": "^4.4.0" | |
}, | |
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Component, h } from '@stencil/core'; | |
import { initFirebaseUI } from '../../helpers/firebase'; | |
@Component({ | |
tag: 'signup-page', | |
styleUrl: 'signup-page.css' | |
}) | |
export class SignupPage { | |
async componentDidLoad() { | |
await initFirebaseUI('#firebaseui-auth-container'); | |
} | |
render() { | |
return ( | |
<div> | |
<p>Hello SignupPage!</p> | |
<div id="firebaseui-auth-container"></div> | |
</div> | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Config } from '@stencil/core'; | |
// https://stenciljs.com/docs/config | |
export const config: Config = { | |
outputTargets: [{ | |
type: 'www', | |
serviceWorker: null | |
}], | |
commonjs: { | |
namedExports: { | |
// NB: Left-hand side can be an absolute path, a path relative to the current directory, or the name of a module in node_modules. | |
// Firebase needs this: | |
'idb/build/idb.js': ['openDb'], | |
// FirebaseUI was needing this, but not anymore since loading FirebaseUI outside of Rollup; leaving for a while until other Firebase features are implemented. | |
// 'firebase/dist/index.cjs.js': ['initializeApp', 'auth', 'app', 'firebase'], | |
// 'firebase/app/dist/index.cjs.js': ['initializeApp', 'auth', 'app', 'firebase'], | |
} | |
}, | |
globalScript: 'src/global/app.ts', | |
globalStyle: 'src/global/app.css' | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function injectJS(id: string, src: string) { | |
return new Promise((resolve, reject) => { | |
if (document.getElementById(id)) { | |
resolve(`JS already injected for id ${id}`); | |
return; | |
} | |
const script = document.createElement('script'); | |
script.id = id; | |
script.async = true; | |
script.src = src; | |
script.addEventListener('load', resolve); | |
script.addEventListener('error', () => reject('Error loading script.')); | |
script.addEventListener('abort', () => reject('Script loading aborted.')); | |
document.head.appendChild(script); | |
}); | |
} | |
export function injectCSS(id: string, src: string) { | |
return new Promise((resolve, reject) => { | |
if (document.getElementById(id)) { | |
resolve(`CSS already injected for id ${id}`); | |
return; | |
} | |
const script = document.createElement('link'); | |
script.id = id; | |
script.setAttribute('rel', 'stylesheet'); | |
script.setAttribute('href', src); | |
script.addEventListener('load', resolve); | |
script.addEventListener('error', () => reject('Error loading css.')); | |
script.addEventListener('abort', () => reject('CSS loading aborted.')); | |
document.head.appendChild(script); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment