Compare commits
No commits in common. "e4a640cceed4c1a33ed554f3faa10afcb6c96ef2" and "d66f81c96b73a1df5263abb4ed85923db70a41bc" have entirely different histories.
e4a640ccee
...
d66f81c96b
24 changed files with 111 additions and 662 deletions
|
@ -1,23 +0,0 @@
|
||||||
#title:has(+ .breadcrumbs)
|
|
||||||
margin-bottom: .125em
|
|
||||||
|
|
||||||
.breadcrumbs
|
|
||||||
list-style-type: none
|
|
||||||
display: flex
|
|
||||||
flex-direction: row
|
|
||||||
margin-block: .5em
|
|
||||||
font-size: .85em
|
|
||||||
|
|
||||||
li
|
|
||||||
display: flex
|
|
||||||
|
|
||||||
li:not(:first-child)
|
|
||||||
&::before
|
|
||||||
margin-inline: .35em
|
|
||||||
content: "→"
|
|
||||||
|
|
||||||
&[data-relation-to-prev=sibling]::before
|
|
||||||
content: "+"
|
|
||||||
|
|
||||||
&[data-relation-to-prev=menu]::before
|
|
||||||
content: "-"
|
|
|
@ -1,110 +0,0 @@
|
||||||
@import "../partials/clean/constants"
|
|
||||||
|
|
||||||
// When loading, fade in the loading spinner after a brief delay. We only apply
|
|
||||||
// the delay here, not on the base styles, because fading *out* on load should
|
|
||||||
// be instant.
|
|
||||||
//
|
|
||||||
// This is implemented as a mixin, so that the item page can leverage the same
|
|
||||||
// loading state when loading a new preview altogether. Once CSS container
|
|
||||||
// style queries gain wider support, maybe use that instead.
|
|
||||||
=outfit-viewer-loading
|
|
||||||
cursor: wait
|
|
||||||
|
|
||||||
.loading-indicator
|
|
||||||
opacity: 1
|
|
||||||
transition-delay: 2s
|
|
||||||
|
|
||||||
// If the outfit *starts* in loading state, still delay the fade-in.
|
|
||||||
@starting-style
|
|
||||||
opacity: 0
|
|
||||||
|
|
||||||
outfit-viewer
|
|
||||||
display: block
|
|
||||||
position: relative
|
|
||||||
overflow: hidden
|
|
||||||
|
|
||||||
// These are default widths, expected to often be overridden.
|
|
||||||
width: 300px
|
|
||||||
height: 300px
|
|
||||||
|
|
||||||
// There's no useful text in here, but double-clicking the play/pause
|
|
||||||
// button can cause a weird selection state. Disable text selection.
|
|
||||||
user-select: none
|
|
||||||
-webkit-user-select: none
|
|
||||||
|
|
||||||
outfit-layer
|
|
||||||
display: block
|
|
||||||
position: absolute
|
|
||||||
inset: 0
|
|
||||||
|
|
||||||
// We disable pointer-events most importantly for the iframes, which
|
|
||||||
// will ignore our `cursor: wait` and show a plain cursor for the
|
|
||||||
// inside of its own document. But also, the context menus for these
|
|
||||||
// elements are kinda actively misleading, too!
|
|
||||||
pointer-events: none
|
|
||||||
|
|
||||||
img, iframe
|
|
||||||
width: 100%
|
|
||||||
height: 100%
|
|
||||||
|
|
||||||
.loading-indicator
|
|
||||||
position: absolute
|
|
||||||
z-index: 1000
|
|
||||||
bottom: 0px
|
|
||||||
right: 4px
|
|
||||||
padding: 8px
|
|
||||||
background: radial-gradient(circle closest-side, white 45%, #ffffff00)
|
|
||||||
|
|
||||||
opacity: 0
|
|
||||||
|
|
||||||
.play-pause-button
|
|
||||||
position: absolute
|
|
||||||
z-index: 1001
|
|
||||||
left: 8px
|
|
||||||
bottom: 8px
|
|
||||||
display: none
|
|
||||||
align-items: center
|
|
||||||
justify-content: center
|
|
||||||
color: white
|
|
||||||
background: rgba(0, 0, 0, 0.64)
|
|
||||||
width: 2.5em
|
|
||||||
height: 2.5em
|
|
||||||
border-radius: 100%
|
|
||||||
border: 2px solid transparent
|
|
||||||
transition: all .25s
|
|
||||||
|
|
||||||
.playing-label, .paused-label
|
|
||||||
display: none
|
|
||||||
width: 1em
|
|
||||||
height: 1em
|
|
||||||
|
|
||||||
.play-pause-toggle
|
|
||||||
// Visually hidden
|
|
||||||
clip: rect(0 0 0 0)
|
|
||||||
clip-path: inset(50%)
|
|
||||||
height: 1px
|
|
||||||
overflow: hidden
|
|
||||||
position: absolute
|
|
||||||
white-space: nowrap
|
|
||||||
width: 1px
|
|
||||||
|
|
||||||
&:checked ~ .playing-label
|
|
||||||
display: block
|
|
||||||
|
|
||||||
&:not(:checked) ~ .paused-label
|
|
||||||
display: block
|
|
||||||
|
|
||||||
&:hover, &:has(.play-pause-toggle:focus)
|
|
||||||
border: 2px solid $module-border-color
|
|
||||||
background: $module-bg-color
|
|
||||||
color: $text-color
|
|
||||||
|
|
||||||
&:has(.play-pause-toggle:active)
|
|
||||||
transform: translateY(2px)
|
|
||||||
|
|
||||||
&:has(outfit-layer:state(has-animations))
|
|
||||||
.play-pause-button
|
|
||||||
display: flex
|
|
||||||
|
|
||||||
&:has(outfit-layer:state(loading))
|
|
||||||
+outfit-viewer-loading
|
|
|
@ -2,8 +2,6 @@
|
||||||
@import "../partials/clean/mixins"
|
@import "../partials/clean/mixins"
|
||||||
@import "../partials/item_header"
|
@import "../partials/item_header"
|
||||||
|
|
||||||
@import "../application/outfit-viewer"
|
|
||||||
|
|
||||||
#container
|
#container
|
||||||
width: 900px // A bit more generous to the preview area!
|
width: 900px // A bit more generous to the preview area!
|
||||||
|
|
||||||
|
@ -80,10 +78,93 @@
|
||||||
width: var(--natural-width)
|
width: var(--natural-width)
|
||||||
|
|
||||||
outfit-viewer
|
outfit-viewer
|
||||||
|
display: block
|
||||||
|
position: relative
|
||||||
width: 300px
|
width: 300px
|
||||||
height: 300px
|
height: 300px
|
||||||
border: 1px solid $module-border-color
|
border: 1px solid $module-border-color
|
||||||
border-radius: 1em
|
border-radius: 1em
|
||||||
|
overflow: hidden
|
||||||
|
|
||||||
|
// There's no useful text in here, but double-clicking the play/pause
|
||||||
|
// button can cause a weird selection state. Disable text selection.
|
||||||
|
user-select: none
|
||||||
|
-webkit-user-select: none
|
||||||
|
|
||||||
|
outfit-layer
|
||||||
|
display: block
|
||||||
|
position: absolute
|
||||||
|
inset: 0
|
||||||
|
|
||||||
|
// We disable pointer-events most importantly for the iframes, which
|
||||||
|
// will ignore our `cursor: wait` and show a plain cursor for the
|
||||||
|
// inside of its own document. But also, the context menus for these
|
||||||
|
// elements are kinda actively misleading, too!
|
||||||
|
pointer-events: none
|
||||||
|
|
||||||
|
img, iframe
|
||||||
|
width: 100%
|
||||||
|
height: 100%
|
||||||
|
|
||||||
|
.loading-indicator
|
||||||
|
position: absolute
|
||||||
|
z-index: 1000
|
||||||
|
bottom: 0px
|
||||||
|
right: 4px
|
||||||
|
padding: 8px
|
||||||
|
background: radial-gradient(circle closest-side, white 45%, #ffffff00)
|
||||||
|
|
||||||
|
opacity: 0
|
||||||
|
transition: opacity .5s
|
||||||
|
|
||||||
|
.play-pause-button
|
||||||
|
position: absolute
|
||||||
|
z-index: 1001
|
||||||
|
left: 8px
|
||||||
|
bottom: 8px
|
||||||
|
display: none
|
||||||
|
align-items: center
|
||||||
|
justify-content: center
|
||||||
|
color: white
|
||||||
|
background: rgba(0, 0, 0, 0.64)
|
||||||
|
width: 2.5em
|
||||||
|
height: 2.5em
|
||||||
|
border-radius: 100%
|
||||||
|
border: 2px solid transparent
|
||||||
|
transition: all .25s
|
||||||
|
|
||||||
|
.playing-label, .paused-label
|
||||||
|
display: none
|
||||||
|
width: 1em
|
||||||
|
height: 1em
|
||||||
|
|
||||||
|
.play-pause-toggle
|
||||||
|
// Visually hidden
|
||||||
|
clip: rect(0 0 0 0)
|
||||||
|
clip-path: inset(50%)
|
||||||
|
height: 1px
|
||||||
|
overflow: hidden
|
||||||
|
position: absolute
|
||||||
|
white-space: nowrap
|
||||||
|
width: 1px
|
||||||
|
|
||||||
|
&:checked ~ .playing-label
|
||||||
|
display: block
|
||||||
|
|
||||||
|
&:not(:checked) ~ .paused-label
|
||||||
|
display: block
|
||||||
|
|
||||||
|
&:hover, &:has(.play-pause-toggle:focus)
|
||||||
|
border: 2px solid $module-border-color
|
||||||
|
background: $module-bg-color
|
||||||
|
color: $text-color
|
||||||
|
|
||||||
|
&:has(.play-pause-toggle:active)
|
||||||
|
transform: translateY(2px)
|
||||||
|
|
||||||
|
&:has(outfit-layer:state(has-animations))
|
||||||
|
.play-pause-button
|
||||||
|
display: flex
|
||||||
|
|
||||||
.error-indicator
|
.error-indicator
|
||||||
font-size: 85%
|
font-size: 85%
|
||||||
|
@ -98,8 +179,16 @@ outfit-viewer
|
||||||
//
|
//
|
||||||
// We only apply the delay here, not on the base styles, because fading
|
// We only apply the delay here, not on the base styles, because fading
|
||||||
// *out* on load should be instant.
|
// *out* on load should be instant.
|
||||||
#item-preview[busy] outfit-viewer
|
#item-preview[busy] outfit-viewer, outfit-viewer:has(outfit-layer:state(loading))
|
||||||
+outfit-viewer-loading
|
cursor: wait
|
||||||
|
|
||||||
|
.loading-indicator
|
||||||
|
opacity: 1
|
||||||
|
transition-delay: 2s
|
||||||
|
|
||||||
|
// If the outfit *starts* in loading state, still delay the fade-in.
|
||||||
|
@starting-style
|
||||||
|
opacity: 0
|
||||||
|
|
||||||
#item-preview:has(outfit-layer:state(error))
|
#item-preview:has(outfit-layer:state(error))
|
||||||
outfit-viewer
|
outfit-viewer
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
@import "../partials/clean/constants"
|
|
||||||
|
|
||||||
outfit-viewer
|
|
||||||
margin: 0 auto
|
|
||||||
|
|
||||||
.pose-options
|
|
||||||
list-style-type: none
|
|
||||||
display: grid
|
|
||||||
grid-template-columns: 1fr 1fr 1fr
|
|
||||||
gap: .25em
|
|
||||||
|
|
||||||
label
|
|
||||||
display: flex
|
|
||||||
align-items: center
|
|
||||||
gap: .5em
|
|
||||||
padding: .5em 1em
|
|
||||||
border: 1px solid $soft-border-color
|
|
||||||
border-radius: 1em
|
|
||||||
|
|
||||||
input
|
|
||||||
margin: 0
|
|
||||||
|
|
||||||
&:has(:checked)
|
|
||||||
background: $module-bg-color
|
|
||||||
border-color: $module-border-color
|
|
|
@ -1,5 +0,0 @@
|
||||||
outfit-viewer
|
|
||||||
margin: 0 auto
|
|
||||||
|
|
||||||
dt
|
|
||||||
cursor: help
|
|
|
@ -1,55 +0,0 @@
|
||||||
@import "../partials/clean/constants"
|
|
||||||
|
|
||||||
.pet-filters
|
|
||||||
fieldset
|
|
||||||
display: flex
|
|
||||||
flex-direction: row
|
|
||||||
align-items: center
|
|
||||||
justify-content: center
|
|
||||||
gap: .5em
|
|
||||||
|
|
||||||
legend
|
|
||||||
display: contents
|
|
||||||
font-weight: bold
|
|
||||||
|
|
||||||
[role=navigation]
|
|
||||||
margin-block: .5em
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
.pet-types
|
|
||||||
list-style-type: none
|
|
||||||
display: flex
|
|
||||||
flex-wrap: wrap
|
|
||||||
justify-content: center
|
|
||||||
gap: .5em
|
|
||||||
|
|
||||||
> li
|
|
||||||
width: 150px
|
|
||||||
max-width: calc(50% - .25em)
|
|
||||||
min-width: 150px
|
|
||||||
box-sizing: border-box
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
a
|
|
||||||
display: block
|
|
||||||
border-radius: 1em
|
|
||||||
padding: .5em
|
|
||||||
text-decoration: none
|
|
||||||
background: white
|
|
||||||
&:hover
|
|
||||||
outline: 1px solid $module-border-color
|
|
||||||
background: $module-bg-color
|
|
||||||
|
|
||||||
img
|
|
||||||
width: 100%
|
|
||||||
height: auto
|
|
||||||
aspect-ratio: 1 / 1
|
|
||||||
margin-bottom: -1em
|
|
||||||
|
|
||||||
.name
|
|
||||||
background: inherit
|
|
||||||
padding: .25em .5em
|
|
||||||
border-radius: .5em
|
|
||||||
margin: 0 auto
|
|
||||||
position: relative
|
|
||||||
z-index: 1
|
|
|
@ -1,43 +0,0 @@
|
||||||
@import "../partials/clean/constants"
|
|
||||||
|
|
||||||
.pet-states
|
|
||||||
list-style-type: none
|
|
||||||
display: flex
|
|
||||||
flex-wrap: wrap
|
|
||||||
justify-content: center
|
|
||||||
gap: .5em
|
|
||||||
|
|
||||||
> li
|
|
||||||
width: 200px
|
|
||||||
max-width: calc(50% - .25em)
|
|
||||||
min-width: 150px
|
|
||||||
box-sizing: border-box
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
a
|
|
||||||
display: block
|
|
||||||
border-radius: 1em
|
|
||||||
padding: .5em
|
|
||||||
background: white
|
|
||||||
text-decoration: none
|
|
||||||
&:hover
|
|
||||||
outline: 1px solid $module-border-color
|
|
||||||
background: $module-bg-color
|
|
||||||
|
|
||||||
outfit-viewer
|
|
||||||
width: 100%
|
|
||||||
height: auto
|
|
||||||
aspect-ratio: 1 / 1
|
|
||||||
position: relative
|
|
||||||
z-index: 0
|
|
||||||
margin-bottom: -1em
|
|
||||||
|
|
||||||
.name
|
|
||||||
background: inherit
|
|
||||||
padding: .25em .5em
|
|
||||||
border-radius: .5em
|
|
||||||
position: relative
|
|
||||||
z-index: 1
|
|
||||||
|
|
||||||
.glitched
|
|
||||||
cursor: help
|
|
|
@ -110,11 +110,5 @@ class ApplicationController < ActionController::Base
|
||||||
Rails.logger.debug "Using return_to path: #{return_to.inspect}"
|
Rails.logger.debug "Using return_to path: #{return_to.inspect}"
|
||||||
return_to || root_path
|
return_to || root_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def support_staff_only
|
|
||||||
unless current_user&.support_staff?
|
|
||||||
raise AccessDenied, "Support staff only"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
class PetStatesController < ApplicationController
|
|
||||||
before_action :find_pet_state
|
|
||||||
before_action :support_staff_only, except: [:show]
|
|
||||||
|
|
||||||
def show
|
|
||||||
end
|
|
||||||
|
|
||||||
def edit
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
if @pet_state.update(pet_state_params)
|
|
||||||
flash[:notice] = "Pet appearance \##{@pet_state.id} successfully saved!"
|
|
||||||
redirect_to @pet_type
|
|
||||||
else
|
|
||||||
render action: :edit, status: :bad_request
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
def find_pet_state
|
|
||||||
@pet_type = PetType.matching_name_param(params[:pet_type_name]).first!
|
|
||||||
@pet_state = @pet_type.pet_states.find(params[:id])
|
|
||||||
end
|
|
||||||
|
|
||||||
def pet_state_params
|
|
||||||
params.require(:pet_state).permit(:pose, :glitched)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,78 +1,10 @@
|
||||||
class PetTypesController < ApplicationController
|
class PetTypesController < ApplicationController
|
||||||
def index
|
|
||||||
@species_names = Species.order(:name).map(&:human_name)
|
|
||||||
@color_names = Color.order(:name).map(&:human_name)
|
|
||||||
|
|
||||||
if params[:species].present?
|
|
||||||
@selected_species = Species.find_by!(name: params[:species])
|
|
||||||
@selected_species_name = @selected_species.human_name
|
|
||||||
end
|
|
||||||
if params[:color].present?
|
|
||||||
@selected_color = Color.find_by!(name: params[:color])
|
|
||||||
@selected_color_name = @selected_color.human_name
|
|
||||||
end
|
|
||||||
|
|
||||||
@pet_types = PetType.
|
|
||||||
includes(:color, :species).
|
|
||||||
order(created_at: :desc).
|
|
||||||
paginate(page: params[:page], per_page: 30)
|
|
||||||
|
|
||||||
if @selected_species
|
|
||||||
@pet_types = @pet_types.where(species_id: @selected_species)
|
|
||||||
end
|
|
||||||
if @selected_color
|
|
||||||
@pet_types = @pet_types.where(color_id: @selected_color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@pet_type = find_pet_type
|
@pet_type = PetType.
|
||||||
|
where(species_id: params[:species_id]).
|
||||||
|
where(color_id: params[:color_id]).
|
||||||
|
first
|
||||||
|
|
||||||
respond_to do |format|
|
render json: @pet_type
|
||||||
format.html do
|
|
||||||
@pet_states = group_pet_states @pet_type.pet_states
|
|
||||||
end
|
|
||||||
format.json { render json: @pet_type }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# The API-ish route uses IDs, but the human-facing route uses names.
|
|
||||||
def find_pet_type
|
|
||||||
if params[:species_id] && params[:color_id]
|
|
||||||
PetType.find_by!(
|
|
||||||
species_id: params[:species_id],
|
|
||||||
color_id: params[:color_id],
|
|
||||||
)
|
|
||||||
elsif params[:name]
|
|
||||||
color_name, _, species_name = params[:name].rpartition("-")
|
|
||||||
raise ActiveRecord::RecordNotFound if species_name.blank?
|
|
||||||
PetType.matching_name(color_name, species_name).first!
|
|
||||||
else
|
|
||||||
raise "expected params: species_id and color_id, or name"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# The `canonical` pet states are the main ones we want to show: the most
|
|
||||||
# canonical state for each pose. The `other` pet states are, the others!
|
|
||||||
#
|
|
||||||
# If no main poses are available, then we just make all the poses
|
|
||||||
# "canonical", and show the whole mish-mash!
|
|
||||||
MAIN_POSES = %w(HAPPY_FEM HAPPY_MASC SAD_FEM SAD_MASC SICK_FEM SICK_MASC)
|
|
||||||
def group_pet_states(pet_states)
|
|
||||||
pose_groups = pet_states.emotion_order.group_by(&:pose)
|
|
||||||
main_groups = pose_groups.select { |k| MAIN_POSES.include?(k) }.values
|
|
||||||
other_groups = pose_groups.reject { |k| MAIN_POSES.include?(k) }.values
|
|
||||||
|
|
||||||
if main_groups.empty?
|
|
||||||
return {canonical: other_groups.flatten(1).sort_by(&:pose), other: []}
|
|
||||||
end
|
|
||||||
|
|
||||||
canonical = main_groups.map(&:first).sort_by(&:pose)
|
|
||||||
main_others = main_groups.map { |l| l.drop(1) }.flatten(1)
|
|
||||||
other = (main_others + other_groups.flatten(1)).sort_by(&:pose)
|
|
||||||
|
|
||||||
{canonical:, other:}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,30 +14,19 @@ module ItemsHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
Sizes = {
|
Sizes = {
|
||||||
face: 1, # 50x50
|
face: 1,
|
||||||
face_3x: 6, # 150x150
|
thumb: 2,
|
||||||
|
zoom: 3,
|
||||||
thumb: 2, # 150x150
|
full: 4,
|
||||||
full: 4, # 300x300
|
face_2x: 6,
|
||||||
large: 5, # 500x500
|
|
||||||
xlarge: 7, # 640x640
|
|
||||||
|
|
||||||
zoom: 3, # 80x80
|
|
||||||
autocrop: 9, # <varies>
|
|
||||||
}
|
|
||||||
|
|
||||||
SizeUpgrades = {
|
|
||||||
face: :face_3x,
|
|
||||||
thumb: :full,
|
|
||||||
full: :xlarge,
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def pet_type_image_url(pet_type, emotion: :happy, size: :face)
|
def pet_type_image_url(pet_type, emotion: :happy, size: :face)
|
||||||
PetTypeImage::Template.expand(
|
PetTypeImage::Template.expand(
|
||||||
hash: pet_type.basic_image_hash || pet_type.image_hash,
|
hash: pet_type.basic_image_hash || pet_type.image_hash,
|
||||||
emotion: PetTypeImage::Emotions.fetch(emotion),
|
emotion: PetTypeImage::Emotions[emotion],
|
||||||
size: PetTypeImage::Sizes.fetch(size),
|
size: PetTypeImage::Sizes[size],
|
||||||
).to_s
|
).to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -257,10 +246,8 @@ module ItemsHelper
|
||||||
|
|
||||||
def pet_type_image(pet_type, emotion, size, **options)
|
def pet_type_image(pet_type, emotion, size, **options)
|
||||||
src = pet_type_image_url(pet_type, emotion:, size:)
|
src = pet_type_image_url(pet_type, emotion:, size:)
|
||||||
|
srcset = if size == :face
|
||||||
size_2x = PetTypeImage::SizeUpgrades[size]
|
[[pet_type_image_url(pet_type, emotion:, size: :face_2x), "2x"]]
|
||||||
srcset = if size_2x
|
|
||||||
[[pet_type_image_url(pet_type, emotion:, size: size_2x), "2x"]]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
image_tag(src, srcset:, **options)
|
image_tag(src, srcset:, **options)
|
||||||
|
|
|
@ -69,17 +69,5 @@ module OutfitsHelper
|
||||||
options = {:spellcheck => false, :id => nil}.merge(options)
|
options = {:spellcheck => false, :id => nil}.merge(options)
|
||||||
text_field_tag 'name', nil, options
|
text_field_tag 'name', nil, options
|
||||||
end
|
end
|
||||||
|
|
||||||
def outfit_viewer(outfit_or_options)
|
|
||||||
outfit = if outfit_or_options.is_a? Hash
|
|
||||||
Outfit.new(outfit_or_options)
|
|
||||||
elsif outfit_or_options.is_a? Outfit
|
|
||||||
outfit_or_options
|
|
||||||
else
|
|
||||||
raise TypeError, "must be an outfit or hash of options to create one"
|
|
||||||
end
|
|
||||||
|
|
||||||
render partial: "outfit_viewer", locals: {outfit:}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
module PetStatesHelper
|
|
||||||
def pose_name(pose)
|
|
||||||
case pose
|
|
||||||
when "HAPPY_FEM"
|
|
||||||
"Happy (Feminine)"
|
|
||||||
when "HAPPY_MASC"
|
|
||||||
"Happy (Masculine)"
|
|
||||||
when "SAD_FEM"
|
|
||||||
"Sad (Feminine)"
|
|
||||||
when "SAD_MASC"
|
|
||||||
"Sad (Masculine)"
|
|
||||||
when "SICK_FEM"
|
|
||||||
"Sick (Feminine)"
|
|
||||||
when "SICK_MASC"
|
|
||||||
"Sick (Masculine)"
|
|
||||||
when "UNCONVERTED"
|
|
||||||
"Unconverted"
|
|
||||||
else
|
|
||||||
"Not labeled yet"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
POSE_OPTIONS = %w(HAPPY_FEM SAD_FEM SICK_FEM HAPPY_MASC SAD_MASC SICK_MASC
|
|
||||||
UNCONVERTED UNKNOWN)
|
|
||||||
def pose_options
|
|
||||||
POSE_OPTIONS
|
|
||||||
end
|
|
||||||
|
|
||||||
def useful_pet_state_path(...)
|
|
||||||
if support_staff?
|
|
||||||
edit_pet_type_pet_state_path(...)
|
|
||||||
else
|
|
||||||
pet_type_pet_state_path(...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -71,28 +71,6 @@ class PetState < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: More and more, wanting to refactor poses…
|
|
||||||
def pose=(pose)
|
|
||||||
case pose
|
|
||||||
when "UNKNOWN"
|
|
||||||
label_pose nil, nil, unconverted: nil, labeled: false
|
|
||||||
when "HAPPY_MASC"
|
|
||||||
label_pose 1, false
|
|
||||||
when "HAPPY_FEM"
|
|
||||||
label_pose 1, true
|
|
||||||
when "SAD_MASC"
|
|
||||||
label_pose 2, false
|
|
||||||
when "SAD_FEM"
|
|
||||||
label_pose 2, true
|
|
||||||
when "SICK_MASC"
|
|
||||||
label_pose 4, false
|
|
||||||
when "SICK_FEM"
|
|
||||||
label_pose 4, true
|
|
||||||
when "UNCONVERTED"
|
|
||||||
label_pose nil, nil, unconverted: true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def reassign_children_to!(main_pet_state)
|
def reassign_children_to!(main_pet_state)
|
||||||
self.contributions.each do |contribution|
|
self.contributions.each do |contribution|
|
||||||
contribution.contributed = main_pet_state
|
contribution.contributed = main_pet_state
|
||||||
|
@ -132,7 +110,7 @@ class PetState < ApplicationRecord
|
||||||
def swf_asset_ids=(ids)
|
def swf_asset_ids=(ids)
|
||||||
self['swf_asset_ids'] = ids
|
self['swf_asset_ids'] = ids
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_assets!
|
def handle_assets!
|
||||||
@parent_swf_asset_relationships_to_update.each do |rel|
|
@parent_swf_asset_relationships_to_update.each do |rel|
|
||||||
rel.swf_asset.save!
|
rel.swf_asset.save!
|
||||||
|
@ -140,10 +118,6 @@ class PetState < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_param
|
|
||||||
"#{id}-#{pose.split('_').map(&:capitalize).join('-')}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.from_pet_type_and_biology_info(pet_type, info)
|
def self.from_pet_type_and_biology_info(pet_type, info)
|
||||||
swf_asset_ids = []
|
swf_asset_ids = []
|
||||||
info.each do |zone_id, asset_info|
|
info.each do |zone_id, asset_info|
|
||||||
|
@ -197,15 +171,5 @@ class PetState < ApplicationRecord
|
||||||
pet_state.parent_swf_asset_relationships_to_update = relationships
|
pet_state.parent_swf_asset_relationships_to_update = relationships
|
||||||
pet_state
|
pet_state
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# A helper for the `pose=` method.
|
|
||||||
def label_pose(mood_id, female, unconverted: false, labeled: true)
|
|
||||||
self.labeled = labeled
|
|
||||||
self.mood_id = mood_id
|
|
||||||
self.female = female
|
|
||||||
self.unconverted = unconverted
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -15,10 +15,6 @@ class PetType < ApplicationRecord
|
||||||
species = Species.find_by_name!(species_name)
|
species = Species.find_by_name!(species_name)
|
||||||
where(color_id: color.id, species_id: species.id)
|
where(color_id: color.id, species_id: species.id)
|
||||||
}
|
}
|
||||||
scope :matching_name_param, ->(name_param) {
|
|
||||||
color_name, _, species_name = name_param.rpartition("-")
|
|
||||||
matching_name(color_name, species_name)
|
|
||||||
}
|
|
||||||
scope :preferring_species, ->(species_id) {
|
scope :preferring_species, ->(species_id) {
|
||||||
joins(:species).order([Arel.sql("species_id = ? DESC"), species_id])
|
joins(:species).order([Arel.sql("species_id = ? DESC"), species_id])
|
||||||
}
|
}
|
||||||
|
@ -112,10 +108,6 @@ class PetType < ApplicationRecord
|
||||||
Item.appearances_for(item, self, ...)
|
Item.appearances_for(item, self, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_param
|
|
||||||
"#{color.human_name}-#{species.human_name}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.all_by_ids_or_children(ids, pet_states)
|
def self.all_by_ids_or_children(ids, pet_states)
|
||||||
pet_states_by_pet_type_id = {}
|
pet_states_by_pet_type_id = {}
|
||||||
pet_states.each do |pet_state|
|
pet_states.each do |pet_state|
|
||||||
|
|
|
@ -21,6 +21,6 @@
|
||||||
- if swf_asset.canvas_movie?
|
- if swf_asset.canvas_movie?
|
||||||
%iframe{src: swf_asset_path(swf_asset, playing: outfit_viewer_is_playing ? true : nil)}
|
%iframe{src: swf_asset_path(swf_asset, playing: outfit_viewer_is_playing ? true : nil)}
|
||||||
- elsif swf_asset.image_url.present?
|
- elsif swf_asset.image_url.present?
|
||||||
= image_tag swf_asset.image_url, alt: "", loading: "lazy"
|
= image_tag swf_asset.image_url, alt: ""
|
||||||
- else
|
- else
|
||||||
/ No movie or image available for SWF asset: #{swf_asset.url}
|
/ No movie or image available for SWF asset: #{swf_asset.url}
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
= turbo_frame_tag "item-preview" do
|
= turbo_frame_tag "item-preview" do
|
||||||
.preview-area
|
.preview-area
|
||||||
= outfit_viewer @preview_outfit
|
= render partial: "outfit_viewer", locals: {outfit: @preview_outfit}
|
||||||
.error-indicator
|
.error-indicator
|
||||||
💥 We couldn't load all of this outfit. Try again?
|
💥 We couldn't load all of this outfit. Try again?
|
||||||
= link_to wardrobe_path(params: @preview_outfit.wardrobe_params),
|
= link_to wardrobe_path(params: @preview_outfit.wardrobe_params),
|
||||||
|
@ -122,8 +122,6 @@
|
||||||
|
|
||||||
- content_for :stylesheets do
|
- content_for :stylesheets do
|
||||||
= stylesheet_link_tag "application/hanger-spinner"
|
= stylesheet_link_tag "application/hanger-spinner"
|
||||||
-# This is imported into items/show directly, to gain access to its mixins.
|
|
||||||
-# = stylesheet_link_tag "application/outfit-viewer"
|
|
||||||
= page_stylesheet_link_tag "layouts/items"
|
= page_stylesheet_link_tag "layouts/items"
|
||||||
= page_stylesheet_link_tag "items/show"
|
= page_stylesheet_link_tag "items/show"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
%li
|
|
||||||
= link_to useful_pet_state_path(pet_state.pet_type, pet_state) do
|
|
||||||
= outfit_viewer pet_state:
|
|
||||||
.name= pose_name pet_state.pose
|
|
||||||
- if pet_state.glitched?
|
|
||||||
%span.glitched{title: "Glitched"} 👾
|
|
|
@ -1,50 +0,0 @@
|
||||||
- title "#{@pet_type.human_name}: #{pose_name @pet_state.pose}"
|
|
||||||
- use_responsive_design
|
|
||||||
|
|
||||||
%ol.breadcrumbs
|
|
||||||
%li
|
|
||||||
= link_to "Rainbow Pool", pet_types_path
|
|
||||||
%li
|
|
||||||
= link_to @pet_type.color.human_name,
|
|
||||||
pet_types_path(color: @pet_type.color.human_name)
|
|
||||||
%li{"data-relation-to-prev": "sibling"}
|
|
||||||
= link_to @pet_type.species.human_name,
|
|
||||||
pet_types_path(species: @pet_type.species.human_name)
|
|
||||||
%li
|
|
||||||
= link_to "Appearances", @pet_type
|
|
||||||
%li
|
|
||||||
= link_to "\##{@pet_state.id}", [@pet_type, @pet_state]
|
|
||||||
%li
|
|
||||||
Edit
|
|
||||||
|
|
||||||
= outfit_viewer pet_state: @pet_state
|
|
||||||
|
|
||||||
= form_with model: [@pet_type, @pet_state] do |f|
|
|
||||||
- if @pet_state.errors.any?
|
|
||||||
%p
|
|
||||||
Could not save:
|
|
||||||
%ul.errors
|
|
||||||
- @pet_state.errors.each do |error|
|
|
||||||
%li= error.full_message
|
|
||||||
%dl
|
|
||||||
%dt Pose
|
|
||||||
%dd
|
|
||||||
%ul.pose-options
|
|
||||||
- pose_options.each do |pose|
|
|
||||||
%li
|
|
||||||
%label
|
|
||||||
= f.radio_button :pose, pose
|
|
||||||
= pose_name pose
|
|
||||||
%dt Glitched?
|
|
||||||
%dd
|
|
||||||
= f.select :glitched, [["✅ Not marked as Glitched", false],
|
|
||||||
["👾 Yes, it's bad news bonko'd", true]]
|
|
||||||
= f.submit "Save"
|
|
||||||
|
|
||||||
- content_for :stylesheets do
|
|
||||||
= stylesheet_link_tag "application/breadcrumbs"
|
|
||||||
= stylesheet_link_tag "application/outfit-viewer"
|
|
||||||
= page_stylesheet_link_tag "pet_states/edit"
|
|
||||||
|
|
||||||
- content_for :javascripts do
|
|
||||||
= javascript_include_tag "outfit-viewer"
|
|
|
@ -1,54 +0,0 @@
|
||||||
- title "#{@pet_type.human_name}: #{pose_name @pet_state.pose}"
|
|
||||||
- use_responsive_design
|
|
||||||
|
|
||||||
%ol.breadcrumbs
|
|
||||||
%li
|
|
||||||
= link_to "Rainbow Pool", pet_types_path
|
|
||||||
%li
|
|
||||||
= link_to @pet_type.color.human_name,
|
|
||||||
pet_types_path(color: @pet_type.color.human_name)
|
|
||||||
%li{"data-relation-to-prev": "sibling"}
|
|
||||||
= link_to @pet_type.species.human_name,
|
|
||||||
pet_types_path(species: @pet_type.species.human_name)
|
|
||||||
%li
|
|
||||||
= link_to "Appearances", @pet_type
|
|
||||||
%li
|
|
||||||
\##{@pet_state.id}
|
|
||||||
- if support_staff?
|
|
||||||
%li{"data-relation-to-prev": "menu"}
|
|
||||||
= link_to "Edit", edit_pet_type_pet_state_path(@pet_type, @pet_state)
|
|
||||||
|
|
||||||
= outfit_viewer pet_state: @pet_state
|
|
||||||
|
|
||||||
%dl
|
|
||||||
%dt{title: "Pose usually affects just the eyes and mouth. Neopets " +
|
|
||||||
"genders these as Male/Female, but I don't like those " +
|
|
||||||
"terms for like… it's just eyelashes! Sheesh!"}
|
|
||||||
Pose
|
|
||||||
%dd
|
|
||||||
= pose_name @pet_state.pose
|
|
||||||
- if @pet_state.pose == "UNCONVERTED"
|
|
||||||
(Retired, replaced by #{link_to "Alt Styles", alt_styles_path})
|
|
||||||
|
|
||||||
%dt{title: "This is our own internal ID number, nothing to do with " +
|
|
||||||
"Neopets's official data."}
|
|
||||||
DTI ID
|
|
||||||
%dd= @pet_state.id
|
|
||||||
|
|
||||||
%dt{title: "When we notice a form looks wrong, we mark it Glitched, to " +
|
|
||||||
"tell our systems to prefer other forms for this pose instead."}
|
|
||||||
Glitched?
|
|
||||||
%dd
|
|
||||||
- if @pet_state.glitched?
|
|
||||||
👾 Yes, it's bad news bonko'd
|
|
||||||
- else
|
|
||||||
✅ Not marked as Glitched
|
|
||||||
|
|
||||||
- content_for :stylesheets do
|
|
||||||
= stylesheet_link_tag "application/breadcrumbs"
|
|
||||||
= stylesheet_link_tag "application/hanger-spinner"
|
|
||||||
= stylesheet_link_tag "application/outfit-viewer"
|
|
||||||
= page_stylesheet_link_tag "pet_states/show"
|
|
||||||
|
|
||||||
- content_for :javascripts do
|
|
||||||
= javascript_include_tag "outfit-viewer", async: true
|
|
|
@ -1,4 +0,0 @@
|
||||||
%li
|
|
||||||
= link_to pet_type do
|
|
||||||
= pet_type_image pet_type, :happy, :thumb
|
|
||||||
.name= pet_type.human_name
|
|
|
@ -1,18 +0,0 @@
|
||||||
- title "Rainbow Pool"
|
|
||||||
- use_responsive_design
|
|
||||||
|
|
||||||
= form_with method: :get, class: "pet-filters" do |form|
|
|
||||||
%fieldset
|
|
||||||
%legend Filter by:
|
|
||||||
= form.select :color, @color_names, selected: @selected_color&.human_name, include_blank: "Color…"
|
|
||||||
= form.select :species, @species_names, selected: @selected_species&.human_name, include_blank: "Species…"
|
|
||||||
= form.submit "Go"
|
|
||||||
|
|
||||||
= will_paginate @pet_types
|
|
||||||
|
|
||||||
%ui.pet-types= render @pet_types
|
|
||||||
|
|
||||||
= will_paginate @pet_types
|
|
||||||
|
|
||||||
- content_for :stylesheets do
|
|
||||||
= page_stylesheet_link_tag "pet_types/index"
|
|
|
@ -1,32 +0,0 @@
|
||||||
- title "#{@pet_type.human_name}"
|
|
||||||
- use_responsive_design
|
|
||||||
|
|
||||||
%ol.breadcrumbs
|
|
||||||
%li
|
|
||||||
= link_to "Rainbow Pool", pet_types_path
|
|
||||||
%li
|
|
||||||
= link_to @pet_type.color.human_name,
|
|
||||||
pet_types_path(color: @pet_type.color.human_name)
|
|
||||||
%li{"data-relation-to-prev": "sibling"}
|
|
||||||
= link_to @pet_type.species.human_name,
|
|
||||||
pet_types_path(species: @pet_type.species.human_name)
|
|
||||||
%li
|
|
||||||
Appearances
|
|
||||||
|
|
||||||
%ul.pet-states
|
|
||||||
= render @pet_states[:canonical]
|
|
||||||
|
|
||||||
- if @pet_states[:other].present?
|
|
||||||
%details
|
|
||||||
%summary Other
|
|
||||||
%ul.pet-states
|
|
||||||
= render @pet_states[:other]
|
|
||||||
|
|
||||||
- content_for :stylesheets do
|
|
||||||
= stylesheet_link_tag "application/breadcrumbs"
|
|
||||||
= stylesheet_link_tag "application/hanger-spinner"
|
|
||||||
= stylesheet_link_tag "application/outfit-viewer"
|
|
||||||
= page_stylesheet_link_tag "pet_types/show"
|
|
||||||
|
|
||||||
- content_for :javascripts do
|
|
||||||
= javascript_include_tag "outfit-viewer", async: true
|
|
|
@ -37,10 +37,6 @@ OpenneoImpressItems::Application.routes.draw do
|
||||||
end
|
end
|
||||||
resources :alt_styles, path: 'alt-styles', only: [:index]
|
resources :alt_styles, path: 'alt-styles', only: [:index]
|
||||||
resources :swf_assets, path: 'swf-assets', only: [:show]
|
resources :swf_assets, path: 'swf-assets', only: [:show]
|
||||||
resources :pet_types, path: 'rainbow-pool', param: "name",
|
|
||||||
only: [:index, :show] do
|
|
||||||
resources :pet_states, only: [:show, :edit, :update], path: "appearances"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Loading and modeling pets!
|
# Loading and modeling pets!
|
||||||
post '/pets/load' => 'pets#load', :as => :load_pet
|
post '/pets/load' => 'pets#load', :as => :load_pet
|
||||||
|
|
Loading…
Reference in a new issue