Fix trade offer png generation (fixes #27).

- Updated `card_badge.html` to adjust width properties for better layout consistency.
- Modified `trade_offer_png.html` to change padding for improved visual appearance.
- Enhanced `bubble_up_trade_offer_updates` signal to delete cached images when related instances change, ensuring up-to-date content.
- Updated `TradeOfferPNGView` to pass the request context when rendering the template, improving compatibility with Django's template rendering.
- Refactored `render_trade_offer_png` to use constants for dimensions and improve readability, while also updating context handling for better integration.
This commit is contained in:
badblocks 2025-05-05 21:50:52 -07:00
parent 9c41f63247
commit 9b3b3d099f
5 changed files with 40 additions and 17 deletions

View file

@ -3,8 +3,8 @@
<a href="{{ url }}" @click.stop> <a href="{{ url }}" @click.stop>
<div class="relative block"> <div class="relative block">
{% if not expanded %} {% if not expanded %}
<div class="flex flex-row items-center h-[32px] p-1.5 w-30 sm:w-40 text-white shadow-lg" style="{{ style }}"> <div class="flex flex-row items-center h-[32px] p-1.5 w-40 text-white shadow-lg" style="{{ style }}">
<div class="grow"><div class="truncate text-ellipsis font-semibold leading-tight text-sm marquee-calc w-4/5 sm:w-24">{{ name }}</div></div> <div class="grow"><div class="truncate text-ellipsis font-semibold leading-tight text-sm marquee-calc w-24">{{ name }}</div></div>
<div class="grow-0 shrink-0 text-right truncate font-semibold leading-tight text-sm">{{ cardset }}</div> <div class="grow-0 shrink-0 text-right truncate font-semibold leading-tight text-sm">{{ cardset }}</div>
{% if quantity != None %} {% if quantity != None %}
<div class="grow-0 shrink-0 relative w-fit ps-1"> <div class="grow-0 shrink-0 relative w-fit ps-1">
@ -13,7 +13,7 @@
{% endif %} {% endif %}
</div> </div>
{% else %} {% else %}
<div class="grid grid-rows-2 grid-cols-4 h-[52px] p-1.5 w-30 sm:w-40 text-white shadow-lg" style="{{ style }}"> <div class="grid grid-rows-2 grid-cols-4 h-[52px] p-1.5 w-40 text-white shadow-lg" style="{{ style }}">
<div class="row-start-1 col-start-1 {% if quantity != None %}col-span-3{% else %}col-span-4{% endif %} self-start leading-tight truncate text-ellipsis"><span class="font-semibold text-sm marquee-calc">{{ name }}</span></div> <div class="row-start-1 col-start-1 {% if quantity != None %}col-span-3{% else %}col-span-4{% endif %} self-start leading-tight truncate text-ellipsis"><span class="font-semibold text-sm marquee-calc">{{ name }}</span></div>
{% if quantity != None %} {% if quantity != None %}
<div class="row-start-1 col-start-4 col-span-1 self-start ms-auto leading-tight relative w-fit ps-1"> <div class="row-start-1 col-start-4 col-span-1 self-start ms-auto leading-tight relative w-fit ps-1">

View file

@ -9,7 +9,7 @@
</head> </head>
<body style="background-color: transparent !important"> <body style="background-color: transparent !important">
<div <div
class="trade-offer-card-screenshot p-4 h-full w-auto flex justify-center" class="trade-offer-card-screenshot p-3 h-full w-auto flex justify-center"
style="background-color: transparent !important" style="background-color: transparent !important"
> >
<div class="transition-all duration-500 trade-offer-card my-auto"> <div class="transition-all duration-500 trade-offer-card my-auto">

View file

@ -271,8 +271,12 @@ def trade_acceptance_reputation_delete(sender, instance, **kwargs):
@receiver(post_delete, sender=TradeAcceptance) @receiver(post_delete, sender=TradeAcceptance)
def bubble_up_trade_offer_updates(sender, instance, **kwargs): def bubble_up_trade_offer_updates(sender, instance, **kwargs):
""" """
Bubble up updates to the TradeOffer model when TradeOfferHaveCard, TradeOfferWantCard, Bubble up updated_at to the TradeOffer model when related instances change.
or TradeAcceptance instances are created, updated, or deleted. Also invalidates any cached image by deleting the file.
""" """
if instance.trade_offer: trade_offer = getattr(instance, 'trade_offer', None)
instance.trade_offer.save(update_fields=['updated_at'])
if trade_offer and trade_offer.image:
trade_offer.image.delete(save=True) # deleting the image will trigger a save, which updates the updated_at field
elif trade_offer:
trade_offer.save(update_fields=['updated_at'])

View file

@ -87,20 +87,30 @@ def action_button_class(state_value):
@register.inclusion_tag('templatetags/trade_offer_png.html', takes_context=True) @register.inclusion_tag('templatetags/trade_offer_png.html', takes_context=True)
def render_trade_offer_png(context, offer, show_friend_code=False): def render_trade_offer_png(context, offer, show_friend_code=False):
CARD_HEIGHT = 32
CARD_WIDTH = 160
HEADER_HEIGHT = 69
FOOTER_HEIGHT = 37
CARD_WIDTH_PADDING = 64
EXPANDED_CARD_WIDTH_PADDING = 80
CARD_COL_GAP = 4
OUTPUT_PADDING = 24 # height padding is handled by the HTML
have_cards_available = offer.have_cards_available have_cards_available = offer.have_cards_available
want_cards_available = offer.want_cards_available want_cards_available = offer.want_cards_available
num_cards = max(len(have_cards_available), len(want_cards_available)) num_cards = max(len(have_cards_available), len(want_cards_available))
expanded = (len(have_cards_available) + len(want_cards_available)) > 4 expanded = (len(have_cards_available) + len(want_cards_available)) > 4
if expanded: if expanded:
num_cards = ceil(num_cards / 2) num_cards = ceil(num_cards / 2) # 2 cards per row if expanded
image_height = (num_cards * 40) + 106 image_height = (num_cards * CARD_HEIGHT) + ((num_cards - 1) * CARD_COL_GAP) + HEADER_HEIGHT + FOOTER_HEIGHT
# cards incl pad + header/footer
if expanded: if expanded:
image_width = (4 * 160) + 110 image_width = (4 * CARD_WIDTH) + EXPANDED_CARD_WIDTH_PADDING
else: else:
image_width = (2 * 160) + 92 image_width = (2 * CARD_WIDTH) + CARD_WIDTH_PADDING
image_width += OUTPUT_PADDING
image_height += OUTPUT_PADDING # height padding is handled by the HTML, but we need to also calculate it here for og meta tag use
request = context.get("request") request = context.get("request")
if request.get_host().startswith("localhost"): if request.get_host().startswith("localhost"):
@ -108,7 +118,7 @@ def render_trade_offer_png(context, offer, show_friend_code=False):
else: else:
base_url = "https://{0}".format(request.get_host()) base_url = "https://{0}".format(request.get_host())
return { tag_context = {
'offer_pk': offer.pk, 'offer_pk': offer.pk,
'offer_hash': offer.hash, 'offer_hash': offer.hash,
'rarity_icon': offer.rarity_icon, 'rarity_icon': offer.rarity_icon,
@ -124,4 +134,8 @@ def render_trade_offer_png(context, offer, show_friend_code=False):
'image_width': image_width, 'image_width': image_width,
'image_height': image_height, 'image_height': image_height,
'base_url': base_url, 'base_url': base_url,
'cache_key': f'trade_offer_png_{offer.pk}_{offer.updated_at.timestamp()}_{expanded}',
} }
context.update(tag_context)
return context

View file

@ -1,3 +1,4 @@
from django.template import RequestContext
from django.views.generic import DeleteView, CreateView, ListView, DetailView, UpdateView from django.views.generic import DeleteView, CreateView, ListView, DetailView, UpdateView
from django.views import View from django.views import View
from django.urls import reverse_lazy from django.urls import reverse_lazy
@ -521,9 +522,13 @@ class TradeOfferPNGView(View):
image_height = tag_context.get('image_height') image_height = tag_context.get('image_height')
if not image_width or not image_height: if not image_width or not image_height:
raise ValueError("Could not determine image dimensions from tag_context") raise ValueError("Could not determine image dimensions from tag_context")
html = render_to_string("templatetags/trade_offer_png.html", tag_context) html = render_to_string(
"templatetags/trade_offer_png.html",
context=tag_context,
request=request
)
# if query string has "debug=true", render the HTML instead of the PNG # if query string has "debug", render the HTML instead of the PNG
if request.GET.get("debug") == "html": if request.GET.get("debug") == "html":
return render(request, "templatetags/trade_offer_png.html", tag_context) return render(request, "templatetags/trade_offer_png.html", tag_context)