diff --git a/www/package.json b/www/package.json index 035d7ad1..062b8048 100644 --- a/www/package.json +++ b/www/package.json @@ -42,6 +42,7 @@ "react-dnd-html5-backend": "^11.1.3", "react-dom": "^16.9.0", "react-dropzone": "^10.1.8", + "react-firebaseui": "^5.0.2", "react-hook-form": "^6.15.5", "react-image": "^4.0.3", "react-joyride": "^2.3.0", diff --git a/www/src/App.tsx b/www/src/App.tsx index 0e588cfe..70282635 100644 --- a/www/src/App.tsx +++ b/www/src/App.tsx @@ -17,7 +17,7 @@ import { FiretableContextProvider } from "contexts/FiretableContext"; import { SnackLogProvider } from "contexts/SnackLogContext"; import routes from "constants/routes"; -import AuthView from "pages/Auth/GoogleAuth"; +import AuthView from "pages/Auth"; import SignOutView from "pages/Auth/SignOut"; import TestView from "pages/Test"; const AuthSetupGuidePage = lazy( diff --git a/www/src/assets/bg-pattern.svg b/www/src/assets/bg-pattern.svg new file mode 100644 index 00000000..f03da026 --- /dev/null +++ b/www/src/assets/bg-pattern.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/www/src/components/Auth/AuthLayout.tsx b/www/src/components/Auth/AuthLayout.tsx new file mode 100644 index 00000000..ffb3b5b0 --- /dev/null +++ b/www/src/components/Auth/AuthLayout.tsx @@ -0,0 +1,91 @@ +import React from "react"; +import Div100vh from "react-div-100vh"; + +import { makeStyles, createStyles, Paper, Typography } from "@material-ui/core"; +import { fade, lighten } from "@material-ui/core/styles"; + +import bgPattern from "assets/bg-pattern.svg"; + +const useStyles = makeStyles((theme) => + createStyles({ + root: { + backgroundBlendMode: "normal, overlay, normal, normal", + // backgroundImage: ` + // linear-gradient(to bottom, rgba(255,255,255,0), #fff), + // linear-gradient(155deg, #303030 -4%, ${theme.palette.primary.main} 92%), + // url('${bgPattern}'), + // linear-gradient(161deg, #ecf4ff -31%, #fff4f4 160%) + // `, + backgroundImage: ` + linear-gradient(to bottom, ${fade( + theme.palette.background.default, + 0 + )}, ${theme.palette.background.default} 75%), + linear-gradient(155deg, ${theme.palette.primary.main} 10%, ${ + theme.palette.secondary.main + } 90%), + url('${bgPattern}'), + linear-gradient(161deg, ${fade( + theme.palette.background.default, + 0.95 + )} -31%, ${fade(theme.palette.background.default, 0.98)} 160%) + `, + + display: "grid", + placeItems: "center", + padding: theme.spacing(1), + + cursor: "default", + }, + + paper: { + maxWidth: 400, + width: "100%", + padding: theme.spacing(4), + backgroundColor: + theme.palette.background.elevation?.[8] || + theme.palette.background.paper, + + "& > * + *": { marginTop: theme.spacing(4) }, + }, + + wordmark: { + display: "block", + textAlign: "center", + + color: theme.palette.primary.main, + letterSpacing: 0, + fontVariantLigatures: "common-ligatures", + }, + + projectName: { + display: "block", + textAlign: "center", + marginTop: theme.spacing(1), + + color: theme.palette.text.disabled, + }, + }) +); + +export interface IAuthLayoutProps { + children: React.ReactNode; +} + +export default function AuthLayout({ children }: IAuthLayoutProps) { + const classes = useStyles(); + + return ( + + + + firetable + + + {process.env.REACT_APP_FIREBASE_PROJECT_ID} + + {children} + + + ); +} diff --git a/www/src/constants/firebaseui.ts b/www/src/constants/firebaseui.ts new file mode 100644 index 00000000..278cd457 --- /dev/null +++ b/www/src/constants/firebaseui.ts @@ -0,0 +1,34 @@ +import firebase from "firebase/app"; +import * as firebaseui from "firebaseui"; +const authOptions = { + google: firebase.auth.GoogleAuthProvider.PROVIDER_ID, + twitter: firebase.auth.TwitterAuthProvider.PROVIDER_ID, + facebook: firebase.auth.FacebookAuthProvider.PROVIDER_ID, + github: firebase.auth.GithubAuthProvider.PROVIDER_ID, + microsoft: { + provider: "microsoft.com", + loginHintKey: "login_hint", + }, + apple: { provider: "apple.com" }, + email: { + provider: firebase.auth.EmailAuthProvider.PROVIDER_ID, + requireDisplayName: true, + disableSignUp: { status: true }, + }, + anonymous: firebaseui.auth.AnonymousAuthProvider.PROVIDER_ID +} +export const getUiConfig = (selectedSignInOptions: { google?: Boolean, twitter?: Boolean, github?: Boolean, facebook?: Boolean, email?: Boolean,microsoft?:Boolean, apple?: Boolean,anonymous?:Boolean}) => ({ + signInFlow: "popup", + signInSuccessUrl: "/", + signInOptions: Object.keys(selectedSignInOptions).map((option) => { + if (selectedSignInOptions[option]) { + return authOptions[option] + } + }).filter((option) =>option), + callbacks: { + uiShown: () => { + const node = document.getElementById("firetable-firebaseui-skeleton"); + if (node) node.style.display = "none"; + }, + }, +}); diff --git a/www/src/pages/Auth/index.tsx b/www/src/pages/Auth/index.tsx index 2e729f3c..b14cc9b3 100644 --- a/www/src/pages/Auth/index.tsx +++ b/www/src/pages/Auth/index.tsx @@ -1,78 +1,210 @@ -import React, { useState } from "react"; -import Div100vh from "react-div-100vh"; +import React,{useState,useEffect} from "react"; +import { auth,db } from "../../firebase"; +import { getUiConfig } from "constants/firebaseui"; +import StyledFirebaseAuth from "react-firebaseui/StyledFirebaseAuth"; -import { - makeStyles, - createStyles, - Grid, - Button, - CircularProgress, -} from "@material-ui/core"; +import { makeStyles, createStyles, Typography } from "@material-ui/core"; +import { fade } from "@material-ui/core/styles"; +import Skeleton from "@material-ui/lab/Skeleton"; -import { googleProvider, auth } from "../../firebase"; -import useRouter from "hooks/useRouter"; -import FiretableLogo from "assets/firetable-with-wordmark.svg"; +import AuthLayout from "components/Auth/AuthLayout"; +import * as firebaseui from "firebaseui"; const useStyles = makeStyles((theme) => createStyles({ - root: { - height: "100%", - padding: theme.spacing(3), + "@global": { + ".firetable-firebaseui": { + "& .firebaseui-container": { + backgroundColor: "transparent", + color: theme.palette.text.primary, + fontFamily: theme.typography.fontFamily, + }, + "& .firebaseui-text": { + color: theme.palette.text.secondary, + fontFamily: theme.typography.fontFamily, + }, + "& .firebaseui-title": { + ...theme.typography.h5, + color: theme.palette.text.primary, + }, + "& .firebaseui-subtitle": { + ...theme.typography.h6, + color: theme.palette.text.secondary, + }, + "& .firebaseui-error": { + ...theme.typography.caption, + color: theme.palette.error.main, + }, - margin: 0, - width: "100%", + "& .firebaseui-card-content, & .firebaseui-card-footer": { padding: 0 }, + "& .firebaseui-idp-list, & .firebaseui-tenant-list": { margin: 0 }, + "& .firebaseui-idp-list>.firebaseui-list-item, & .firebaseui-tenant-list>.firebaseui-list-item": { + margin: 0, + }, + "& .firebaseui-list-item + .firebaseui-list-item": { + paddingTop: theme.spacing(2), + }, + + "& .mdl-button": { + borderRadius: 24, + ...theme.typography.button, + }, + "& .mdl-button--raised": { boxShadow: "none" }, + "& .mdl-card": { + boxShadow: "none", + minHeight: 0, + }, + "& .mdl-button--primary.mdl-button--primary": { + color: theme.palette.primary.main, + }, + "& .mdl-button--raised.mdl-button--colored": { + backgroundColor: theme.palette.primary.main, + color: theme.palette.primary.contrastText, + + "&:active, &:focus:not(:active), &:hover": { + backgroundColor: theme.palette.primary.main, + }, + }, + + "& .firebaseui-idp-button, & .firebaseui-tenant-button": { + maxWidth: "none", + minHeight: 48, + }, + "& .firebaseui-idp-text": { + ...theme.typography.button, + + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2) + 18, + marginLeft: -18, + width: "100%", + textAlign: "center", + + [theme.breakpoints.down("xs")]: { + "&.firebaseui-idp-text-long": { display: "none" }, + "&.firebaseui-idp-text-short": { display: "table-cell" }, + }, + }, + + "& .firebaseui-idp-google": { + backgroundColor: "#4285F4 !important", + + "& .firebaseui-idp-icon-wrapper::before": { + content: "''", + display: "block", + position: "absolute", + top: 2, + left: 2, + width: 48 - 4, + height: 48 - 4, + zIndex: 0, + + backgroundColor: "#fff", + borderRadius: "50%", + }, + "& .firebaseui-idp-icon-wrapper img": { + position: "relative", + left: -1, + }, + + "&>.firebaseui-idp-text": { + color: "#fff", + }, + }, + '& .firebaseui-idp-github, & [data-provider-id="microsoft.com"]': { + backgroundColor: "#000 !important", + }, + + "& .firebaseui-card-header": { padding: 0 }, + "& .firebaseui-card-actions": { padding: 0 }, + + "& .firebaseui-input, & .firebaseui-input-invalid": { + ...theme.typography.body1, + color: theme.palette.text.primary, + }, + "& .firebaseui-textfield.mdl-textfield .firebaseui-input": { + borderColor: theme.palette.divider, + }, + "& .mdl-textfield.is-invalid .mdl-textfield__input": { + borderColor: theme.palette.error.main, + }, + "& .firebaseui-label": { + ...theme.typography.subtitle2, + color: theme.palette.text.secondary, + }, + "& .mdl-textfield--floating-label.is-dirty .mdl-textfield__label, .mdl-textfield--floating-label.is-focused .mdl-textfield__label": { + color: theme.palette.text.primary, + }, + "& .firebaseui-textfield.mdl-textfield .firebaseui-label:after": { + backgroundColor: theme.palette.primary.main, + }, + "& .mdl-textfield.is-invalid .mdl-textfield__label:after": { + backgroundColor: theme.palette.error.main, + }, + + "& .mdl-progress>.bufferbar": { + background: fade(theme.palette.primary.main, 0.25), + }, + "& .mdl-progress>.progressbar": { + backgroundColor: theme.palette.primary.main + " !important", + }, + }, + }, + + signInText: { + display: "none", + [theme.breakpoints.down("xs")]: { display: "block" }, + + textAlign: "center", + color: theme.palette.text.disabled, + marginBottom: theme.spacing(-1), + }, + + skeleton: { + marginBottom: theme.spacing(-4), + + "& > *": { + width: "100%", + height: 48, + borderRadius: 24, + }, + + "& > * + *": { + marginTop: theme.spacing(2), + }, }, - logo: { display: "block" }, }) ); export default function AuthPage() { const classes = useStyles(); - const router = useRouter(); - const [loading, setLoading] = useState(false); - - const handleAuth = async () => { - setLoading(true); - await auth.signInWithPopup(googleProvider); - router.history.replace("/"); - }; + const [uiConfig,setUiConfig] = useState(); + useEffect(() =>{ + db.doc("/_FIRETABLE_/publicSettings").get().then((doc) =>{ + const signInOptions = doc?.get("signInOptions") + setUiConfig(getUiConfig(signInOptions)) + }).catch((err) =>{ + setUiConfig(getUiConfig({google:true})) + }) + },[]) return ( - - - firetable - + + {uiConfig?<> + + Sign in with + - - {loading ? ( - - ) : ( - - )} - - +
+ {uiConfig.signInOptions?.map((_, i) => ( + + ))} +
+ + :<>} + ); } diff --git a/www/yarn.lock b/www/yarn.lock index 70d7925f..6cebe4cc 100644 --- a/www/yarn.lock +++ b/www/yarn.lock @@ -5481,6 +5481,11 @@ detect-port-alt@1.1.6: address "^1.0.1" debug "^2.6.0" +dialog-polyfill@^0.4.7: + version "0.4.10" + resolved "https://registry.yarnpkg.com/dialog-polyfill/-/dialog-polyfill-0.4.10.tgz#c4ea68a0deed4abb59a6a2a025c548b278cd532e" + integrity sha512-j5yGMkP8T00UFgyO+78OxiN5vC5dzRQF3BEio+LhNvDbyfxWBsi3sfPArDm54VloaJwy2hm3erEiDWqHRC8rzw== + diff-sequences@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" @@ -6783,6 +6788,14 @@ firebase@8.6.8: "@firebase/storage" "0.5.5" "@firebase/util" "1.1.0" +firebaseui@^4.8.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/firebaseui/-/firebaseui-4.8.0.tgz#74c10a30db17f2cbfe020c91b97d5e3c6e8efbbc" + integrity sha512-DG8CD+969JHMailhOm8nKo+eJlumIHex0TH18eJeTo0Q2KEt5m/b61S1ky4bavK/nGmLJBRECJytq09/pwhZ0A== + dependencies: + dialog-polyfill "^0.4.7" + material-design-lite "^1.2.0" + flat-arguments@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/flat-arguments/-/flat-arguments-1.0.2.tgz#9baa780adf0501f282d726c9c6a038dba44ea76f" @@ -9741,6 +9754,11 @@ material-colors@^1.2.1: resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46" integrity sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg== +material-design-lite@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/material-design-lite/-/material-design-lite-1.3.0.tgz#d004ce3fee99a1eeb74a78b8a325134a5f1171d3" + integrity sha1-0ATOP+6Zoe63Sni4oyUTSl8RcdM= + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -12379,6 +12397,13 @@ react-error-overlay@^6.0.7: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.7.tgz#1dcfb459ab671d53f660a991513cb2f0a0553108" integrity sha512-TAv1KJFh3RhqxNvhzxj6LeT5NWklP6rDr2a0jaTfsZ5wSZWHOGeqQyejUp3xxLfPt2UpyJEcVQB/zyPcmonNFA== +react-firebaseui@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/react-firebaseui/-/react-firebaseui-5.0.2.tgz#7496bc595454abdd3e1c10612bb3446cb125181a" + integrity sha512-ZfIWfRKlKAkMwEI+41rMPkwdNEQEfBLXV6TVaN5er3cdwMD0vQecWt6YOE4/v1ELziaj10NNRB8yB42V56QBMQ== + dependencies: + firebaseui "^4.8.0" + react-floater@^0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/react-floater/-/react-floater-0.7.2.tgz#c0c7ddab18a955b1c4a3934890fa804b3d2a4801"