Compare commits
18 commits
68578aa929
...
42b1bf3e8c
Author | SHA1 | Date | |
---|---|---|---|
42b1bf3e8c | |||
5d577db649 | |||
8e3d2b994f | |||
507b346c2c | |||
77d88e50a6 | |||
abe2747b93 | |||
31468c9682 | |||
e8832f2c36 | |||
e4fb067e45 | |||
b9bb697ca1 | |||
eb6f196b15 | |||
4b9e11fc2a | |||
bfb11e94e3 | |||
c6927c2ce8 | |||
402e3d4afb | |||
a03ae90697 | |||
80e158caf7 | |||
b1c1bea7be |
23 changed files with 502 additions and 376 deletions
22
app/assets/javascripts/items/item_header.js
Normal file
22
app/assets/javascripts/items/item_header.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
const userListSections = document.querySelectorAll(
|
||||||
|
".item-header .user-lists-section");
|
||||||
|
for (const section of userListSections) {
|
||||||
|
try {
|
||||||
|
const dialog = section.querySelector("dialog");
|
||||||
|
const opener = section.querySelector(".dialog-opener");
|
||||||
|
const closer = section.querySelector(".dialog-closer");
|
||||||
|
if (dialog.showModal) { // check browser support
|
||||||
|
opener.addEventListener("click", (event) => {
|
||||||
|
dialog.show();
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
document.body.addEventListener("click", (event) => {
|
||||||
|
if (dialog.open && !section.contains(event.target)) {
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error applying dialog behavior to item header:`, error);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
@import "partials/campaign-progress"
|
@import "partials/campaign-progress"
|
||||||
|
|
||||||
body.items
|
body.items, body.item_trades
|
||||||
+campaign-progress
|
+campaign-progress
|
||||||
|
|
||||||
text-align: center
|
text-align: center
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
@import items
|
@import items
|
||||||
@import items/index
|
@import items/index
|
||||||
@import items/show
|
@import items/show
|
||||||
|
@import item_trades/index
|
||||||
@import outfits/index
|
@import outfits/index
|
||||||
@import outfits/new
|
@import outfits/new
|
||||||
@import pets/bulk
|
@import pets/bulk
|
||||||
|
|
29
app/assets/stylesheets/item_trades/_index.sass
Normal file
29
app/assets/stylesheets/item_trades/_index.sass
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
@import "../partials/item_header"
|
||||||
|
|
||||||
|
body.item_trades-index
|
||||||
|
.item-header
|
||||||
|
+item-header
|
||||||
|
|
||||||
|
.item-subpage-title
|
||||||
|
text-align: left
|
||||||
|
margin-bottom: .5em
|
||||||
|
|
||||||
|
.trades-table
|
||||||
|
text-align: left
|
||||||
|
width: 100%
|
||||||
|
table-layout: fixed
|
||||||
|
|
||||||
|
th, td
|
||||||
|
&:nth-child(1), &:nth-child(2)
|
||||||
|
width: 15ch
|
||||||
|
overflow: hidden
|
||||||
|
text-overflow: ellipsis
|
||||||
|
|
||||||
|
.trade-list-names
|
||||||
|
list-style: none
|
||||||
|
|
||||||
|
li
|
||||||
|
display: inline
|
||||||
|
|
||||||
|
&:not(:last-child)::after
|
||||||
|
content: ", "
|
|
@ -1,152 +1,11 @@
|
||||||
@import "../partials/clean/constants"
|
@import "../partials/clean/constants"
|
||||||
@import "../partials/clean/mixins"
|
@import "../partials/clean/mixins"
|
||||||
|
@import "../partials/item_header"
|
||||||
|
|
||||||
body.items-show
|
body.items-show
|
||||||
#item-header
|
.item-header
|
||||||
border-bottom: 1px solid $module-border-color
|
+item-header
|
||||||
margin-bottom: 1em
|
|
||||||
padding: 1em 0
|
|
||||||
|
|
||||||
display: grid
|
|
||||||
grid-template-areas: "img gap1" "img name" "img links" "img gap2"
|
|
||||||
align-items: center
|
|
||||||
justify-content: center
|
|
||||||
column-gap: 1em
|
|
||||||
row-gap: .5em
|
|
||||||
|
|
||||||
#item-thumbnail
|
|
||||||
grid-area: img
|
|
||||||
|
|
||||||
border: 1px solid $module-border-color
|
|
||||||
height: 80px
|
|
||||||
width: 80px
|
|
||||||
|
|
||||||
#item-name
|
|
||||||
grid-area: name
|
|
||||||
|
|
||||||
text-align: left
|
|
||||||
line-height: 100%
|
|
||||||
|
|
||||||
#item-links
|
|
||||||
grid-area: links
|
|
||||||
|
|
||||||
text-align: left
|
|
||||||
a
|
|
||||||
font-size: 75%
|
|
||||||
margin-left: 1em
|
|
||||||
|
|
||||||
#item-name
|
|
||||||
margin-bottom: 0
|
|
||||||
|
|
||||||
#item-info-section
|
|
||||||
display: grid
|
|
||||||
|
|
||||||
grid-template-areas: "info form"
|
|
||||||
grid-template-columns: 1fr auto
|
|
||||||
|
|
||||||
#item-info
|
|
||||||
grid-area: info
|
|
||||||
|
|
||||||
#item-zones
|
|
||||||
font:
|
|
||||||
family: $text-font
|
|
||||||
size: 85%
|
|
||||||
margin-bottom: 1em
|
|
||||||
|
|
||||||
p
|
|
||||||
display: inline
|
|
||||||
|
|
||||||
&:first-child
|
|
||||||
margin-right: 1em
|
|
||||||
|
|
||||||
#your-items-form
|
|
||||||
grid-area: form
|
|
||||||
|
|
||||||
border: 1px solid $module-border-color
|
|
||||||
font-size: 85%
|
|
||||||
margin-bottom: 3em
|
|
||||||
margin-left: 1em
|
|
||||||
padding: 1em
|
|
||||||
width: 30em
|
|
||||||
|
|
||||||
// compete with #trade-hangers
|
|
||||||
position: relative
|
|
||||||
z-index: 2
|
|
||||||
|
|
||||||
h3
|
|
||||||
font-size: 150%
|
|
||||||
font-weight: bold
|
|
||||||
margin-bottom: .25em
|
|
||||||
|
|
||||||
#closet-hangers-ownership-groups
|
|
||||||
+clearfix
|
|
||||||
margin-bottom: .5em
|
|
||||||
|
|
||||||
div
|
|
||||||
float: left
|
|
||||||
margin: 0 5%
|
|
||||||
text-align: left
|
|
||||||
width: 40%
|
|
||||||
|
|
||||||
li
|
|
||||||
list-style: none
|
|
||||||
word-wrap: break-word
|
|
||||||
|
|
||||||
label.unlisted
|
|
||||||
font-style: italic
|
|
||||||
|
|
||||||
form
|
|
||||||
padding: .5em 0
|
|
||||||
|
|
||||||
select
|
|
||||||
width: 9em
|
|
||||||
|
|
||||||
input[type=number]
|
|
||||||
margin-right: .5em
|
|
||||||
width: 3em
|
|
||||||
|
|
||||||
#trade-hangers
|
|
||||||
font-size: 85%
|
|
||||||
margin-bottom: 3em
|
|
||||||
text-align: left
|
|
||||||
|
|
||||||
p
|
|
||||||
position: relative
|
|
||||||
|
|
||||||
&:first-child
|
|
||||||
margin-bottom: .5em
|
|
||||||
|
|
||||||
&[data-overflows]
|
|
||||||
overflow: hidden
|
|
||||||
.toggle
|
|
||||||
display: block
|
|
||||||
|
|
||||||
&[data-showing-more]
|
|
||||||
.toggle
|
|
||||||
.less
|
|
||||||
display: block
|
|
||||||
|
|
||||||
.more
|
|
||||||
display: none
|
|
||||||
|
|
||||||
.toggle
|
|
||||||
background: white
|
|
||||||
bottom: 0
|
|
||||||
cursor: pointer
|
|
||||||
display: none
|
|
||||||
font-family: $main-font
|
|
||||||
padding: 0 1em
|
|
||||||
position: absolute
|
|
||||||
right: 0
|
|
||||||
|
|
||||||
button
|
|
||||||
+reset-awesome-button
|
|
||||||
&:hover
|
|
||||||
text-decoration: underline
|
|
||||||
|
|
||||||
.less
|
|
||||||
display: none
|
|
||||||
|
|
||||||
#item-contributors
|
#item-contributors
|
||||||
+subtle-banner
|
+subtle-banner
|
||||||
clear: both
|
clear: both
|
||||||
|
@ -172,13 +31,3 @@ body.items-show
|
||||||
.nc-icon
|
.nc-icon
|
||||||
height: 16px
|
height: 16px
|
||||||
width: 16px
|
width: 16px
|
||||||
|
|
||||||
&.js
|
|
||||||
#trade-hangers
|
|
||||||
p
|
|
||||||
max-height: 3em
|
|
||||||
overflow: hidden
|
|
||||||
|
|
||||||
&.showing-more
|
|
||||||
max-height: none
|
|
||||||
|
|
||||||
|
|
158
app/assets/stylesheets/partials/_item_header.sass
Normal file
158
app/assets/stylesheets/partials/_item_header.sass
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
@import "../partials/clean/constants"
|
||||||
|
@import "../partials/clean/mixins"
|
||||||
|
|
||||||
|
=item-header
|
||||||
|
border-bottom: 1px solid $module-border-color
|
||||||
|
margin-top: 1em
|
||||||
|
margin-bottom: 1em
|
||||||
|
|
||||||
|
.item-header-main
|
||||||
|
display: grid
|
||||||
|
grid-template-areas: "img gap1" "img name" "img links" "img lists" "img gap2" "nav nav"
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
column-gap: 1em
|
||||||
|
row-gap: .5em
|
||||||
|
|
||||||
|
.item-thumbnail
|
||||||
|
grid-area: img
|
||||||
|
|
||||||
|
border: 1px solid $module-border-color
|
||||||
|
height: 80px
|
||||||
|
width: 80px
|
||||||
|
|
||||||
|
.item-name
|
||||||
|
grid-area: name
|
||||||
|
|
||||||
|
text-align: left
|
||||||
|
line-height: 100%
|
||||||
|
margin-bottom: 0
|
||||||
|
|
||||||
|
.item-links
|
||||||
|
grid-area: links
|
||||||
|
|
||||||
|
font-size: 85%
|
||||||
|
text-align: left
|
||||||
|
display: flex
|
||||||
|
align-items: center
|
||||||
|
gap: 1em
|
||||||
|
|
||||||
|
.item-kind
|
||||||
|
padding: .25em .5em
|
||||||
|
border-radius: .25em
|
||||||
|
cursor: help
|
||||||
|
text-decoration: none
|
||||||
|
|
||||||
|
font-weight: bold
|
||||||
|
line-height: 1
|
||||||
|
|
||||||
|
// These colors are copied from DTI 2020, for initial consistency!
|
||||||
|
// They're based on the Chakra UI colors, which I think are in turn the
|
||||||
|
// Bootstrap colors? Or something?
|
||||||
|
&[data-type=nc]
|
||||||
|
background: #E9D8FD
|
||||||
|
color: #44337A
|
||||||
|
&[data-type=pb]
|
||||||
|
background: #FEEBC8
|
||||||
|
color: #7B341E
|
||||||
|
&[data-type=np]
|
||||||
|
background: #E2E8F0
|
||||||
|
color: #1A202C
|
||||||
|
|
||||||
|
.user-lists-section
|
||||||
|
grid-area: lists
|
||||||
|
font-size: 85%
|
||||||
|
text-align: left
|
||||||
|
|
||||||
|
.dialog-opener
|
||||||
|
&::after
|
||||||
|
content: " ›"
|
||||||
|
|
||||||
|
dialog
|
||||||
|
background: $background-color
|
||||||
|
border: 1px solid $module-border-color
|
||||||
|
border-radius: .5em
|
||||||
|
padding: 1em
|
||||||
|
width: 30em
|
||||||
|
text-align: center
|
||||||
|
z-index: 2
|
||||||
|
margin-top: .5em
|
||||||
|
box-shadow: 0px 1px 4px #666
|
||||||
|
|
||||||
|
h3
|
||||||
|
font-size: 150%
|
||||||
|
font-weight: bold
|
||||||
|
margin-bottom: .25em
|
||||||
|
|
||||||
|
.closet-hangers-ownership-groups
|
||||||
|
+clearfix
|
||||||
|
margin-bottom: .5em
|
||||||
|
|
||||||
|
div
|
||||||
|
float: left
|
||||||
|
margin: 0 5%
|
||||||
|
text-align: left
|
||||||
|
width: 40%
|
||||||
|
|
||||||
|
li
|
||||||
|
list-style: none
|
||||||
|
word-wrap: break-word
|
||||||
|
|
||||||
|
label.unlisted
|
||||||
|
font-style: italic
|
||||||
|
|
||||||
|
form
|
||||||
|
padding: .5em 0
|
||||||
|
|
||||||
|
select
|
||||||
|
width: 9em
|
||||||
|
|
||||||
|
input[type=number]
|
||||||
|
margin-right: .5em
|
||||||
|
width: 3em
|
||||||
|
|
||||||
|
.item-description
|
||||||
|
margin-top: .5em
|
||||||
|
margin-bottom: 1em
|
||||||
|
|
||||||
|
.item-subpages-nav
|
||||||
|
display: flex
|
||||||
|
align-items: flex-end
|
||||||
|
|
||||||
|
.preview-link
|
||||||
|
margin-right: auto
|
||||||
|
|
||||||
|
.trades-section
|
||||||
|
display: flex
|
||||||
|
gap: .5em
|
||||||
|
|
||||||
|
header
|
||||||
|
align-self: center
|
||||||
|
font-weight: bold
|
||||||
|
|
||||||
|
&::after
|
||||||
|
content: ":"
|
||||||
|
|
||||||
|
ul
|
||||||
|
align-self: flex-end
|
||||||
|
list-style: none
|
||||||
|
display: flex
|
||||||
|
gap: .5em
|
||||||
|
|
||||||
|
li
|
||||||
|
display: block
|
||||||
|
|
||||||
|
a
|
||||||
|
display: block
|
||||||
|
border: 1px solid $module-border-color
|
||||||
|
border-bottom: 0
|
||||||
|
border-radius: .5em .5em 0 0
|
||||||
|
padding: .5em 1em
|
||||||
|
text-decoration: none
|
||||||
|
|
||||||
|
&:hover, &:focus
|
||||||
|
text-decoration: underline
|
||||||
|
|
||||||
|
&[data-is-current=true]
|
||||||
|
background: $module-bg-color
|
||||||
|
font-weight: bold
|
29
app/controllers/item_trades_controller.rb
Normal file
29
app/controllers/item_trades_controller.rb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
class ItemTradesController < ApplicationController
|
||||||
|
def index
|
||||||
|
@item = Item.find params[:item_id]
|
||||||
|
@type = type_from_params
|
||||||
|
|
||||||
|
@item_trades = @item.closet_hangers.trading.includes(:user, :list).
|
||||||
|
user_is_active.order('users.last_trade_activity_at DESC').to_trades
|
||||||
|
@trades = @item_trades[@type]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
render layout: 'items'
|
||||||
|
end
|
||||||
|
|
||||||
|
def type_from_params
|
||||||
|
case params[:type]
|
||||||
|
when 'offering'
|
||||||
|
:offering
|
||||||
|
when 'seeking'
|
||||||
|
:seeking
|
||||||
|
else
|
||||||
|
raise ArgumentError, "unexpected trades type: #{params[:type].inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -58,49 +58,15 @@ class ItemsController < ApplicationController
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do
|
format.html do
|
||||||
@occupied_zones = @item.occupied_zones(
|
@trades = @item.closet_hangers.trading.user_is_active.to_trades
|
||||||
scope: Zone.includes_translations.alphabetical
|
|
||||||
)
|
|
||||||
@restricted_zones = @item.restricted_zones(
|
|
||||||
scope: Zone.includes_translations.alphabetical
|
|
||||||
)
|
|
||||||
|
|
||||||
@contributors_with_counts = @item.contributors_with_counts
|
@contributors_with_counts = @item.contributors_with_counts
|
||||||
|
|
||||||
@supported_species_ids = @item.supported_species_ids
|
|
||||||
@basic_colored_pet_types_by_species_id = PetType.special_color_or_basic(@item.special_color).
|
|
||||||
includes_child_translations.group_by(&:species)
|
|
||||||
|
|
||||||
trading_closet_hangers = @item.closet_hangers.trading.includes(:user).
|
|
||||||
user_is_active.order('users.last_trade_activity_at DESC')
|
|
||||||
|
|
||||||
|
|
||||||
owned_trading_hangers = trading_closet_hangers.filter { |c| c.owned? }
|
|
||||||
wanted_trading_hangers = trading_closet_hangers.filter { |c| c.wanted? }
|
|
||||||
|
|
||||||
@trading_users_by_owned = {
|
|
||||||
true => owned_trading_hangers.map(&:user).uniq,
|
|
||||||
false => wanted_trading_hangers.map(&:user).uniq,
|
|
||||||
}
|
|
||||||
|
|
||||||
if user_signed_in?
|
if user_signed_in?
|
||||||
# Empty arrays are important so that we can loop over this and still
|
@current_user_lists = current_user.closet_lists.alphabetical.
|
||||||
# show the generic no-list case
|
group_by_owned
|
||||||
@current_user_lists = {true => [], false => []}
|
@current_user_quantities = current_user.item_quantities_for(@item)
|
||||||
current_user.closet_lists.alphabetical.each do |list|
|
|
||||||
@current_user_lists[list.hangers_owned] << list
|
|
||||||
end
|
|
||||||
|
|
||||||
@current_user_quantities = Hash.new(0) # default is zero
|
|
||||||
hangers = current_user.closet_hangers.where(item_id: @item.id).
|
|
||||||
select([:owned, :list_id, :quantity])
|
|
||||||
|
|
||||||
hangers.each do |hanger|
|
|
||||||
key = hanger.list_id || hanger.owned
|
|
||||||
@current_user_quantities[key] = hanger.quantity
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
format.gif do
|
format.gif do
|
||||||
|
|
26
app/helpers/item_trades_helper.rb
Normal file
26
app/helpers/item_trades_helper.rb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module ItemTradesHelper
|
||||||
|
def vague_trade_timestamp(last_trade_activity_at)
|
||||||
|
if last_trade_activity_at >= 1.week.ago
|
||||||
|
translate "item_trades.index.table.last_active.this_week"
|
||||||
|
else
|
||||||
|
last_trade_activity_at.strftime("%b %Y")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sorted_vaguely_by_trade_activity(trades)
|
||||||
|
# First, sort the list in ascending order.
|
||||||
|
trades_ascending = trades.sort_by do |trade|
|
||||||
|
if trade.user.last_trade_activity_at >= 1.week.ago
|
||||||
|
# Sort recent trades in a random order, but still collectively as the
|
||||||
|
# most recent. (This discourages spamming updates to game the system!)
|
||||||
|
[1, rand]
|
||||||
|
else
|
||||||
|
# Sort older trades by last trade activity.
|
||||||
|
[0, trade.user.last_trade_activity_at]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Then, reverse it!
|
||||||
|
trades_ascending.reverse!
|
||||||
|
end
|
||||||
|
end
|
|
@ -49,10 +49,6 @@ module ItemsHelper
|
||||||
|
|
||||||
content_tag :div, content, :class => 'closeted-icons'
|
content_tag :div, content, :class => 'closeted-icons'
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_zones(zones, method=:label)
|
|
||||||
zones.map(&method).join(', ')
|
|
||||||
end
|
|
||||||
|
|
||||||
def nc_icon
|
def nc_icon
|
||||||
image_tag 'nc.png', :title => t('items.item.nc.description'),
|
image_tag 'nc.png', :title => t('items.item.nc.description'),
|
||||||
|
@ -83,17 +79,6 @@ module ItemsHelper
|
||||||
"https://www.neopets.com/genie.phtml?type=process_genie&criteria=exact&auctiongenie=#{CGI::escape item.name}"
|
"https://www.neopets.com/genie.phtml?type=process_genie&criteria=exact&auctiongenie=#{CGI::escape item.name}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def trading_users_header(owned, count)
|
|
||||||
ownership_key = owned ? 'owned' : 'wanted'
|
|
||||||
translate ".trading_users.header.#{ownership_key}", :count => count
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_trading_users(owned)
|
|
||||||
@trading_users_by_owned[owned].map do |user|
|
|
||||||
link_to user.name, user_closet_hangers_path(user)
|
|
||||||
end.to_sentence.html_safe
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_contribution_count(count)
|
def format_contribution_count(count)
|
||||||
" (×#{count})".html_safe if count > 1
|
" (×#{count})".html_safe if count > 1
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,29 +13,3 @@ ReactDOM.render(
|
||||||
</AppProvider>,
|
</AppProvider>,
|
||||||
rootNode,
|
rootNode,
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
|
||||||
const tradeHangers = document.querySelector("#trade-hangers");
|
|
||||||
const tradeSections = document.querySelectorAll("#trade-hangers p");
|
|
||||||
for (const section of tradeSections) {
|
|
||||||
const oneLine = parseFloat(getComputedStyle(section)['line-height']);
|
|
||||||
const maxHeight = Math.ceil(oneLine * 2);
|
|
||||||
|
|
||||||
if (section.clientHeight > maxHeight) {
|
|
||||||
section.style.maxHeight = `${maxHeight}px`;
|
|
||||||
section.setAttribute("data-overflows", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
section.querySelector(".more")?.addEventListener("click", (event) => {
|
|
||||||
section.setAttribute("data-showing-more", "");
|
|
||||||
section.style.maxHeight = "none";
|
|
||||||
});
|
|
||||||
|
|
||||||
section.querySelector(".less")?.addEventListener("click", (event) => {
|
|
||||||
section.removeAttribute("data-showing-more");
|
|
||||||
section.style.maxHeight = `${maxHeight}px`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error applying trade list more/less toggle", error);
|
|
||||||
}
|
|
||||||
|
|
|
@ -153,6 +153,43 @@ class ClosetHanger < ApplicationRecord
|
||||||
# If quantity is zero and there's no hanger, good. Do nothing.
|
# If quantity is zero and there's no hanger, good. Do nothing.
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Use this with a scoped relation to convert it into a list of trades, e.g.
|
||||||
|
# `item.hangers.trading.to_trades`.
|
||||||
|
#
|
||||||
|
# A trade includes the user who's trading, and the available closet hangers
|
||||||
|
# (which you can use to get e.g. the list name).
|
||||||
|
#
|
||||||
|
# We don't preload anything here - if you want user names or list names, you
|
||||||
|
# should `includes` them in the hanger scope first, to avoid extra queries!
|
||||||
|
def self.to_trades
|
||||||
|
# Let's ensure that the `trading` filter is applied, to avoid data leaks.
|
||||||
|
# (I still recommend doing it at the call site too for clarity, though!)
|
||||||
|
all_trading_hangers = trading.to_a
|
||||||
|
|
||||||
|
owned_hangers = all_trading_hangers.filter(&:owned?)
|
||||||
|
wanted_hangers = all_trading_hangers.filter(&:wanted?)
|
||||||
|
|
||||||
|
# Group first into offering vs seeking, then by user.
|
||||||
|
offering, seeking = [owned_hangers, wanted_hangers].map do |hangers|
|
||||||
|
hangers.group_by(&:user_id).map do |user_id, user_hangers|
|
||||||
|
Trade.new(user_id, user_hangers)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
{offering: offering, seeking: seeking}
|
||||||
|
end
|
||||||
|
|
||||||
|
Trade = Struct.new('Trade', :user_id, :hangers) do
|
||||||
|
def user
|
||||||
|
# Take advantage of `includes(:user)` on the hangers, if applied.
|
||||||
|
hangers.first.user
|
||||||
|
end
|
||||||
|
|
||||||
|
def lists
|
||||||
|
hangers.map(&:list).filter(&:present?)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def list_belongs_to_user
|
def list_belongs_to_user
|
||||||
|
|
|
@ -87,6 +87,13 @@ class ClosetList < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.group_by_owned
|
||||||
|
h = all.group_by(&:hangers_owned?)
|
||||||
|
h[true] ||= []
|
||||||
|
h[false] ||= []
|
||||||
|
h
|
||||||
|
end
|
||||||
|
|
||||||
include VisibilityMethods
|
include VisibilityMethods
|
||||||
|
|
||||||
class Null
|
class Null
|
||||||
|
|
|
@ -278,17 +278,6 @@ class Item < ApplicationRecord
|
||||||
write_attribute('species_support_ids', replacement)
|
write_attribute('species_support_ids', replacement)
|
||||||
end
|
end
|
||||||
|
|
||||||
def supported_species_ids
|
|
||||||
return Species.select([:id]).map(&:id) if modeled_body_ids.include?(0)
|
|
||||||
|
|
||||||
pet_types = PetType.where(:body_id => modeled_body_ids).select('DISTINCT species_id')
|
|
||||||
species_ids = pet_types.map(&:species_id)
|
|
||||||
|
|
||||||
# If there are multiple known supported species, it probably supports them
|
|
||||||
# all. (I've never heard of only a handful of species being supported :P)
|
|
||||||
species_ids.size >= 2 ? Species.select([:id]).map(&:id) : species_ids
|
|
||||||
end
|
|
||||||
|
|
||||||
def support_species?(species)
|
def support_species?(species)
|
||||||
species_support_ids.blank? || species_support_ids.include?(species.id)
|
species_support_ids.blank? || species_support_ids.include?(species.id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -175,6 +175,18 @@ class User < ApplicationRecord
|
||||||
contact_neopets_connection.try(:neopets_username)
|
contact_neopets_connection.try(:neopets_username)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def item_quantities_for(item_id)
|
||||||
|
quantities = Hash.new(0)
|
||||||
|
|
||||||
|
hangers = closet_hangers.where(item_id: item_id).
|
||||||
|
select([:owned, :list_id, :quantity])
|
||||||
|
hangers.each do |hanger|
|
||||||
|
quantities[hanger.list_id || hanger.owned?] = hanger.quantity
|
||||||
|
end
|
||||||
|
|
||||||
|
quantities
|
||||||
|
end
|
||||||
|
|
||||||
def log_trade_activity
|
def log_trade_activity
|
||||||
touch(:last_trade_activity_at)
|
touch(:last_trade_activity_at)
|
||||||
end
|
end
|
||||||
|
|
36
app/views/item_trades/index.html.haml
Normal file
36
app/views/item_trades/index.html.haml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
- title t(".title.#{@type}")
|
||||||
|
- hide_title_header
|
||||||
|
|
||||||
|
= render partial: "items/item_header",
|
||||||
|
locals: {item: @item, trades: @item_trades,
|
||||||
|
current_subpage: "trades_#{@type}",
|
||||||
|
current_user_lists: @current_user_lists,
|
||||||
|
current_user_quantities: @current_user_quantities}
|
||||||
|
|
||||||
|
%h2.item-subpage-title= t(".title.#{@type}")
|
||||||
|
|
||||||
|
- if @trades.present?
|
||||||
|
%table.trades-table
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th= t(".table.headings.last_active")
|
||||||
|
%th= t(".table.headings.user.#{@type}")
|
||||||
|
%th= t(".table.headings.lists")
|
||||||
|
%tbody
|
||||||
|
- sorted_vaguely_by_trade_activity(@trades).each do |trade|
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
= vague_trade_timestamp trade.user.last_trade_activity_at
|
||||||
|
%td= trade.user.name
|
||||||
|
%td
|
||||||
|
- if trade.lists.present?
|
||||||
|
%ul.trade-list-names
|
||||||
|
- trade.lists.each do |list|
|
||||||
|
%li= link_to list.name, user_closet_hangers_path(trade.user,
|
||||||
|
anchor: "closet-list-#{list.id}")
|
||||||
|
- else
|
||||||
|
= link_to t(".table.not_in_a_list.#{@type}"), user_closet_hangers_path(trade.user,
|
||||||
|
anchor: "closet-hangers-group-#{@type == :offering}"),
|
||||||
|
class: "not-in-a-list"
|
||||||
|
- else
|
||||||
|
%p= t(".no_trades_yet")
|
90
app/views/items/_item_header.haml
Normal file
90
app/views/items/_item_header.haml
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
- raise ArgumentError unless defined? item
|
||||||
|
- raise ArgumentError unless defined? trades
|
||||||
|
- raise ArgumentError unless defined? current_user_lists
|
||||||
|
- raise ArgumentError unless defined? current_user_quantities
|
||||||
|
- raise ArgumentError unless defined? current_subpage
|
||||||
|
|
||||||
|
%header.item-header
|
||||||
|
.item-header-main
|
||||||
|
= image_tag item.thumbnail_url, class: 'item-thumbnail'
|
||||||
|
%h2.item-name= item.name
|
||||||
|
%nav.item-links
|
||||||
|
- if item.nc?
|
||||||
|
%abbr.item-kind{'data-type' => 'nc', title: t('items.show.item_kinds.nc.description')}
|
||||||
|
= t('items.show.item_kinds.nc.label')
|
||||||
|
- elsif item.pb?
|
||||||
|
%abbr.item-kind{'data-type' => 'pb', title: t('items.show.item_kinds.pb.description')}
|
||||||
|
= t('items.show.item_kinds.pb.label')
|
||||||
|
- else
|
||||||
|
%abbr.item-kind{'data-type' => 'np', title: t('items.show.item_kinds.np.description')}
|
||||||
|
= t('items.show.item_kinds.np.label')
|
||||||
|
|
||||||
|
= link_to t('items.show.resources.jn_items'), jn_items_url_for(item)
|
||||||
|
- if item.nc_trade_value
|
||||||
|
= link_to t('items.show.resources.owls', value: item.nc_trade_value.value_text),
|
||||||
|
"https://www.neopets.com/~owls",
|
||||||
|
title: nc_trade_value_updated_at_text(item.nc_trade_value)
|
||||||
|
- unless item.nc?
|
||||||
|
= link_to t('items.show.resources.shop_wizard'), shop_wizard_url_for(item)
|
||||||
|
= link_to t('items.show.resources.super_shop_wizard'), super_shop_wizard_url_for(item)
|
||||||
|
= link_to t('items.show.resources.trading_post'), trading_post_url_for(item)
|
||||||
|
= link_to t('items.show.resources.auction_genie'), auction_genie_url_for(item)
|
||||||
|
|
||||||
|
- if user_signed_in?
|
||||||
|
.user-lists-section
|
||||||
|
= link_to t('items.show.closet_hangers.button'),
|
||||||
|
user_closet_hangers_path(current_user),
|
||||||
|
class: 'dialog-opener'
|
||||||
|
%dialog
|
||||||
|
%h3
|
||||||
|
= t 'items.show.closet_hangers.header_html',
|
||||||
|
user_items_link: link_to(t('your_items'),
|
||||||
|
user_closet_hangers_path(current_user))
|
||||||
|
= form_tag update_quantities_user_item_closet_hangers_path(:user_id => current_user, :item_id => item), :method => :put do
|
||||||
|
.closet-hangers-ownership-groups
|
||||||
|
- current_user_lists.each do |owned, lists|
|
||||||
|
%div
|
||||||
|
%h4= closet_lists_group_name(:you, owned)
|
||||||
|
%ul
|
||||||
|
- lists.each_with_index do |list, index|
|
||||||
|
%li
|
||||||
|
= number_field_tag "quantity[#{list.id}]",
|
||||||
|
current_user_quantities[list.id], min: 0,
|
||||||
|
autofocus: owned && index == 0
|
||||||
|
= label_tag "quantity[#{list.id}]", list.name
|
||||||
|
|
||||||
|
%li
|
||||||
|
= number_field_tag "quantity[#{owned}]",
|
||||||
|
current_user_quantities[owned], min: 0,
|
||||||
|
autofocus: owned && lists.empty?
|
||||||
|
|
||||||
|
- unless lists.empty?
|
||||||
|
= label_tag "quantity[#{owned}]",
|
||||||
|
t('closet_lists.unlisted_name'),
|
||||||
|
:class => 'unlisted'
|
||||||
|
- else
|
||||||
|
= label_tag "quantity[#{owned}]",
|
||||||
|
t('items.show.closet_hangers.quantity_label')
|
||||||
|
= submit_tag t('items.show.closet_hangers.submit')
|
||||||
|
|
||||||
|
%p.item-description= item.description
|
||||||
|
|
||||||
|
%nav.item-subpages-nav
|
||||||
|
= link_to t('items.show.subpages_nav.preview'), item,
|
||||||
|
class: ['preview-link'], 'data-is-current' => current_subpage == 'preview'
|
||||||
|
.trades-section
|
||||||
|
%header= t('items.show.subpages_nav.trades.header')
|
||||||
|
%ul
|
||||||
|
%li
|
||||||
|
= link_to t('items.show.subpages_nav.trades.offering',
|
||||||
|
count: trades[:offering].size),
|
||||||
|
item_trades_path(item, type: 'offering'),
|
||||||
|
'data-is-current' => current_subpage == 'trades_offering'
|
||||||
|
%li
|
||||||
|
= link_to t('items.show.subpages_nav.trades.seeking',
|
||||||
|
count: trades[:offering].size),
|
||||||
|
item_trades_path(item, type: 'seeking'),
|
||||||
|
'data-is-current' => current_subpage == 'trades_seeking'
|
||||||
|
|
||||||
|
- content_for :javascripts do
|
||||||
|
= javascript_include_tag 'items/item_header'
|
|
@ -1,84 +1,10 @@
|
||||||
- title @item.name
|
- title @item.name
|
||||||
- canonical_path @item
|
- canonical_path @item
|
||||||
|
|
||||||
%header#item-header
|
= render partial: "item_header",
|
||||||
= image_tag @item.thumbnail_url, :id => 'item-thumbnail'
|
locals: {item: @item, trades: @trades, current_subpage: "preview",
|
||||||
%h2#item-name= @item.name
|
current_user_lists: @current_user_lists,
|
||||||
%nav#item-links
|
current_user_quantities: @current_user_quantities}
|
||||||
= nc_icon_for(@item)
|
|
||||||
- unless @item.rarity.blank?
|
|
||||||
== #{t '.rarity'}: #{@item.rarity_index} (#{@item.rarity})
|
|
||||||
= link_to t('.resources.jn_items'), jn_items_url_for(@item)
|
|
||||||
- if @item.nc_trade_value
|
|
||||||
= link_to t('.resources.owls', value: @item.nc_trade_value.value_text),
|
|
||||||
"https://www.neopets.com/~owls",
|
|
||||||
title: nc_trade_value_updated_at_text(@item.nc_trade_value)
|
|
||||||
- unless @item.nc?
|
|
||||||
= link_to t('.resources.shop_wizard'), shop_wizard_url_for(@item)
|
|
||||||
= link_to t('.resources.super_shop_wizard'), super_shop_wizard_url_for(@item)
|
|
||||||
= link_to t('.resources.trading_post'), trading_post_url_for(@item)
|
|
||||||
= link_to t('.resources.auction_genie'), auction_genie_url_for(@item)
|
|
||||||
|
|
||||||
%section#item-info-section
|
|
||||||
#item-info
|
|
||||||
%p= @item.description
|
|
||||||
|
|
||||||
#item-zones
|
|
||||||
%p
|
|
||||||
%strong #{t '.zones.occupied_header'}:
|
|
||||||
= list_zones @occupied_zones, :uncertain_label
|
|
||||||
%p
|
|
||||||
%strong #{t '.zones.restricted_header'}:
|
|
||||||
- if @restricted_zones.empty?
|
|
||||||
= t '.zones.none'
|
|
||||||
- else
|
|
||||||
= list_zones @restricted_zones
|
|
||||||
|
|
||||||
#trade-hangers
|
|
||||||
- if Time.now < Date.new(2024, 1, 26)
|
|
||||||
%p
|
|
||||||
✨⏳️
|
|
||||||
%i We now only show recently-updated lists here!
|
|
||||||
⏳️✨
|
|
||||||
- [true, false].each do |owned|
|
|
||||||
%p
|
|
||||||
%strong
|
|
||||||
= trading_users_header(owned, @trading_users_by_owned[owned].size)
|
|
||||||
= render_trading_users(owned)
|
|
||||||
%span.toggle
|
|
||||||
%button.more= t '.trading_users.show_more'
|
|
||||||
%button.less= t '.trading_users.show_less'
|
|
||||||
|
|
||||||
- if user_signed_in?
|
|
||||||
#your-items-form
|
|
||||||
%h3
|
|
||||||
= t '.closet_hangers.header_html',
|
|
||||||
:user_items_link => link_to(t('your_items'),
|
|
||||||
user_closet_hangers_path(current_user))
|
|
||||||
= form_tag update_quantities_user_item_closet_hangers_path(:user_id => current_user, :item_id => @item), :method => :put do
|
|
||||||
#closet-hangers-ownership-groups
|
|
||||||
- @current_user_lists.each do |owned, lists|
|
|
||||||
%div
|
|
||||||
%h4= closet_lists_group_name(:you, owned)
|
|
||||||
%ul
|
|
||||||
- lists.each do |list|
|
|
||||||
%li
|
|
||||||
= number_field_tag "quantity[#{list.id}]",
|
|
||||||
@current_user_quantities[list.id], :min => 0
|
|
||||||
= label_tag "quantity[#{list.id}]", list.name
|
|
||||||
|
|
||||||
%li
|
|
||||||
= number_field_tag "quantity[#{owned}]",
|
|
||||||
@current_user_quantities[owned], :min => 0
|
|
||||||
|
|
||||||
- unless lists.empty?
|
|
||||||
= label_tag "quantity[#{owned}]",
|
|
||||||
t('closet_lists.unlisted_name'),
|
|
||||||
:class => 'unlisted'
|
|
||||||
- else
|
|
||||||
= label_tag "quantity[#{owned}]",
|
|
||||||
t('.closet_hangers.quantity_label')
|
|
||||||
= submit_tag t('.closet_hangers.submit')
|
|
||||||
|
|
||||||
#outfit-preview-root{'data-item-id': @item.id}
|
#outfit-preview-root{'data-item-id': @item.id}
|
||||||
|
|
||||||
|
|
|
@ -270,7 +270,6 @@ en-MEEP:
|
||||||
description: You want this meepit
|
description: You want this meepit
|
||||||
|
|
||||||
show:
|
show:
|
||||||
rarity: Meepity
|
|
||||||
resources:
|
resources:
|
||||||
jn_items: JN Meepits
|
jn_items: JN Meepits
|
||||||
shop_wizard: Meep Wizard
|
shop_wizard: Meep Wizard
|
||||||
|
@ -285,18 +284,6 @@ en-MEEP:
|
||||||
occupied_header: Occupeeps
|
occupied_header: Occupeeps
|
||||||
restricted_header: Restreeps
|
restricted_header: Restreeps
|
||||||
none: Meepless
|
none: Meepless
|
||||||
trading_users:
|
|
||||||
header:
|
|
||||||
owned:
|
|
||||||
zero: We don't know anymeep who has this item meep for trade.
|
|
||||||
one: "1 user has this item meep for trade:"
|
|
||||||
other: "%{count} users have this item meep for trade:"
|
|
||||||
wanted:
|
|
||||||
zero: "We don't know anymeep who meeps this item."
|
|
||||||
one: "1 user meeps this item:"
|
|
||||||
other: "%{count} users meep this item:"
|
|
||||||
show_more: meep more
|
|
||||||
show_less: meep less
|
|
||||||
preview:
|
preview:
|
||||||
header: Meepview
|
header: Meepview
|
||||||
customize_more: Customize meep
|
customize_more: Customize meep
|
||||||
|
|
|
@ -298,7 +298,16 @@ en:
|
||||||
description: You want this item
|
description: You want this item
|
||||||
|
|
||||||
show:
|
show:
|
||||||
rarity: Rarity
|
item_kinds:
|
||||||
|
nc:
|
||||||
|
label: NC
|
||||||
|
description: Purchaseable with Neocash
|
||||||
|
np:
|
||||||
|
label: NP
|
||||||
|
description: Purchaseable with Neopoints
|
||||||
|
pb:
|
||||||
|
label: PB
|
||||||
|
description: Only obtainable via paintbrush
|
||||||
resources:
|
resources:
|
||||||
jn_items: JN Items
|
jn_items: JN Items
|
||||||
owls: "Owls: %{value}"
|
owls: "Owls: %{value}"
|
||||||
|
@ -307,6 +316,7 @@ en:
|
||||||
trading_post: Trades
|
trading_post: Trades
|
||||||
auction_genie: Auctions
|
auction_genie: Auctions
|
||||||
closet_hangers:
|
closet_hangers:
|
||||||
|
button: Add to your lists
|
||||||
header_html: Track this in %{user_items_link}
|
header_html: Track this in %{user_items_link}
|
||||||
quantity_label: How many?
|
quantity_label: How many?
|
||||||
submit: Save to Your Items
|
submit: Save to Your Items
|
||||||
|
@ -314,18 +324,12 @@ en:
|
||||||
occupied_header: Occupies
|
occupied_header: Occupies
|
||||||
restricted_header: Restricts
|
restricted_header: Restricts
|
||||||
none: None
|
none: None
|
||||||
trading_users:
|
subpages_nav:
|
||||||
header:
|
preview: Preview
|
||||||
owned:
|
trades:
|
||||||
zero: We don't know anyone who has this item up for trade.
|
header: Trades
|
||||||
one: "1 user has this item up for trade:"
|
offering: Offering (%{count})
|
||||||
other: "%{count} users have this item up for trade:"
|
seeking: Seeking (%{count})
|
||||||
wanted:
|
|
||||||
zero: "We don't know anyone who wants this item."
|
|
||||||
one: "1 user wants this item:"
|
|
||||||
other: "%{count} users want this item:"
|
|
||||||
show_more: more
|
|
||||||
show_less: less
|
|
||||||
preview:
|
preview:
|
||||||
header: Preview
|
header: Preview
|
||||||
customize_more: Customize more
|
customize_more: Customize more
|
||||||
|
@ -369,6 +373,27 @@ en:
|
||||||
user_wants: wants
|
user_wants: wants
|
||||||
fits_pet_type: fits
|
fits_pet_type: fits
|
||||||
|
|
||||||
|
item_trades:
|
||||||
|
index:
|
||||||
|
title:
|
||||||
|
offering: "Trades: Offering"
|
||||||
|
seeking: "Trades: Seeking"
|
||||||
|
table:
|
||||||
|
headings:
|
||||||
|
last_active: Last active
|
||||||
|
user:
|
||||||
|
offering: Owner
|
||||||
|
seeking: Seeker
|
||||||
|
lists: Lists
|
||||||
|
last_active:
|
||||||
|
this_week: This week
|
||||||
|
not_in_a_list:
|
||||||
|
offering: Items they own
|
||||||
|
seeking: Items they want
|
||||||
|
no_trades_yet:
|
||||||
|
No trades yet! To add your name to this page, add this item to one of
|
||||||
|
your lists, and mark the list as "Trading".
|
||||||
|
|
||||||
neopets_page_import_tasks:
|
neopets_page_import_tasks:
|
||||||
create:
|
create:
|
||||||
success: Page %{index} saved!
|
success: Page %{index} saved!
|
||||||
|
|
|
@ -209,7 +209,6 @@ es:
|
||||||
abbr: Buscado
|
abbr: Buscado
|
||||||
description: Quieres este objeto
|
description: Quieres este objeto
|
||||||
show:
|
show:
|
||||||
rarity: Rareza
|
|
||||||
resources:
|
resources:
|
||||||
jn_items: Objetos de JN
|
jn_items: Objetos de JN
|
||||||
shop_wizard: Asistente de Tiendas
|
shop_wizard: Asistente de Tiendas
|
||||||
|
@ -224,18 +223,6 @@ es:
|
||||||
occupied_header: Ocupa
|
occupied_header: Ocupa
|
||||||
restricted_header: Restringe
|
restricted_header: Restringe
|
||||||
none: Nada
|
none: Nada
|
||||||
trading_users:
|
|
||||||
header:
|
|
||||||
owned:
|
|
||||||
zero: No conocemos a nadie que tenga este objeto para intercambiar.
|
|
||||||
one: "1 usuario tiene este objeto para intercambiar:"
|
|
||||||
other: "%{count} usuarios tienen este objeto para intercambiar:"
|
|
||||||
wanted:
|
|
||||||
zero: "No conocemos a nadie que busque este objeto."
|
|
||||||
one: "1 usuario busca este objeto:"
|
|
||||||
other: "%{count} usuarios buscan este objeto:"
|
|
||||||
show_more: más
|
|
||||||
show_less: menos
|
|
||||||
preview:
|
preview:
|
||||||
header: Previsualizar
|
header: Previsualizar
|
||||||
customize_more: Personalizar más
|
customize_more: Personalizar más
|
||||||
|
|
|
@ -209,7 +209,6 @@ pt:
|
||||||
abbr: Procura
|
abbr: Procura
|
||||||
description: Você procura esse item
|
description: Você procura esse item
|
||||||
show:
|
show:
|
||||||
rarity: Raridade
|
|
||||||
resources:
|
resources:
|
||||||
jn_items: JN Itens
|
jn_items: JN Itens
|
||||||
shop_wizard: Mágico Pecincheiro
|
shop_wizard: Mágico Pecincheiro
|
||||||
|
@ -224,18 +223,6 @@ pt:
|
||||||
occupied_header: Ocupações
|
occupied_header: Ocupações
|
||||||
restricted_header: Restrições
|
restricted_header: Restrições
|
||||||
none: Nada
|
none: Nada
|
||||||
trading_users:
|
|
||||||
header:
|
|
||||||
owned:
|
|
||||||
zero: Ninguém quer trocar esse item
|
|
||||||
one: "1 usuário quer trocar esse item:"
|
|
||||||
other: "%{count} usuários possuem, e querem trocar esse item:"
|
|
||||||
wanted:
|
|
||||||
zero: "Nós não conhecemos ninguém que procure esse item."
|
|
||||||
one: "1 usuário procura esse item:"
|
|
||||||
other: "%{count} usuários procuram esse item:"
|
|
||||||
show_more: mais
|
|
||||||
show_less: menos
|
|
||||||
preview:
|
preview:
|
||||||
header: Pré-Visualizar
|
header: Pré-Visualizar
|
||||||
customize_more: Personalize mais
|
customize_more: Personalize mais
|
||||||
|
|
|
@ -18,7 +18,11 @@ OpenneoImpressItems::Application.routes.draw do
|
||||||
|
|
||||||
# Our customization data! Both the item pages, and JSON API endpoints.
|
# Our customization data! Both the item pages, and JSON API endpoints.
|
||||||
resources :items, :only => [:index, :show] do
|
resources :items, :only => [:index, :show] do
|
||||||
|
resources :trades, path: 'trades/:type', controller: 'item_trades',
|
||||||
|
only: [:index], constraints: {type: /offering|seeking/}
|
||||||
|
|
||||||
resources :appearances, controller: 'item_appearances', only: [:index]
|
resources :appearances, controller: 'item_appearances', only: [:index]
|
||||||
|
|
||||||
collection do
|
collection do
|
||||||
get :needed
|
get :needed
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue