2010-10-10 19:18:42 -07:00
|
|
|
class OutfitsController < ApplicationController
|
2023-08-02 16:05:02 -07:00
|
|
|
before_action :find_authorized_outfit, :only => [:update, :destroy]
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-10 13:59:54 -08:00
|
|
|
def create
|
2023-11-02 13:50:33 -07:00
|
|
|
@outfit = Outfit.new(outfit_params)
|
|
|
|
|
@outfit.user = current_user
|
|
|
|
|
|
2011-02-10 14:50:47 -08:00
|
|
|
if @outfit.save
|
2012-07-17 09:15:04 -07:00
|
|
|
render :json => @outfit
|
2010-11-10 13:59:54 -08:00
|
|
|
else
|
2011-02-10 14:50:47 -08:00
|
|
|
render_outfit_errors
|
2010-11-10 13:59:54 -08:00
|
|
|
end
|
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2023-08-10 18:19:03 -07:00
|
|
|
def edit
|
|
|
|
|
render "outfits/edit", layout: false
|
|
|
|
|
end
|
|
|
|
|
|
2011-03-23 15:23:01 -07:00
|
|
|
def index
|
|
|
|
|
if user_signed_in?
|
2012-08-09 15:32:33 -07:00
|
|
|
@outfits = current_user.outfits.
|
|
|
|
|
includes(:item_outfit_relationships, {:pet_state => :pet_type}).
|
|
|
|
|
wardrobe_order
|
2011-03-23 15:23:01 -07:00
|
|
|
respond_to do |format|
|
|
|
|
|
format.html { render }
|
|
|
|
|
format.json { render :json => @outfits }
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
respond_to do |format|
|
2023-08-06 15:52:05 -07:00
|
|
|
format.html { redirect_to new_auth_user_session_path(:return_to => request.fullpath) }
|
2011-03-23 15:23:01 -07:00
|
|
|
format.json { render :json => [] }
|
|
|
|
|
end
|
|
|
|
|
end
|
2010-11-11 10:43:22 -08:00
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-11 10:43:22 -08:00
|
|
|
def destroy
|
2013-01-03 17:32:17 -08:00
|
|
|
@outfit.destroy
|
|
|
|
|
|
|
|
|
|
respond_to do |format|
|
|
|
|
|
format.html {
|
2023-08-06 17:33:57 -07:00
|
|
|
flash[:notice] = t('outfits.destroy.success',
|
2013-01-03 17:32:17 -08:00
|
|
|
:outfit_name => @outfit.name)
|
|
|
|
|
redirect_to current_user_outfits_path
|
|
|
|
|
}
|
|
|
|
|
format.json { render :json => true }
|
2010-11-24 17:51:01 -08:00
|
|
|
end
|
2010-11-11 10:43:22 -08:00
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-05 15:45:05 -07:00
|
|
|
def new
|
2024-09-27 19:38:53 -07:00
|
|
|
@colors = Color.alphabetical
|
2023-10-12 22:30:16 -07:00
|
|
|
@species = Species.alphabetical
|
2013-12-14 15:19:27 -08:00
|
|
|
|
2024-11-20 10:44:33 -08:00
|
|
|
newest_items = Item.newest.limit(18)
|
2024-10-02 17:21:37 -07:00
|
|
|
@newest_modeled_items, @newest_unmodeled_items =
|
|
|
|
|
newest_items.partition(&:predicted_fully_modeled?)
|
2024-03-13 13:51:28 -07:00
|
|
|
|
2024-10-02 17:21:37 -07:00
|
|
|
@newest_unmodeled_items_predicted_missing_species_by_color = {}
|
|
|
|
|
@newest_unmodeled_items_predicted_modeled_ratio = {}
|
|
|
|
|
@newest_unmodeled_items.each do |item|
|
|
|
|
|
h = item.predicted_missing_nonstandard_body_ids_by_species_by_color
|
|
|
|
|
standard_body_ids_by_species = item.
|
|
|
|
|
predicted_missing_standard_body_ids_by_species
|
|
|
|
|
if standard_body_ids_by_species.present?
|
|
|
|
|
h[:standard] = standard_body_ids_by_species
|
2013-12-14 15:19:27 -08:00
|
|
|
end
|
2024-10-02 17:21:37 -07:00
|
|
|
@newest_unmodeled_items_predicted_missing_species_by_color[item] = h
|
|
|
|
|
@newest_unmodeled_items_predicted_modeled_ratio[item] = item.predicted_modeled_ratio
|
2012-08-09 19:35:30 -07:00
|
|
|
end
|
2023-08-02 12:02:19 -07:00
|
|
|
|
|
|
|
|
@species_count = Species.count
|
2025-11-02 00:43:54 -07:00
|
|
|
|
2023-08-02 12:04:09 -07:00
|
|
|
@latest_contribution = Contribution.recent.first
|
|
|
|
|
Contribution.preload_contributeds_and_parents([@latest_contribution].compact)
|
2014-01-17 09:12:56 -08:00
|
|
|
|
|
|
|
|
@neopets_usernames = user_signed_in? ? current_user.neopets_usernames : []
|
2014-09-11 16:09:00 -07:00
|
|
|
|
2024-02-18 20:29:31 -08:00
|
|
|
@campaign = Fundraising::Campaign.current rescue nil
|
2010-11-05 15:45:05 -07:00
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2025-11-02 00:43:54 -07:00
|
|
|
def new_v2
|
2025-11-02 01:46:53 -07:00
|
|
|
# Get selected species and color from params, or default to Blue Acara
|
|
|
|
|
@selected_species = params[:species] ? Species.find_by_id(params[:species]) : Species.find_by_name("Acara")
|
|
|
|
|
@selected_color = params[:color] ? Color.find_by_id(params[:color]) : Color.find_by_name("Blue")
|
|
|
|
|
|
|
|
|
|
# Load valid colors for the selected species (colors that have existing pet types)
|
2025-11-02 00:43:54 -07:00
|
|
|
@species = Species.alphabetical
|
2025-11-02 01:46:53 -07:00
|
|
|
@colors = @selected_species.compatible_colors
|
2025-11-02 00:43:54 -07:00
|
|
|
|
2025-11-02 01:46:53 -07:00
|
|
|
# Find the best pet type for this species+color combo
|
|
|
|
|
# If the exact combo doesn't exist, this will fall back to a simple color
|
|
|
|
|
@pet_type = PetType.for_species_and_color(
|
|
|
|
|
species_id: @selected_species.id,
|
|
|
|
|
color_id: @selected_color.id
|
|
|
|
|
)
|
2025-11-02 00:43:54 -07:00
|
|
|
|
2025-11-02 01:46:53 -07:00
|
|
|
# Use the pet type's actual color as the selected color
|
|
|
|
|
# (might differ from requested color if we fell back to a simple color)
|
|
|
|
|
@selected_color = @pet_type&.color
|
2025-11-02 00:43:54 -07:00
|
|
|
|
|
|
|
|
# Load items from the objects[] parameter
|
|
|
|
|
item_ids = params[:objects] || []
|
|
|
|
|
items = Item.where(id: item_ids)
|
|
|
|
|
|
|
|
|
|
# Build the outfit
|
|
|
|
|
@outfit = Outfit.new(
|
|
|
|
|
pet_state: @pet_type&.canonical_pet_state,
|
|
|
|
|
worn_items: items,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Preload the manifests for all visible layers, so they load efficiently
|
|
|
|
|
# in parallel rather than sequentially when rendering
|
2025-11-02 00:58:30 -07:00
|
|
|
SwfAsset.preload_manifests(@outfit.visible_layers)
|
2025-11-02 00:43:54 -07:00
|
|
|
|
2025-11-02 23:04:59 -08:00
|
|
|
# Handle search mode
|
|
|
|
|
@search_mode = params[:q].present?
|
|
|
|
|
if @search_mode
|
|
|
|
|
search_filters = build_search_filters(params[:q], @outfit)
|
|
|
|
|
query_params = ActionController::Parameters.new(
|
|
|
|
|
search_filters.each_with_index.map { |filter, i| [i.to_s, filter] }.to_h
|
|
|
|
|
)
|
|
|
|
|
@query = Item::Search::Query.from_params(query_params, current_user)
|
|
|
|
|
@search_results = @query.results.paginate(page: params.dig(:q, :page), per_page: 30)
|
|
|
|
|
end
|
|
|
|
|
|
2025-11-02 00:43:54 -07:00
|
|
|
render layout: false
|
|
|
|
|
end
|
|
|
|
|
|
2010-11-12 17:58:28 -08:00
|
|
|
def show
|
2010-11-13 14:26:14 -08:00
|
|
|
@outfit = Outfit.find(params[:id])
|
2023-11-02 13:50:33 -07:00
|
|
|
|
|
|
|
|
respond_to do |format|
|
|
|
|
|
format.html { render "outfits/edit", layout: false }
|
|
|
|
|
format.json { render json: @outfit }
|
|
|
|
|
end
|
2010-11-12 17:58:28 -08:00
|
|
|
end
|
2012-06-05 09:44:11 -07:00
|
|
|
|
|
|
|
|
def start
|
2013-01-21 14:01:41 -08:00
|
|
|
# Start URLs are always in English, so let's make sure we search in
|
|
|
|
|
# English.
|
|
|
|
|
I18n.locale = I18n.default_locale
|
|
|
|
|
|
|
|
|
|
@species = Species.find_by_name params[:species_name]
|
|
|
|
|
@color = Color.find_by_name params[:color_name]
|
2013-01-21 12:55:48 -08:00
|
|
|
|
2012-06-05 09:44:11 -07:00
|
|
|
if @species && @color
|
|
|
|
|
redirect_to wardrobe_path(:species => @species.id, :color => @color.id)
|
|
|
|
|
else
|
|
|
|
|
not_found('species/color')
|
|
|
|
|
end
|
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-11 10:43:22 -08:00
|
|
|
def update
|
2023-11-02 13:50:33 -07:00
|
|
|
if @outfit.update(outfit_params)
|
2012-07-26 20:47:22 -07:00
|
|
|
render :json => @outfit
|
2010-11-11 10:43:22 -08:00
|
|
|
else
|
2010-11-24 17:51:01 -08:00
|
|
|
render_outfit_errors
|
2010-11-11 10:43:22 -08:00
|
|
|
end
|
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-24 17:51:01 -08:00
|
|
|
private
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2023-07-29 10:52:23 -07:00
|
|
|
def outfit_params
|
|
|
|
|
params.require(:outfit).permit(
|
2024-02-01 05:55:19 -08:00
|
|
|
:name, :starred, :alt_style_id, item_ids: {worn: [], closeted: []},
|
Oops, fix bug with saving outfits of pets loaded from Neopets.com
Okay right, the wardrobe-2020 app treats `state` as a bit of an
override thing, and `pose` is the main canonical field for how a pet
looks. We were missing a few pieces here:
1. After loading a pet, we weren't including the `pose` field in the
initial query string for the wardrobe URL, but we _were_ including
the `state` field, so the outfit would get set up with a conflicting
pet state ID vs pose.
2. When saving an outfit, we weren't taking the `state` field into
account at all. This could cause the saved outfit to not quite match
how it actually looked in-app, because the default pet state for
that species/color/pose trio could be different; and regardless, the
outfit state would come back with `appearanceId` set to `null`,
which wouldn't match the local outfit state, which would trigger an
infinite loop.
Here, we complete the round-trip of the `state` field, from pet loading
to outfit saving to the outfit data that comes back after saving!
2024-02-08 09:51:31 -08:00
|
|
|
biology: [:species_id, :color_id, :pose, :pet_state_id])
|
2023-07-29 10:52:23 -07:00
|
|
|
end
|
|
|
|
|
|
2010-11-13 14:26:14 -08:00
|
|
|
def find_authorized_outfit
|
2010-11-12 17:58:28 -08:00
|
|
|
raise ActiveRecord::RecordNotFound unless user_signed_in?
|
|
|
|
|
@outfit = current_user.outfits.find(params[:id])
|
|
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|
2010-11-24 17:51:01 -08:00
|
|
|
def render_outfit_errors
|
2013-01-04 18:08:41 -08:00
|
|
|
render :json => {:errors => @outfit.errors,
|
|
|
|
|
:full_error_messages => @outfit.errors.full_messages},
|
|
|
|
|
:status => :bad_request
|
2010-11-24 17:51:01 -08:00
|
|
|
end
|
2025-11-02 23:04:59 -08:00
|
|
|
|
|
|
|
|
def build_search_filters(query_params, outfit)
|
|
|
|
|
filters = []
|
|
|
|
|
|
|
|
|
|
# Add name filter if present
|
|
|
|
|
if query_params[:name].present?
|
|
|
|
|
filters << { key: "name", value: query_params[:name] }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Add item kind filter if present
|
|
|
|
|
if query_params[:item_kind].present?
|
|
|
|
|
case query_params[:item_kind]
|
|
|
|
|
when "nc"
|
|
|
|
|
filters << { key: "is_nc", value: "true" }
|
|
|
|
|
when "np"
|
|
|
|
|
filters << { key: "is_np", value: "true" }
|
|
|
|
|
when "pb"
|
|
|
|
|
filters << { key: "is_pb", value: "true" }
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Add zone filter if present
|
|
|
|
|
if query_params[:zone].present?
|
|
|
|
|
filters << { key: "occupied_zone_set_name", value: query_params[:zone] }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
# Always add auto-filter for items that fit the current pet
|
|
|
|
|
pet_type = outfit.pet_type
|
|
|
|
|
if pet_type
|
|
|
|
|
fit_filter = {
|
|
|
|
|
key: "fits",
|
|
|
|
|
value: {
|
|
|
|
|
species_id: pet_type.species_id.to_s,
|
|
|
|
|
color_id: pet_type.color_id.to_s
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Include alt_style_id if present
|
|
|
|
|
if outfit.alt_style_id.present?
|
|
|
|
|
fit_filter[:value][:alt_style_id] = outfit.alt_style_id.to_s
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
filters << fit_filter
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
filters
|
|
|
|
|
end
|
2010-10-10 19:18:42 -07:00
|
|
|
end
|
2011-03-23 15:23:01 -07:00
|
|
|
|