From b932761e4ddc5c1b2b4c688b3503916752b4bb58 Mon Sep 17 00:00:00 2001 From: Barry Chen Date: Wed, 19 Sep 2018 18:16:27 -0500 Subject: [PATCH] Add favorite shot promo card. (#4878) --- locales/en-US/server.ftl | 11 ++++-- server/src/pages/shot/controller.js | 8 ++--- server/src/pages/shot/model.js | 14 ++++++++ server/src/pages/shot/promo-dialog.js | 23 +++++-------- server/src/pages/shot/shotpage-header.js | 20 +++++++++++ server/src/pages/shot/view.js | 43 +++++++++++++++++++----- static/css/frame.scss | 6 ++++ 7 files changed, 95 insertions(+), 30 deletions(-) diff --git a/locales/en-US/server.ftl b/locales/en-US/server.ftl index 2d02cd46f1..2681853d7f 100644 --- a/locales/en-US/server.ftl +++ b/locales/en-US/server.ftl @@ -199,12 +199,17 @@ errorThirdPartyCookiesEnabled = If you took this shot and cannot delete it, you ## Shot Page New Feature Promotion Dialog. # Note: If possible, choose a short translation to better fit into the card. -promoTitle = Take Note! -promoMessage = Updated editing tools let you crop, highlight, and even add text to your shot. -promoLink = Give them a try promoCloseButton = .title = Close notification +editorPromoTitle = Take Note! +editorPromoMessage = Updated editing tools let you crop, highlight, and even add text to your shot. +editorPromoCallToAction = Give them a try + +favShotsPromoTitle = Favorite Shots! +favShotsPromoMessage = You can now sign in with Firefox Account to favorite your shots and save forever. +favShotPromoCallToAction = Give it a try! + ## Annotations annotationPenButton = diff --git a/server/src/pages/shot/controller.js b/server/src/pages/shot/controller.js index 0335a679cf..68e27a6c5b 100644 --- a/server/src/pages/shot/controller.js +++ b/server/src/pages/shot/controller.js @@ -25,12 +25,12 @@ function shouldShowPromo(model) { return false; } let show = false; - const count = localStorage.hasSeenPromoDialog; + const count = localStorage.hasSeenEditorPromoDialog; if (!count) { - localStorage.hasSeenPromoDialog = 1; + localStorage.hasSeenEditorPromoDialog = 1; show = true; } else if (count < 3) { - localStorage.hasSeenPromoDialog = parseInt(count, 10) + 1; + localStorage.hasSeenEditorPromoDialog = parseInt(count, 10) + 1; show = true; } return show; @@ -77,7 +77,7 @@ exports.launch = function(data) { } } model.highlightEditButton = shouldHighlightEditIcon(model); - model.promoDialog = shouldShowPromo(model); + model.showEditorPromoDialog = shouldShowPromo(model); if (firstSet) { refreshHash(); } diff --git a/server/src/pages/shot/model.js b/server/src/pages/shot/model.js index 6b132918e6..17753f0b4c 100644 --- a/server/src/pages/shot/model.js +++ b/server/src/pages/shot/model.js @@ -13,6 +13,18 @@ exports.createModel = function(req) { const title = req.getText("shotPageTitle", {originalTitle: req.shot.title}); const enableAnnotations = req.config.enableAnnotations; const isFxaAuthenticated = req.accountId && req.accountId === req.shot.accountId; + const promos = { + editor: { + title: req.getText("editorPromoTitle"), + message: req.getText("editorPromoMessage"), + callToAction: req.getText("editorPromoCallToAction"), + }, + favShot: { + title: req.getText("favShotsPromoTitle"), + message: req.getText("favShotsPromoMessage"), + callToAction: req.getText("favShotPromoCallToAction"), + }, + }; const serverPayload = { title, staticLink: req.staticLink, @@ -42,6 +54,7 @@ exports.createModel = function(req) { downloadUrl, isMobile, enableAnnotations, + promos, }; const clientPayload = { title, @@ -74,6 +87,7 @@ exports.createModel = function(req) { downloadUrl, isMobile, enableAnnotations, + promos, }; if (serverPayload.expireTime !== null && Date.now() > serverPayload.expireTime) { clientPayload.shot = { diff --git a/server/src/pages/shot/promo-dialog.js b/server/src/pages/shot/promo-dialog.js index 1ef34360b4..be0a2cc85c 100644 --- a/server/src/pages/shot/promo-dialog.js +++ b/server/src/pages/shot/promo-dialog.js @@ -1,7 +1,6 @@ const React = require("react"); const PropTypes = require("prop-types"); const { Localized } = require("fluent-react/compat"); -const sendEvent = require("../../browser-send-event.js"); exports.PromoDialog = class PromoDialog extends React.Component { constructor(props) { @@ -11,18 +10,12 @@ exports.PromoDialog = class PromoDialog extends React.Component { render() { if (this.props.display) { return
- - - - -

Take Note!

-
- -

- Updated editing tools let you crop, highlight, and even add text to your shot. -

-
-

Give them a try

+ +

{this.props.title}

+

+ {this.props.message} +

+

{this.props.callToAction}

; } return null; @@ -30,11 +23,13 @@ exports.PromoDialog = class PromoDialog extends React.Component { closePanel(event) { this.props.promoClose(); - sendEvent("promo-closed"); } }; exports.PromoDialog.propTypes = { display: PropTypes.bool, promoClose: PropTypes.func, + title: PropTypes.string, + message: PropTypes.string, + callToAction: PropTypes.string, }; diff --git a/server/src/pages/shot/shotpage-header.js b/server/src/pages/shot/shotpage-header.js index 87d0b3e539..52802d98bc 100644 --- a/server/src/pages/shot/shotpage-header.js +++ b/server/src/pages/shot/shotpage-header.js @@ -6,10 +6,20 @@ const sendEvent = require("../../browser-send-event.js"); const { SignInButton } = require("../../signin-button.js"); const { Header } = require("../../header.js"); const { TimeDiff } = require("./time-diff"); +const { PromoDialog } = require("./promo-dialog"); exports.ShotPageHeader = class ShotPageHeader extends React.Component { constructor(props) { super(props); + this.state = { + showPromo: false, + }; + } + + componentDidUpdate(prevProps) { + if (this.props.showPromo !== prevProps.showPromo) { + this.setState({showPromo: this.props.showPromo}); + } } renderMyShotsText() { @@ -90,10 +100,19 @@ exports.ShotPageHeader = class ShotPageHeader extends React.Component { } renderFxASignIn() { + let promo = null; + if (this.props.promo) { + promo = ; + } + return ( this.props.isOwner ?
+ {promo}
: null ); } @@ -121,6 +140,7 @@ exports.ShotPageHeader.propTypes = { shot: PropTypes.object, isFxaAuthenticated: PropTypes.bool, expireTime: PropTypes.number, + promo: PropTypes.object, }; class EditableTitle extends React.Component { diff --git a/server/src/pages/shot/view.js b/server/src/pages/shot/view.js index 2ecce9f2e8..cfe7e1360d 100644 --- a/server/src/pages/shot/view.js +++ b/server/src/pages/shot/view.js @@ -175,12 +175,13 @@ class Body extends React.Component { hidden: false, closeBanner: false, imageEditing: false, + showFavShotPromo: false, }; } componentDidMount() { - this.setState({highlightEditButton: this.props.highlightEditButton || this.props.promoDialog}); - this.setState({promoDialog: this.props.promoDialog}); + this.setState({highlightEditButton: this.props.highlightEditButton || this.props.showEditorPromoDialog}); + this.setState({showEditorPromoDialog: this.props.showEditorPromoDialog}); } doCloseBanner() { @@ -341,10 +342,10 @@ class Body extends React.Component { let editButton = null; const highlight = this.state.highlightEditButton ?
: null; const activeFavClass = this.props.expireTime ? "" : "is-fav"; - const shouldShow = this.props.isFxaAuthenticated ? "" : "hidden"; favoriteShotButton =
- - + { highlight }
: null; } @@ -403,7 +407,10 @@ class Body extends React.Component { { renderGetFirefox ? this.renderFirefoxRequired() : null }
+ shot={this.props.shot} expireTime={this.props.expireTime} + promo={this.props.promos.favShot} + showPromo={this.state.showFavShotPromo} + closePromoCallback={this.closeFavShotPromo.bind(this)}> { favoriteShotButton } { editButton } { downloadButton } @@ -419,10 +426,15 @@ class Body extends React.Component { ); } - promoClose() { - this.setState({promoDialog: false}); + closeEditorPromo() { + this.setState({showEditorPromoDialog: false}); // set counter to max to stop showing notification again localStorage.hasSeenPromoDialog = 3; + sendEvent("editor-promo-closed"); + } + + closeFavShotPromo() { + this.setState({showFavShotPromo: false}); } onMouseOverHighlight() { @@ -455,7 +467,7 @@ class Body extends React.Component { } // Close promo dialog if user clicked edit after seeing new edit tool promo if (this.props.promoDialog) { - this.promoClose(); + this.closeEditorPromo(); } } @@ -490,6 +502,14 @@ class Body extends React.Component { } onClickFavorite() { + if (!this.props.isFxaAuthenticated) { + this.setState({ + showEditorPromoDialog: false, + showFavShotPromo: true, + }); + return; + } + if (this.props.expireTime) { sendEvent("set-favorite", "navbar"); const INDEFINITE = 0; @@ -500,6 +520,11 @@ class Body extends React.Component { } } + onCloseFavShotPromo() { + this.setState({showFavShotPromo: false}); + sendEvent("fav-shot-promo-closed"); + } + onClickDownload() { sendEvent("download", "navbar"); location.href = this.props.downloadUrl; diff --git a/static/css/frame.scss b/static/css/frame.scss index b942fffdf0..4b0975076b 100644 --- a/static/css/frame.scss +++ b/static/css/frame.scss @@ -195,6 +195,12 @@ } } +.shot-fxa-signin { + .promo-panel { + right: 10px; + } +} + .delete-confirmation-dialog { &.right-align { top: 88px;