diff --git a/app/assets/images/pages/play/modal/subscribe-background.png b/app/assets/images/pages/play/modal/subscribe-background.png new file mode 100644 index 000000000..a497a024c Binary files /dev/null and b/app/assets/images/pages/play/modal/subscribe-background.png differ diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 4f24d6583..3a010d364 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -332,6 +332,11 @@ prompt_body: "Do you want to get more?" prompt_button: "Enter Shop" + subscribe: + subscribe_title: "Subscribe" + subscribe_button: "$9.99/mo - Subscribe" + stripe_description: "Monthly Subscription" + choose_hero: choose_hero: "Choose Your Hero" programming_language: "Programming Language" diff --git a/app/styles/play/level/tome/cast_button.sass b/app/styles/play/level/tome/cast_button.sass index 4ecd74768..4f442e660 100644 --- a/app/styles/play/level/tome/cast_button.sass +++ b/app/styles/play/level/tome/cast_button.sass @@ -115,7 +115,7 @@ html.no-borderimage #cast-button-view border: 0 &.submit-button, &.done-button - background-image: url(/images/level/code_toolbar_submit_button_active_pressed.png) + background-image: url(/images/level/code_toolbar_submit_button_active.png) border: 0 &:active diff --git a/app/styles/play/modal/buy-gems-modal.sass b/app/styles/play/modal/buy-gems-modal.sass index e147d6041..7ffef0fa3 100644 --- a/app/styles/play/modal/buy-gems-modal.sass +++ b/app/styles/play/modal/buy-gems-modal.sass @@ -22,7 +22,6 @@ top: 242px width: 720px height: 140px - index: 1 .product width: 228px diff --git a/app/styles/play/modal/play-heroes-modal.sass b/app/styles/play/modal/play-heroes-modal.sass index fcbef62b4..c9d9ff770 100644 --- a/app/styles/play/modal/play-heroes-modal.sass +++ b/app/styles/play/modal/play-heroes-modal.sass @@ -28,7 +28,7 @@ $heroCanvasHeight: 265px left: 154px top: 25px margin: 0 - width: 350px + width: 450px text-align: center color: rgb(254,188,68) font-size: 38px diff --git a/app/styles/play/modal/subscribe-modal.sass b/app/styles/play/modal/subscribe-modal.sass new file mode 100644 index 000000000..43c6f83de --- /dev/null +++ b/app/styles/play/modal/subscribe-modal.sass @@ -0,0 +1,139 @@ +@import "app/styles/mixins" +@import "app/styles/bootstrap/variables" + +#subscribe-modal + + //- Clear modal defaults + .modal-dialog + margin: 60px auto 0 auto + padding: 0 + width: 746px + height: 520px + background: none + + + //- Background + #subscribe-background + position: absolute + top: -61px + left: 0px + + + //- Header + h1 + position: absolute + left: 150px + top: 25px + margin: 0 + width: 410px + text-align: center + color: rgb(254,188,68) + font-size: 38px + text-shadow: black 4px 4px 0, black -4px -4px 0, black 4px -4px 0, black -4px 4px 0, black 4px 0px 0, black 0px -4px 0, black -4px 0px 0, black 0px 4px 0, black 6px 6px 6px + font-variant: normal + text-transform: uppercase + + + //- Close modal button + + #close-modal + position: absolute + left: 568px + top: 17px + width: 60px + height: 60px + color: white + text-align: center + font-size: 30px + padding-top: 15px + cursor: pointer + @include rotate(-3deg) + + &:hover + color: yellow + + + //- Selling points + + #selling-points + position: absolute + left: 55px + top: 142px + width: 681px + height: 140px + + .point + width: 150px + height: 256px + overflow: none + float: left + text-align: center + margin-right: 10px + background-color: rgba(156, 204, 10, 0.2) + + h4 + font-size: 20px + color: rgb(22,16,5) + + h3 + margin-top: 10px + text-transform: uppercase + color: rgb(22,16,5) + + button + width: 80% + + + //- Purchase button + + .purchase-button + position: absolute + left: 73px + width: 600px + height: 70px + top: 430px + font-size: 32px + line-height: 42px + border-image: url(/images/level/code_toolbar_submit_button_active.png) 14 20 20 20 fill round + border-width: 14px 20px 20px 20px + color: darken(white, 5%) + + span + pointer-events: none + + &:hover + border-image: url(/images/level/code_toolbar_submit_button_zazz.png) 14 20 20 20 fill round + color: white + + &:active + border-image: url(/images/level/code_toolbar_submit_button_zazz_pressed.png) 14 20 20 20 fill round + padding: 2px 0 0 2px + color: white + + + //- Errors + + .alert + position: absolute + left: 10% + width: 80% + top: 20px + border: 5px solid gray + + +html.no-borderimage #subscribe-modal + .purchase-button + border: 0 + background-image: url(/images/level/code_toolbar_submit_button_active.png) + background-size: 100% 100% + padding: 7px 10px 10px 10px + + &:hover + background-image: url(/images/level/code_toolbar_submit_button_zazz.png) + border: 0 + + &:active + background-image: url(/images/level/code_toolbar_submit_button_zazz_pressed.png) + padding: 9px 8px 8px 12px + border: 0 + diff --git a/app/templates/play/modal/subscribe-modal.jade b/app/templates/play/modal/subscribe-modal.jade new file mode 100644 index 000000000..ec7667223 --- /dev/null +++ b/app/templates/play/modal/subscribe-modal.jade @@ -0,0 +1,41 @@ +.modal-dialog + .modal-content + if state === 'purchasing' + .alert.alert-info(data-i18n="buy_gems.purchasing") + + else if state === 'retrying' + #retrying-alert.alert.alert-danger(data-i18n="buy_gems.retrying") + + else + img(src="/images/pages/play/modal/subscribe-background.png")#subscribe-background + + h1(data-i18n="subscribe.subscribe_title") Subscribe + + div#close-modal + span.glyphicon.glyphicon-remove + + #selling-points + #point-levels.point + .blurb 25 more levels. 5 new levels every week. + #point-heroes.point + .blurb 13 more heroes to unleash on the ogre hordes. + #point-items.point + .blurb 275 items to unlock and explore. + #point-gems.point + .blurb Subscribers get 3500 bonus gems per month. + + button.btn.btn-lg.btn-illustrated.purchase-button(data-i18n="subscribe.subscribe_button") + span $9.99/mo - Subscribe + + if state === 'declined' + #declined-alert.alert.alert-danger.alert-dismissible + span(data-i18n="buy_gems.declined") + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + + if state === 'unknown_error' + #error-alert.alert.alert-danger.alert-dismissible + button.close(type="button" data-dismiss="alert") + span(aria-hidden="true") × + p(data-i18n="loading_error.unknown") + p= stateMessage diff --git a/app/views/play/WorldMapView.coffee b/app/views/play/WorldMapView.coffee index c53acde7e..36ed12f8c 100644 --- a/app/views/play/WorldMapView.coffee +++ b/app/views/play/WorldMapView.coffee @@ -9,6 +9,7 @@ ThangType = require 'models/ThangType' MusicPlayer = require 'lib/surface/MusicPlayer' storage = require 'core/storage' AuthModal = require 'views/core/AuthModal' +SubscribeModal = require 'views/play/modal/SubscribeModal' trackedHourOfCode = false @@ -77,6 +78,8 @@ module.exports = class WorldMapView extends RootView $('body').append($('')) trackedHourOfCode = true + @requiresSubscription = me.isAdmin() and @terrain isnt 'dungeon' and not me.get('stripe')?.subscriptionID + destroy: -> @setupManager?.destroy() $(window).off 'resize', @onWindowResize @@ -112,6 +115,8 @@ module.exports = class WorldMapView extends RootView @fullyRendered = true @render() @preloadTopHeroes() unless me.get('heroConfig')?.thangType + if @requiresSubscription + _.delay (=> @openModalView? new SubscribeModal() unless window.currentModal), 2000 getRenderData: (context={}) -> context = super(context) @@ -145,7 +150,7 @@ module.exports = class WorldMapView extends RootView @$el.find('.level').tooltip() @$el.addClass _.string.slugify @terrain @updateVolume() - unless window.currentModal or not @fullyRendered + unless window.currentModal or not @fullyRendered or @requiresSubscription @highlightElement '.level.next', delay: 500, duration: 60000, rotation: 0, sides: ['top'] if levelID = @$el.find('.level.next').data('level-id') @$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show() @@ -182,20 +187,23 @@ module.exports = class WorldMapView extends RootView e.preventDefault() e.stopPropagation() @$levelInfo?.hide() + levelElement = $(e.target).parents('.level') + levelID = levelElement.data('level-id') + campaign = _.find campaigns, id: @terrain + level = _.find campaign.levels, id: levelID if application.isIPadApp - levelID = $(e.target).parents('.level').data('level-id') @$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show() @adjustLevelInfoPosition e @endHighlight() else - if $(e.target).attr('disabled') + if @requiresSubscription + @openModalView new SubscribeModal() + else if $(e.target).attr('disabled') Backbone.Mediator.publish 'router:navigate', route: '/contribute/adventurer' return else if $(e.target).parent().hasClass 'locked' return else - levelElement = $(e.target).parents('.level') - levelID = levelElement.data('level-id') @startLevel levelElement window.tracker?.trackEvent 'Clicked Level', category: 'World Map', levelID: levelID, ['Google Analytics'] diff --git a/app/views/play/modal/BuyGemsModal.coffee b/app/views/play/modal/BuyGemsModal.coffee index 38f298a20..a852d5649 100644 --- a/app/views/play/modal/BuyGemsModal.coffee +++ b/app/views/play/modal/BuyGemsModal.coffee @@ -57,7 +57,7 @@ module.exports = class BuyGemsModal extends ModalView Backbone.Mediator.publish 'buy-gems-modal:purchase-initiated', { productID: productID } else - application.tracker?.trackEvent 'Started purchase', {productID:productID}, ['Google Analytics'] + application.tracker?.trackEvent 'Started purchase', { productID: productID } stripeHandler.open({ description: $.t(product.i18n) amount: product.amount diff --git a/app/views/play/modal/SubscribeModal.coffee b/app/views/play/modal/SubscribeModal.coffee new file mode 100644 index 000000000..75a4055e7 --- /dev/null +++ b/app/views/play/modal/SubscribeModal.coffee @@ -0,0 +1,67 @@ +ModalView = require 'views/core/ModalView' +template = require 'templates/play/modal/subscribe-modal' +stripeHandler = require 'core/services/stripe' +utils = require 'core/utils' + +module.exports = class SubscribeModal extends ModalView + id: 'subscribe-modal' + template: template + plain: true + closesOnClickOutside: false + product: + amount: 999 + id: 'basic_subscription' + + subscriptions: + 'stripe:received-token': 'onStripeReceivedToken' + + events: + 'click .purchase-button': 'onClickPurchaseButton' + 'click #close-modal': 'hide' + + constructor: (options) -> + super(options) + @state = 'standby' + + getRenderData: -> + c = super() + c.state = @state + c.stateMessage = @stateMessage + return c + + onClickPurchaseButton: (e) -> + @playSound 'menu-button-click' + application.tracker?.trackEvent 'Started subscription purchase', {} + stripeHandler.open({ + description: $.t 'subscribe.stripe_description' + amount: @product.amount + }) + + onStripeReceivedToken: (e) -> + @timestampForPurchase = new Date().getTime() + data = { + productID: @product.id + stripe: { + token: e.token.id + timestamp: @timestampForPurchase + } + } + @state = 'purchasing' + @render() + jqxhr = $.post('/db/payment', data) + jqxhr.done(=> + document.location.reload() + ) + jqxhr.fail(=> + if jqxhr.status is 402 + @state = 'declined' + @stateMessage = arguments[2] + else if jqxhr.status is 500 + @state = 'retrying' + f = _.bind @onStripeReceivedToken, @, e + _.delay f, 2000 + else + @state = 'unknown_error' + @stateMessage = "#{jqxhr.status}: #{jqxhr.responseText}" + @render() + )