codecombat/app/views/play/level/tome/ProblemAlertView.coffee
Phoenix Eliot 663c220eaf Show wev-dev iFrame error messages like Aether's
This heavily refactors SpellView and adds infrastructure for receiving and reporting Errors raised by the web-dev iFrame. The web-dev error system, the Aether error system, and the Ace html-worker avoid disturbing each others' errors/annotations (though currently Aether+web-dev errors won't coexist), and they clear/update their own asynchronously.

Show web-dev iFrame errors as Ace annotations

Add functional error banners (with poor messages)

Improve error banners, don't allow duplicate Problems

Refactor setAnnotations override

Convert all constructor calls for Problems

Add comments, clean up

Clean up

Don't clear things unnecessarily

Clean up error message sending from iFrame

Add web-dev:error schema

Clarify error message attributes

Refactor displaying AetherProblems

Refactor displaying user problem banners

Refactor onWebDevError

Set ace styles on updating @problems

Clean up, fix off-by-1 error

Add comment

Show stale web-dev errors differently
Some web-dev errors are generated by "stale" code — code that's still running in the iFrame but doesn't have the player's recent changes.
This shows those errors differently than if they weren't "stale", and suggests they re-run their code.

Hook up web-dev event schema

Destroy ignored duplicate problems

Functionalize a bit of stuff

Fix ProblemAlertView never loading
2016-08-31 10:59:06 -07:00

107 lines
3.6 KiB
CoffeeScript

CocoView = require 'views/core/CocoView'
GameMenuModal = require 'views/play/menu/GameMenuModal'
template = require 'templates/play/level/tome/problem_alert'
{me} = require 'core/auth'
module.exports = class ProblemAlertView extends CocoView
id: 'problem-alert-view'
className: 'problem-alert'
template: template
subscriptions:
'tome:show-problem-alert': 'onShowProblemAlert'
'tome:hide-problem-alert': 'onHideProblemAlert'
'level:restart': 'onHideProblemAlert'
'tome:jiggle-problem-alert': 'onJiggleProblemAlert'
'tome:manual-cast': 'onHideProblemAlert'
events:
'click .close': 'onRemoveClicked'
'click': -> Backbone.Mediator.publish 'tome:focus-editor', {}
constructor: (options) ->
@supermodel = options.supermodel # Has to go before super so events are hooked up
super options
@level = options.level
@session = options.session
if options.problem?
@problem = options.problem
@onWindowResize()
else
@$el.hide()
$(window).on 'resize', @onWindowResize
destroy: ->
$(window).off 'resize', @onWindowResize
super()
afterRender: ->
super()
if @problem?
@$el.addClass('alert').addClass("alert-#{@problem.level}").hide().fadeIn('slow')
@$el.addClass('no-hint') unless @problem.hint
@playSound 'error_appear'
setProblemMessage: ->
if @problem?
format = (s) -> marked(s.replace(/</g, '&lt;').replace(/>/g, '&gt;')) if s?
message = @problem.message
# Add time to problem message if hint is for a missing null check
# NOTE: This may need to be updated with Aether error hint changes
if @problem.hint? and /(?:null|undefined)/.test @problem.hint
age = @problem.userInfo?.age
if age?
if /^Line \d+:/.test message
message = message.replace /^(Line \d+)/, "$1, time #{age.toFixed(1)}"
else
message = "Time #{age.toFixed(1)}: #{message}"
@message = format message
@hint = format @problem.hint
onShowProblemAlert: (data) ->
return unless $('#code-area').is(":visible")
if @problem?
if @$el.hasClass "alert-#{@problem.level}"
@$el.removeClass "alert-#{@problem.level}"
if @$el.hasClass "no-hint"
@$el.removeClass "no-hint"
@problem = data.problem
@lineOffsetPx = data.lineOffsetPx or 0
@$el.show()
@onWindowResize()
@setProblemMessage()
@render()
@onJiggleProblemAlert()
application.tracker?.trackEvent 'Show problem alert', {levelID: @level.get('slug'), ls: @session?.get('_id')}
onJiggleProblemAlert: ->
return unless @problem?
@$el.show() unless @$el.is(":visible")
@$el.addClass 'jiggling'
@playSound 'error_appear'
pauseJiggle = =>
@$el?.removeClass 'jiggling'
_.delay pauseJiggle, 1000
onHideProblemAlert: ->
return unless @$el.is(':visible')
@onRemoveClicked()
onRemoveClicked: ->
@playSound 'menu-button-click'
@$el.hide()
Backbone.Mediator.publish 'tome:focus-editor', {}
onWindowResize: (e) =>
# TODO: This all seems a little hacky
if @problem?
levelContentWidth = $('.level-content').outerWidth(true)
goalsViewWidth = $('#goals-view').outerWidth(true)
codeAreaWidth = $('#code-area').outerWidth(true)
# problem alert view has 20px padding
@$el.css('max-width', levelContentWidth - codeAreaWidth - goalsViewWidth + 40 + 'px')
@$el.css('right', codeAreaWidth + 'px')
# 110px 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', (110 + @lineOffsetPx) + 'px')