diff --git a/src/ui/EditorContainer.js b/src/ui/EditorContainer.js index c100282ed..865a55fc2 100644 --- a/src/ui/EditorContainer.js +++ b/src/ui/EditorContainer.js @@ -42,6 +42,7 @@ import defaultTemplateUrl from "./../assets/templates/crater.spoke"; import tutorialTemplateUrl from "./../assets/templates/tutorial.spoke"; import { TERMS, PRIVACY } from "../constants"; +import NotificationDialog from "./dialogs/NotificationDialog"; const StyledEditorContainer = styled.div` display: flex; @@ -104,23 +105,43 @@ class EditorContainer extends Component { const projectId = match.params.projectId; const queryParams = new URLSearchParams(location.search); - if (projectId === "new") { - if (queryParams.has("template")) { - this.loadProjectTemplate(queryParams.get("template")); - } else if (queryParams.has("sceneId")) { - this.loadScene(queryParams.get("sceneId")); + const load = () => { + if (projectId === "new") { + if (queryParams.has("template")) { + this.loadProjectTemplate(queryParams.get("template")); + } else if (queryParams.has("sceneId")) { + this.loadScene(queryParams.get("sceneId")); + } else { + this.loadProjectTemplate(defaultTemplateUrl); + } + } else if (projectId === "tutorial") { + this.loadProjectTemplate(tutorialTemplateUrl, true); } else { - this.loadProjectTemplate(defaultTemplateUrl); + this.loadProject(projectId); } - } else if (projectId === "tutorial") { - this.loadProjectTemplate(tutorialTemplateUrl, true); - } else { - this.loadProject(projectId); - } - if (projectId === "tutorial") { - trackEvent("Tutorial Start"); - this.setState({ onboardingContext: { enabled: true } }); + if (projectId === "tutorial") { + trackEvent("Tutorial Start"); + this.setState({ onboardingContext: { enabled: true } }); + } + }; + + const features = { + show_global_notification: true, + global_notification_body: "COPY HERE", + global_notification_link: "https://mozilla.org" + }; + if (features["show_global_notification"]) { + this.showDialog(NotificationDialog, { + title: "Admin notification", + message: features["global_notification_body"], + link: features["global_notification_link"], + onClosed: load, + onConfirm: load, + onCancel: null + }); + } else { + load(); } } diff --git a/src/ui/dialogs/NotificationDialog.js b/src/ui/dialogs/NotificationDialog.js new file mode 100644 index 000000000..568d25f0f --- /dev/null +++ b/src/ui/dialogs/NotificationDialog.js @@ -0,0 +1,56 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import Dialog, { DialogContent } from "./Dialog"; +import styled from "styled-components"; +import { Button } from "../inputs/Button"; + +const NotificationDialogContainer = styled(Dialog)` + max-width: 600px; + + ${DialogContent} { + padding: 0; + } +`; + +const NotificationMessage = styled.code` + white-space: pre-wrap; + overflow-wrap: break-word; + overflow-x: hidden; + overflow-y: auto; + padding: 16px; + color: ${props => props.theme.red}; +`; + +export default class NotificationDialog extends Component { + componentDidMount() {} + + openLink = () => { + window.open(this.props.link); + this.props.onClosed(); + }; + + renderBottomNav() { + return this.props.link ? : null; + } + + render() { + const { message, onClosed, ...props } = this.props; + + return ( + + {message} + + ); + } +} + +NotificationDialog.propTypes = { + title: PropTypes.string.isRequired, + message: PropTypes.string.isRequired, + link: PropTypes.string, + onClosed: PropTypes.func +}; + +NotificationDialog.defaultProps = { + title: "Notification" +}; diff --git a/src/ui/layout/Notification.js b/src/ui/layout/Notification.js new file mode 100644 index 000000000..3ea42b7ab --- /dev/null +++ b/src/ui/layout/Notification.js @@ -0,0 +1,60 @@ +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import styled from "styled-components"; + +const NotificationContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; +`; + +const StyledNotification = styled.div` + min-height: 24px; + margin: 1em 20px; + display: flex; + justify-content: center; + align-items: center; + font-size: 1.1em; + padding: 1em; + border-radius: 6px; + background-color: ${props => props.theme.red}; +`; + +const Content = styled.span` + max-width: 50vw; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +`; + +const ViewMore = styled.div` + text-align: center; + margin-left: 1em; + a { + color: ${props => props.theme.blue}; + } +`; + +export default class Notification extends Component { + render() { + const { body, link } = this.props; + return ( + + + {body} + + + Learn more + + + + + ); + } +} + +Notification.propTypes = { + body: PropTypes.string.isRequired, + link: PropTypes.string, + onClosed: PropTypes.func +}; diff --git a/src/ui/projects/ProjectsPage.js b/src/ui/projects/ProjectsPage.js index 375bb6b0d..173838d76 100644 --- a/src/ui/projects/ProjectsPage.js +++ b/src/ui/projects/ProjectsPage.js @@ -18,6 +18,7 @@ import { Link } from "react-router-dom"; import LatestUpdate from "../whats-new/LatestUpdate"; import { connectMenu, ContextMenu, MenuItem } from "../layout/ContextMenu"; import styled from "styled-components"; +import Notification from "../layout/Notification"; export const ProjectsSection = styled.section` padding-bottom: 100px; @@ -87,7 +88,10 @@ class ProjectsPage extends Component { scenes: [], loading: isAuthenticated, isAuthenticated, - error: null + error: null, + showGlobalNotification: false, + globalNotificationBody: null, + globalNotificationLink: null }; } @@ -122,6 +126,12 @@ class ProjectsPage extends Component { this.setState({ error, loading: false }); }); } + + this.setState({ + showGlobalNotification: true, + globalNotificationBody: "COPY HERE", + globalNotificationLink: "https://mozilla.org" + }); } onDeleteProject = project => { @@ -172,6 +182,9 @@ class ProjectsPage extends Component {

Projects

+ {this.state.showGlobalNotification && ( + + )}