Fix expanded state handling for trade offers and modify expand all on trade offers listing to persist between pages
This commit is contained in:
parent
6a61b79bbe
commit
63e20bace6
7 changed files with 74 additions and 128 deletions
|
|
@ -24,9 +24,9 @@
|
|||
<div x-show="activeTab === 'dash'">
|
||||
<div class="card bg-base-100 shadow-xl mb-4">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{{ _('Trade Summary') }}</h2>
|
||||
<h2 class="card-title mb-2">{{ _('Trade Summary') }}</h2>
|
||||
<div class="flex flex-col md:flex-row justify-center gap-4">
|
||||
<div class="stats shadow">
|
||||
<div class="stats shadow-lg bg-base-300">
|
||||
<div class="stat">
|
||||
<div class="stat-title">{{ _('Your Reputation') }}</div>
|
||||
<div class="stat-value">{{ request.user.reputation_score }}</div>
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
<div class="stat-desc">{{ _('Active Offers') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats shadow">
|
||||
<div class="stats shadow-lg bg-base-300">
|
||||
<div class="stat">
|
||||
<div class="stat-title">{{ _('Waiting on You') }}</div>
|
||||
<div class="stat-value">{{ trade_acceptances_waiting_paginated.page_obj.count }}</div>
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
</div>
|
||||
<div class="card bg-base-100 shadow-xl">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{{ _('Quick Actions') }}</h2>
|
||||
<h2 class="card-title mb-2">{{ _('Quick Actions') }}</h2>
|
||||
<div class="flex space-x-4">
|
||||
<a href="{% url 'trade_offer_create' %}" class="btn btn-primary grow">{{ _('Create New Offer') }}</a>
|
||||
<a href="{% url 'trade_offer_list' %}" class="btn btn-secondary grow">{{ _('View All Offers') }}</a>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
{# Render a trade acceptance using our new tag #}
|
||||
{% render_trade_acceptance offer %}
|
||||
{% else %}
|
||||
{% render_trade_offer offer %}
|
||||
{% render_trade_offer offer expanded=expanded %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% empty %}
|
||||
|
|
|
|||
|
|
@ -4,15 +4,48 @@
|
|||
{% block title %}Trade Offers{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mx-auto max-w-4xl mt-6" x-data="{ allExpanded: false }">
|
||||
<!-- Header-->
|
||||
<div class="container mx-auto max-w-4xl mt-6" x-data="{
|
||||
// Initialize allExpanded from the URL if present, defaulting to false.
|
||||
allExpanded: new URLSearchParams(window.location.search).get('expanded') === 'true',
|
||||
page: {{ page_obj.number|default:1 }},
|
||||
loadOffers() {
|
||||
let url = new URL('{% url 'trade_offer_list' %}', window.location.origin);
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
params.set('page', this.page);
|
||||
// Include the expanded state if active
|
||||
if (this.allExpanded) {
|
||||
params.set('expanded', 'true');
|
||||
} else {
|
||||
params.delete('expanded');
|
||||
}
|
||||
url.search = params.toString();
|
||||
fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' }})
|
||||
.then(response => response.text())
|
||||
.then(html => { this.$refs.offersContainer.innerHTML = html; });
|
||||
},
|
||||
// Update the URL so that navigation preserves our expanded state.
|
||||
updateUrl() {
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
if (this.allExpanded) {
|
||||
params.set('expanded', 'true');
|
||||
} else {
|
||||
params.delete('expanded');
|
||||
}
|
||||
history.replaceState(null, '', window.location.pathname + '?' + params.toString());
|
||||
}
|
||||
}"
|
||||
x-init="loadOffers()"
|
||||
x-on:change-page.window="page = $event.detail.page; loadOffers()">
|
||||
|
||||
<!-- Header with the toggle -->
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h1 class="text-2xl font-bold">Trade Offers</h1>
|
||||
<div>
|
||||
<form method="get" class="flex items-center gap-4" x-data>
|
||||
<label class="cursor-pointer flex items-center gap-2">
|
||||
<span x-text="allExpanded ? 'Collapse All' : 'Expand All'"></span>
|
||||
<input type="checkbox" name="all_expanded" value="true" class="toggle toggle-primary" @click="allExpanded = !allExpanded; $dispatch('toggle-all', { expanded: allExpanded })">
|
||||
<input type="checkbox" name="all_expanded" value="true" class="toggle toggle-primary"
|
||||
@click="allExpanded = !allExpanded; $dispatch('toggle-all', { expanded: allExpanded }); updateUrl();">
|
||||
</label>
|
||||
<label class="cursor-pointer flex items-center gap-2">
|
||||
<span>Only Closed</span>
|
||||
|
|
@ -27,22 +60,8 @@
|
|||
<div class="flex justify-end mb-4">
|
||||
<a href="{% url 'trade_offer_create' %}" class="btn btn-success">Create New Offer</a>
|
||||
</div>
|
||||
<div id="all-trade-offers"
|
||||
x-data="{
|
||||
page: {{ page_obj.number|default:1 }},
|
||||
loadOffers() {
|
||||
let url = new URL('{% url 'trade_offer_list' %}', window.location.origin);
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
params.set('page', this.page);
|
||||
url.search = params.toString();
|
||||
fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' }})
|
||||
.then(response => response.text())
|
||||
.then(html => { this.$el.innerHTML = html; });
|
||||
}
|
||||
}"
|
||||
x-init="loadOffers()"
|
||||
x-on:change-page.window="page = $event.detail.page; loadOffers()">
|
||||
{% include "trades/_trade_offer_list.html" with offers=offers page_obj=page_obj %}
|
||||
<div id="all-trade-offers" x-ref="offersContainer">
|
||||
{% include "trades/_trade_offer_list.html" with offers=offers page_obj=page_obj expanded=expanded %}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<div class="container mx-auto max-w-2xl mt-6">
|
||||
<h2 class="text-2xl font-bold">Trade Offer Details</h2>
|
||||
<div class="flex justify-center mt-10">
|
||||
{% render_trade_offer object %}
|
||||
{% render_trade_offer object expanded=True %}
|
||||
</div>
|
||||
{% if acceptance_form %}
|
||||
<div class="w-3/4 mx-auto mt-4">
|
||||
|
|
|
|||
|
|
@ -1,24 +1,8 @@
|
|||
{% load gravatar card_badge cache %}
|
||||
|
||||
{% cache 60 trade_offer offer_pk %}
|
||||
<div class="trade-offer-card-screenshot p-4 h-full w-auto flex justify-center"
|
||||
{% if screenshot_mode %}
|
||||
x-data="{
|
||||
setDimension() {
|
||||
const aspectRatio = 1.91;
|
||||
// If the element is taller than it is wide,
|
||||
// adjust the width based on the element's height.
|
||||
if ($el.offsetHeight > $el.offsetWidth) {
|
||||
$el.style.width = ($el.offsetHeight * aspectRatio) + 'px';
|
||||
} else {
|
||||
// Otherwise, adjust the height based on the element's width.
|
||||
$el.style.height = ($el.offsetWidth / aspectRatio) + 'px';
|
||||
}
|
||||
}
|
||||
}"
|
||||
x-init="setDimension(); window.addEventListener('resize', setDimension)"
|
||||
{% endif %}>
|
||||
<div x-data="tradeOfferCard()" x-init="badgeExpanded = {{expanded|lower}}" class="transition-all duration-500 trade-offer-card my-auto"
|
||||
<div class="trade-offer-card m-2 h-full w-auto flex justify-center">
|
||||
<div x-data="tradeOfferCard()" x-init="defaultExpanded = {{expanded|lower}}; badgeExpanded = defaultExpanded; acceptanceExpanded = defaultExpanded; flipped = {{flipped|lower}}" class="transition-all duration-500 trade-offer-card my-auto"
|
||||
@toggle-all.window="setBadge($event.detail.expanded)">
|
||||
|
||||
<!-- Flip container providing perspective -->
|
||||
|
|
@ -27,12 +11,12 @@
|
|||
The rotating element (.flip-inner) now uses CSS Grid to stack its children in a single cell.
|
||||
Persistent border, shadow, and rounding are applied here and the card rotates entirely.
|
||||
-->
|
||||
<div class="flip-inner freeze-bg-color grid grid-cols-1 grid-rows-1 card bg-base-100 card-border shadow-lg {% if screenshot_mode and num_cards_available >= 4 %}w-160{% else %}w-84 md:w-84 lg:w-84{% endif %} transform transition-transform duration-700 ease-in-out"
|
||||
<div class="flip-inner freeze-bg-color grid grid-cols-1 grid-rows-1 card bg-base-100 card-border shadow-lg w-84 transform transition-transform duration-700 ease-in-out"
|
||||
:class="{'rotate-y-180': flipped}">
|
||||
|
||||
<!-- Front Face: Trade Offer -->
|
||||
<!-- Using grid placement classes (col-start-1 row-start-1) ensures both faces overlap -->
|
||||
<div class="flip-face front {% if screenshot_mode %}mb-2{% endif %} col-start-1 row-start-1 grid grid-cols-1 auto-rows-min gap-2 content-between">
|
||||
<div class="flip-face front col-start-1 row-start-1 grid grid-cols-1 auto-rows-min gap-2 content-between">
|
||||
<!-- Header -->
|
||||
<div class="flip-face-header self-start">
|
||||
<a href="{% url 'trade_offer_detail' pk=offer_pk %}" class="no-underline block">
|
||||
|
|
@ -58,71 +42,26 @@
|
|||
<div class="flip-face-body self-start">
|
||||
<a href="{% url 'trade_offer_detail' pk=offer_pk %}" class="no-underline block">
|
||||
<div class="px-2 main-badges pb-0">
|
||||
{% if screenshot_mode and num_cards_available >= 4 %}
|
||||
<!-- When screenshot_mode is true, use an outer grid with 3 columns: Has side, a vertical divider, and Wants side -->
|
||||
<div class="flex flex-row gap-2 justify-around">
|
||||
<!-- Has Side (inner grid of 2 columns) -->
|
||||
<div class="flex flex-row gap-2">
|
||||
{% for card in have_cards_available|slice:"0:2" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- Vertical Divider -->
|
||||
<div class="flex justify-center">
|
||||
<div class="w-px bg-gray-300 h-full"></div>
|
||||
</div>
|
||||
<!-- Wants Side (inner grid of 2 columns) -->
|
||||
<div class="flex flex-row gap-2">
|
||||
{% for card in want_cards_available|slice:"0:2" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- Normal mode: just use an outer grid with 2 columns -->
|
||||
<div class="flex flex-row gap-2 justify-around">
|
||||
<!-- Has Side -->
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for card in have_cards_available|slice:"0:1" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- Wants Side -->
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for card in want_cards_available|slice:"0:1" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- Extra Card Badges (Collapsible) -->
|
||||
{% if screenshot_mode and num_cards_available >= 4 %}
|
||||
<div class="px-2 extra-badges">
|
||||
<!-- In screenshot mode, add a vertical divider between the Has and Wants sides -->
|
||||
<div class="flex flex-row gap-2 justify-around">
|
||||
<!-- Has Side Extra Badges -->
|
||||
<div class="grid grid-cols-2 gap-2 {% if screenshot_mode and num_cards_available >= 4 %}w-[296px]{% endif %}">
|
||||
{% for card in have_cards_available|slice:"2:" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
<!-- Has Side -->
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for card in have_cards_available|slice:"0:1" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- Vertical Divider -->
|
||||
<div class="flex justify-center">
|
||||
<div class="w-px bg-gray-300 h-full"></div>
|
||||
</div>
|
||||
<!-- Wants Side Extra Badges -->
|
||||
<div class="grid grid-cols-2 gap-2 {% if screenshot_mode and num_cards_available >= 4 %}w-[296px]{% endif %}">
|
||||
{% for card in want_cards_available|slice:"2:" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
<!-- Wants Side -->
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for card in want_cards_available|slice:"0:1" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div {% if not screenshot_mode %}x-show="badgeExpanded" x-collapse.duration.500ms x-cloak{% endif %} class="px-2 extra-badges">
|
||||
</a>
|
||||
|
||||
<!-- Extra Card Badges (Collapsible) -->
|
||||
<div x-show="badgeExpanded" x-collapse.duration.500ms x-cloak class="px-2 extra-badges">
|
||||
<a href="{% url 'trade_offer_detail' pk=offer_pk %}" class="no-underline block">
|
||||
<div class="flex flex-row gap-2 justify-around">
|
||||
<!-- Has Side Extra Badges -->
|
||||
|
|
@ -140,9 +79,7 @@
|
|||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if not screenshot_mode and have_cards_available|length > 1 and want_cards_available|length > 1 %}
|
||||
<div @click="badgeExpanded = !badgeExpanded" class="flex justify-center h-5 cursor-pointer">
|
||||
<svg x-bind:class="{ 'rotate-180': badgeExpanded }"
|
||||
class="transition-transform duration-500 h-5 w-5 cursor-pointer"
|
||||
|
|
@ -151,10 +88,6 @@
|
|||
d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="h-5"></div>
|
||||
{% endif %}
|
||||
{% if not screenshot_mode %}
|
||||
<div class="flip-face-footer self-end">
|
||||
<div class="flex justify-between px-2 pb-2">
|
||||
<div class="text-gray-500 text-sm tooltip tooltip-right" data-tip="ID: {{ offer_hash }}">
|
||||
|
|
@ -165,8 +98,7 @@
|
|||
</svg>
|
||||
</div>
|
||||
<!-- Front-to-back flip button -->
|
||||
<div class="cursor-pointer text-gray-500"
|
||||
@click="if(badgeExpanded){ badgeExpanded = false; setTimeout(() => { flipped = true; }, 500); } else { flipped = true; }">
|
||||
<div class="cursor-pointer text-gray-500" @click="flipped = true; acceptanceExpanded = defaultExpanded">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M3 8.689c0-.864.933-1.406 1.683-.977l7.108 4.061a1.125 1.125 0 0 1 0 1.954l-7.108 4.061A1.125 1.125 0 0 1 3 16.811V8.69ZM12.75 8.689c0-.864.933-1.406 1.683-.977l7.108 4.061a1.125 1.125 0 0 1 0 1.954l-7.108 4.061a1.125 1.125 0 0 1-1.683-.977V8.69Z" />
|
||||
</svg>
|
||||
|
|
@ -174,18 +106,10 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="flip-face-footer self-end">
|
||||
<div class="flex flex-col gap-2 text-center">
|
||||
<div class="text-sm font-semibold text-base-content">{{ in_game_name }} {% if show_friend_code %}<span class="text-base-content/50">•</span> {{ friend_code }}{% endif %}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Back Face: Acceptances View -->
|
||||
<!-- Placed in the same grid cell as the front face -->
|
||||
{% if not screenshot_mode %}
|
||||
<div class="flip-face back col-start-1 row-start-1 grid grid-cols-1 auto-rows-min gap-2 content-between" style="transform: rotateY(180deg);">
|
||||
<div class="self-start">
|
||||
<a href="{% url 'trade_offer_detail' pk=offer_pk %}" class="no-underline">
|
||||
|
|
@ -281,8 +205,7 @@
|
|||
</div>
|
||||
<div class="flex justify-between px-2 pb-2 self-end">
|
||||
<!-- Back-to-front flip button -->
|
||||
<div class="text-gray-500 cursor-pointer"
|
||||
@click="if(acceptanceExpanded){ acceptanceExpanded = false; setTimeout(() => { flipped = false; }, 500); } else { flipped = false; }">
|
||||
<div class="text-gray-500 cursor-pointer" @click="flipped = false; badgeExpanded = defaultExpanded">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 16.811c0 .864-.933 1.406-1.683.977l-7.108-4.061a1.125 1.125 0 0 1 0-1.954l7.108-4.061A1.125 1.125 0 0 1 21 8.689v8.122ZM11.25 16.811c0 .864-.933 1.406-1.683.977l-7.108-4.061a1.125 1.125 0 0 1 0-1.954l7.108-4.061a1.125 1.125 0 0 1 1.683.977v8.122Z" />
|
||||
</svg>
|
||||
|
|
@ -301,7 +224,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -324,10 +246,5 @@
|
|||
.rotate-y-180 {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
{% if screenshot_mode %}
|
||||
*:not(.freeze-bg-color) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
{% endif %}
|
||||
</style>
|
||||
{% endcache %}
|
||||
|
|
@ -24,9 +24,17 @@ def render_trade_offer(context, offer, expanded=False):
|
|||
if acceptance.is_active
|
||||
]
|
||||
|
||||
# Determine if the offer should show its back side (acceptances view) by default.
|
||||
# If either side has no available cards, then flip the offer.
|
||||
if not have_cards_available or not want_cards_available:
|
||||
flipped = True
|
||||
else:
|
||||
flipped = False
|
||||
|
||||
return {
|
||||
'offer_pk': offer.pk,
|
||||
'expanded': expanded,
|
||||
'flipped': flipped, # new flag to control the default face
|
||||
'offer_hash': offer.hash,
|
||||
'rarity_icon': offer.rarity_icon,
|
||||
'initiated_by_email': offer.initiated_by.user.email,
|
||||
|
|
|
|||
|
|
@ -75,13 +75,15 @@ class TradeOfferAllListView(ReusablePaginationMixin, ListView):
|
|||
self.per_page = 10
|
||||
paginated_offers, pagination_context = self.paginate_data(queryset, page_number)
|
||||
context["offers"] = paginated_offers
|
||||
# Set pagination context using the key expected in the template
|
||||
context["page_obj"] = pagination_context
|
||||
# Add the expanded flag to the context based on the URL query parameter.
|
||||
context["expanded"] = request.GET.get("expanded", "false").lower() == "true"
|
||||
return context
|
||||
|
||||
def render_to_response(self, context, **response_kwargs):
|
||||
if self.request.headers.get("X-Requested-With") == "XMLHttpRequest":
|
||||
show_closed = self.request.GET.get("show_closed", "false").lower() == "true"
|
||||
expanded = self.request.GET.get("expanded", "false").lower() == "true"
|
||||
queryset = TradeOffer.objects.all()
|
||||
if show_closed:
|
||||
queryset = queryset.filter(is_closed=True)
|
||||
|
|
@ -94,7 +96,7 @@ class TradeOfferAllListView(ReusablePaginationMixin, ListView):
|
|||
return render(
|
||||
self.request,
|
||||
"trades/_trade_offer_list.html",
|
||||
{"offers": paginated_offers, "page_obj": pagination_context}
|
||||
{"offers": paginated_offers, "page_obj": pagination_context, "expanded": expanded}
|
||||
)
|
||||
return super().render_to_response(context, **response_kwargs)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue