diff --git a/.storybook/preview.js b/.storybook/preview.js index a60641947..15887362b 100644 --- a/.storybook/preview.js +++ b/.storybook/preview.js @@ -1,10 +1,11 @@ import React from "react"; import { useAccessibleOutlineStyle } from "../src/react-components/input/useAccessibleOutlineStyle"; import "../src/react-components/styles/global.scss"; +import { WrappedIntlProvider } from "../src/react-components/wrapped-intl-provider"; const Layout = ({ children }) => { useAccessibleOutlineStyle(); - return <>{children}; + return {children}; }; export const decorators = [ diff --git a/src/cloud.js b/src/cloud.js index 4b98c959a..0a4473f5b 100644 --- a/src/cloud.js +++ b/src/cloud.js @@ -4,7 +4,7 @@ import "./utils/configs"; import styles from "./assets/stylesheets/cloud.scss"; import classNames from "classnames"; import { WrappedIntlProvider } from "./react-components/wrapped-intl-provider"; -import { Page } from "./react-components/layout/Page"; +import { PageContainer } from "./react-components/layout/PageContainer"; import { AuthContextProvider } from "./react-components/auth/AuthContext"; import Store from "./storage/store"; @@ -14,7 +14,7 @@ registerTelemetry("/cloud", "Hubs Cloud Landing Page"); function HubsCloudPage() { return ( - +
@@ -76,7 +76,7 @@ function HubsCloudPage() {

- + ); } diff --git a/src/react-components/auth-dialog.js b/src/react-components/auth-dialog.js deleted file mode 100644 index ff6fb14b6..000000000 --- a/src/react-components/auth-dialog.js +++ /dev/null @@ -1,56 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { injectIntl, FormattedMessage } from "react-intl"; -import DialogContainer from "./dialog-container.js"; -import IfFeature from "./if-feature"; - -class AuthDialog extends Component { - static propTypes = { - intl: PropTypes.object, - verifying: PropTypes.bool, - verified: PropTypes.bool, - authOrigin: PropTypes.string - }; - - render() { - const { authOrigin, verifying, verified } = this.props; - const { formatMessage } = this.props.intl; - const title = verifying || !verified ? "" : formatMessage({ id: "auth.verified-title" }); - - if (!verifying && !verified) { - return ( - - - - ); - } else { - return ( - - {verifying ? ( -
-
-
-
-
- ) : authOrigin === "spoke" ? ( - - ) : ( -
- - -

- Want Hubs news sent to your inbox?{"\n"} - - Subscribe for updates - . -

-
-
- )} - - ); - } - } -} - -export default injectIntl(AuthDialog); diff --git a/src/react-components/auth/RoomSignInModalContainer.js b/src/react-components/auth/RoomSignInModalContainer.js new file mode 100644 index 000000000..6255ca090 --- /dev/null +++ b/src/react-components/auth/RoomSignInModalContainer.js @@ -0,0 +1,47 @@ +import React, { useState } from "react"; +import PropTypes from "prop-types"; +import configs from "../../utils/configs"; +import { SignInModal, SignInStep, SubmitEmail, WaitForVerification, SignInComplete } from "./SignInModal"; + +// TODO: Migrate to use AuthContext +export function RoomSignInModalContainer({ onClose, step, onSubmitEmail, message, continueText, onContinue }) { + const [cachedEmail, setCachedEmail] = useState(); + + return ( + + {step === SignInStep.submit && ( + { + setCachedEmail(email); + onSubmitEmail(email); + }} + initialEmail={cachedEmail} + termsUrl={configs.link("terms_of_use", "https://github.com/mozilla/hubs/blob/master/TERMS.md")} + showTerms={configs.feature("show_terms")} + privacyUrl={configs.link("privacy_notice", "https://github.com/mozilla/hubs/blob/master/PRIVACY.md")} + showPrivacy={configs.feature("show_privacy")} + message={message} + /> + )} + {step === SignInStep.waitForVerification && ( + + )} + {step === SignInStep.complete && ( + + )} + + ); +} + +RoomSignInModalContainer.propTypes = { + onClose: PropTypes.func, + onSubmitEmail: PropTypes.func, + step: PropTypes.string, + message: PropTypes.string, + continueText: PropTypes.string, + onContinue: PropTypes.func +}; diff --git a/src/react-components/auth/SignInModal.js b/src/react-components/auth/SignInModal.js new file mode 100644 index 000000000..0fd3a98f6 --- /dev/null +++ b/src/react-components/auth/SignInModal.js @@ -0,0 +1,134 @@ +import React, { useCallback, useState } from "react"; +import PropTypes from "prop-types"; +import { CloseButton } from "../input/CloseButton"; +import { Modal } from "../modal/Modal"; +import { FormattedMessage } from "react-intl"; +import styles from "./SignInModal.scss"; +import { Button } from "../input/Button"; +import { TextInputField } from "../input/TextInputField"; + +export const SignInStep = { + submit: "submit", + waitForVerification: "waitForVerification", + complete: "complete" +}; + +export function SubmitEmail({ onSubmitEmail, initialEmail, showPrivacy, privacyUrl, showTerms, termsUrl, message }) { + const [email, setEmail] = useState(initialEmail); + + const onSubmitForm = useCallback( + e => { + e.preventDefault(); + onSubmitEmail(email); + }, + [onSubmitEmail, email] + ); + + return ( +
+

{message || }

+ setEmail(e.target.value)} + placeholder="example@example.com" + /> + {(showTerms || showPrivacy) && ( + + By proceeding, you agree to the{" "} + {showTerms && ( + <> + + terms of use + {" "} + + )} + {showTerms && showPrivacy && "and "} + {showPrivacy && ( + + privacy notice + + )}. + + )} + + + ); +} + +SubmitEmail.defaultProps = { + initialEmail: "" +}; + +SubmitEmail.propTypes = { + message: PropTypes.string, + showTerms: PropTypes.bool, + termsUrl: PropTypes.string, + showPrivacy: PropTypes.bool, + privacyUrl: PropTypes.string, + initialEmail: PropTypes.string, + onSubmitEmail: PropTypes.func.isRequired +}; + +export function WaitForVerification({ email, onCancel, showNewsletterSignup }) { + return ( +
+

+ +

+ {showNewsletterSignup && ( +

+ Want Hubs news sent to your inbox?
+ + Subscribe for updates + +

+ )} + +
+ ); +} + +WaitForVerification.propTypes = { + showNewsletterSignup: PropTypes.bool, + email: PropTypes.string.isRequired, + onCancel: PropTypes.func.isRequired +}; + +export function SignInComplete({ message, continueText, onContinue }) { + return ( +
+ {message} +

{continueText}

+ +
+ ); +} + +SignInComplete.propTypes = { + message: PropTypes.string.isRequired, + continueText: PropTypes.string.isRequired, + onContinue: PropTypes.func.isRequired +}; + +export function SignInModal({ closeable, onClose, children, ...rest }) { + return ( + } {...rest}> + {children} + + ); +} + +SignInModal.propTypes = { + closeable: PropTypes.bool, + onClose: PropTypes.func, + children: PropTypes.node +}; diff --git a/src/react-components/auth/SignInModal.scss b/src/react-components/auth/SignInModal.scss new file mode 100644 index 000000000..838b0b562 --- /dev/null +++ b/src/react-components/auth/SignInModal.scss @@ -0,0 +1,29 @@ +@use "../styles/theme.scss"; + +:local(.modal-content) { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 20px; + line-height: 1.25; + + & > * { + margin-bottom: 16px; + + &:last-child { + margin-bottom: 0; + } + } +} + +:local(.terms) { + color: theme.$darkgrey; + font-size: theme.$font-size-xs; +} + +:local(.newsletter) { + color: theme.$darkgrey; + font-size: theme.$font-size-sm; + line-height: 1.5; +} \ No newline at end of file diff --git a/src/react-components/auth/SignInModal.stories.js b/src/react-components/auth/SignInModal.stories.js new file mode 100644 index 000000000..0b6cbc351 --- /dev/null +++ b/src/react-components/auth/SignInModal.stories.js @@ -0,0 +1,76 @@ +import React from "react"; +import { Center } from "../layout/Center"; +import { Page } from "../layout/Page"; +import { RoomLayout } from "../layout/RoomLayout"; +import { SignInModal, SubmitEmail, WaitForVerification } from "./SignInModal"; +import backgroundUrl from "../../assets/images/home-hero-background-unbranded.png"; + +export default { + title: "SignInModal" +}; + +export const PageSubmit = () => ( + +
+ + + +
+
+); + +PageSubmit.parameters = { + layout: "fullscreen" +}; + +export const PageWaitForVerification = () => ( + +
+ + + +
+
+); + +PageWaitForVerification.parameters = { + layout: "fullscreen" +}; + +export const RoomSubmit = () => ( + + + + } + /> +); + +RoomSubmit.parameters = { + layout: "fullscreen" +}; + +export const RoomWaitForVerification = () => ( + + + + } + /> +); + +RoomWaitForVerification.parameters = { + layout: "fullscreen" +}; diff --git a/src/react-components/auth/SignInModalContainer.js b/src/react-components/auth/SignInModalContainer.js new file mode 100644 index 000000000..9a160ca81 --- /dev/null +++ b/src/react-components/auth/SignInModalContainer.js @@ -0,0 +1,89 @@ +import React, { useCallback, useReducer, useContext, useEffect } from "react"; +import configs from "../../utils/configs"; +import { AuthContext } from "./AuthContext"; +import { SignInModal, SignInStep, WaitForVerification, SubmitEmail } from "./SignInModal"; + +const SignInAction = { + submitEmail: "submitEmail", + verificationReceived: "verificationReceived", + cancel: "cancel" +}; + +const initialSignInState = { + step: SignInStep.submit, + email: "" +}; + +function loginReducer(state, action) { + switch (action.type) { + case SignInAction.submitEmail: + return { step: SignInStep.waitForVerification, email: action.email }; + case SignInAction.verificationReceived: + return { ...state, step: SignInStep.complete }; + case SignInAction.cancel: + return { ...state, step: SignInStep.submit }; + } +} + +function useSignIn() { + const auth = useContext(AuthContext); + const [state, dispatch] = useReducer(loginReducer, initialSignInState); + + const submitEmail = useCallback( + email => { + auth.signIn(email).then(() => { + dispatch({ type: SignInAction.verificationReceived }); + }); + dispatch({ type: SignInAction.submitEmail, email }); + }, + [auth] + ); + + const cancel = useCallback(() => { + dispatch({ type: SignInAction.cancel }); + }, []); + + return { + step: state.step, + email: state.email, + submitEmail, + cancel + }; +} + +export function SignInModalContainer() { + const qs = new URLSearchParams(location.search); + const { step, submitEmail, cancel, email } = useSignIn(); + const redirectUrl = qs.get("sign_in_destination_url") || "/"; + + useEffect( + () => { + if (step === SignInStep.complete) { + window.location = redirectUrl; + } + }, + [step, redirectUrl] + ); + + return ( + + {step === SignInStep.submit ? ( + + ) : ( + + )} + + ); +} diff --git a/src/react-components/auth/SignInPage.js b/src/react-components/auth/SignInPage.js deleted file mode 100644 index 5d4032685..000000000 --- a/src/react-components/auth/SignInPage.js +++ /dev/null @@ -1,176 +0,0 @@ -import React, { useCallback, useState, useReducer, useContext, useEffect } from "react"; -import PropTypes from "prop-types"; -import { Page } from "../layout/Page"; -import styles from "./SignInPage.scss"; -import configs from "../../utils/configs"; -import IfFeature from "../if-feature"; -import { FormattedMessage } from "react-intl"; -import { AuthContext } from "../auth/AuthContext"; - -const SignInStep = { - submit: "submit", - waitForVerification: "waitForVerification", - complete: "complete" -}; - -const SignInAction = { - submitEmail: "submitEmail", - verificationReceived: "verificationReceived", - cancel: "cancel" -}; - -const initialSignInState = { - step: SignInStep.submit, - email: "" -}; - -function loginReducer(state, action) { - switch (action.type) { - case SignInAction.submitEmail: - return { step: SignInStep.waitForVerification, email: action.email }; - case SignInAction.verificationReceived: - return { ...state, step: SignInStep.complete }; - case SignInAction.cancel: - return { ...state, step: SignInStep.submit }; - } -} - -function useSignIn() { - const auth = useContext(AuthContext); - const [state, dispatch] = useReducer(loginReducer, initialSignInState); - - const submitEmail = useCallback( - email => { - auth.signIn(email).then(() => { - dispatch({ type: SignInAction.verificationReceived }); - }); - dispatch({ type: SignInAction.submitEmail, email }); - }, - [auth] - ); - - const cancel = useCallback(() => { - dispatch({ type: SignInAction.cancel }); - }, []); - - return { - step: state.step, - email: state.email, - submitEmail, - cancel - }; -} - -function SubmitEmail({ onSubmitEmail, initialEmail }) { - const [email, setEmail] = useState(initialEmail); - - const onSubmitForm = useCallback( - e => { - e.preventDefault(); - onSubmitEmail(email); - }, - [onSubmitEmail, email] - ); - - return ( -
-

- -

- - - - setEmail(e.target.value)} - placeholder="example@example.com" - /> - {(configs.feature("show_terms") || configs.feature("show_privacy")) && ( - - By proceeding, you agree to the{" "} - - - terms of use - {" "} - - {configs.feature("show_terms") && configs.feature("show_privacy") && "and "} - - - privacy notice - - . - - )} - -
- ); -} - -SubmitEmail.defaultProps = { - initialEmail: "" -}; - -SubmitEmail.propTypes = { - initialEmail: PropTypes.string, - onSubmitEmail: PropTypes.func.isRequired -}; - -function WaitForVerification({ email, onCancel }) { - return ( -
-

- -

- -

- Want Hubs news sent to your inbox?{"\n"} - - Subscribe for updates - . -

-
- -
- ); -} - -WaitForVerification.propTypes = { - email: PropTypes.string.isRequired, - onCancel: PropTypes.func.isRequired -}; - -export function SignInPage() { - const qs = new URLSearchParams(location.search); - const { step, submitEmail, cancel, email } = useSignIn(); - const redirectUrl = qs.get("sign_in_destination_url") || "/"; - - useEffect( - () => { - if (step === SignInStep.complete) { - window.location = redirectUrl; - } - }, - [step, redirectUrl] - ); - - return ( - - {step === SignInStep.submit ? ( - - ) : ( - - )} - - ); -} diff --git a/src/react-components/auth/SignInPage.scss b/src/react-components/auth/SignInPage.scss deleted file mode 100644 index 4a2d6522d..000000000 --- a/src/react-components/auth/SignInPage.scss +++ /dev/null @@ -1,35 +0,0 @@ -@import '../../assets/stylesheets/shared'; - -main { - display: flex; - background-color: #F3F3F3; - height: 100%; - justify-content: center; - align-items: center; -} - -:local(.sign-in-container) { - @extend %centered-flex-column; - background-color: white; - border-radius: 4px; - width: 480px; - padding: 2em; - - h1 { - margin-top: 0; - } - - input { - @extend %input-field; - margin: 1.5em 0 0.5em 0; - } - - button { - @extend %action-button; - } - - :local(.terms) { - font-size: 7pt; - margin-bottom: 32px; - } -} diff --git a/src/react-components/auth/VerifyModal.js b/src/react-components/auth/VerifyModal.js new file mode 100644 index 000000000..c16bd3c36 --- /dev/null +++ b/src/react-components/auth/VerifyModal.js @@ -0,0 +1,58 @@ +import React from "react"; +import PropTypes from "prop-types"; +import styles from "./VerifyModal.scss"; +import { Spinner } from "../misc/Spinner"; +import { Modal } from "../modal/Modal"; + +export const VerificationStep = { + verifying: "verifying", + complete: "complete", + error: "error" +}; + +export function EmailVerifying() { + return ( +
+ Email Verifying + +
+ ); +} + +export function EmailVerified({ origin }) { + return ( +
+ Verification Complete +

Please close this browser window and return to {origin}.

+
+ ); +} + +EmailVerified.propTypes = { + origin: PropTypes.string.isRequired +}; + +export function VerificationError({ error }) { + return ( +
+ Error Verifying Email +

{(error && error.message) || "Unknown Error"}

+
+ ); +} + +VerificationError.propTypes = { + error: PropTypes.object +}; + +export function VerifyModal({ children }) { + return ( + + {children} + + ); +} + +VerifyModal.propTypes = { + children: PropTypes.node +}; diff --git a/src/react-components/auth/VerifyModal.scss b/src/react-components/auth/VerifyModal.scss new file mode 100644 index 000000000..8b88b716c --- /dev/null +++ b/src/react-components/auth/VerifyModal.scss @@ -0,0 +1,20 @@ +:local(.modal-content) { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 20px; + line-height: 1.25; + + svg, p { + margin-top: 16px; + } + + & > * { + margin-bottom: 16px; + + &:last-child { + margin-bottom: 0; + } + } +} diff --git a/src/react-components/auth/VerifyModal.stories.js b/src/react-components/auth/VerifyModal.stories.js new file mode 100644 index 000000000..25d811e1b --- /dev/null +++ b/src/react-components/auth/VerifyModal.stories.js @@ -0,0 +1,51 @@ +import React from "react"; +import { Center } from "../layout/Center"; +import { Page } from "../layout/Page"; +import { VerifyModal, EmailVerifying, EmailVerified, VerificationError } from "./VerifyModal"; +import backgroundUrl from "../../assets/images/home-hero-background-unbranded.png"; + +export default { + title: "VerifyModal" +}; + +export const Verifying = () => ( + +
+ + + +
+
+); + +Verifying.parameters = { + layout: "fullscreen" +}; + +export const Verified = () => ( + +
+ + + +
+
+); + +Verified.parameters = { + layout: "fullscreen" +}; + +export const Error = () => ( + +
+ + + +
+
+); + +Error.parameters = { + layout: "fullscreen" +}; diff --git a/src/react-components/auth/VerifyModalContainer.js b/src/react-components/auth/VerifyModalContainer.js new file mode 100644 index 000000000..be7430c75 --- /dev/null +++ b/src/react-components/auth/VerifyModalContainer.js @@ -0,0 +1,61 @@ +import React, { useState, useContext, useEffect } from "react"; +import { AuthContext } from "./AuthContext"; +import { VerifyModal, VerificationError, EmailVerified, EmailVerifying } from "./VerifyModal"; + +const VerificationStep = { + verifying: "verifying", + complete: "complete", + error: "error" +}; + +function useVerify() { + const [step, setStep] = useState(VerificationStep.verifying); + const [error, setError] = useState(); + const { verify } = useContext(AuthContext); + + useEffect( + () => { + const verifyAsync = async () => { + try { + const qs = new URLSearchParams(location.search); + + const authParams = { + topic: qs.get("auth_topic"), + token: qs.get("auth_token"), + origin: qs.get("auth_origin"), + payload: qs.get("auth_payload") + }; + + await verify(authParams); + setStep(VerificationStep.complete); + } catch (error) { + setStep(VerificationStep.error); + setError(error); + } + }; + + verifyAsync(); + }, + [verify] + ); + + return { step, error }; +} + +export function VerifyModalContainer() { + const { step, error } = useVerify(); + + let content; + + if (step === VerificationStep.error) { + content = ; + } else if (step === VerificationStep.complete) { + const qs = new URLSearchParams(location.search); + const origin = qs.get("auth_origin"); + content = ; + } else { + content = ; + } + + return {content}; +} diff --git a/src/react-components/auth/VerifyPage.js b/src/react-components/auth/VerifyPage.js deleted file mode 100644 index 5355235f6..000000000 --- a/src/react-components/auth/VerifyPage.js +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useState, useContext, useEffect } from "react"; -import PropTypes from "prop-types"; -import { Page } from "../layout/Page"; -import styles from "./SignInPage.scss"; -import { Loader } from "../misc/Loader"; -import { AuthContext } from "../auth/AuthContext"; -import configs from "../../utils/configs"; - -const VerificationStep = { - verifying: "verifying", - complete: "complete", - error: "error" -}; - -function useVerify() { - const [step, setStep] = useState(VerificationStep.verifying); - const [error, setError] = useState(); - const auth = useContext(AuthContext); - - useEffect(() => { - const verifyAsync = async () => { - try { - const qs = new URLSearchParams(location.search); - - const authParams = { - topic: qs.get("auth_topic"), - token: qs.get("auth_token"), - origin: qs.get("auth_origin"), - payload: qs.get("auth_payload") - }; - - await auth.verify(authParams); - setStep(VerificationStep.complete); - } catch (error) { - setStep(VerificationStep.error); - setError(error); - } - }; - - verifyAsync(); - }, []); - - return { step, error }; -} - -function EmailVerifying() { - return ( -
-

Email Verifying

- -
- ); -} - -function EmailVerified() { - const qs = new URLSearchParams(location.search); - const origin = qs.get("auth_origin"); - - return ( -
-

Verification Complete

- Please close this browser window and return to {origin}. -
- ); -} - -function VerificationError({ error }) { - return ( -
-

Error Verifying Email

- {(error && error.message) || "Unknown Error"} -
- ); -} - -VerificationError.propTypes = { - error: PropTypes.object -}; - -export function VerifyPage() { - const { step, error } = useVerify(); - - let content; - - if (step === VerificationStep.error) { - content = ; - } else if (step === VerificationStep.complete) { - content = ; - } else { - content = ; - } - - return ( - {content} - ); -} diff --git a/src/react-components/avatar-editor.js b/src/react-components/avatar-editor.js index c7400ba4f..91e8f5700 100644 --- a/src/react-components/avatar-editor.js +++ b/src/react-components/avatar-editor.js @@ -45,10 +45,8 @@ const fetchAvatar = async avatarId => { export default class AvatarEditor extends Component { static propTypes = { avatarId: PropTypes.string, - onSignIn: PropTypes.func, onSave: PropTypes.func, onClose: PropTypes.func, - signedIn: PropTypes.bool, hideDelete: PropTypes.bool, debug: PropTypes.bool, className: PropTypes.string @@ -453,7 +451,7 @@ export default class AvatarEditor extends Component {
- ) : this.props.signedIn ? ( + ) : (
{this.textField("name", "Name", false, true)}
@@ -580,10 +578,6 @@ export default class AvatarEditor extends Component {
)}
- ) : ( - - - )}
); diff --git a/src/react-components/home/HomePage.js b/src/react-components/home/HomePage.js index 436424921..ae1559cba 100644 --- a/src/react-components/home/HomePage.js +++ b/src/react-components/home/HomePage.js @@ -3,7 +3,6 @@ import { FormattedMessage } from "react-intl"; import classNames from "classnames"; import configs from "../../utils/configs"; import IfFeature from "../if-feature"; -import { Page } from "../layout/Page"; import { CreateRoomButton } from "./CreateRoomButton"; import { PWAButton } from "./PWAButton"; import { useFavoriteRooms } from "./useFavoriteRooms"; @@ -14,6 +13,7 @@ import { AuthContext } from "../auth/AuthContext"; import { createAndRedirectToNewHub } from "../../utils/phoenix-utils"; import { MediaGrid } from "./MediaGrid"; import { RoomTile } from "./RoomTile"; +import { PageContainer } from "../layout/PageContainer"; export function HomePage() { const auth = useContext(AuthContext); @@ -57,7 +57,7 @@ export function HomePage() { }); return ( - +
@@ -129,6 +129,6 @@ export function HomePage() {
-
+ ); } diff --git a/src/react-components/layout/Center.js b/src/react-components/layout/Center.js new file mode 100644 index 000000000..a17439132 --- /dev/null +++ b/src/react-components/layout/Center.js @@ -0,0 +1,17 @@ +import React from "react"; +import PropTypes from "prop-types"; +import classNames from "classnames"; +import styles from "./Center.scss"; + +export function Center({ children, className, ...rest }) { + return ( +
+ {children} +
+ ); +} + +Center.propTypes = { + children: PropTypes.node, + className: PropTypes.string +}; diff --git a/src/react-components/layout/Center.scss b/src/react-components/layout/Center.scss new file mode 100644 index 000000000..d77ffebc1 --- /dev/null +++ b/src/react-components/layout/Center.scss @@ -0,0 +1,6 @@ +:local(.center) { + display: flex; + justify-content: center; + align-items: center; + flex: 1; +} \ No newline at end of file diff --git a/src/react-components/layout/Footer.js b/src/react-components/layout/Footer.js index 445afc4b6..ac35a319f 100644 --- a/src/react-components/layout/Footer.js +++ b/src/react-components/layout/Footer.js @@ -1,65 +1,73 @@ import React from "react"; +import PropTypes from "prop-types"; import { FormattedMessage } from "react-intl"; -import { WrappedIntlProvider } from "../wrapped-intl-provider"; -import IfFeature from "../if-feature"; -import UnlessFeature from "../unless-feature"; -import configs from "../../utils/configs"; import styles from "./Footer.scss"; -export function Footer() { +export function Footer({ + hidePoweredBy, + showWhatsNewLink, + showTerms, + termsUrl, + showPrivacy, + privacyUrl, + showCompanyLogo, + companyLogoUrl +}) { return ( - -
-
- +
+
+ {!hidePoweredBy && ( + <> - -
- -
- + + )} +
+ +
); } + +Footer.propTypes = { + hidePoweredBy: PropTypes.bool, + showWhatsNewLink: PropTypes.bool, + showTerms: PropTypes.bool, + termsUrl: PropTypes.string, + showPrivacy: PropTypes.bool, + privacyUrl: PropTypes.string, + showCompanyLogo: PropTypes.bool, + companyLogoUrl: PropTypes.string +}; diff --git a/src/react-components/layout/Header.js b/src/react-components/layout/Header.js index fd8cb8f6e..b49906ac6 100644 --- a/src/react-components/layout/Header.js +++ b/src/react-components/layout/Header.js @@ -1,90 +1,109 @@ -import React, { useContext } from "react"; +import React from "react"; +import PropTypes from "prop-types"; import { FormattedMessage } from "react-intl"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faCog } from "@fortawesome/free-solid-svg-icons/faCog"; -import IfFeature from "../if-feature"; -import configs from "../../utils/configs"; import maskEmail from "../../utils/mask-email"; import styles from "./Header.scss"; -import { AuthContext } from "../auth/AuthContext"; -import { WrappedIntlProvider } from "../wrapped-intl-provider"; - -export function Header() { - const auth = useContext(AuthContext); +export function Header({ + showCloud, + enableSpoke, + showDocsLink, + docsUrl, + showSourceLink, + showCommunityLink, + communityUrl, + isAdmin, + isSignedIn, + email, + onSignOut +}) { return ( - -
- +
+ {isSignedIn ? ( +
+ + {maskEmail(email)} + {" "} + + + +
+ ) : ( + + + + )} +
+
); } + +Header.propTypes = { + showCloud: PropTypes.bool, + enableSpoke: PropTypes.bool, + showDocsLink: PropTypes.bool, + docsUrl: PropTypes.string, + showSourceLink: PropTypes.bool, + showCommunityLink: PropTypes.bool, + communityUrl: PropTypes.string, + isAdmin: PropTypes.bool, + isSignedIn: PropTypes.bool, + email: PropTypes.string, + onSignOut: PropTypes.func +}; diff --git a/src/react-components/layout/Page.js b/src/react-components/layout/Page.js index 1ca0e2c94..bc460e7f9 100644 --- a/src/react-components/layout/Page.js +++ b/src/react-components/layout/Page.js @@ -4,16 +4,78 @@ import "./Page.scss"; import { Header } from "./Header"; import { Footer } from "./Footer"; -export function Page({ children, ...rest }) { +export function Page({ + showCloud, + enableSpoke, + showDocsLink, + docsUrl, + showSourceLink, + showCommunityLink, + communityUrl, + isAdmin, + isSignedIn, + email, + onSignOut, + hidePoweredBy, + showWhatsNewLink, + showTerms, + termsUrl, + showPrivacy, + privacyUrl, + showCompanyLogo, + companyLogoUrl, + children, + ...rest +}) { return ( <> -
+
{children}
-