From 54194d3f0afa64da3919d37f08cf002a2ee45a24 Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Tue, 5 May 2015 14:01:39 -0700 Subject: [PATCH 1/5] WIP --- design/pages.graffle | Bin 1165635 -> 1165639 bytes src/coffee/controllers/base_controller.coffee | 4 +- .../controllers/item_page_controller.coffee | 14 +- .../controllers/login_page_controller.coffee | 6 +- .../markdown_section_controller.coffee | 125 +++++++++++++++++- src/coffee/crafting_guide_router.coffee | 13 +- src/jade/templates/markdown_section.jade | 14 +- src/scss/classes.scss | 4 +- src/scss/templates/markdown_section.scss | 55 ++++++++ src/scss/views.scss | 2 - 10 files changed, 215 insertions(+), 22 deletions(-) diff --git a/design/pages.graffle b/design/pages.graffle index a73a2d9427bc18eacf8d126f22ff56dfde8e5ae8..6516509e2ed17ed83d5932a7380f9a4c56a2e468 100644 GIT binary patch delta 31890 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 delta 31843 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

x;8;Q-%Oz?Km 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 @adsenseController = @addChild AdsenseController, '.view__adsense', model:'sidebar_skyscraper' - options = imageLoader:@imageLoader, modPack:@modPack, show:false + options = editable:true, imageLoader:@imageLoader, modPack:@modPack, show:false @_descriptionController = @addChild MarkdownSectionController, '.section.description', options @_similarItemsController = @addChild ItemGroupController, '.view__item_group.similar', options @_usedAsToolToMakeController = @addChild ItemGroupController, '.view__item_group.usedAsToolToMake', options @@ -111,14 +111,18 @@ module.exports = class ItemPageController extends PageController super + setUser: (user)-> + super user + @_descriptionController.user = user if @_descriptionController? + # Backbone.View Overrides ###################################################################### events: -> return _.extend super, - 'click a.craftingPlan': 'routeLinkClick' - 'click .byline a': 'routeLinkClick' - 'click .markdown a': 'routeLinkClick' - 'click button': 'craftingPlanButtonClicked' + 'click a.craftingPlan': 'routeLinkClick' + 'click .byline a': 'routeLinkClick' + 'click .markdown a': 'routeLinkClick' + 'click button.craftingPlan': 'craftingPlanButtonClicked' # Private Methods ############################################################################## diff --git a/src/coffee/controllers/login_page_controller.coffee b/src/coffee/controllers/login_page_controller.coffee index 235d31069..1e3fdb9b6 100644 --- a/src/coffee/controllers/login_page_controller.coffee +++ b/src/coffee/controllers/login_page_controller.coffee @@ -77,7 +77,7 @@ module.exports = class LoginPageController extends PageController attributes = response.json.data.user if attributes? router.user = new User attributes - router.navigate Url.root(), trigger:true + router.resumeAfterLogin() .catch (error)-> logger.error "Failed to get access token: #{error}" @_accessTokenLookupFailed = true @@ -120,9 +120,9 @@ module.exports = class LoginPageController extends PageController @client.completeGitHubLogin code:code .then (response)=> router.user = new User response.json.user - .catch (error)-> + .catch (error)=> @_accessTokenLookupFailed = true - @refresh + @refresh() .done() _redirectToGitHub: -> diff --git a/src/coffee/controllers/markdown_section_controller.coffee b/src/coffee/controllers/markdown_section_controller.coffee index 2226ab5bc..b2bc48aac 100644 --- a/src/coffee/controllers/markdown_section_controller.coffee +++ b/src/coffee/controllers/markdown_section_controller.coffee @@ -8,14 +8,22 @@ All rights reserved. BaseController = require './base_controller' convertMarkdown = require 'marked' _ = require 'underscore' +{Event} = require '../constants' {Url} = require '../constants' ######################################################################################################################## module.exports = class MarkdownSectionController extends BaseController + @State = State = + viewing: 'viewing' + editing: 'editing' + previewing: 'previewing' + error: 'error' + constructor: (options={})-> if not options.modPack? then throw new Error 'options.modPack is required' + options.editable ?= false options.imageBase ?= '' options.model ?= '' options.title ?= 'Description' @@ -26,30 +34,101 @@ module.exports = class MarkdownSectionController extends BaseController @modPack = options.modPack @title = options.title + @_editable = options.editable + @_state = State.viewing + + # Event Methods ################################################################################ + + onCancelClicked: (event)-> + event.preventDefault() + @_state = State.viewing + @_updateStateVisibility() + + onEditClicked: (event)-> + event.preventDefault() + + if global.router.user? + @_state = State.editing + @_updateStateVisibility() + else + global.router.login() + + onPreviewClicked: (event)-> + event.preventDefault() + @_state = State.preview + @_updateStateVisibility() + + onReturnClicked: (event)-> + event.preventDefault() + @_state = State.editing + @_updateStateVisibility() + + onSaveClicked: (event)-> + # TODO: implement logic to actually save changes + @onCancelClicked event + + onTextChanged: (event)-> + event.preventDefault() + @_updatePreview() + @_updateSizer() + + # Property Methods ############################################################################# + + isEditable: -> + return @_editable + + setEditable: (editable)-> + return if @_editable is editable + @_editable = editable + @tryRefresh() + + Object.defineProperties @prototype, + editable: {get:@prototype.isEditable, set:@prototype.setEditable} + # BaseController Overrides ##################################################################### onDidRender: -> - @$title = @$('h2') + @$buttonPanel = @$('.buttons') + @$cancelButton = @$('button.cancel') + @$editButton = @$('button.edit') + @$editorPanel = @$('.editor') + @$errorPanel = @$('.error') + @$errorText = @$('.error p') @$markdownPanel = @$('.markdown') + @$previewButton = @$('button.preview') + @$returnButton = @$('button.return') + @$saveButton = @$('button.save') + @$sizer = @$('.sizer') + @$textarea = @$('textarea') + @$title = @$('h2') super refresh: -> @$title.html @title - text = @model - text = @_convertWikiLinks text - text = @_convertImageLinks text + @$textarea.val @model - @$markdownPanel.html convertMarkdown text + if @editable + @show @$buttonPanel + else + @hide @$buttonPanel + @_updateSizer() + @_updatePreview() super # Backbone.View Overrides ###################################################################### events: -> return _.extend super, - 'click .markdown a': 'routeLinkClick' + 'click .markdown a': 'routeLinkClick' + 'click button.cancel': 'onCancelClicked' + 'click button.edit': 'onEditClicked' + 'click button.preview': 'onPreviewClicked' + 'click button.return': 'onReturnClicked' + 'click button.save': 'onSaveClicked' + 'input textarea': 'onTextChanged' # Private Methods ############################################################################## @@ -66,3 +145,37 @@ module.exports = class MarkdownSectionController extends BaseController result = "[#{name}](#{display.itemUrl})" return result + + _updateSizer: -> + text = @$textarea.val() + text = text.replace /\n/g, '
' + @$sizer.html text + + _updatePreview: -> + text = @$textarea.val() + text = @_convertWikiLinks text + text = @_convertImageLinks text + + @$markdownPanel.html convertMarkdown text + + _updateStateVisibility: -> + toHide = [] + toShow = [] + errorText = '' + + if @_state is State.viewing + toHide = [@$cancelButton, @$editorPanel, @$errorPanel, @$previewButton, @$returnButton, @$saveButton] + toShow = [@$editButton, @$markdownPanel] + else if @_state is State.editing + toHide = [@$editButton, @$errorPanel, @$markdownPanel, @$previewButton, @$returnButton] + toShow = [@$cancelButton, @$editorPanel, @$previewButton, @$saveButton] + else if @_state is State.preview + toHide = [@$cancelButton, @$editButton, @$editorPanel, @$previewButton, @$saveButton] + toShow = [@$errorPanel, @$markdownPanel, @$returnButton] + errorText = "remember: your changes aren't saved yet!" + # else if @_state is State.error + # TODO: Implement change to error state + + @hide $el for $el in toHide + @once Event.animate.hide.finish, => @show $el for $el in toShow + @$errorText.html errorText diff --git a/src/coffee/crafting_guide_router.coffee b/src/coffee/crafting_guide_router.coffee index a05ef9479..64ff616d4 100644 --- a/src/coffee/crafting_guide_router.coffee +++ b/src/coffee/crafting_guide_router.coffee @@ -36,9 +36,10 @@ backbone = require 'backbone' module.exports = class CraftingGuideRouter extends backbone.Router constructor: (options={})-> + @_lastReported = null @_page = null @_pageControllers = {} - @_lastReported = null + @_postLoginUrl = Url.root @_user = null super options @@ -80,6 +81,16 @@ module.exports = class CraftingGuideRouter extends backbone.Router logger.info "User is not logged in" .done() + login: -> + @storage.store 'post-login-url', window.location.pathname + @navigate Url.login(), trigger:true + + resumeAfterLogin: -> + url = @storage.load 'post-login-url' + url ?= Url.root() + @navigate url, trigger:true + @storage.store 'post-login-url', null + # Property Methods ############################################################################# getUser: -> diff --git a/src/jade/templates/markdown_section.jade b/src/jade/templates/markdown_section.jade index 4d2f8f476..82eed7a1d 100644 --- a/src/jade/templates/markdown_section.jade +++ b/src/jade/templates/markdown_section.jade @@ -7,4 +7,16 @@ .view__markdown_section.section h2 - .panel.markdown \ No newline at end of file + .panel + .markdown.hideable + .editor.hideable.hidden + textarea + .sizer + .images.hideable.hidden + .buttons.hideable.hidden + .error.hideable.hidden: p + button.return.hideable.hidden return + button.cancel.hideable.hidden cancel + button.preview.hideable.hidden preview + button.save.hideable.hidden save + button.edit.hideable edit diff --git a/src/scss/classes.scss b/src/scss/classes.scss index 48960bc5b..40b57a9cd 100644 --- a/src/scss/classes.scss +++ b/src/scss/classes.scss @@ -32,9 +32,9 @@ All rights reserved. width: 100%; } -.error { background: $color-error !important; } +.error { background: $color-error; } -.error-new { background: $color-error-new !important; } +.error-new { background: $color-error-new; } .float-right { float: right; diff --git a/src/scss/templates/markdown_section.scss b/src/scss/templates/markdown_section.scss index 9f49252c5..7b3459d23 100644 --- a/src/scss/templates/markdown_section.scss +++ b/src/scss/templates/markdown_section.scss @@ -6,7 +6,62 @@ All rights reserved. */ .view__markdown_section { + .editor { width: 100%; + + position: relative; + min-height: 10em; + + textarea, .sizer { + width: 100%; + border: 1px solid $color-gray-light; + font-family: $font-family-input; + font-size: $font-size-normal; + } + + textarea { + position: absolute; height: 100%; width: 100%; + + background: $color-gray-faint; + resize: none; + z-index: $layer-normal; + + &:focus { + background: $color-background-panel; + border: 1px solid $color-gray-medium; + } + } + + .sizer { + padding-bottom: 1.5em; + visibility: hidden; + z-index: $layer-normal - 1; + } + } + + .buttons { + position: relative; width: 100%; + + text-align: right; + + button { + margin: 0.66em 0 0 0.66em; + min-width: 6.66em; + padding: 0.25em 0.5em; + } + + .error { + + height: 100%; + + background: none; + display: inline-block; + + p { + color: $color-error-new; + font-weight: bold; + } + } } } diff --git a/src/scss/views.scss b/src/scss/views.scss index be9588c28..0ab6b691d 100644 --- a/src/scss/views.scss +++ b/src/scss/views.scss @@ -57,8 +57,6 @@ All rights reserved. position: absolute; bottom: 5em; height: 3.2em; width: 100%; .login { - display: none; // Just until there's a feature which needs it. - position: absolute; top: 50%; right: 2em; @include transform(translateY(-50%)); From 8b94f432b06e86fff792651b679d000225ad6909 Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Thu, 7 May 2015 11:19:36 -0700 Subject: [PATCH 2/5] Update MarkdownSectionController with support for editing This allows the MarkdownSectionController and ItemPageController to work together to begin an editing session for an item description. --- src/coffee/constants.coffee | 6 +- .../controllers/item_page_controller.coffee | 34 ++- .../markdown_section_controller.coffee | 203 ++++++++++++------ src/jade/templates/markdown_section.jade | 10 +- src/scss/templates/markdown_section.scss | 71 ++++-- static/images/wait.gif | Bin 0 -> 9331 bytes 6 files changed, 226 insertions(+), 98 deletions(-) create mode 100644 static/images/wait.gif diff --git a/src/coffee/constants.coffee b/src/coffee/constants.coffee index c3a28ea18..7dcf4a3bb 100644 --- a/src/coffee/constants.coffee +++ b/src/coffee/constants.coffee @@ -66,7 +66,7 @@ Event.route = 'route' Event.sort = 'sort' Event.sync = 'sync' # model, response Event.transitionEnd = (-> - return unless document? + return 'transitionend' unless document? transitions = 'WebkitTransition': 'webkitTransitionEnd' 'MozTransition': 'transitionend' @@ -88,6 +88,10 @@ Key.Escape = 27 Key.UpArrow = 38 Key.DownArrow = 40 +exports.GitHub = GitHub = {} +GitHub.file = {} +GitHub.file.itemDescription = _.template "/data/<%= modSlug %>/items/<%= itemSlug %>/item.cg" + exports.Login = Login = {} Login.authorizeUrl = _.template "https://github.com/login/oauth/authorize" + "?client_id=<%= clientId %>&scope=public_repo&state=<%= state %>" diff --git a/src/coffee/controllers/item_page_controller.coffee b/src/coffee/controllers/item_page_controller.coffee index e8cac6e6f..2426c5af8 100644 --- a/src/coffee/controllers/item_page_controller.coffee +++ b/src/coffee/controllers/item_page_controller.coffee @@ -16,8 +16,10 @@ MarkdownSectionController = require './markdown_section_controller' PageController = require './page_controller' VideoController = require './video_controller' _ = require 'underscore' +w = require 'when' {Duration} = require '../constants' {Event} = require '../constants' +{GitHub} = require '../constants' {Text} = require '../constants' {Url} = require '../constants' @@ -26,6 +28,7 @@ _ = require 'underscore' module.exports = class ItemPageController extends PageController constructor: (options={})-> + if not options.client? then throw new Error 'options.client is required' if not options.itemSlug? then throw new Error 'options.itemSlug is required' if not options.imageLoader? then throw new Error 'options.imageLoader is required' if not options.modPack? then throw new Error 'options.modPack is required' @@ -35,9 +38,12 @@ module.exports = class ItemPageController extends PageController super options + @client = options.client @imageLoader = options.imageLoader @modPack = options.modPack - @_itemSlug = options.itemSlug + + @_editingFile = null + @_itemSlug = options.itemSlug @modPack.on Event.change, => @tryRefresh() @@ -58,12 +64,17 @@ module.exports = class ItemPageController extends PageController onDidRender: -> @adsenseController = @addChild AdsenseController, '.view__adsense', model:'sidebar_skyscraper' - options = editable:true, imageLoader:@imageLoader, modPack:@modPack, show:false - @_descriptionController = @addChild MarkdownSectionController, '.section.description', options + options = imageLoader:@imageLoader, modPack:@modPack, show:false @_similarItemsController = @addChild ItemGroupController, '.view__item_group.similar', options @_usedAsToolToMakeController = @addChild ItemGroupController, '.view__item_group.usedAsToolToMake', options @_usedToMakeController = @addChild ItemGroupController, '.view__item_group.usedToMake', options + @_descriptionController = @addChild MarkdownSectionController, '.section.description', + editable: true + modPack: @modPack + beginEditing: => @_beginEditingDescription() + endEditing: => @_endEditingDescription() + @$byline = @$('.byline') @$bylineLink = @$('.byline a') @$descriptionSection = @$('.description') @@ -126,6 +137,23 @@ module.exports = class ItemPageController extends PageController # Private Methods ############################################################################## + _beginEditingDescription: -> + if not global.router.user? + global.router.login() + return w.reject new Error 'must be logged in to edit' + + if not @model.item? + return w.reject new Error 'must have an item' + + @client.fetchFile file:GitHub.file.itemDescription modSlug:@_itemSlug.mod, itemSlug:@_itemSlug.item + .then (fileRecord)=> + @_editingFile = fileRecord + @model.item.parse fileRecord.content + @_descriptionController.model = @model.item.description + + _endEditingDescription: -> + return w(true).delay(1000) + _refreshByline: -> mod = @model.item?.modVersion?.mod if mod?.name?.length > 0 diff --git a/src/coffee/controllers/markdown_section_controller.coffee b/src/coffee/controllers/markdown_section_controller.coffee index b2bc48aac..7dd2a59d6 100644 --- a/src/coffee/controllers/markdown_section_controller.coffee +++ b/src/coffee/controllers/markdown_section_controller.coffee @@ -10,94 +10,118 @@ convertMarkdown = require 'marked' _ = require 'underscore' {Event} = require '../constants' {Url} = require '../constants' +w = require 'when' ######################################################################################################################## module.exports = class MarkdownSectionController extends BaseController @State = State = - viewing: 'viewing' - editing: 'editing' - previewing: 'previewing' - error: 'error' + appologizing: 'applogizing' + confirming: 'confirming' + editing: 'editing' + previewing: 'previewing' + viewing: 'viewing' + waiting: 'waiting' constructor: (options={})-> + @_state = State.waiting + if not options.modPack? then throw new Error 'options.modPack is required' - options.editable ?= false - options.imageBase ?= '' - options.model ?= '' - options.title ?= 'Description' - options.templateName = 'markdown_section' + options.model ?= null + options.templateName = 'markdown_section' super options - @imageBase = options.imageBase - @modPack = options.modPack - @title = options.title + @confirmactionMessage = options.confirmationMessage + @confirmDuration = options.confirmationDuration ?= 5000 + @imageBase = options.imageBase ?= '' + @modPack = options.modPack + @title = options.title ?= 'Description' - @_editable = options.editable - @_state = State.viewing + # @_beginEditing must be a function returning a promise which resolves when all actions necessary prior to an + # editing session have been completed (e.g., loading the latest content from a remote server). The promise must + # reject if an error occured which should prevent editing. + @_beginEditing = options.beginEditing + + # @_endEditing must be a function returning a promise which resolves when the model of this controller has been + # saved however is appropriate. The promise must reject if saving failed for some reason. + @_endEditing = options.endEditing # Event Methods ################################################################################ onCancelClicked: (event)-> event.preventDefault() - @_state = State.viewing - @_updateStateVisibility() + @model = @_originalModel + @state = State.viewing onEditClicked: (event)-> event.preventDefault() + return unless @editable - if global.router.user? - @_state = State.editing - @_updateStateVisibility() - else - global.router.login() + @_originalModel = "#{@model}" + @state = State.waiting + @_beginEditing() + .then => + @state = State.editing + .catch (e)=> + logger.warning "cannot begin editing: #{e.stack}" + @state = State.appologizing + w(true).delay(@confirmDuration).then => @state = State.viewing onPreviewClicked: (event)-> event.preventDefault() - @_state = State.preview - @_updateStateVisibility() + @state = State.previewing onReturnClicked: (event)-> event.preventDefault() - @_state = State.editing - @_updateStateVisibility() + @state = State.editing onSaveClicked: (event)-> - # TODO: implement logic to actually save changes - @onCancelClicked event + event.preventDefault() + + nextState = State.viewing + @state = State.waiting + @_endEditing() + .then => + @state = State.confirming + .catch (e)=> + logger.error "failed to end editing: #{e}" + @state = State.appologizing + nextState = State.editing + .delay @confirmDuration + .then => + @state = nextState onTextChanged: (event)-> event.preventDefault() - @_updatePreview() - @_updateSizer() + @model = @$textarea.val() # Property Methods ############################################################################# isEditable: -> - return @_editable + return _.isFunction(@_beginEditing) and _.isFunction(@_endEditing) - setEditable: (editable)-> - return if @_editable is editable - @_editable = editable - @tryRefresh() + getState: -> + return @_state + + setState: (newState)-> + oldState = @_state + return if oldState is newState + @_state = newState + + logger.verbose => "MarkdownSectionController.#{@cid} changed state from #{oldState} to #{newState}" + @trigger Event.change + ':state', this, oldState, newState + @_updateStateVisibility() Object.defineProperties @prototype, - editable: {get:@prototype.isEditable, set:@prototype.setEditable} + editable: {get:@prototype.isEditable} + state: {get:@prototype.getState, set:@prototype.setState} # BaseController Overrides ##################################################################### onDidRender: -> - @$buttonPanel = @$('.buttons') - @$cancelButton = @$('button.cancel') - @$editButton = @$('button.edit') - @$editorPanel = @$('.editor') - @$errorPanel = @$('.error') @$errorText = @$('.error p') @$markdownPanel = @$('.markdown') - @$previewButton = @$('button.preview') - @$returnButton = @$('button.return') - @$saveButton = @$('button.save') @$sizer = @$('.sizer') @$textarea = @$('textarea') @$title = @$('h2') @@ -107,17 +131,23 @@ module.exports = class MarkdownSectionController extends BaseController refresh: -> @$title.html @title - @$textarea.val @model - - if @editable - @show @$buttonPanel - else - @hide @$buttonPanel - @_updateSizer() @_updatePreview() + @_updateStateVisibility() super + onWillChangeModel: (oldModel, newModel)-> + result = super oldModel, newModel + + if @state is State.waiting and newModel? + @state = State.viewing + else if not newModel? + @state = State.waiting + + @$textarea.val newModel + + return result + # Backbone.View Overrides ###################################################################### events: -> @@ -147,35 +177,66 @@ module.exports = class MarkdownSectionController extends BaseController return result _updateSizer: -> - text = @$textarea.val() - text = text.replace /\n/g, '
' + text = @model + if @model? + text = text.replace /\n/g, '
' + @$sizer.html text _updatePreview: -> - text = @$textarea.val() - text = @_convertWikiLinks text - text = @_convertImageLinks text + text = @model + if @model? + text = @_convertWikiLinks text + text = @_convertImageLinks text + text = convertMarkdown text - @$markdownPanel.html convertMarkdown text + @$markdownPanel.html text _updateStateVisibility: -> - toHide = [] - toShow = [] + return if @_lastUpdatedState is @state + @_lastUpdatedState = @state + + elements = + appologizingPanel: @$('.appologizing') + buttonPanel: @$('.buttons') + cancelButton: @$('button.cancel') + confirmingPanel: @$('.confirming') + editButton: @$('button.edit') + editorPanel: @$('.editor') + errorPanel: @$('.error') + markdownPanel: @$markdownPanel + previewButton: @$('button.preview') + returnButton: @$('button.return') + saveButton: @$('button.save') + waitingPanel: @$('.waiting') + + visible = {} errorText = '' - if @_state is State.viewing - toHide = [@$cancelButton, @$editorPanel, @$errorPanel, @$previewButton, @$returnButton, @$saveButton] - toShow = [@$editButton, @$markdownPanel] - else if @_state is State.editing - toHide = [@$editButton, @$errorPanel, @$markdownPanel, @$previewButton, @$returnButton] - toShow = [@$cancelButton, @$editorPanel, @$previewButton, @$saveButton] - else if @_state is State.preview - toHide = [@$cancelButton, @$editButton, @$editorPanel, @$previewButton, @$saveButton] - toShow = [@$errorPanel, @$markdownPanel, @$returnButton] + if @state is State.appologizing + visible = appologizingPanel:true + else if @state is State.confirming + visible = confirmingPanel:true + else if @state is State.editing + visible = buttonPanel:true, cancelButton:true, editorPanel:true, previewButton:true, saveButton:true + else if @state is State.previewing + visible = buttonPanel:true, errorPanel:true, markdownPanel:true, returnButton:true errorText = "remember: your changes aren't saved yet!" - # else if @_state is State.error - # TODO: Implement change to error state + else if @state is State.viewing + visible = markdownPanel:true + if @editable then _.extend visible, {buttonPanel:true, editButton:true} + else # assume any unknown state is the same as "waiting" + visible = waitingPanel:true + + toHide = ($el for name, $el of elements when not visible[name]) + toShow = ($el for name, $el of elements when visible[name]) + + if toHide.length > 0 + @hide $el for $el in toHide + if toShow.length > 0 + @once Event.animate.hide.finish, => + @show $el for $el in toShow + else if toShow.length > 0 + @show $el for $el in toShow - @hide $el for $el in toHide - @once Event.animate.hide.finish, => @show $el for $el in toShow @$errorText.html errorText diff --git a/src/jade/templates/markdown_section.jade b/src/jade/templates/markdown_section.jade index 82eed7a1d..44e83fb57 100644 --- a/src/jade/templates/markdown_section.jade +++ b/src/jade/templates/markdown_section.jade @@ -8,11 +8,11 @@ .view__markdown_section.section h2 .panel - .markdown.hideable + .waiting.hideable: img(src='/images/wait.gif') + .markdown.hideable.hidden .editor.hideable.hidden textarea .sizer - .images.hideable.hidden .buttons.hideable.hidden .error.hideable.hidden: p button.return.hideable.hidden return @@ -20,3 +20,9 @@ button.preview.hideable.hidden preview button.save.hideable.hidden save button.edit.hideable edit + .confirming.hideable.hidden + p Thanks for your help! + p Your changes will appear on the site in a few minutes. + .appologizing.hideable.hidden + p Sorry, but something went wrong. + p Please try again after a few moments. diff --git a/src/scss/templates/markdown_section.scss b/src/scss/templates/markdown_section.scss index 7b3459d23..33ad6d612 100644 --- a/src/scss/templates/markdown_section.scss +++ b/src/scss/templates/markdown_section.scss @@ -7,6 +7,51 @@ All rights reserved. .view__markdown_section { + .appologizing, .confirming { + width: 100%; + + p { + width: 100%; + + margin: 1em auto; + text-align: center; + + &:first-child { + font-size: $font-size-x-large; + margin-top: 2em; + } + + &:last-child { + margin-bottom: 4em; + } + } + } + + .buttons { + position: relative; width: 100%; + + text-align: right; + + button { + margin: 0.66em 0 0 0.66em; + min-width: 6.66em; + padding: 0.25em 0.5em; + } + + .error { + + height: 100%; + + background: none; + display: inline-block; + + p { + color: $color-error-new; + font-weight: bold; + } + } + } + .editor { width: 100%; @@ -40,28 +85,12 @@ All rights reserved. } } - .buttons { - position: relative; width: 100%; + .waiting { + position: relative; min-height: 20em; width: 100%; - text-align: right; - - button { - margin: 0.66em 0 0 0.66em; - min-width: 6.66em; - padding: 0.25em 0.5em; - } - - .error { - - height: 100%; - - background: none; - display: inline-block; - - p { - color: $color-error-new; - font-weight: bold; - } + img { + position: absolute; top: 50%; left: 50%; + @include transform(translate(-50%, -50%)); } } } diff --git a/static/images/wait.gif b/static/images/wait.gif new file mode 100644 index 0000000000000000000000000000000000000000..27f20ed91a1438cc3d4eaf5675850d6057e506ad GIT binary patch literal 9331 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 literal 0 HcmV?d00001 From 1254183c2c3d39e725618b9d7203d93dde22941c Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Fri, 8 May 2015 16:14:53 -0700 Subject: [PATCH 3/5] Update watch tasks to allow watch to run tests --- Gruntfile.coffee | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index a468c0a8b..2725faa11 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -137,13 +137,14 @@ module.exports = (grunt)-> sass: files: ['./src/**/*.scss'] tasks: ['sass', 'copy:styles'] + test: + files: ['./src/**/*.coffee', './src/**/*.js', './test/**/*.coffee'] + tasks: ['test'] grunt.registerTask 'default', 'build' grunt.registerTask 'build', ['rsync', 'sass:build', 'jade', 'copy', 'browserify:dev', 'exorcise'] - grunt.registerTask 'clean-watch', ['clean', 'build', 'watch'] - grunt.registerTask 'dist', ['rsync', 'sass:dist', 'jade', 'copy', 'browserify:prod', 'uglify'] grunt.registerTask 'prepublish', ['clean', 'coffee'] @@ -155,6 +156,10 @@ module.exports = (grunt)-> grunt.registerTask 'test', ['mochaTest'] + grunt.registerTask 'watch-dev', ['clean', 'build', 'watch:static', 'watch:coffee', 'watch:jade', 'watch:sass'] + + grunt.registerTask 'watch-test', ['clean', 'build', 'watch:test'] + args = process.argv[..] while args.length > 0 switch args[0] From 8eb5cef71b803f560a2969aa21a67d7878ad8292 Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Fri, 8 May 2015 16:16:17 -0700 Subject: [PATCH 4/5] Allow submitting changes for item desc. to GitHub --- .../controllers/item_page_controller.coffee | 22 +++- .../markdown_section_controller.coffee | 4 +- src/coffee/models/item.coffee | 5 + .../parser_versions/item_parser_v1.coffee | 36 ++++++- .../item_version_parser_v1.test.coffee | 100 ++++++++++++++++++ 5 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 test/parser_versions/item_version_parser_v1.test.coffee diff --git a/src/coffee/controllers/item_page_controller.coffee b/src/coffee/controllers/item_page_controller.coffee index 2426c5af8..f161be91d 100644 --- a/src/coffee/controllers/item_page_controller.coffee +++ b/src/coffee/controllers/item_page_controller.coffee @@ -145,14 +145,26 @@ module.exports = class ItemPageController extends PageController if not @model.item? return w.reject new Error 'must have an item' - @client.fetchFile file:GitHub.file.itemDescription modSlug:@_itemSlug.mod, itemSlug:@_itemSlug.item - .then (fileRecord)=> - @_editingFile = fileRecord - @model.item.parse fileRecord.content + @client.fetchFile path:GitHub.file.itemDescription modSlug:@_itemSlug.mod, itemSlug:@_itemSlug.item + .then (response)=> + @_editingFile = response.json.data + @model.item.parse @_editingFile.content @_descriptionController.model = @model.item.description _endEditingDescription: -> - return w(true).delay(1000) + oldDescription = @model.item.description + @model.item.description = @_descriptionController.model + + args = + content: @model.item.unparse() + message: "User-submitted text for #{@model.item.name}" + path: GitHub.file.itemDescription modSlug:@_itemSlug.mod, itemSlug:@_itemSlug.item + sha: @_editingFile.sha + + @client.updateFile args + .catch (e)=> + @model.item.description = oldDescription + throw e _refreshByline: -> mod = @model.item?.modVersion?.mod diff --git a/src/coffee/controllers/markdown_section_controller.coffee b/src/coffee/controllers/markdown_section_controller.coffee index 7dd2a59d6..570b24959 100644 --- a/src/coffee/controllers/markdown_section_controller.coffee +++ b/src/coffee/controllers/markdown_section_controller.coffee @@ -79,7 +79,6 @@ module.exports = class MarkdownSectionController extends BaseController onSaveClicked: (event)-> event.preventDefault() - nextState = State.viewing @state = State.waiting @_endEditing() .then => @@ -87,10 +86,9 @@ module.exports = class MarkdownSectionController extends BaseController .catch (e)=> logger.error "failed to end editing: #{e}" @state = State.appologizing - nextState = State.editing .delay @confirmDuration .then => - @state = nextState + @state = State.viewing onTextChanged: (event)-> event.preventDefault() diff --git a/src/coffee/models/item.coffee b/src/coffee/models/item.coffee index 444962f77..590adb456 100644 --- a/src/coffee/models/item.coffee +++ b/src/coffee/models/item.coffee @@ -45,6 +45,11 @@ module.exports = class Item extends BaseModel return if this.name < that.name then -1 else +1 return 0 + unparse: -> + ItemParser = require './item_parser' # to avoid require cycles + @_parser ?= new ItemParser model:this + return @_parser.unparse() + # Property Methods ############################################################################# getIsCraftable: -> diff --git a/src/coffee/models/parser_versions/item_parser_v1.coffee b/src/coffee/models/parser_versions/item_parser_v1.coffee index 251bce482..6cc05e8c2 100644 --- a/src/coffee/models/parser_versions/item_parser_v1.coffee +++ b/src/coffee/models/parser_versions/item_parser_v1.coffee @@ -17,17 +17,23 @@ module.exports = class ItemParserV1 extends CommandParserVersionBase @_buildItem rawData, model _unparseModel: (builder, model)-> + builder.line 'schema: ', 1 + builder.line() + @_unparseItem builder, model # Command Methods ############################################################################## _command_description: (textParts...)-> - @_rawData.description ?= '' + if not @_rawData.description? + @_rawData.description = '' + else + @_rawData.description += '\n' @_rawData.description += textParts.join ', ' _command_officialUrl: (officialUrl)-> if @_rawData.officialUrl? then throw new Error 'duplicate declaration of "officialUrl"' - if officialUrl.length is 0 then throw new Error 'officialUrl cannot be empty' + if not officialUrl? or (officialUrl.length is 0) then throw new Error 'officialUrl cannot be empty' @_rawData.officialUrl = officialUrl _command_video: (youTubeId, nameParts...)-> @@ -41,6 +47,26 @@ module.exports = class ItemParserV1 extends CommandParserVersionBase # Object Building Methods ###################################################################### _buildItem: (rawData, model)-> - model.description = rawData.description if rawData.description - model.officialUrl = rawData.officialUrl if rawData.officialUrl - model.videos = rawData.videos if rawData.videos + model.description = rawData.description if rawData.description? + model.officialUrl = rawData.officialUrl if rawData.officialUrl? + model.videos = rawData.videos if rawData.videos? + + # Un-parsing Methods ########################################################################### + + _unparseItem: (builder, model)-> + if model.officialUrl? + builder.line 'officialUrl: ', model.officialUrl + builder.line() + + if model.description? + if model.description.indexOf('\n') isnt -1 + builder.line 'description: <<-END' + builder.line model.description + builder.line 'END' + else + builder.line 'description: ', model.description + builder.line() + + for video in model.videos + builder.line 'video: ', video.youTubeId, ', ', video.name + builder.line() diff --git a/test/parser_versions/item_version_parser_v1.test.coffee b/test/parser_versions/item_version_parser_v1.test.coffee new file mode 100644 index 000000000..386aaf272 --- /dev/null +++ b/test/parser_versions/item_version_parser_v1.test.coffee @@ -0,0 +1,100 @@ +### +Crafting Guide - item_parser_v1.test.coffee + +Copyright (c) 2015 by Redwood Labs +All rights reserved. +### + +Item = require '../../src/coffee/models/item' +ItemParserV1 = require '../../src/coffee/models/parser_versions/item_parser_v1' +_ = require 'underscore' + +######################################################################################################################## + +baseText = item = parser = null + +######################################################################################################################## + +describe 'item_parser_v1.coffee', -> + + beforeEach -> + item = new Item name:'alpha' + parser = new ItemParserV1 model:item + + describe 'officialUrl', -> + + it 'may be omitted', -> + parser.parse 'schema: 1\nvideo: youtubeid, Video Alpha\ndescription: Bravo, Charlie' + expect(item.officialUrl).to.be.null + + it 'is assigned properly when given', -> + parser.parse 'schema: 1\nofficialUrl: http://testurl.com' + item.officialUrl.should.equal 'http://testurl.com' + + it 'does not allow duplicate declarations', -> + func = -> parser.parse 'schema: 1\nofficialUrl: http://testurl.com\nofficialUrl: http://testurl2.com' + expect(func).to.throw Error, 'duplicate' + + it 'does not allow an empty value if given', -> + func = -> parser.parse 'schema: 1\nofficialUrl:' + expect(func).to.throw Error, 'empty' + + describe 'description', -> + + it 'may be omitted', -> + parser.parse 'schema: 1\nvideo: youTubeId, Video Alpha\nofficialUrl: http://testurl.com' + expect(item.description).to.be.null + + it 'is assigned properly when given', -> + parser.parse 'schema: 1\ndescription: Alpha Bravo Charlie' + item.description.should.equal 'Alpha Bravo Charlie' + + it 'can be a heredoc', -> + parser.parse 'schema: 1\ndescription: <<-END\nAlpha\nBravo\nCharlie\nEND' + item.description.should.equal 'Alpha\nBravo\nCharlie' + + it 'concatenates multiple declarations', -> + parser.parse 'schema: 1\ndescription: Alpha\ndescription: Bravo' + item.description.should.equal 'Alpha\nBravo' + + describe 'video', -> + + it 'may be omitted', -> + parser.parse 'schema: 1\nofficialUrl: http://testurl.com\ndescription: Alpha Bravo Charlie' + item.videos.should.eql [] + + it 'is assigned properly when given', -> + parser.parse 'schema: 1\nvideo: youtubeid, Alpha Bravo' + item.videos[0].should.eql youTubeId:'youtubeid', name:'Alpha Bravo' + item.videos.length.should.equal 1 + + it 'may be included multiple times', -> + parser.parse 'schema: 1\nvideo: youtubeid1, Alpha\nvideo: youtubeid2, Bravo' + item.videos[0].should.eql youTubeId:'youtubeid1', name:'Alpha' + item.videos[1].should.eql youTubeId:'youtubeid2', name:'Bravo' + item.videos.length.should.equal 2 + + it 'requires a YouTubeId and name', -> + func = -> parser.parse 'schema: 1\nvideo: alpha' + expect(func).to.throw Error, 'requires a name' + + describe 'unparsing', -> + + it 'can round-trip a fully described item', -> + text = """ + schema: 1 + + officialUrl: http://testurl.com + + description: <<-END + Alpha + Bravo + END + + video: youtubeid1, Alpha Bravo + video: youtubeid2, Charlie Delta + + + """ + parser.parse text + parser.unparse().should.equal text From 23e98f5d31c08cdf13e58333ddaf41e9fc203aad Mon Sep 17 00:00:00 2001 From: Andrew Miner Date: Mon, 11 May 2015 09:20:42 -0700 Subject: [PATCH 5/5] Allow new item description files to be created --- Gruntfile.coffee | 4 +- src/coffee/controllers/base_controller.coffee | 44 ++++++++++++------- .../controllers/feedback_controller.coffee | 1 + .../controllers/item_page_controller.coffee | 8 +++- .../markdown_section_controller.coffee | 39 ++++++++++------ src/jade/templates/markdown_section.jade | 2 + src/scss/templates/markdown_section.scss | 11 +++++ 7 files changed, 75 insertions(+), 34 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 2725faa11..4be0962df 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -133,7 +133,7 @@ module.exports = (grunt)-> tasks: ['browserify:dev', 'exorcise'] jade: files: ['./src/**/*.jade'] - tasks: ['jade:pages', 'jade:templates', 'browserify:dev', 'exorcise'] + tasks: ['jade:pages', 'jade:templates'] sass: files: ['./src/**/*.scss'] tasks: ['sass', 'copy:styles'] @@ -156,7 +156,7 @@ module.exports = (grunt)-> grunt.registerTask 'test', ['mochaTest'] - grunt.registerTask 'watch-dev', ['clean', 'build', 'watch:static', 'watch:coffee', 'watch:jade', 'watch:sass'] + grunt.registerTask 'watch-dev', ['clean', 'build', 'watch'] grunt.registerTask 'watch-test', ['clean', 'build', 'watch:test'] diff --git a/src/coffee/controllers/base_controller.coffee b/src/coffee/controllers/base_controller.coffee index 0dbf0198e..54876e7c0 100644 --- a/src/coffee/controllers/base_controller.coffee +++ b/src/coffee/controllers/base_controller.coffee @@ -49,14 +49,17 @@ module.exports = class BaseController extends backbone.View if not ($el.hasClass('hiding') or $el.hasClass('hidden')) logger.verbose => "#{this} is hiding \"#{$el.selector}\"" - @_onAnimationComplete $el, Duration.normal, => - $el.addClass 'hidden' - $el.removeClass 'hiding' - logger.verbose => "#{this} is finished hiding #{$el.selector}" - @trigger Event.animate.hide.finish, this, $el - $el.addClass 'hiding' @trigger Event.animate.hide.start, this, $el + @_onAnimationComplete $el, Duration.normal, => @hide $el + return + + if $el.hasClass('hiding') and not $el.hasClass('hidden') + $el.removeClass 'hiding' + $el.addClass 'hidden' + logger.verbose => "#{this} is finished hiding #{$el.selector}" + + @trigger Event.animate.hide.finish, this, $el refresh: -> logger.verbose => "#{this} refreshing" @@ -86,20 +89,27 @@ module.exports = class BaseController extends backbone.View show: ($el)-> $el ?= @$el - $el.addClass 'hideable' - if $el.hasClass('hidden') or $el.hasClass('hiding') + if not $el.hasClass 'hideable' + $el.addClass 'hideable' + _.defer => @show $el + return + + if $el.hasClass 'hidden' logger.verbose => "#{this} is showing \"#{$el.selector}\"" - - @_onAnimationComplete $el, Duration.normal, => - logger.verbose => "#{this} is finished showing #{$el.selector}" - @trigger Event.animate.show.finish, this, $el - - $el.addClass 'hiding' $el.removeClass 'hidden' - _.defer => - $el.removeClass 'hiding' - @trigger Event.animate.show.start, this, $el + $el.addClass 'hiding' + @trigger Event.animate.show.start, this, $el + _.defer => @show $el + return + + if $el.hasClass 'hiding' + $el.removeClass 'hiding' + @_onAnimationComplete $el, Duration.normal, => @show $el + return + + logger.verbose => "#{this} is finished showing #{$el.selector}" + @trigger Event.animate.show.finish, this, $el unrender: -> @undelegateEvents() diff --git a/src/coffee/controllers/feedback_controller.coffee b/src/coffee/controllers/feedback_controller.coffee index 2f3430755..5eaef9ab3 100644 --- a/src/coffee/controllers/feedback_controller.coffee +++ b/src/coffee/controllers/feedback_controller.coffee @@ -9,6 +9,7 @@ $ = require 'jquery' BaseController = require './base_controller' EmailClient = require '../models/email_client' _ = require 'underscore' +w = require 'when' {Duration} = require '../constants' {Key} = require '../constants' diff --git a/src/coffee/controllers/item_page_controller.coffee b/src/coffee/controllers/item_page_controller.coffee index f161be91d..d45a43b5b 100644 --- a/src/coffee/controllers/item_page_controller.coffee +++ b/src/coffee/controllers/item_page_controller.coffee @@ -148,7 +148,11 @@ module.exports = class ItemPageController extends PageController @client.fetchFile path:GitHub.file.itemDescription modSlug:@_itemSlug.mod, itemSlug:@_itemSlug.item .then (response)=> @_editingFile = response.json.data - @model.item.parse @_editingFile.content + if @_editingFile.content.length > 0 + @model.item.parse @_editingFile.content + else + @model.item.description = '' + @_descriptionController.model = @model.item.description _endEditingDescription: -> @@ -180,6 +184,8 @@ module.exports = class ItemPageController extends PageController if @model.item?.description?.length > 0 @_descriptionController.model = @model.item.description @show @$descriptionSection + else if @user? + @show @$descriptionSection else @hide @$descriptionSection diff --git a/src/coffee/controllers/markdown_section_controller.coffee b/src/coffee/controllers/markdown_section_controller.coffee index 570b24959..166d8f20b 100644 --- a/src/coffee/controllers/markdown_section_controller.coffee +++ b/src/coffee/controllers/markdown_section_controller.coffee @@ -19,19 +19,22 @@ module.exports = class MarkdownSectionController extends BaseController @State = State = appologizing: 'applogizing' confirming: 'confirming' + creating: 'creating' editing: 'editing' previewing: 'previewing' viewing: 'viewing' waiting: 'waiting' constructor: (options={})-> - @_state = State.waiting - if not options.modPack? then throw new Error 'options.modPack is required' - options.model ?= null - options.templateName = 'markdown_section' + options.model ?= null + options.templateName = 'markdown_section' + @_state = State.waiting super options + @on Event.animate.hide.finish, => @_updateStateVisibility() + @on Event.animate.show.finish, => @_updateStateVisibility() + @confirmactionMessage = options.confirmationMessage @confirmDuration = options.confirmationDuration ?= 5000 @imageBase = options.imageBase ?= '' @@ -52,13 +55,13 @@ module.exports = class MarkdownSectionController extends BaseController onCancelClicked: (event)-> event.preventDefault() @model = @_originalModel - @state = State.viewing + @_resetToDefaultState() onEditClicked: (event)-> event.preventDefault() return unless @editable - @_originalModel = "#{@model}" + @_originalModel = @model @state = State.waiting @_beginEditing() .then => @@ -66,7 +69,7 @@ module.exports = class MarkdownSectionController extends BaseController .catch (e)=> logger.warning "cannot begin editing: #{e.stack}" @state = State.appologizing - w(true).delay(@confirmDuration).then => @state = State.viewing + w(true).delay(@confirmDuration).then => @_resetToDefaultState() onPreviewClicked: (event)-> event.preventDefault() @@ -124,6 +127,8 @@ module.exports = class MarkdownSectionController extends BaseController @$textarea = @$('textarea') @$title = @$('h2') + @_resetToDefaultState() + super refresh: -> @@ -131,17 +136,10 @@ module.exports = class MarkdownSectionController extends BaseController @_updateSizer() @_updatePreview() - @_updateStateVisibility() super onWillChangeModel: (oldModel, newModel)-> result = super oldModel, newModel - - if @state is State.waiting and newModel? - @state = State.viewing - else if not newModel? - @state = State.waiting - @$textarea.val newModel return result @@ -190,6 +188,16 @@ module.exports = class MarkdownSectionController extends BaseController @$markdownPanel.html text + _resetToDefaultState: -> + if @model? + @state = State.viewing + else + if @editable? + @state = State.creating + else + @state = State.waiting + + _updateStateVisibility: -> return if @_lastUpdatedState is @state @_lastUpdatedState = @state @@ -199,6 +207,7 @@ module.exports = class MarkdownSectionController extends BaseController buttonPanel: @$('.buttons') cancelButton: @$('button.cancel') confirmingPanel: @$('.confirming') + creatingPanel: @$('.creating') editButton: @$('button.edit') editorPanel: @$('.editor') errorPanel: @$('.error') @@ -215,6 +224,8 @@ module.exports = class MarkdownSectionController extends BaseController visible = appologizingPanel:true else if @state is State.confirming visible = confirmingPanel:true + else if @state is State.creating + visible = buttonPanel:true, creatingPanel:true, editButton:true else if @state is State.editing visible = buttonPanel:true, cancelButton:true, editorPanel:true, previewButton:true, saveButton:true else if @state is State.previewing diff --git a/src/jade/templates/markdown_section.jade b/src/jade/templates/markdown_section.jade index 44e83fb57..b06fe439f 100644 --- a/src/jade/templates/markdown_section.jade +++ b/src/jade/templates/markdown_section.jade @@ -13,6 +13,8 @@ .editor.hideable.hidden textarea .sizer + .creating.hideable.hidden + p Please help by adding a description! .buttons.hideable.hidden .error.hideable.hidden: p button.return.hideable.hidden return diff --git a/src/scss/templates/markdown_section.scss b/src/scss/templates/markdown_section.scss index 33ad6d612..9fbdfcd91 100644 --- a/src/scss/templates/markdown_section.scss +++ b/src/scss/templates/markdown_section.scss @@ -52,6 +52,17 @@ All rights reserved. } } + .creating { + width: 100%; + text-align: center; + + p { + color: $color-gray-light; + font-size: $font-size-x-large; + margin: 1em; + } + } + .editor { width: 100%;