Compare commits

..

No commits in common. "d34bebc336e5bccc5ddc53bc855ccc7ddfdbeffa" and "ecde507b60770c9233e0ece322641f85fc0f1e3e" have entirely different histories.

10 changed files with 28 additions and 162 deletions

View file

@ -4,9 +4,6 @@
.item-list .item-list
border-collapse: collapse border-collapse: collapse
border: 1px solid $soft-border-color border: 1px solid $soft-border-color
width: 60%
table-layout: auto
margin-bottom: 2em
td, th td, th
border-top: 1px solid $soft-border-color border-top: 1px solid $soft-border-color
@ -20,29 +17,20 @@
&:last-child &:last-child
padding-right: .5em padding-right: .5em
.thumbnail-cell .thumbnail-cell img
display: block
width: 2.5em width: 2.5em
height: 2.5em height: 2.5em
img
display: block
width: 100%
height: 100%
.name-cell a
text-decoration: none
&:hover, &:focus
text-decoration: underline
.actions-cell .actions-cell
text-align: right text-align: right
padding-left: 1em padding-left: 1em
font-size: 85% font-size: 85%
tbody .name-cell a
tr text-decoration: none
&:hover, &:focus-within &:hover
background: rgba($module-bg-color, 0.5) text-decoration: underline
thead thead
background: $module-bg-color background: $module-bg-color
@ -50,23 +38,6 @@
th th
text-align: left text-align: left
.thumbnail-cell img
outline: 1px solid $soft-border-color
.actions-cell button .actions-cell button
/* Bootstrap's Purple 600 */ /* Bootstrap's Purple 600 */
+awesome-button-color(#59359a) +awesome-button-color(#59359a)
.special-color-explanation
text-wrap: balance
font-style: italic
/* For wearable items that belong to a specific set that all come together,
* like a Paint Brush. */
&[data-group-type="bundle"]
tbody
.thumbnail-cell
opacity: 0.65
tr:hover .thumbnail-cell
opacity: 0.85

View file

@ -114,36 +114,18 @@ class ItemsController < ApplicationController
def sources def sources
item_ids = params[:ids].split(",") item_ids = params[:ids].split(",")
@items = Item.where(id: item_ids).includes(:nc_mall_record).order(:name). @items = Item.where(id: item_ids).includes(:nc_mall_record).order(:name)
limit(50)
if @items.empty? if @items.empty?
render file: "public/404.html", status: :not_found, layout: nil render file: "public/404.html", status: :not_found, layout: nil
return return
end end
# Group the items by category!
@nc_mall_items = @items.select(&:currently_in_mall?) @nc_mall_items = @items.select(&:currently_in_mall?)
@other_nc_items = @items.select(&:nc?).reject(&:currently_in_mall?) @other_nc_items = @items.select(&:nc?).reject(&:currently_in_mall?)
@np_items = @items.select(&:np?) @np_items = @items.select(&:np?)
@pb_items = @items.select(&:pb?) @pb_items = @items.select(&:pb?)
# 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 = @pb_items.group_by(&:pb_color).
sort_by { |color, items| color.name }.to_h
colors_without_thumbnails =
@pb_items_by_color.keys.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
render layout: "application" render layout: "application"
end end

View file

@ -2,34 +2,23 @@ require "addressable/template"
module ItemsHelper module ItemsHelper
module PetTypeImage module PetTypeImage
Template = Addressable::Template.new( Format = 'https://pets.neopets.com/cp/%s/%i/%i.png'
"https://pets.neopets.com/cp/{hash}/{emotion}/{size}.png"
)
Emotions = { Emotions = {
happy: 1, :happy => 1,
sad: 2, :sad => 2,
angry: 3, :angry => 3,
ill: 4, :ill => 4
} }
Sizes = { Sizes = {
face: 1, :face => 1,
thumb: 2, :thumb => 2,
zoom: 3, :zoom => 3,
full: 4, :full => 4
face_2x: 6,
} }
end end
def pet_type_image_url(pet_type, emotion: :happy, size: :face)
PetTypeImage::Template.expand(
hash: pet_type.basic_image_hash || pet_type.image_hash,
emotion: PetTypeImage::Emotions[emotion],
size: PetTypeImage::Sizes[size],
).to_s
end
def standard_species_search_links def standard_species_search_links
build_on_pet_types(Species.alphabetical) do |pet_type| build_on_pet_types(Species.alphabetical) do |pet_type|
image = pet_type_image(pet_type, :happy, :zoom) image = pet_type_image(pet_type, :happy, :zoom)
@ -108,9 +97,8 @@ module ItemsHelper
SHOP_WIZARD_URL_TEMPLATE = Addressable::Template.new( SHOP_WIZARD_URL_TEMPLATE = Addressable::Template.new(
"https://www.neopets.com/shops/wizard.phtml{?string}" "https://www.neopets.com/shops/wizard.phtml{?string}"
) )
def shop_wizard_url_for(item_or_name) def shop_wizard_url_for(item)
item_or_name = item_or_name.name if item_or_name.is_a? Item SHOP_WIZARD_URL_TEMPLATE.expand(string: item.name).to_s
SHOP_WIZARD_URL_TEMPLATE.expand(string: item_or_name).to_s
end end
SUPER_SHOP_WIZARD_URL_TEMPLATE = Addressable::Template.new( SUPER_SHOP_WIZARD_URL_TEMPLATE = Addressable::Template.new(
@ -123,9 +111,8 @@ module ItemsHelper
TRADING_POST_URL_TEMPLATE = Addressable::Template.new( TRADING_POST_URL_TEMPLATE = Addressable::Template.new(
"https://www.neopets.com/island/tradingpost.phtml?type=browse&criteria=item_exact{&search_string}" "https://www.neopets.com/island/tradingpost.phtml?type=browse&criteria=item_exact{&search_string}"
) )
def trading_post_url_for(item_or_name) def trading_post_url_for(item)
item_or_name = item_or_name.name if item_or_name.is_a? Item TRADING_POST_URL_TEMPLATE.expand(search_string: item.name).to_s
TRADING_POST_URL_TEMPLATE.expand(search_string: item_or_name).to_s
end end
AUCTION_GENIE_URL_TEMPLATE = Addressable::Template.new( AUCTION_GENIE_URL_TEMPLATE = Addressable::Template.new(
@ -168,7 +155,9 @@ module ItemsHelper
end end
def pet_type_image(pet_type, emotion, size) def pet_type_image(pet_type, emotion, size)
src = pet_type_image_url(pet_type, emotion:, size:) emotion_id = PetTypeImage::Emotions[emotion]
size_id = PetTypeImage::Sizes[size]
src = sprintf(PetTypeImage::Format, pet_type.basic_image_hash, emotion_id, size_id)
human_name = pet_type.species.name.humanize human_name = pet_type.species.name.humanize
image_tag(src, :alt => human_name, :title => human_name) image_tag(src, :alt => human_name, :title => human_name)
end end

View file

@ -21,11 +21,6 @@ class Color < ApplicationRecord
end end
end end
def example_pet_type(preferred_species: Species.first)
pet_types.order([Arel.sql("species_id = ? DESC"), preferred_species.id],
"species_id ASC").first
end
def unfunny_human_name def unfunny_human_name
if name if name
name.split(' ').map { |word| word.capitalize }.join(' ') name.split(' ').map { |word| word.capitalize }.join(' ')

View file

@ -179,36 +179,6 @@ class Item < ApplicationRecord
nc_mall_record&.current_price nc_mall_record&.current_price
end end
# If this is a PB item, return the corresponding Color, inferred from the
# item name. If it's not a PB item, or we fail to infer, return nil.
def pb_color
return nil unless pb?
# NOTE: To handle colors like "Royalboy", where the items aren't consistent
# with the color name regarding whether or not there's spaces, we remove
# all spaces from the item name and color name when matching. We also
# hackily handle the fact that "Elderlyboy" color has items named "Elderly
# Male" (and same for Girl/Female) by replacing those words, too. These
# hacks could cause false matches in theory, but I'm not aware of any rn!
normalized_name = name.downcase.gsub("female", "girl").gsub("male", "boy").
gsub(/\s/, "")
Color.order(:name).
find { |c| normalized_name.include?(c.name.downcase.gsub(/\s/, "")) }
end
# If this is a PB item, return the corresponding Species, inferred from the
# item name. If it's not a PB item, or we fail to infer, return nil.
def pb_species
return nil unless pb?
normalized_name = name.downcase
Species.order(:name).find { |s| normalized_name.include?(s.name.downcase) }
end
def pb_item_name
pb_color&.pb_item_name
end
def restricted_zones(options={}) def restricted_zones(options={})
options[:scope] ||= Zone.all options[:scope] ||= Zone.all
options[:scope].find(restricted_zone_ids) options[:scope].find(restricted_zone_ids)

View file

@ -1,6 +1,4 @@
%tr %tr
%td.thumbnail-cell %td.thumbnail-cell= link_to item_thumbnail_for(item), item, target: "_blank"
= link_to item_thumbnail_for(item), item, target: "_blank",
tabindex: "-1"
%td.name-cell= link_to item.name, item, target: "_blank" %td.name-cell= link_to item.name, item, target: "_blank"
%td.actions-cell= yield %td.actions-cell= yield

View file

@ -54,40 +54,13 @@
target: "_blank", icon: search_icon target: "_blank", icon: search_icon
- if @pb_items.present? - if @pb_items.present?
%h2 Paint Brush items %h2 Paintbrush items
:markdown :markdown
These items are part of a paint brush set. Once you paint your pet, These items are part of a paintbrush set. Once you paint your pet,
these items will be semi-permanently added to your Closet, even if your these items will be semi-permanently added to your Closet, even if your
pet changes color again! You can use this to mix-and-match styles for pet changes color again! You can use this to mix-and-match styles for
"cross-paint" outfits. "cross-paint" outfits.
- @pb_items_by_color.each do |color, items| = render @pb_items
%table.item-list{"data-group-type": "bundle"}
%thead
%td.thumbnail-cell
- if color.pb_item_thumbnail_url?
= image_tag color.pb_item_thumbnail_url,
alt: "Item thumbnail for #{color.pb_item_name}"
- else
= image_tag pet_type_image_url(@pb_color_pet_types[color], size: :face),
srcset: ["#{pet_type_image_url(@pb_color_pet_types[color], size: :face_2x)} 2x"],
alt: @pb_color_pet_types[color].human_name
%th
#{color.pb_item_name || color.name.humanize}
(#{pluralize items.size, "item"})
%td.actions-cell
- if color.pb_item_name?
= button_link_to "Shops",
shop_wizard_url_for(color.pb_item_name),
target: "_blank", icon: search_icon
= button_link_to "Trades",
trading_post_url_for(color.pb_item_name),
target: "_blank", icon: search_icon
- else
.special-color-explanation
Get via Lab Ray, morphing potions, etc.
%tbody
- items.each do |item|
= render "item_list_row", item:
- if @other_nc_items.present? - if @other_nc_items.present?
%h2 Neocash items (Capsules, Dyeworks, events, retired, etc.) %h2 Neocash items (Capsules, Dyeworks, events, retired, etc.)

View file

@ -1,5 +0,0 @@
class AddPbItemNameToColors < ActiveRecord::Migration[7.1]
def change
add_column :colors, :pb_item_name, :string
end
end

View file

@ -1,5 +0,0 @@
class AddPbItemThumbnailUrlToColors < ActiveRecord::Migration[7.1]
def change
add_column :colors, :pb_item_thumbnail_url, :string
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_05_22_233638) do ActiveRecord::Schema[7.1].define(version: 2024_05_11_003019) do
create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t| create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t|
t.integer "species_id", null: false t.integer "species_id", null: false
t.integer "color_id", null: false t.integer "color_id", null: false
@ -76,8 +76,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_05_22_233638) do
t.boolean "standard" t.boolean "standard"
t.boolean "prank", default: false, null: false t.boolean "prank", default: false, null: false
t.string "name", null: false t.string "name", null: false
t.string "pb_item_name"
t.string "pb_item_thumbnail_url"
end end
create_table "contributions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t| create_table "contributions", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_520_ci", force: :cascade do |t|