diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 3b057c748..87946c40b 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -188,7 +188,7 @@ module.exports = (grunt)-> tasks: ['sass'] test: files: ['./src/**/*.coffee', './src/**/*.js', './test/**/*.coffee'] - tasks: ['script:clear', 'test'] + tasks: ['test'] # Compound Tasks ################################################################################################### @@ -223,7 +223,7 @@ module.exports = (grunt)-> ['prepublish', 'script:publish', 'clean', 'build'] grunt.registerTask 'start', 'build the project and start a local HTTP server', - ['build', 'script:start'] + ['script:start'] grunt.registerTask 'test', 'run unit tests', ['mochaTest'] diff --git a/assets/images/wait-small.gif b/assets/images/wait-small.gif new file mode 100644 index 000000000..8543aa5b5 Binary files /dev/null and b/assets/images/wait-small.gif differ diff --git a/scripts/start b/scripts/start index f506f49b9..4456981cc 100755 --- a/scripts/start +++ b/scripts/start @@ -1,5 +1,7 @@ #!/bin/bash +grunt watch & + pushd './build/' &>/dev/null clear nodemon ./server.coffee $* diff --git a/src/client/client.coffee b/src/client/client.coffee index 8feea423a..87b1f114d 100644 --- a/src/client/client.coffee +++ b/src/client/client.coffee @@ -37,11 +37,11 @@ switch global.hostName when 'prerender.crafting-guide.com' global.env = 'prerender' logger.level = Logger.FATAL - apiBaseUrl = 'http://local.crafting-guide.com:4347' + apiBaseUrl = 'http://prerender.crafting-guide.com:4347' when 'local.crafting-guide.com', 'localhost' global.env = 'local' logger.level = Logger.DEBUG - apiBaseUrl = 'http://local.crafting-guide.com:4347' + apiBaseUrl = "http://#{global.hostName}:4347" when 'staging.crafting-guide.com' global.env = 'staging' logger.level = Logger.VERBOSE @@ -70,7 +70,7 @@ client.onStatusChanged = (client, oldStatus, newStatus)-> client.trigger 'change:status', client, oldStatus, newStatus client.trigger 'change', client -client.checkStatus() +client.startMonitoringStatus() ######################################################################################################################## diff --git a/src/client/site/browse_page/browse_page.jade b/src/client/site/browse_page/browse_page.jade index 1a93459fa..6e13350ef 100644 --- a/src/client/site/browse_page/browse_page.jade +++ b/src/client/site/browse_page/browse_page.jade @@ -10,4 +10,4 @@ .right .tile_container - .view__suggest_mod_panel \ No newline at end of file + section.view__mod_ballot diff --git a/src/client/site/browse_page/browse_page_controller.coffee b/src/client/site/browse_page/browse_page_controller.coffee index 11d6437f2..61b5593e5 100644 --- a/src/client/site/browse_page/browse_page_controller.coffee +++ b/src/client/site/browse_page/browse_page_controller.coffee @@ -5,22 +5,38 @@ # All rights reserved. # -ModTileController = require './mod_tile/mod_tile_controller' -PageController = require '../page_controller' -SuggestModPanelController = require './suggest_mod_panel/suggest_mod_panel_controller' +ModBallotController = require './mod_ballot/mod_ballot_controller' +ModTileController = require './mod_tile/mod_tile_controller' +PageController = require '../page_controller' +{CraftingGuideClient} = require 'crafting-guide-common' ######################################################################################################################## module.exports = class BrowsePageController extends PageController constructor: (options={})-> + if not options.client then throw new Error 'options.client is required' if not options.modPack then throw new Error 'options.modPack is required' options.templateName = 'browse_page' super options + @_client = options.client @_modPack = options.modPack @_tileControllers = [] + @_client.on c.event.change, => @tryRefresh() + + # Property Methods ############################################################################# + + Object.defineProperties @prototype, + + user: + get: -> @_user + + set: (newUser)-> + @_user = newUser + @tryRefresh() + # PageController Overrides ##################################################################### getMetaDescription: -> @@ -32,12 +48,14 @@ module.exports = class BrowsePageController extends PageController # BaseController Overrides ##################################################################### onDidRender: -> - @_suggestController = @addChild SuggestModPanelController, '.view__suggest_mod_panel' + @_modBallotController = @addChild ModBallotController, '.view__mod_ballot', client:@_client, user:@_user @$tileContainer = @$('.tile_container') refresh: -> + @_modBallotController.user = @user @_refreshModTiles() + @_refreshBallot() super # Backbone.View Overrides ###################################################################### @@ -48,6 +66,12 @@ module.exports = class BrowsePageController extends PageController # Private Methods ############################################################################## + _refreshBallot: -> + if @_client.status is CraftingGuideClient.Status.Down + @_modBallotController.hide() + else + @_modBallotController.show() + _refreshModTiles: -> index = 0 mods = @_modPack.getAllMods() diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot.jade b/src/client/site/browse_page/mod_ballot/mod_ballot.jade new file mode 100644 index 000000000..4690efcd1 --- /dev/null +++ b/src/client/site/browse_page/mod_ballot/mod_ballot.jade @@ -0,0 +1,29 @@ +//- +//- Crafting Guide - mod_ballot.jade +//- +//- Copyright © 2014-2016 by Redwood Labs +//- All rights reserved. +//- + +.view__mod_ballot + .status.initial + h2 Are we missing a mod you like?
Vote for your favorites! + .button.vote-now: .bezel: p Vote Now! + + .status.loading + .status-message + + .status.loaded + h2 Are we missing a mod you like?
Vote for your favorites! + .instructions. + You can cast up to 3 votes. Change them any time. You get a vote back when a mod is added. + .instructions.remaining. + You have 0 votes remaining. + + .ballot-list + + .view__suggest_mod_panel + + .status.error + .status-message + .button.reload: .bezel: p Try Again diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot.scss b/src/client/site/browse_page/mod_ballot/mod_ballot.scss new file mode 100644 index 000000000..097f78e93 --- /dev/null +++ b/src/client/site/browse_page/mod_ballot/mod_ballot.scss @@ -0,0 +1,79 @@ +// +// Crafting Guide - mod_ballot.scss +// +// Copyright © 2014-2016 by Redwood Labs +// All rights reserved. +// + +$ballot-move-duration: 0.2s; + +.view__mod_ballot { + @include floating-panel; + + display : flex; + padding : $size-margin-medium; + + .status { + display : flex; + flex-direction : column; + + h2 { + margin-bottom: $size-margin-large; + text-align: center; + } + + &.initial { + padding: 0 15%; + } + + &.loaded { + padding: 0 15%; + + & > .instructions { + margin-bottom: $size-margin-small; + + font-family: $font-family-text; + font-size: $font-size-medium; + } + + .remaining { + margin-top: $size-margin-medium; + font-size: $font-size-large; + text-align: center; + } + + .ballot-list { + position: relative; + margin-top: $size-margin-large; + + .view__mod_ballot_line { + position: absolute; + margin-bottom: $size-margin-small; + transition: top $ballot-move-duration ease-in-out; + } + } + + .view__suggest_mod_panel { + margin-top: $size-margin-xlarge; + } + } + + &.error { + display: flex; + flex-direction: column; + align-items: center; + + .status-message { + margin-bottom: $size-margin-large; + + font-family: $font-family-header; + font-size: $font-size-medium; + text-align: center; + } + + .button { + width: $size-minecraft-block * 10; + } + } + } +} diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot_controller.coffee b/src/client/site/browse_page/mod_ballot/mod_ballot_controller.coffee new file mode 100644 index 000000000..82e5ed3dc --- /dev/null +++ b/src/client/site/browse_page/mod_ballot/mod_ballot_controller.coffee @@ -0,0 +1,270 @@ +# +# Crafting Guide - mod_ballot_controller.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +BaseController = require '../../base_controller' +ModBallotLineController = require './mod_ballot_line/mod_ballot_line_controller' +SuggestModPanelController = require './suggest_mod_panel/suggest_mod_panel_controller' + +######################################################################################################################## + +module.exports = class ModBallotController extends BaseController + + @::LOAD_ERROR = 'Sorry! Something went wrong loading the mod list. Please try again.' + + @::MAX_VOTES = 3 + + @::STATUS = + ERROR: 'error' + INITIAL: 'initial' + LOADED: 'loaded' + LOADING: 'loading' + LOGGED_OUT: 'logged-out' + + @::VOTE_ERROR = 'Sorry! Something went wrong casting your vote. Please try again.' + + constructor: (options={})-> + if not options.client then throw new Error "options.client is required" + options.model = ballot:null, votes:null + options.templateName = 'browse_page/mod_ballot' + super options + + @_client = options.client + @_lineControllers = {} + @_status = @STATUS.INITIAL + + # Property Methods ############################################################################# + + Object.defineProperties @prototype, + + isVotingAllowed: + get: -> + voteCount = @model.votes?.length or 0 + return voteCount < @MAX_VOTES + + user: + get: -> @_user + + set: (newUser)-> + @_user = newUser + + # Event Methods ################################################################################ + + onReloadClicked: -> + tracker.trackEvent c.tracking.category.modVote, 'reload' + @_reloadData() + return false + + onVoteButtonClicked: (controller)-> + modId = controller.model.ballotLine.modId + + if controller.model.vote? + tracker.trackEvent c.tracking.category.modVote, 'cancel-vote', @_findModName modId + @_cancelVote modId + else + tracker.trackEvent c.tracking.category.modVote, 'cast-vote', @_findModName modId + @_castVote modId + + onVoteNowClicked: -> + tracker.trackEvent c.tracking.category.modVote, 'vote-now' + + if not @user? + global.site.login() + else + @_reloadData() + + return false + + # BaseController Overrides ##################################################################### + + onDidRender: -> + @_suggestPanelController = @addChild SuggestModPanelController, '.view__suggest_mod_panel' + + @$ballotList = @$('.ballot-list') + @$remainingCount = @$('.remaining-count') + @$remainingMessage = @$('.remaining') + @$statusMessage = @$('.status-message') + @$statusSections = @$('.status') + super + + refresh: -> + @_refreshLines() + @_refreshRemaining() + @_refreshStatus() + super + + # Backbone.View Methods ######################################################################## + + events: -> + _.extend super, + 'click .button.reload': 'onReloadClicked' + 'click .button.vote-now': 'onVoteNowClicked' + + # Private Methods ############################################################################## + + _cancelVote: (modId)-> + controller = @_lineControllers[modId] + vote = @_findVote modId + + controller.isWaiting = true + controller.tryRefresh() + + @_client.cancelVote modVoteId:vote.id + .then => + @model.votes = _(@model.votes).without vote + controller.model.vote = null + controller.model.ballotLine.voteCount -= 1 + .catch => + @_changeStatus @STATUS.ERROR, @VOTE_ERROR + .finally => + controller.isWaiting = false + @tryRefresh() + + _castVote: (modId)-> + controller = @_lineControllers[modId] + vote = @_findVote modId + + controller.isWaiting = true + controller.tryRefresh() + + @_client.castVote modId:modId, userId:@user.id + .then (response)=> + vote = response.json + @model.votes.push vote + controller.model.vote = vote + controller.model.ballotLine.voteCount += 1 + .catch (error)=> + logger.error -> "vote could not be cast: #{error}" + @_changeStatus @STATUS.ERROR, @VOTE_ERROR + .finally => + controller.isWaiting = false + @tryRefresh() + + _computeLines: -> + return [] unless @model.ballot?.lines? + + @model.ballot.lines.sort (a, b)-> + if a.voteCount != b.voteCount + return if a.voteCount > b.voteCount then -1 else +1 + if a.name != b.name + return if a.name < b.name then -1 else +1 + return 0 + + return @model.ballot.lines + + _changeStatus: (status, message='')-> + @_status = status + @_message = message + + _findVote: (modId)-> + return null unless @model.votes? + + for vote in @model.votes + return vote if vote.modId is modId + return null + + _findModName: (modId)-> + return '' unless @model.ballot? + + for line in @model.ballot.lines + return line.name if line.modId is modId + return '' + + _refreshLines: -> + extraControllers = _.clone @_lineControllers + lines = @_computeLines() + + for line, index in lines + controller = @_lineControllers[line.modId] + delete extraControllers[line.modId] + + model = {ballotLine:line, vote:@_findVote(line.modId)} + if not controller? + controller = new ModBallotLineController + model: model + onVoteButtonClicked: (controller)=> @onVoteButtonClicked controller + controller.render() + @$ballotList.append controller.$el + @_lineControllers[line.modId] = controller + else + controller.model = model + + controller.isVotingAllowed = @isVotingAllowed + + for modId, controller of extraControllers + delete @_lineControllers[modId] + controller.remove() + + _.delay (=> @_refreshLineLayout()), 100 + + _refreshLineLayout: -> + lines = @_computeLines() + offset = @$ballotList.offset() + + for line in lines + controller = @_lineControllers[line.modId] + continue unless controller? + + if controller.$el.height() is 0 + # keep trying until the elements have been created and laid out by the browser + _.delay (=> @_refreshLineLayout()), 100 + return + + controller.$el.offset offset + offset.top += controller.$el.outerHeight(true) + + @$ballotList.height offset.top - @$ballotList.offset().top + + _refreshRemaining: -> + if @model.votes? + @$remainingCount.html @MAX_VOTES - @model.votes.length + @show @$remainingMessage + else + @hide @$remainingMessage + + _refreshStatus: -> + @hide @$('.status') + @show @$(".status.#{@_status}") + @$statusMessage.html @_message + + _reloadBallot: -> + return @_loadingBallot if @_loadingBallot? + + @model.ballot = null + @_changeStatus @STATUS.LOADING, "Loading ballot data..." + @tryRefresh() + + @_loadingBallot = @_client.getModBallot() + .then (response)=> + @model.ballot = response.json + if not @user? or @model.votes? then @_changeStatus @STATUS.LOADED + @tryRefresh() + .catch (error)=> + @_changeStatus @STATUS.ERROR, @LOAD_ERROR + .finally => + @_loadingBallot = null + + _reloadData: -> + @_reloadVotes() + @_reloadBallot() + + _reloadVotes: -> + return @_loadingVotes if @_loadingVotes? + return unless @user? + + @model.votes = null + @_changeStatus @STATUS.LOADING, "Loading ballot data..." + @tryRefresh() + + @_loadingVotes = @_client.getModVotes() + .then (response)=> + @model.votes = response.json + if @model.ballot? then @_changeStatus @STATUS.LOADED + @tryRefresh() + .catch (error)=> + @_changeStatus @STATUS.ERROR, @LOAD_ERROR + .finally => + @_loadingVotes = null diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.jade b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.jade new file mode 100644 index 000000000..81f63706d --- /dev/null +++ b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.jade @@ -0,0 +1,14 @@ +//- +//- Crafting Guide - mod_ballot_line.jade +//- +//- Copyright © 2014-2016 by Redwood Labs +//- All rights reserved. +//- + +.view__mod_ballot_line + .vote-count + img(src='/images/unknown.png') + .name + .button.vote: .bezel: p ✓ + .wait + img(src='/images/wait-small.gif') \ No newline at end of file diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.scss b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.scss new file mode 100644 index 000000000..a2dc29cd5 --- /dev/null +++ b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line.scss @@ -0,0 +1,53 @@ +// +// Crafting Guide - mod_ballot_line.scss +// +// Copyright © 2014-2016 by Redwood Labs +// All rights reserved. +// + +.view__mod_ballot_line { + width: 100%; + + display : flex; + align-items : center; + + .vote-count { + flex : 0 0 $size-minecraft-block; + margin-right : $size-margin-medium; + + font-family : $font-family-text; + font-size : $font-size-large; + text-align : right; + } + + & > img { + margin-right : $size-margin-medium; + + flex : 0 0 auto; + height : $size-minecraft-block; + width : $size-minecraft-block; + } + + .name { + margin-right : $size-margin-medium; + + flex : 1 1 auto; + font-family : $font-family-text; + font-size : $font-size-large; + } + + .button { + flex : 0 0 auto; + width : $size-minecraft-block * 1.5; + } + + .wait { + flex : 0 0 auto; + height : $size-button-height; + width : $size-minecraft-block * 1.5; + + display : flex; + align-items : center; + justify-content : center; + } +} diff --git a/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line_controller.coffee b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line_controller.coffee new file mode 100644 index 000000000..2a5bdf8c0 --- /dev/null +++ b/src/client/site/browse_page/mod_ballot/mod_ballot_line/mod_ballot_line_controller.coffee @@ -0,0 +1,86 @@ +# +# Crafting Guide - mod_ballot_line_controller.coffee +# +# Copyright © 2014-2016 by Redwood Labs +# All rights reserved. +# + +BaseController = require '../../../base_controller' + +######################################################################################################################## + +module.exports = class ModBallotLineController extends BaseController + + constructor: (options={})-> + options.templateName = 'browse_page/mod_ballot/mod_ballot_line' + super options + + @_onVoteButtonClicked = options.onVoteButtonClicked or (controller)-> # do nothing + + # Event Methods ################################################################################ + + onButtonClicked: -> + return if @$button.hasClass 'disabled' + + @_onVoteButtonClicked this + return false + + # Property Methods ############################################################################# + + Object.defineProperties @prototype, + + isVotingAllowed: + get: -> @_isVotingAllowed + + set: (isVotingAllowed)-> + @_isVotingAllowed = !! isVotingAllowed + @tryRefresh() + + isWaiting: + get: -> @_isWaiting + + set: (isWaiting)-> + @_isWaiting = !! isWaiting + @tryRefresh() + + # BaseController Overrides ##################################################################### + + onDidRender: -> + @$button = @$('.button') + @$image = @$('img') + @$name = @$('.name') + @$voteCount = @$('.vote-count') + @$wait = @$('.wait') + super + + refresh: -> + @$name.html @model.ballotLine?.name or '' + @$voteCount.html @model.ballotLine?.voteCount or '' + @_refreshVoteButton() + super + + # Backbone.View Overrides ###################################################################### + + events: -> + return _.extend super, + 'click .button': 'onButtonClicked' + + # Private Methods ############################################################################## + + _refreshVoteButton: -> + if @isWaiting + @$wait.removeClass 'hidden' + @$button.addClass 'hidden' + else + @$wait.addClass 'hidden' + @$button.removeClass 'hidden' + + if @model.vote? + @$button.addClass 'active' + else + @$button.removeClass 'active' + + if @isVotingAllowed + @$button.removeClass 'disabled' + else + @$button.addClass 'disabled' diff --git a/src/client/site/browse_page/suggest_mod_panel/suggest_mod_panel.jade b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.jade similarity index 87% rename from src/client/site/browse_page/suggest_mod_panel/suggest_mod_panel.jade rename to src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.jade index 1e30e69e3..1ad427d3f 100644 --- a/src/client/site/browse_page/suggest_mod_panel/suggest_mod_panel.jade +++ b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.jade @@ -7,7 +7,7 @@ .view__suggest_mod_panel .initial - .instructions Are we missing a mod you like? Tell us! + .instructions Still don't see the mod you want?
Tell us about it! .entry-form input.name(placeholder="enter the mod name") diff --git a/src/client/site/browse_page/suggest_mod_panel/suggest_mod_panel.scss b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.scss similarity index 66% rename from src/client/site/browse_page/suggest_mod_panel/suggest_mod_panel.scss rename to src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.scss index 2eccf22b1..9a33b386a 100644 --- a/src/client/site/browse_page/suggest_mod_panel/suggest_mod_panel.scss +++ b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel.scss @@ -6,30 +6,24 @@ // .view__suggest_mod_panel { - @include floating-panel; - display: flex; flex-direction: column; - align-items: center; - padding: $size-margin-large; .instructions { font-family: $font-family-header; font-size: $font-size-xlarge; + text-align: center; } .initial { display: flex; flex-direction: column; - align-items: center; .instructions { margin-bottom: $size-margin-large; } .entry-form { - width: $size-minecraft-block * 15; - display: flex; flex-direction: column; @@ -68,36 +62,3 @@ margin: $size-margin-xlarge 0; } } - -@include screen-size(mobile) { - - .view__suggest_mod_panel { - .instructions { - } - - .initial { - .instructions { - } - - .entry-form { - width: 100%; - - input { - &.invalid { - } - - &.valid { - } - } - - .buttons { - .button { - } - } - } - } - - .error, .sending, .success { - } - } -} diff --git a/src/client/site/browse_page/suggest_mod_panel/suggest_mod_panel_controller.coffee b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel_controller.coffee similarity index 88% rename from src/client/site/browse_page/suggest_mod_panel/suggest_mod_panel_controller.coffee rename to src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel_controller.coffee index e0e0e8fa1..bc096947d 100644 --- a/src/client/site/browse_page/suggest_mod_panel/suggest_mod_panel_controller.coffee +++ b/src/client/site/browse_page/mod_ballot/suggest_mod_panel/suggest_mod_panel_controller.coffee @@ -5,15 +5,15 @@ # All rights reserved. # -BaseController = require '../../base_controller' -EmailClient = require '../../../models/site/email_client' +BaseController = require '../../../base_controller' +EmailClient = require '../../../../models/site/email_client' ######################################################################################################################## module.exports = class SuggestModPanelController extends BaseController constructor: (options={})-> - options.templateName = 'browse_page/suggest_mod_panel' + options.templateName = 'browse_page/mod_ballot/suggest_mod_panel' super options @_emailClient = new EmailClient @@ -38,6 +38,10 @@ module.exports = class SuggestModPanelController extends BaseController return '' unless @rendered return @$urlField.val() + user: + get: -> @_user + set: (user)-> @_user = user + # Event Methods ################################################################################ onButtonClicked: (event)-> @@ -96,6 +100,12 @@ module.exports = class SuggestModPanelController extends BaseController @hide $panel for $panel in [@$initialPanel, @$sendingPanel, @$successPanel, @$errorPanel] @show $selectedPanel + data = + name: @name + url: @url + user: @user?.name + email: @user?.email + showPanel @$sendingPanel w(@_emailClient.send 'mod-suggestion', name:@name, url:@url) .then => diff --git a/src/client/site/login_page/login_page.jade b/src/client/site/login_page/login_page.jade index bfc7d1ec2..0989023e8 100644 --- a/src/client/site/login_page/login_page.jade +++ b/src/client/site/login_page/login_page.jade @@ -18,11 +18,11 @@ p. All changes you make on the site get submitted directly to our GitHub repo, and then published through the same automated deployment process as changes made by anyone else. That way, anyone - can contribute easily: whether developer or end user. + can contribute easily: whether programmer or player. p. - To log in, we redirect you to GitHub's site, and once you're signed in, they'll send you back - here. We never see your credentials, and only get the minimum set of permissions necessary to - submit changes to the Crafting Guide repo on your behalf. + To log in, we redirect you to GitHub's site. Once you're signed in over there, they'll send you + back here. We never see your credentials, and only get the minimum set of permissions necessary + to submit changes to the Crafting Guide repo on your behalf. p. GitHub offers free accounts, so even if you don't already have one, you've got nothing to lose! @@ -75,6 +75,6 @@ .left h1 Sorry! p. - Our interactive editing service is down right now, but you can always submit a pull request over at the - Crafting Guide GitHub Repository. We'll - have the editing service back up soon! + Our interactive services are down right now, but you can always submit an issue or pull request over at + the Crafting Guide GitHub Repository. We'll + have things back to normal soon! diff --git a/src/common/constants.coffee b/src/common/constants.coffee index a179c6802..aa098bf4a 100644 --- a/src/common/constants.coffee +++ b/src/common/constants.coffee @@ -166,6 +166,7 @@ tracking.category.craftWant = 'craft-want' tracking.category.feedback = 'feedback' tracking.category.markdown = 'markdown' tracking.category.modPack = 'mod-pack' +tracking.category.modVote = 'mod-vote' tracking.category.multiblock = 'multiblock' tracking.category.navigate = 'navigate' tracking.category.search = 'search'