diff --git a/Gemfile b/Gemfile index ce79d514..0c091191 100644 --- a/Gemfile +++ b/Gemfile @@ -64,6 +64,8 @@ gem "rails-i18n" gem 'rack-attack', '~> 2.2.0' +gem 'react-rails', '~> 0.8.0.0' + # Needed for the new asset pipeline group :assets do diff --git a/Gemfile.lock b/Gemfile.lock index f249239f..b37a0528 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -222,6 +222,11 @@ GEM rdiscount (1.6.8) rdoc (3.12.2) json (~> 1.4) + react-rails (0.8.0.0) + execjs + rails (>= 3.1) + react-source (= 0.8.0) + react-source (0.8.0) redis (3.0.3) redis-namespace (1.2.1) redis (~> 3.0.0) @@ -333,6 +338,7 @@ DEPENDENCIES rails (= 3.2.16) rails-i18n rdiscount (~> 1.6.5) + react-rails (~> 0.8.0.0) resque (~> 1.23.0) resque-retry (~> 0.1.0) resque-scheduler (~> 2.0.0.d) diff --git a/app/assets/javascripts/modeling.js.jsx b/app/assets/javascripts/modeling.js.jsx new file mode 100644 index 00000000..38fff43f --- /dev/null +++ b/app/assets/javascripts/modeling.js.jsx @@ -0,0 +1,130 @@ +/** @jsx React.DOM */ + +// Console-polyfill. MIT license. +// https://github.com/paulmillr/console-polyfill +// Make it safe to do console.log() always. +(function (con) { + 'use strict'; + var prop, method; + var empty = {}; + var dummy = function() {}; + var properties = 'memory'.split(','); + var methods = ('assert,count,debug,dir,dirxml,error,exception,group,' + + 'groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,' + + 'time,timeEnd,trace,warn').split(','); + while (prop = properties.pop()) con[prop] = con[prop] || empty; + while (method = methods.pop()) con[method] = con[method] || dummy; +})(window.console = window.console || {}); + +(function() { + var Neopia = { + User: { + get: function(id) { + return Neopia.getJSON("/users/" + id).then(function(response) { + return response.users[0]; + }); + } + }, + Customization: { + get: function(petId) { + return Neopia.getJSON("/pets/" + petId + "/customization").then(function(response) { + return response.custom_pet; + }); + } + }, + getJSON: function(path) { + return $.getJSON(Neopia.API_URL + path); + }, + init: function() { + var hostEl = $('meta[name=neopia-host]'); + if (!hostEl.length) { + throw "missing neopia-host meta tag"; + } + var host = hostEl.attr('content'); + if (!host) { + throw "neopia-host meta tag exists, but is empty"; + } + Neopia.API_URL = "http://" + host + "/api/1"; + } + }; + + var Modeling = { + _modelForItemComponents: [], + _customizationsByPetId: {}, + _addCustomization: function(customization) { + this._customizationsByPetId[customization.name] = customization; + this._update(); + }, + _getCustomizations: function() { + var modelCustomizationsByPetId = this._customizationsByPetId; + return Object.keys(modelCustomizationsByPetId).map(function(petId) { + return modelCustomizationsByPetId[petId]; + }); + }, + _loadPetCustomization: function(neopiaPetId) { + return Neopia.Customization.get(neopiaPetId) + .done(this._addCustomization.bind(this)); + }, + _loadManyPetsCustomizations: function(neopiaPetIds) { + return neopiaPetIds.map(this._loadPetCustomization.bind(this)); + }, + _loadUserCustomizations: function(neopiaUserId) { + return Neopia.User.get(neopiaUserId).then(function(neopiaUser) { + return neopiaUser.links.pets; + }).then(this._loadManyPetsCustomizations.bind(this)); + }, + _loadManyUsersCustomizations: function(neopiaUserIds) { + return neopiaUserIds.map(this._loadUserCustomizations.bind(this)); + }, + _update: function() { + var state = { + customizations: this._getCustomizations() + }; + this._modelForItemComponents.forEach(function(c) { + c.setState(state); + }); + }, + init: function() { + Neopia.init(); + this._modelForItemComponents = $('#newest-unmodeled-items li').map(function() { + return React.renderComponent(, + $(this).find('.models').get(0)); + }).toArray(); + var users = ["borovan", "donna"]; + this._loadManyUsersCustomizations(users); + } + }; + + var ModelForItem = React.createClass({ + getInitialState: function() { + return {customizations: []}; + }, + render: function() { + function createModelPet(customization) { + return ; + } + var sortedCustomizations = this.state.customizations.sort(function(a, b) { + var aName = a.name.toLowerCase(); + var bName = b.name.toLowerCase(); + if (aName < bName) return -1; + if (aName > bName) return 1; + return 0; + }); + return ; + } + }); + + var ModelPet = React.createClass({ + render: function() { + var petName = this.props.customization.name; + var imageSrc = "http://pets.neopets.com/cpn/" + petName + "/1/1.png"; + return
  • ; + } + }); + + Modeling.init(); +})(); diff --git a/app/assets/stylesheets/outfits/_new.sass b/app/assets/stylesheets/outfits/_new.sass index 752e036f..52438ed6 100644 --- a/app/assets/stylesheets/outfits/_new.sass +++ b/app/assets/stylesheets/outfits/_new.sass @@ -164,7 +164,7 @@ body.outfits-new #newest-unmodeled-items list-style: none - li + > li +clearfix margin: .5em 0 @@ -208,13 +208,44 @@ body.outfits-new height: 80px width: 80px + .missing-bodies, .models + margin-left: 82px + padding-left: 8px + padding-right: 8px + .missing-bodies font-size: 85% - margin-left: 82px - padding: .5em 8px + padding-bottom: .5em + padding-top: .5em p font-family: $main-font margin-bottom: .5em + + .models + font-size: 85% + + li + display: inline-block + margin-bottom: 2px + margin-right: 2px + + button + +reset-awesome-button + +border-radius(4px) + +box-shadow(0 1px 3px rgba(0, 0, 0, .5)) + background: $module-bg-color + border: 1px solid $module-border-color + padding-right: 8px + + &:active + position: relative + top: 1px + + img + height: 40px + margin-right: 8px + vertical-align: middle + width: 40px #latest-contribution +subtle-banner diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e427f9eb..705983c1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -87,6 +87,7 @@ module ApplicationHelper :bitly => 'http://bit.ly/javascript-api.js?version=latest&login=openneo&apiKey=R_4d0438829b7a99860de1d3edf55d8dc8', :html5 => 'http://html5shim.googlecode.com/svn/trunk/html5.js', :jquery => 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js', + :jquery20 => 'http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js', :jquery_tmpl => 'http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js', :swfobject => 'http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js' } diff --git a/app/helpers/outfits_helper.rb b/app/helpers/outfits_helper.rb index f76a5c2d..ed50c393 100644 --- a/app/helpers/outfits_helper.rb +++ b/app/helpers/outfits_helper.rb @@ -50,8 +50,12 @@ module OutfitsHelper content_tag(:dd, search_query_description(base, filter_key)) end + def neopia_host + Rails.configuration.neopia_host + end + def remote_load_pet_path - "http://#{Rails.configuration.neopia_host}/api/1/pet/customization" + "http://#{neopia_host}/api/1/pet/customization" end def render_predicted_missing_species_by_color(species_by_color) diff --git a/app/views/outfits/new.html.haml b/app/views/outfits/new.html.haml index 1f57426d..f706ca3a 100644 --- a/app/views/outfits/new.html.haml +++ b/app/views/outfits/new.html.haml @@ -87,6 +87,7 @@ %span.meter{style: "width: #{@newest_unmodeled_items_predicted_modeled_ratio[item]*100}%"} .missing-bodies = render_predicted_missing_species_by_color(@newest_unmodeled_items_predicted_missing_species_by_color[item]) + .models - if @newest_modeled_items.present? %h3= t '.newest_items.modeled.header' %ul#newest-modeled-items @@ -110,6 +111,9 @@ %script#preview-pet-not-found-template{:type => 'text/x-jquery-tmpl'} = t '.preview.pet_not_found' +- content_for :meta do + %meta{name: 'neopia-host', content: neopia_host} + - content_for :javascripts do - = include_javascript_libraries :jquery, :jquery_tmpl - = javascript_include_tag 'jquery.timeago', 'pet_query', 'outfits/new' \ No newline at end of file + = include_javascript_libraries :jquery20, :jquery_tmpl + = javascript_include_tag 'react', 'jquery.timeago', 'pet_query', 'outfits/new', 'modeling' \ No newline at end of file diff --git a/config/environments/development.rb b/config/environments/development.rb index 7cc98857..60312fd4 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -30,6 +30,8 @@ OpenneoImpressItems::Application.configure do # Expands the lines which load the assets config.assets.debug = true + + config.react.variant = :development end LocalImpressHost = 'betanewimpress.openneo.net' diff --git a/config/environments/production.rb b/config/environments/production.rb index 6f76de21..361292e5 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -59,6 +59,8 @@ OpenneoImpressItems::Application.configure do # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true + + config.react.variant = :production end LocalImpressHost = 'newimpress.openneo.net' diff --git a/vendor/cache/react-rails-0.8.0.0.gem b/vendor/cache/react-rails-0.8.0.0.gem new file mode 100644 index 00000000..68761c11 Binary files /dev/null and b/vendor/cache/react-rails-0.8.0.0.gem differ diff --git a/vendor/cache/react-source-0.8.0.gem b/vendor/cache/react-source-0.8.0.gem new file mode 100644 index 00000000..6ea3fd31 Binary files /dev/null and b/vendor/cache/react-source-0.8.0.gem differ