diff --git a/app/schemas/subscriptions/tome.coffee b/app/schemas/subscriptions/tome.coffee index d5e061eb5..035961159 100644 --- a/app/schemas/subscriptions/tome.coffee +++ b/app/schemas/subscriptions/tome.coffee @@ -126,3 +126,7 @@ module.exports = 'tome:winnability-updated': c.object {title: 'Winnability Updated', description: 'When we think we can now win (or can no longer win), we may want to emphasize the submit button versus the run button (or vice versa), so this fires when we get new goal states (even preloaded goal states) suggesting success or failure change.', required: ['winnable']}, winnable: {type: 'boolean'} + + 'tome:show-problem-alert': c.object {title: 'Show Problem Alert', description: 'A problem alert needs to be shown.', required: ['problem']}, + problem: {type: 'object'} + lineOffsetPx: {type: ['number', 'undefined']} diff --git a/app/styles/play/level/tome/problem_alert.sass b/app/styles/play/level/tome/problem_alert.sass index 2ffb62181..42c7be399 100644 --- a/app/styles/play/level/tome/problem_alert.sass +++ b/app/styles/play/level/tome/problem_alert.sass @@ -1,16 +1,17 @@ @import "app/styles/mixins" @import "app/styles/bootstrap/variables" -.problem-alert + +#problem-alert-view.problem-alert z-index: 10 position: absolute // Position these at the end of the spell editor, right above the spell toolbar. - bottom: -20px - left: 10px - right: 10px + top: 45px + right: 500px background: transparent border: 1px solid transparent padding: 0 + font-size: 18px text-shadow: none color: white word-wrap: break-word diff --git a/app/templates/play/level.jade b/app/templates/play/level.jade index 6396233dc..b012ac1a8 100644 --- a/app/templates/play/level.jade +++ b/app/templates/play/level.jade @@ -20,6 +20,8 @@ #gold-view + #problem-alert-view + #level-chat-view #multiplayer-status-view diff --git a/app/templates/play/level/tome/problem_alert.jade b/app/templates/play/level/tome/problem_alert.jade index 25df2198a..0ca57fe69 100644 --- a/app/templates/play/level/tome/problem_alert.jade +++ b/app/templates/play/level/tome/problem_alert.jade @@ -1,4 +1,4 @@ -button.close(type="button", data-dismiss="alert") × +button.close(type="button") × if hint span.problem-title!= hint br diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index 1c31dfc64..84385a055 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -23,6 +23,7 @@ RealTimeCollection = require 'collections/RealTimeCollection' # subviews LevelLoadingView = require './LevelLoadingView' +ProblemAlertView = require './tome/ProblemAlertView' TomeView = require './tome/TomeView' ChatView = require './LevelChatView' HUDView = require './LevelHUDView' @@ -249,6 +250,7 @@ module.exports = class PlayLevelView extends RootView @insertSubView new ChatView levelID: @levelID, sessionID: @session.id, session: @session if @level.get('type') in ['ladder', 'hero-ladder'] @insertSubView new MultiplayerStatusView levelID: @levelID, session: @session, level: @level + @insertSubView new ProblemAlertView {} worldName = utils.i18n @level.attributes, 'name' @controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel} #_.delay (=> Backbone.Mediator.publish('level:set-debug', debug: true)), 5000 if @isIPadApp() # if me.displayName() is 'Nick' diff --git a/app/views/play/level/tome/Problem.coffee b/app/views/play/level/tome/Problem.coffee index 5c8e3d2e1..027cad84b 100644 --- a/app/views/play/level/tome/Problem.coffee +++ b/app/views/play/level/tome/Problem.coffee @@ -1,20 +1,14 @@ -ProblemAlertView = require './ProblemAlertView' Range = ace.require('ace/range').Range module.exports = class Problem annotation: null - alertView: null markerRange: null - constructor: (@aether, @aetherProblem, @ace, withAlert=false, isCast=false, @levelID) -> + constructor: (@aether, @aetherProblem, @ace, isCast=false, @levelID) -> @buildAnnotation() - @buildAlertView() if withAlert @buildMarkerRange() if isCast Backbone.Mediator.publish("problem:problem-created", line:@annotation.row, text: @annotation.text) if application.isIPadApp destroy: -> - unless @alertView?.destroyed - @alertView?.$el?.remove() - @alertView?.destroy() @removeMarkerRange() @userCodeProblem.off() if @userCodeProblem @@ -29,11 +23,6 @@ module.exports = class Problem text: text, type: @aetherProblem.level ? 'error' - buildAlertView: -> - @alertView = new ProblemAlertView problem: @ - @alertView.render() - $(@ace.container).append @alertView.el - buildMarkerRange: -> return unless @aetherProblem.range [start, end] = @aetherProblem.range diff --git a/app/views/play/level/tome/ProblemAlertView.coffee b/app/views/play/level/tome/ProblemAlertView.coffee index 7f43af83e..e19163df9 100644 --- a/app/views/play/level/tome/ProblemAlertView.coffee +++ b/app/views/play/level/tome/ProblemAlertView.coffee @@ -3,32 +3,68 @@ template = require 'templates/play/level/tome/problem_alert' {me} = require 'lib/auth' module.exports = class ProblemAlertView extends CocoView + id: 'problem-alert-view' className: 'problem-alert' template: template - subscriptions: {} + subscriptions: + 'tome:show-problem-alert': 'onShowProblemAlert' + 'tome:manual-cast': 'onHideProblemAlert' + 'real-time-multiplayer:manual-cast': 'onHideProblemAlert' events: 'click .close': 'onRemoveClicked' constructor: (options) -> super options - @problem = options.problem + if options.problem? + @problem = options.problem + @onWindowResize() + else + @$el.hide() + $(window).on 'resize', @onWindowResize + + destroy: -> + $(window).off 'resize', @onWindowResize + super() getRenderData: (context={}) -> context = super context - format = (s) -> s?.replace(//g, '>').replace(/\n/g, '
') - context.message = format @problem.aetherProblem.message - context.hint = format @problem.aetherProblem.hint + if @problem? + format = (s) -> s?.replace(//g, '>').replace(/\n/g, '
') + context.message = format @problem.aetherProblem.message + context.hint = format @problem.aetherProblem.hint context afterRender: -> super() - @$el.addClass('alert').addClass("alert-#{@problem.aetherProblem.level}").hide().fadeIn('slow') - @$el.addClass('no-hint') unless @problem.aetherProblem.hint - Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'error_appear', volume: 1.0 + if @problem? + @$el.addClass('alert').addClass("alert-#{@problem.aetherProblem.level}").hide().fadeIn('slow') + @$el.addClass('no-hint') unless @problem.aetherProblem.hint + Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'error_appear', volume: 1.0 + + onShowProblemAlert: (data) -> + return if @problem? and data.problem.aetherProblem.message is @problem.aetherProblem.message and data.problem.aetherProblem.hint is @problem.aetherProblem.hint + return unless $('#code-area').is(":visible") + @problem = data.problem + @lineOffsetPx = data.lineOffsetPx or 0 + @$el.show() + @onWindowResize() + @render() + + onHideProblemAlert: -> + @onRemoveClicked() onRemoveClicked: -> - @$el.remove() - @destroy() - #@problem.destroy() # let's try leaving the annotations / marker ranges alone + @$el.hide() + @problem = null + + onWindowResize: (e) => + # TODO: This all seems a little hacky + if @problem? + @$el.css('left', $('#goals-view').outerWidth(true) + 'px') + @$el.css('right', $('#code-area').outerWidth(true) + 'px') + + # 45px from top roughly aligns top of alert with top of first code line + # TODO: calculate this in a more dynamic, less sketchy way + @$el.css('top', (45 + @lineOffsetPx) + 'px') diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee index 17477be54..bd9ce0eb4 100644 --- a/app/views/play/level/tome/SpellView.coffee +++ b/app/views/play/level/tome/SpellView.coffee @@ -515,7 +515,13 @@ module.exports = class SpellView extends CocoView for aetherProblem, problemIndex in aether.getAllProblems() continue if key = aetherProblem.userInfo?.key and key of seenProblemKeys seenProblemKeys[key] = true if key - @problems.push problem = new Problem aether, aetherProblem, @ace, isCast and problemIndex is 0, isCast, @spell.levelID + @problems.push problem = new Problem aether, aetherProblem, @ace, isCast, @spell.levelID + if isCast and problemIndex is 0 + if problem.aetherProblem.range? + # Line height is 20px + # TODO: Can we get line height dynamically from ace? + lineOffsetPx = problem.aetherProblem.range[0].row * 20 - @ace.session.getScrollTop() + Backbone.Mediator.publish 'tome:show-problem-alert', problem: problem, lineOffsetPx: lineOffsetPx @saveUserCodeProblem(aether, aetherProblem) if isCast annotations.push problem.annotation if problem.annotation @aceSession.setAnnotations annotations