Emi Matchu
3940513244
Oh, right, silly: my previous version of this change still grouped by zone, then mapped the zones to their labels. This didn't *merge* the lists of appearances for zones that share the same label; just one of the zones would win, and the others would disappear. In this change, I just go upstream and actually group them by label in the first place, instead of grouping by zone then trying to merge and transform them.
272 lines
8.5 KiB
Ruby
272 lines
8.5 KiB
Ruby
class ItemsController < ApplicationController
|
|
before_action :set_query
|
|
before_action :support_staff_only, except: [:index, :show, :sources]
|
|
rescue_from Item::Search::Error, :with => :search_error
|
|
|
|
def index
|
|
if @query
|
|
if params[:per_page]
|
|
per_page = params[:per_page].to_i
|
|
per_page = 50 if per_page && per_page > 50
|
|
else
|
|
per_page = 30
|
|
end
|
|
|
|
@items = @query.results.paginate(
|
|
page: params[:page], per_page: per_page)
|
|
assign_closeted!(@items)
|
|
|
|
respond_to do |format|
|
|
format.html {
|
|
@campaign = Fundraising::Campaign.current rescue nil
|
|
if @items.count == 1
|
|
redirect_to @items.first
|
|
else
|
|
render
|
|
end
|
|
}
|
|
format.json {
|
|
render json: {
|
|
items: @items.as_json(
|
|
methods: [:nc?, :pb?, :owned?, :wanted?],
|
|
include: {
|
|
restricted_zones: {
|
|
only: [:id, :depth, :label],
|
|
methods: [:is_commonly_used_by_items],
|
|
},
|
|
},
|
|
),
|
|
appearances: load_appearances.as_json(
|
|
include: {
|
|
swf_assets: {
|
|
only: [:id, :remote_id, :body_id],
|
|
include: {
|
|
zone: {
|
|
only: [:id, :depth, :label],
|
|
methods: [:is_commonly_used_by_items],
|
|
},
|
|
restricted_zones: {
|
|
only: [:id, :depth, :label],
|
|
methods: [:is_commonly_used_by_items],
|
|
},
|
|
},
|
|
methods: [:urls, :known_glitches],
|
|
},
|
|
}
|
|
),
|
|
total_pages: @items.total_pages,
|
|
query: @query.to_s,
|
|
}
|
|
}
|
|
end
|
|
elsif params.has_key?(:ids) && params[:ids].is_a?(Array)
|
|
@items = Item.find(params[:ids])
|
|
assign_closeted!(@items)
|
|
respond_to do |format|
|
|
format.json { render json: @items }
|
|
end
|
|
else
|
|
respond_to do |format|
|
|
format.html {
|
|
@campaign = Fundraising::Campaign.current rescue nil
|
|
@newest_items = Item.newest.limit(18)
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
def show
|
|
@item = Item.find params[:id]
|
|
|
|
respond_to do |format|
|
|
format.html do
|
|
@trades = @item.closet_hangers.trading.user_is_active.to_trades
|
|
|
|
@contributors_with_counts = @item.contributors_with_counts
|
|
|
|
if user_signed_in?
|
|
@current_user_lists = current_user.closet_lists.alphabetical.
|
|
group_by_owned
|
|
@current_user_quantities = current_user.item_quantities_for(@item)
|
|
end
|
|
|
|
@selected_preview_pet_type = load_selected_preview_pet_type
|
|
@preview_outfit = Outfit.new(
|
|
pet_state: load_preview_pet_type.canonical_pet_state,
|
|
worn_items: [@item],
|
|
)
|
|
@preview_error = validate_preview
|
|
|
|
@all_appearances = @item.appearances
|
|
@appearances_by_occupied_zone_label =
|
|
@item.appearances_by_occupied_zone_label.sort_by { |l, a| l }
|
|
@selected_item_appearance = @preview_outfit.item_appearances.first
|
|
|
|
@preview_pet_type_options = PetType.where(color: @preview_outfit.color).
|
|
includes(:species).merge(Species.alphabetical)
|
|
end
|
|
|
|
format.gif do
|
|
expires_in 1.month
|
|
redirect_to @item.thumbnail_url, allow_other_host: true
|
|
end
|
|
end
|
|
end
|
|
|
|
def edit
|
|
@item = Item.find params[:id]
|
|
render layout: "application"
|
|
end
|
|
|
|
def update
|
|
@item = Item.find params[:id]
|
|
if @item.update(item_params)
|
|
flash[:notice] = "\"#{@item.name}\" successfully saved!"
|
|
redirect_to @item
|
|
else
|
|
render action: "edit", layout: "application", status: :bad_request
|
|
end
|
|
end
|
|
|
|
def sources
|
|
# Load all the items, then group them by source.
|
|
item_ids = params[:ids].split(",")
|
|
@all_items = Item.where(id: item_ids).includes(:nc_mall_record).
|
|
includes(:dyeworks_base_item).order(:name).limit(50)
|
|
@items = @all_items.group_by(&:source).tap { |i| i.default = [] }
|
|
|
|
assign_closeted!(@all_items)
|
|
|
|
if @all_items.empty?
|
|
render file: "public/404.html", status: :not_found, layout: nil
|
|
return
|
|
end
|
|
|
|
# For Dyeworks items whose base is currently in the NC Mall, preload their
|
|
# trade values. We'll use this to determine which ones are fully buyable rn
|
|
# (because Owls tracks this data and we don't).
|
|
Item.preload_nc_trade_values(@items[:dyeworks])
|
|
|
|
# Start loading the NC trade values for the non-Mall NC items.
|
|
trade_values_task = Async { Item.preload_nc_trade_values(@items[:other_nc]) }
|
|
|
|
# Also, PB items have some special handling: we group them by color, then
|
|
# load example pet types for the colors that don't have paint brushes.
|
|
@pb_items_by_color = @items[:pb].group_by(&:pb_color).
|
|
sort_by { |color, items| color&.name }.to_h
|
|
|
|
colors_without_thumbnails = @pb_items_by_color.keys.
|
|
select(&:present?).reject(&:pb_item_thumbnail_url?)
|
|
|
|
@pb_color_pet_types = colors_without_thumbnails.map do |color|
|
|
# Infer the ideal species from the first item we can, then try to find a
|
|
# matching pet type to use as the thumbnail, if needed.
|
|
species = @pb_items_by_color[color].map(&:pb_species).select(&:present?)
|
|
.first
|
|
[color, color.example_pet_type(preferred_species: species)]
|
|
end.to_h
|
|
|
|
# Create a second value that only include the items the user *needs*: that
|
|
# is, that they don't already own.
|
|
@items_needed = @items.transform_values { |items| items.reject(&:owned?) }
|
|
@pb_items_needed_by_color =
|
|
@pb_items_by_color.transform_values { |items| items.reject(&:owned?) }
|
|
|
|
# Finish loading the NC trade values.
|
|
trade_values_task.wait
|
|
|
|
render layout: "application"
|
|
end
|
|
|
|
protected
|
|
|
|
def item_params
|
|
params.require(:item).permit(
|
|
:name, :thumbnail_url, :description, :modeling_status_hint,
|
|
:is_manually_nc, :explicitly_body_specific,
|
|
).tap do |p|
|
|
p[:modeling_status_hint] = nil if p[:modeling_status_hint] == ""
|
|
end
|
|
end
|
|
|
|
def assign_closeted!(items)
|
|
current_user.assign_closeted_to_items!(items) if user_signed_in?
|
|
end
|
|
|
|
def load_appearances
|
|
appearance_params = params[:with_appearances_for]
|
|
return {} if appearance_params.blank?
|
|
|
|
if appearance_params[:alt_style_id].present?
|
|
target = Item::Search::Query.load_alt_style_by_id(
|
|
appearance_params[:alt_style_id])
|
|
else
|
|
target = Item::Search::Query.load_pet_type_by_color_and_species(
|
|
appearance_params[:color_id], appearance_params[:species_id])
|
|
end
|
|
|
|
target.appearances_for(@items, swf_asset_includes: [:zone]).
|
|
tap do |appearances|
|
|
# Preload the manifests for these SWF assets concurrently, rather than
|
|
# loading them in sequence when we generate the JSON.
|
|
swf_assets = appearances.values.map(&:swf_assets).flatten
|
|
SwfAsset.preload_manifests(swf_assets)
|
|
end
|
|
end
|
|
|
|
def load_selected_preview_pet_type
|
|
color_id = params.dig(:preview, :color_id)
|
|
species_id = params.dig(:preview, :species_id)
|
|
|
|
return load_default_preview_pet_type if color_id.nil? || species_id.nil?
|
|
|
|
PetType.find_or_initialize_by(color_id:, species_id:).tap do |pet_type|
|
|
if pet_type.persisted?
|
|
cookies["preferred-preview-color-id"] = color_id
|
|
cookies["preferred-preview-species-id"] = species_id
|
|
end
|
|
end
|
|
end
|
|
|
|
def load_preview_pet_type
|
|
if @selected_preview_pet_type.persisted?
|
|
@selected_preview_pet_type
|
|
else
|
|
load_default_preview_pet_type
|
|
end
|
|
end
|
|
|
|
def load_default_preview_pet_type
|
|
@item.compatible_pet_types.
|
|
preferring_species(cookies["preferred-preview-species-id"] || "<ignore>").
|
|
preferring_color(cookies["preferred-preview-color-id"] || "<ignore>").
|
|
preferring_simple.first ||
|
|
PetType.matching_name("Blue", "Acara").first!
|
|
end
|
|
|
|
def validate_preview
|
|
if @selected_preview_pet_type.new_record?
|
|
:pet_type_does_not_exist
|
|
elsif @preview_outfit.item_appearances.any?(&:empty?)
|
|
:no_item_data
|
|
end
|
|
end
|
|
|
|
def search_error(e)
|
|
@items = []
|
|
@query = params[:q]
|
|
respond_to do |format|
|
|
format.html { flash.now[:alert] = e.message; render }
|
|
format.json { render :json => {error: e.message} }
|
|
end
|
|
end
|
|
|
|
def set_query
|
|
q = params[:q]
|
|
if q.is_a?(String)
|
|
@query = Item::Search::Query.from_text(q, current_user)
|
|
elsif q.is_a?(ActionController::Parameters)
|
|
@query = Item::Search::Query.from_params(q, current_user)
|
|
end
|
|
end
|
|
end
|