diff --git a/theme/templates/trades/_trade_offer_list.html b/theme/templates/trades/_trade_offer_list.html
index 8bfc930..980be3b 100644
--- a/theme/templates/trades/_trade_offer_list.html
+++ b/theme/templates/trades/_trade_offer_list.html
@@ -17,42 +17,4 @@
{% empty %}
No trade offers available.
{% endfor %}
-
-
-{% if offers.has_other_pages %}
-
-
- {% if offers.has_previous %}
-
-
- Previous
-
-
- {% else %}
-
- Previous
-
- {% endif %}
-
- {% for num in offers.paginator.page_range %}
-
-
- {{ num }}
-
-
- {% endfor %}
-
- {% if offers.has_next %}
-
-
- Next
-
-
- {% else %}
-
- Next
-
- {% endif %}
-
-
-{% endif %}
\ No newline at end of file
+
\ No newline at end of file
diff --git a/theme/templates/trades/_trade_offer_list_paginated.html b/theme/templates/trades/_trade_offer_list_paginated.html
new file mode 100644
index 0000000..f3ff479
--- /dev/null
+++ b/theme/templates/trades/_trade_offer_list_paginated.html
@@ -0,0 +1,38 @@
+{% include "trades/_trade_offer_list.html" %}
+{% if offers.has_other_pages %}
+
+
+ {% if offers.has_previous %}
+
+
+ Previous
+
+
+ {% else %}
+
+ Previous
+
+ {% endif %}
+
+ {% for num in offers.paginator.page_range %}
+
+
+ {{ num }}
+
+
+ {% endfor %}
+
+ {% if offers.has_next %}
+
+
+ Next
+
+
+ {% else %}
+
+ Next
+
+ {% endif %}
+
+
+{% endif %}
\ No newline at end of file
diff --git a/theme/templates/trades/trade_offer_all_list.html b/theme/templates/trades/trade_offer_all_list.html
index 31b4807..1dd2860 100644
--- a/theme/templates/trades/trade_offer_all_list.html
+++ b/theme/templates/trades/trade_offer_all_list.html
@@ -25,24 +25,47 @@
All Trade Offers
- {% if all_trade_offers_paginated.object_list %}
- {% include "trades/_trade_offer_list.html" with offers=all_trade_offers_paginated %}
-
- {% if all_trade_offers_paginated.has_previous %}
-
Previous
- {% else %}
-
- {% endif %}
-
Page {{ all_trade_offers_paginated.number }} of {{ all_trade_offers_paginated.paginator.num_pages }}
- {% if all_trade_offers_paginated.has_next %}
-
Next
- {% else %}
-
- {% endif %}
-
- {% else %}
- No trade offers found.
- {% endif %}
+
+ {% include "trades/_trade_offer_list_paginated.html" with offers=all_trade_offers_paginated %}
+
+
+
{% endblock content %}
\ No newline at end of file
diff --git a/theme/templates/trades/trade_offer_my_list.html b/theme/templates/trades/trade_offer_my_list.html
index ea8b14f..9c322e3 100644
--- a/theme/templates/trades/trade_offer_my_list.html
+++ b/theme/templates/trades/trade_offer_my_list.html
@@ -30,70 +30,70 @@
Waiting for Your Response
- {% if trade_acceptances_waiting_paginated.object_list %}
- {% include "trades/_trade_offer_list.html" with offers=trade_acceptances_waiting_paginated %}
-
- {% if trade_acceptances_waiting_paginated.has_previous %}
-
Previous
- {% else %}
-
- {% endif %}
-
Page {{ trade_acceptances_waiting_paginated.number }} of {{ trade_acceptances_waiting_paginated.paginator.num_pages }}
- {% if trade_acceptances_waiting_paginated.has_next %}
-
Next
- {% else %}
-
- {% endif %}
-
- {% else %}
- None at this time.
- {% endif %}
+
+ {% include "trades/_trade_offer_list_paginated.html" with offers=trade_acceptances_waiting_paginated %}
+
Waiting for Their Response
- {% if other_party_trade_acceptances_paginated.object_list %}
- {% include "trades/_trade_offer_list.html" with offers=other_party_trade_acceptances_paginated %}
-
- {% if other_party_trade_acceptances_paginated.has_previous %}
-
Previous
- {% else %}
-
- {% endif %}
-
Page {{ other_party_trade_acceptances_paginated.number }} of {{ other_party_trade_acceptances_paginated.paginator.num_pages }}
- {% if other_party_trade_acceptances_paginated.has_next %}
-
Next
- {% else %}
-
- {% endif %}
-
- {% else %}
- None at this time.
- {% endif %}
+
+ {% include "trades/_trade_offer_list_paginated.html" with offers=other_party_trade_acceptances_paginated %}
+
My Trade Offers
- {% if my_trade_offers_paginated.object_list %}
- {% include "trades/_trade_offer_list.html" with offers=my_trade_offers_paginated %}
-
- {% if my_trade_offers_paginated.has_previous %}
-
Previous
- {% else %}
-
- {% endif %}
-
Page {{ my_trade_offers_paginated.number }} of {{ my_trade_offers_paginated.paginator.num_pages }}
- {% if my_trade_offers_paginated.has_next %}
-
Next
- {% else %}
-
- {% endif %}
-
- {% else %}
- No trade offers found.
- {% endif %}
+
+ {% include "trades/_trade_offer_list_paginated.html" with offers=my_trade_offers_paginated %}
+
+
+
{% endblock content %}
\ No newline at end of file
diff --git a/trades/views.py b/trades/views.py
index d0bdb84..2ae145b 100644
--- a/trades/views.py
+++ b/trades/views.py
@@ -72,13 +72,13 @@ class TradeOfferAllListView(ListView):
model = TradeOffer
template_name = "trades/trade_offer_all_list.html"
-
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
request = self.request
show_closed = request.GET.get("show_closed", "false").lower() == "true"
context["show_closed"] = show_closed
+ # Build the queryset with our related objects.
queryset = (
TradeOffer.objects.select_related('initiated_by')
.prefetch_related(
@@ -96,12 +96,42 @@ class TradeOfferAllListView(ListView):
else:
queryset = queryset.filter(is_closed=False)
+ # On initial load, use the 'offers_page' parameter.
offers_page = request.GET.get("offers_page")
offers_paginator = Paginator(queryset, 10)
context["all_trade_offers_paginated"] = offers_paginator.get_page(offers_page)
-
return context
+ def render_to_response(self, context, **response_kwargs):
+ # For AJAX requests, return only the paginated fragment.
+ if self.request.headers.get("X-Requested-With") == "XMLHttpRequest":
+ page = self.request.GET.get("page")
+ show_closed = self.request.GET.get("show_closed", "false").lower() == "true"
+
+ queryset = (
+ TradeOffer.objects.select_related('initiated_by')
+ .prefetch_related(
+ 'trade_offer_have_cards__card',
+ 'trade_offer_want_cards__card',
+ Prefetch(
+ 'acceptances',
+ queryset=TradeAcceptance.objects.select_related('accepted_by', 'requested_card', 'offered_card')
+ )
+ )
+ .order_by("-updated_at")
+ )
+ if show_closed:
+ queryset = queryset.filter(is_closed=True)
+ else:
+ queryset = queryset.filter(is_closed=False)
+ paginated_offers = Paginator(queryset, 10).get_page(page)
+ return render(
+ self.request,
+ "trades/_trade_offer_list_paginated.html",
+ {"offers": paginated_offers}
+ )
+ return super().render_to_response(context, **response_kwargs)
+
class TradeOfferMyListView(LoginRequiredMixin, ListView):
model = TradeOffer # Fallback model; our context data holds separate filtered querysets.
template_name = "trades/trade_offer_my_list.html"
@@ -125,40 +155,35 @@ class TradeOfferMyListView(LoginRequiredMixin, ListView):
.order_by("-updated_at")
)
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- request = self.request
- show_closed = request.GET.get("show_closed", "false").lower() == "true"
- context["show_closed"] = show_closed
-
- friend_codes = request.user.friend_codes.all()
- friend_code_param = request.GET.get("friend_code")
+ def get_selected_friend_code(self):
+ friend_codes = self.request.user.friend_codes.all()
+ friend_code_param = self.request.GET.get("friend_code")
if friend_code_param:
try:
selected_friend_code = friend_codes.get(pk=friend_code_param)
except friend_codes.model.DoesNotExist:
- selected_friend_code = request.user.default_friend_code or friend_codes.first()
+ selected_friend_code = self.request.user.default_friend_code or friend_codes.first()
else:
- selected_friend_code = request.user.default_friend_code or friend_codes.first()
+ selected_friend_code = self.request.user.default_friend_code or friend_codes.first()
if not selected_friend_code:
raise PermissionDenied("You do not have an active friend code associated with your account.")
- context["selected_friend_code"] = selected_friend_code
- context["friend_codes"] = friend_codes
+ return selected_friend_code
+ def get_show_closed(self):
+ return self.request.GET.get("show_closed", "false").lower() == "true"
+
+ def get_my_trade_offers_paginated(self, page_param):
+ selected_friend_code = self.get_selected_friend_code()
queryset = self.get_queryset().filter(initiated_by=selected_friend_code)
- if show_closed:
+ if self.get_show_closed():
queryset = queryset.filter(is_closed=True)
else:
queryset = queryset.filter(is_closed=False)
+ return Paginator(queryset, 10).get_page(page_param)
- offers_page = request.GET.get("offers_page")
- offers_paginator = Paginator(queryset, 10)
- context["my_trade_offers_paginated"] = offers_paginator.get_page(offers_page)
-
- # ----- Trade Acceptances involving the user -----
- # Update terminal states to include the thanked and rejected states.
+ def get_involved_acceptances(self, selected_friend_code):
terminal_states = [
TradeAcceptance.AcceptanceState.THANKED_BY_INITIATOR,
TradeAcceptance.AcceptanceState.THANKED_BY_ACCEPTOR,
@@ -169,34 +194,72 @@ class TradeOfferMyListView(LoginRequiredMixin, ListView):
involved_acceptances_qs = TradeAcceptance.objects.filter(
Q(trade_offer__initiated_by=selected_friend_code) | Q(accepted_by=selected_friend_code)
).order_by("-updated_at")
+ if self.get_show_closed():
+ return involved_acceptances_qs.filter(state__in=terminal_states)
+ return involved_acceptances_qs.exclude(state__in=terminal_states)
- if show_closed:
- involved_acceptances = involved_acceptances_qs.filter(state__in=terminal_states)
- else:
- involved_acceptances = involved_acceptances_qs.exclude(state__in=terminal_states)
-
- # ----- Split Acceptances into "Waiting for Your Response" and "Other" -----
+ def get_trade_acceptances_waiting_paginated(self, page_param):
+ selected_friend_code = self.get_selected_friend_code()
+ involved_acceptances = self.get_involved_acceptances(selected_friend_code)
waiting_acceptances = involved_acceptances.filter(
Q(trade_offer__initiated_by=selected_friend_code, state__in=[
TradeAcceptance.AcceptanceState.ACCEPTED,
TradeAcceptance.AcceptanceState.RECEIVED,
]) |
- Q(accepted_by=selected_friend_code, state__in=[
- TradeAcceptance.AcceptanceState.SENT
- ])
+ Q(accepted_by=selected_friend_code, state__in=[TradeAcceptance.AcceptanceState.SENT])
)
- other_party_trade_acceptances = involved_acceptances.exclude(pk__in=waiting_acceptances.values("pk"))
+ return Paginator(waiting_acceptances, 10).get_page(page_param)
+ def get_other_party_trade_acceptances_paginated(self, page_param):
+ selected_friend_code = self.get_selected_friend_code()
+ involved_acceptances = self.get_involved_acceptances(selected_friend_code)
+ waiting_acceptances = involved_acceptances.filter(
+ Q(trade_offer__initiated_by=selected_friend_code, state__in=[
+ TradeAcceptance.AcceptanceState.ACCEPTED,
+ TradeAcceptance.AcceptanceState.RECEIVED,
+ ]) |
+ Q(accepted_by=selected_friend_code, state__in=[TradeAcceptance.AcceptanceState.SENT])
+ )
+ other_acceptances = involved_acceptances.exclude(pk__in=waiting_acceptances.values("pk"))
+ return Paginator(other_acceptances, 10).get_page(page_param)
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ request = self.request
+ show_closed = self.get_show_closed()
+ context["show_closed"] = show_closed
+
+ selected_friend_code = self.get_selected_friend_code()
+ context["selected_friend_code"] = selected_friend_code
+ context["friend_codes"] = request.user.friend_codes.all()
+
+ # Use request params for initial full page load (could be None)
+ offers_page = request.GET.get("offers_page")
waiting_page = request.GET.get("waiting_page")
other_page = request.GET.get("other_page")
- waiting_paginator = Paginator(waiting_acceptances, 10)
- other_party_paginator = Paginator(other_party_trade_acceptances, 10)
-
- context["trade_acceptances_waiting_paginated"] = waiting_paginator.get_page(waiting_page)
- context["other_party_trade_acceptances_paginated"] = other_party_paginator.get_page(other_page)
+ context["my_trade_offers_paginated"] = self.get_my_trade_offers_paginated(offers_page)
+ context["trade_acceptances_waiting_paginated"] = self.get_trade_acceptances_waiting_paginated(waiting_page)
+ context["other_party_trade_acceptances_paginated"] = self.get_other_party_trade_acceptances_paginated(other_page)
return context
+ def render_to_response(self, context, **response_kwargs):
+ # For AJAX requests, return only the paginated fragment.
+ if self.request.headers.get("X-Requested-With") == "XMLHttpRequest":
+ ajax_section = self.request.GET.get("ajax_section")
+ page = self.request.GET.get("page")
+ if ajax_section == "my_trade_offers":
+ offers = self.get_my_trade_offers_paginated(page)
+ elif ajax_section == "waiting_acceptances":
+ offers = self.get_trade_acceptances_waiting_paginated(page)
+ elif ajax_section == "other_party_acceptances":
+ offers = self.get_other_party_trade_acceptances_paginated(page)
+ else:
+ # Fallback to my trade offers.
+ offers = self.get_my_trade_offers_paginated(page)
+ return render(self.request, "trades/_trade_offer_list_paginated.html", {"offers": offers})
+ return super().render_to_response(context, **response_kwargs)
+
class TradeOfferDeleteView(LoginRequiredMixin, DeleteView):
model = TradeOffer
success_url = reverse_lazy("trade_offer_list")