Factor out firebase.tsx, FirebaseAppContext (#216)
This factors out a ` firebase.tsx` file and a `FirebaseAppContext` in preparation for gallery work (#26).pull/217/head
rodzic
bec2c4eb49
commit
22ae85c512
|
@ -0,0 +1,38 @@
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic interface for authentication.
|
||||||
|
*/
|
||||||
|
export interface AuthContext {
|
||||||
|
/**
|
||||||
|
* The currently logged-in user. This will be
|
||||||
|
* null if the user isn't logged in, otherwise it will
|
||||||
|
* be their name.
|
||||||
|
*/
|
||||||
|
loggedInUser: string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the authentication provider, e.g. "GitHub",
|
||||||
|
* or null if auth is disabled.
|
||||||
|
*/
|
||||||
|
providerName: string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If authentication failed for some reason, this will
|
||||||
|
* be a string describing the error.
|
||||||
|
*/
|
||||||
|
error?: string;
|
||||||
|
|
||||||
|
/** Begin the login UI flow. */
|
||||||
|
login(): void;
|
||||||
|
|
||||||
|
/** Log out the user. */
|
||||||
|
logout(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AuthContext = React.createContext<AuthContext>({
|
||||||
|
loggedInUser: null,
|
||||||
|
providerName: null,
|
||||||
|
login() {},
|
||||||
|
logout() {},
|
||||||
|
});
|
105
lib/auth.tsx
105
lib/auth.tsx
|
@ -1,105 +0,0 @@
|
||||||
import { FirebaseApp, FirebaseOptions, initializeApp } from "firebase/app";
|
|
||||||
import {
|
|
||||||
getAuth,
|
|
||||||
signInWithPopup,
|
|
||||||
GithubAuthProvider,
|
|
||||||
onAuthStateChanged,
|
|
||||||
signOut,
|
|
||||||
Auth,
|
|
||||||
User,
|
|
||||||
} from "firebase/auth";
|
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
|
||||||
|
|
||||||
const DEFAULT_APP_CONFIG: FirebaseOptions = {
|
|
||||||
apiKey: "AIzaSyAV1kkVvSKEicEa8rLke9o_BxYBu1rb8kw",
|
|
||||||
authDomain: "mystic-addaf.firebaseapp.com",
|
|
||||||
projectId: "mystic-addaf",
|
|
||||||
storageBucket: "mystic-addaf.appspot.com",
|
|
||||||
messagingSenderId: "26787182745",
|
|
||||||
appId: "1:26787182745:web:e4fbd9439b9279fe966008",
|
|
||||||
measurementId: "G-JHKRSK1PR6",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic interface for authentication.
|
|
||||||
*/
|
|
||||||
export interface AuthContext {
|
|
||||||
/**
|
|
||||||
* The currently logged-in user. This will be
|
|
||||||
* null if the user isn't logged in, otherwise it will
|
|
||||||
* be their name.
|
|
||||||
*/
|
|
||||||
loggedInUser: string | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the authentication provider, e.g. "GitHub",
|
|
||||||
* or null if auth is disabled.
|
|
||||||
*/
|
|
||||||
providerName: string | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If authentication failed for some reason, this will
|
|
||||||
* be a string describing the error.
|
|
||||||
*/
|
|
||||||
error?: string;
|
|
||||||
|
|
||||||
/** Begin the login UI flow. */
|
|
||||||
login(): void;
|
|
||||||
|
|
||||||
/** Log out the user. */
|
|
||||||
logout(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
type FirebaseGithubAuthState = {
|
|
||||||
app: FirebaseApp;
|
|
||||||
auth: Auth;
|
|
||||||
provider: GithubAuthProvider;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Firebase GitHub authentication provider.
|
|
||||||
*
|
|
||||||
* Note this component is assumed to never be unmounted, nor
|
|
||||||
* for its props to change.
|
|
||||||
*/
|
|
||||||
export const FirebaseGithubAuthProvider: React.FC<{
|
|
||||||
config?: FirebaseOptions;
|
|
||||||
}> = ({ config, children }) => {
|
|
||||||
const [state, setState] = useState<FirebaseGithubAuthState | null>(null);
|
|
||||||
const [user, setUser] = useState<User | null>(null);
|
|
||||||
const [error, setError] = useState<string | undefined>(undefined);
|
|
||||||
|
|
||||||
const handleError = (e: Error) => setError(e.message);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const app = initializeApp(config || DEFAULT_APP_CONFIG);
|
|
||||||
const auth = getAuth(app);
|
|
||||||
const provider = new GithubAuthProvider();
|
|
||||||
|
|
||||||
setState({ app, auth, provider });
|
|
||||||
onAuthStateChanged(auth, setUser);
|
|
||||||
}, [config]);
|
|
||||||
|
|
||||||
const context: AuthContext = {
|
|
||||||
loggedInUser: user && user.displayName,
|
|
||||||
providerName: "GitHub",
|
|
||||||
error,
|
|
||||||
login: useCallback(() => {
|
|
||||||
setError(undefined);
|
|
||||||
state && signInWithPopup(state.auth, state.provider).catch(handleError);
|
|
||||||
}, [state]),
|
|
||||||
logout: useCallback(() => {
|
|
||||||
setError(undefined);
|
|
||||||
state && signOut(state.auth).catch(handleError);
|
|
||||||
}, [state]),
|
|
||||||
};
|
|
||||||
|
|
||||||
return <AuthContext.Provider value={context} children={children} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AuthContext = React.createContext<AuthContext>({
|
|
||||||
loggedInUser: null,
|
|
||||||
providerName: null,
|
|
||||||
login() {},
|
|
||||||
logout() {},
|
|
||||||
});
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useCallback, useEffect, useState } from "react";
|
import React, { useCallback, useEffect, useState } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { FirebaseGithubAuthProvider } from "./auth";
|
import { FirebaseAppProvider, FirebaseGithubAuthProvider } from "./firebase";
|
||||||
import { PageContext, PAGE_QUERY_ARG } from "./page";
|
import { PageContext, PAGE_QUERY_ARG } from "./page";
|
||||||
import { pageNames, Pages, toPageName, DEFAULT_PAGE } from "./pages";
|
import { pageNames, Pages, toPageName, DEFAULT_PAGE } from "./pages";
|
||||||
|
|
||||||
|
@ -57,11 +57,13 @@ const App: React.FC<{}> = (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FirebaseGithubAuthProvider>
|
<FirebaseAppProvider>
|
||||||
<PageContext.Provider value={ctx}>
|
<FirebaseGithubAuthProvider>
|
||||||
<PageComponent />
|
<PageContext.Provider value={ctx}>
|
||||||
</PageContext.Provider>
|
<PageComponent />
|
||||||
</FirebaseGithubAuthProvider>
|
</PageContext.Provider>
|
||||||
|
</FirebaseGithubAuthProvider>
|
||||||
|
</FirebaseAppProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { FirebaseApp, FirebaseOptions, initializeApp } from "firebase/app";
|
||||||
|
import {
|
||||||
|
getAuth,
|
||||||
|
signInWithPopup,
|
||||||
|
GithubAuthProvider,
|
||||||
|
onAuthStateChanged,
|
||||||
|
signOut,
|
||||||
|
Auth,
|
||||||
|
User,
|
||||||
|
} from "firebase/auth";
|
||||||
|
import React, { useCallback, useContext, useEffect, useState } from "react";
|
||||||
|
import { AuthContext } from "./auth-context";
|
||||||
|
|
||||||
|
const DEFAULT_APP_CONFIG: FirebaseOptions = {
|
||||||
|
apiKey: "AIzaSyAV1kkVvSKEicEa8rLke9o_BxYBu1rb8kw",
|
||||||
|
authDomain: "mystic-addaf.firebaseapp.com",
|
||||||
|
projectId: "mystic-addaf",
|
||||||
|
storageBucket: "mystic-addaf.appspot.com",
|
||||||
|
messagingSenderId: "26787182745",
|
||||||
|
appId: "1:26787182745:web:e4fbd9439b9279fe966008",
|
||||||
|
measurementId: "G-JHKRSK1PR6",
|
||||||
|
};
|
||||||
|
|
||||||
|
type FirebaseAppContext = {
|
||||||
|
app: FirebaseApp;
|
||||||
|
auth: Auth;
|
||||||
|
provider: GithubAuthProvider;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FirebaseAppContext =
|
||||||
|
React.createContext<FirebaseAppContext | null>(null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Firebase app provider. Any other components that use Firebase must
|
||||||
|
* be a child of this.
|
||||||
|
*
|
||||||
|
* Note this component is assumed to never be unmounted, nor
|
||||||
|
* for its non-children props to change.
|
||||||
|
*/
|
||||||
|
export const FirebaseAppProvider: React.FC<{ config?: FirebaseOptions }> = ({
|
||||||
|
config,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [value, setValue] = useState<FirebaseAppContext | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const app = initializeApp(config || DEFAULT_APP_CONFIG);
|
||||||
|
const auth = getAuth(app);
|
||||||
|
const provider = new GithubAuthProvider();
|
||||||
|
|
||||||
|
setValue({ app, auth, provider });
|
||||||
|
}, [config]);
|
||||||
|
|
||||||
|
return <FirebaseAppContext.Provider value={value} children={children} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Firebase GitHub authentication provider. Must be a child of a
|
||||||
|
* `FirebaseAppProvider`.
|
||||||
|
*
|
||||||
|
* Note this component is assumed to never be unmounted.
|
||||||
|
*/
|
||||||
|
export const FirebaseGithubAuthProvider: React.FC<{}> = ({ children }) => {
|
||||||
|
const appCtx = useContext(FirebaseAppContext);
|
||||||
|
const [user, setUser] = useState<User | null>(null);
|
||||||
|
const [error, setError] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
|
const handleError = (e: Error) => setError(e.message);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!appCtx) return;
|
||||||
|
|
||||||
|
onAuthStateChanged(appCtx.auth, setUser);
|
||||||
|
}, [appCtx]);
|
||||||
|
|
||||||
|
const context: AuthContext = {
|
||||||
|
loggedInUser: user && user.displayName,
|
||||||
|
providerName: appCtx && "GitHub",
|
||||||
|
error,
|
||||||
|
login: useCallback(() => {
|
||||||
|
setError(undefined);
|
||||||
|
appCtx &&
|
||||||
|
signInWithPopup(appCtx.auth, appCtx.provider).catch(handleError);
|
||||||
|
}, [appCtx]),
|
||||||
|
logout: useCallback(() => {
|
||||||
|
setError(undefined);
|
||||||
|
appCtx && signOut(appCtx.auth).catch(handleError);
|
||||||
|
}, [appCtx]),
|
||||||
|
};
|
||||||
|
|
||||||
|
return <AuthContext.Provider value={context} children={children} />;
|
||||||
|
};
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useContext, useState } from "react";
|
import React, { useContext, useState } from "react";
|
||||||
import { AuthContext } from "../auth";
|
import { AuthContext } from "../auth-context";
|
||||||
import { AutoSizingSvg } from "../auto-sizing-svg";
|
import { AutoSizingSvg } from "../auto-sizing-svg";
|
||||||
import { CreatureContext, CreatureContextType } from "../creature-symbol";
|
import { CreatureContext, CreatureContextType } from "../creature-symbol";
|
||||||
import { createCreatureSymbolFactory } from "../creature-symbol-factory";
|
import { createCreatureSymbolFactory } from "../creature-symbol-factory";
|
||||||
|
|
Ładowanie…
Reference in New Issue