From 4dcc5bd8ee47cbb87b8129c6b9083d144f26a4c4 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Sun, 10 Aug 2014 15:56:34 -0700 Subject: [PATCH] Adding some options to the game menu options tab. --- app/locale/en.coffee | 4 +- app/styles/game-menu/game-menu-modal.sass | 11 ++- app/styles/game-menu/options-view.sass | 68 +++++++++++++- app/templates/game-menu/options-view.jade | 73 +++++++++++---- app/views/game-menu/GameMenuModal.coffee | 6 +- app/views/game-menu/OptionsView.coffee | 104 +++++++++++++++++++++- server/delighted.coffee | 1 - 7 files changed, 243 insertions(+), 24 deletions(-) diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 557b15cd8..ca80c2416 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -463,9 +463,11 @@ inventory_caption: "Equip your hero" choose_hero_caption: "Choose hero, language" save_load_caption: "... and view history" - options_caption: "Music and other settings" + options_caption: "Configure settings" guide_caption: "Docs and tips" multiplayer_caption: "Play with friends!" + autorun: "Autorun" + autorun_description: "Control automatic code execution." save_load: granularity_saved_games: "Saved" diff --git a/app/styles/game-menu/game-menu-modal.sass b/app/styles/game-menu/game-menu-modal.sass index dcd4f7abf..428dc8e72 100644 --- a/app/styles/game-menu/game-menu-modal.sass +++ b/app/styles/game-menu/game-menu-modal.sass @@ -1,5 +1,14 @@ #game-menu-modal + .modal-dialog + margin-top: 0 + .nav-tabs + h2 + margin: 0 + + .tab-pane + h3:first-child + margin-top: 0 // http://stackoverflow.com/questions/18432577/stacked-tabs-in-bootstrap-3 .tabs-left @@ -25,7 +34,7 @@ > a min-width: 74px margin-right: 0 - margin-bottom: 3px + margin-bottom: 0 .tabs-left > .nav-tabs border-right: 1px solid #ddd diff --git a/app/styles/game-menu/options-view.sass b/app/styles/game-menu/options-view.sass index 190a9b1c1..b84c149d7 100644 --- a/app/styles/game-menu/options-view.sass +++ b/app/styles/game-menu/options-view.sass @@ -1,3 +1,67 @@ +@import "app/styles/bootstrap/variables" + #options-view - h3 - text-decoration: underline + .select-group + display: block + min-height: 20px + margin-top: 10px + margin-bottom: 10px + padding-left: 20px + vertical-align: middle + + label + font-weight: normal + margin-right: 20px + margin-bottom: 0 + + .form-group.radio-inline + input + margin-left: 0px + margin-right: 5px + + .radio-inline-parent-label + padding-left: 0 + + #player-avatar-container + position: relative + margin: 0px 0px 15px 15px + width: 230px + max-height: 230px + border-radius: 6px + + img.profile-photo + width: 100% + + &.saving + opacity: 0.5 + + .profile-caption + background-color: rgba(0, 0, 0, 0.5) + color: white + border-bottom-right-radius: 6px + border-bottom-left-radius: 6px + position: absolute + width: 100% + bottom: 0px + left: 0px + text-align: center + border: 0 + font-size: 18px + + .editable-icon + display: block + position: absolute + right: 5px + top: 5px + font-size: 20px + color: $blue + opacity: 0.5 + + &:hover + cursor: pointer + box-shadow: 0px 0px 2px 1px $blue + + .editable-icon + opacity: 1.0 + cursor: pointer + diff --git a/app/templates/game-menu/options-view.jade b/app/templates/game-menu/options-view.jade index b44e9bf35..2a33464b0 100644 --- a/app/templates/game-menu/options-view.jade +++ b/app/templates/game-menu/options-view.jade @@ -1,15 +1,58 @@ -h3 Stuff to put in there maybe -ul - li Show/Hide Grid (may destroy the grid) - li Show/Hide FPS (may keep admin-only) - li Player Name - li Player Avatar (currently just in settings, but it would be interesting to feature it in more places so that they’d actually start using it and increasing how much we can rely on it in social/multiplayer–and wherever it’s featured that’s not a tiny icon, we could let them change it) - li Default programming language - li Editor key bindings (default ACE, Emacs, Vim) - li Live autocompletion - li Show invisibles (visualize spaces/tabs) - li Show indent guides (displays vertical lines to see indentation better) - li Smart behaviors (autocompleting braces, brackets, and quotes) - li Music on/off - li Volume (assuming we’re also keeping volume next to play button in playback bar) - li Autocast Delay +#player-avatar-container(title="Click to change your avatar").pull-right + if !me.get('photoURL') + .editable-icon.glyphicon.glyphicon-pencil + img.profile-photo(src=me.getPhotoURL(230)) + .form-group + input#player-name.profile-caption(name="playerName", type="text", value=me.get('name') || 'Anoner') + +.form + h3(data-i18n="game_menu.general_options") General Options + + .form-group.checkbox + label(for="option-music") + input#option-music(name="option-music", type="checkbox", checked=music) + span(data-i18n="game_menu.music") Music + span.help-block(data-i18n="game_menu.music_description") Turn background music on/off. + + .form-group.select-group + label.control-label(for="option-autorun-delay", data-i18n="game_menu.autorun") Autorun + select#option-autorun-delay(name="autorunDelay") + option(value=1000, selected=(autorunDelay === 1000), data-i18n="common.delay_1_sec") 1 second + option(value=3000, selected=(autorunDelay === 3000), data-i18n="common.delay_3_sec") 3 seconds + option(value=5000, selected=(autorunDelay === 5000), data-i18n="common.delay_5_sec") 5 seconds + option(value=90019001, selected=(autorunDelay === 90019001), data-i18n="common.manual") Manual + span.help-block(data-i18n="game_menu.autorun_description") Control automatic code execution. + + h3(data-i18n="game_menu.editor_config_title") Editor Configuration + + .form-group.select-group + label.control-label(for="option-key-bindings", data-i18n="game_menu.editor_config_keybindings_label") Key Bindings + select#option-key-bindings(name="keyBindings") + option(value="default", selected=(aceConfig.keyBindings === "default"), data-i18n="game_menu.editor_config_keybindings_default") Default (Ace) + option(value="vim", selected=(aceConfig.keyBindings === "vim")) Vim + option(value="emacs", selected=(aceConfig.keyBindings === "emacs")) Emacs + span.help-block(data-i18n="game_menu.editor_config_keybindings_description") Adds additional shortcuts known from the common editors. + + .form-group.checkbox + label(for="option-live-completion") + input#option-live-completion(name="liveCompletion", type="checkbox", checked=aceConfig.liveCompletion) + span(data-i18n="game_menu.editor_config_livecompletion_label") Live Autocompletion + span.help-block(data-i18n="game_menu.editor_config_livecompletion_description") Displays autocomplete suggestions while typing. + + .form-group.checkbox + label(for="option-invisibles") + input#option-invisibles(name="invisibles", type="checkbox", checked=aceConfig.invisibles) + span(data-i18n="game_menu.editor_config_invisibles_label") Show Invisibles + span.help-block(data-i18n="game_menu.editor_config_invisibles_description") Displays invisibles such as spaces or tabs. + + .form-group.checkbox + label(for="option-indent-guides") + input#option-indent-guides(name="indentGuides", type="checkbox", checked=aceConfig.indentGuides) + span(data-i18n="game_menu.editor_config_indentguides_label") Show Indent Guides + span.help-block(data-i18n="game_menu.editor_config_indentguides_description") Displays vertical lines to see indentation better. + + .form-group.checkbox + label(for="option-behaviors") + input#option-behaviors(name="behaviors", type="checkbox", checked=aceConfig.behaviors) + span(data-i18n="game_menu.editor_config_behaviors_label") Smart Behaviors + span.help-block(data-i18n="game_menu.editor_config_behaviors_description") Autocompletes brackets, braces, and quotes. diff --git a/app/views/game-menu/GameMenuModal.coffee b/app/views/game-menu/GameMenuModal.coffee index 87753b52e..18b000857 100644 --- a/app/views/game-menu/GameMenuModal.coffee +++ b/app/views/game-menu/GameMenuModal.coffee @@ -11,7 +11,7 @@ submenuViews = [ module.exports = class GameMenuModal extends ModalView template: template - modalWidthPercent: 80 + modalWidthPercent: 95 id: 'game-menu-modal' events: @@ -25,3 +25,7 @@ module.exports = class GameMenuModal extends ModalView super() @insertSubView new submenuView @options for submenuView in submenuViews @subviews.inventory_view.$el.addClass 'active' + + onHidden: -> + subview.onHidden?() for subviewKey, subview of @subviews + me.patch() diff --git a/app/views/game-menu/OptionsView.coffee b/app/views/game-menu/OptionsView.coffee index 701e61372..8654554ef 100644 --- a/app/views/game-menu/OptionsView.coffee +++ b/app/views/game-menu/OptionsView.coffee @@ -2,15 +2,113 @@ CocoView = require 'views/kinds/CocoView' template = require 'templates/game-menu/options-view' {me} = require 'lib/auth' ThangType = require 'models/ThangType' +User = require 'models/User' +forms = require 'lib/forms' module.exports = class OptionsView extends CocoView id: 'options-view' className: 'tab-pane' template: template + aceConfig: {} + defaultConfig: + language: 'javascript' + keyBindings: 'default' + invisibles: false + indentGuides: false + behaviors: false + liveCompletion: true - getRenderData: (context={}) -> - context = super(context) - context + events: + 'change #option-music': 'updateMusic' + 'change #option-autorun-delay': 'updateAutorun' + 'change #option-key-bindings': 'updateInvisibles' + 'change #option-key-bindings': 'updateKeyBindings' + 'change #option-indent-guides': 'updateIndentGuides' + 'change #option-behaviors': 'updateBehaviors' + 'change #option-live-completion': 'updateLiveCompletion' + 'click .profile-photo': 'onEditProfilePhoto' + 'click .editable-icon': 'onEditProfilePhoto' + 'keyup #player-name': -> @trigger 'nameChanged' + + constructor: (options) -> + @uploadFilePath = "db/user/#{me.id}" + @onNameChange = _.debounce(@checkNameExists, 500) + @on 'nameChanged', @onNameChange + @playerName = me.get 'name' + super options + + getRenderData: (c={}) -> + c = super(c) + @aceConfig = _.cloneDeep me.get('aceConfig') ? {} + @aceConfig = _.defaults @aceConfig, @defaultConfig + c.aceConfig = @aceConfig + c.music = me.get('music') + c.autorunDelay = me.get('autocastDelay') ? 5000 + c afterRender: -> super() + + onHidden: -> + if @playerName and @playerName isnt me.get('name') + me.set 'name', @playerName + @aceConfig.invisibles = @$el.find('#option-invisibles').prop('checked') + @aceConfig.keyBindings = @$el.find('#option-key-bindings').val() + @aceConfig.indentGuides = @$el.find('#option-indent-guides').prop('checked') + @aceConfig.behaviors = @$el.find('#option-behaviors').prop('checked') + @aceConfig.liveCompletion = @$el.find('#option-live-completion').prop('checked') + me.set 'aceConfig', @aceConfig + Backbone.Mediator.publish 'tome:change-config' + + updateMusic: -> + me.set 'music', @$el.find('#option-music').prop('checked') + + updateAutorun: -> + me.set 'autocastDelay', parseInt(@$el.find('#option-autorun-delay').val()) + + updateInvisibles: -> + @aceConfig.invisibles = @$el.find('#option-invisibles').prop('checked') + + updateKeyBindings: -> + @aceConfig.keyBindings = @$el.find('#option-key-bindings').val() + + updateIndentGuides: -> + @aceConfig.indentGuides = @$el.find('#option-indent-guides').prop('checked') + + updateBehaviors: -> + @aceConfig.behaviors = @$el.find('#option-behaviors').prop('checked') + + updateLiveCompletion: -> + @aceConfig.liveCompletion = @$el.find('#option-live-completion').prop('checked') + + checkNameExists: => + forms.clearFormAlerts(@$el) + name = $('#player-name').val() + User.getUnconflictedName name, (newName) => + forms.clearFormAlerts(@$el) + if name isnt newName + forms.setErrorToProperty @$el, 'playerName', 'This name is already taken so you won\'t be able to keep it.', true + else + @playerName = newName + + onEditProfilePhoto: (e) -> + photoContainer = @$el.find('.profile-photo') + onSaving = => + photoContainer.addClass('saving') + onSaved = (uploadingPath) => + me.set('photoURL', uploadingPath) + photoContainer.removeClass('saving').attr('src', me.getPhotoURL(photoContainer.width())) + filepicker.pick {mimetypes: 'image/*'}, @onImageChosen(onSaving, onSaved) + + formatImagePostData: (inkBlob) -> + url: inkBlob.url, filename: inkBlob.filename, mimetype: inkBlob.mimetype, path: @uploadFilePath, force: true + + onImageChosen: (onSaving, onSaved) -> + (inkBlob) => + onSaving() + uploadingPath = [@uploadFilePath, inkBlob.filename].join('/') + $.ajax '/file', type: 'POST', data: @formatImagePostData(inkBlob), success: @onImageUploaded(onSaved, uploadingPath) + + onImageUploaded: (onSaved, uploadingPath) -> + (e) => + onSaved uploadingPath diff --git a/server/delighted.coffee b/server/delighted.coffee index 352287488..81fd5503e 100644 --- a/server/delighted.coffee +++ b/server/delighted.coffee @@ -3,7 +3,6 @@ request = require 'request' log = require 'winston' DELIGHTED_EMAIL_DELAY = 1 * 86400 # in seconds -DELIGHTED_EMAIL_DELAY = 10 module.exports.addDelightedUser = addDelightedUser = (user) -> return unless key = config.mail.delightedAPIKey