Fix gravatar hovercards, and add trade_offer image generation with playwright, for use with opengraph tags on trade_offer_detal.html
This commit is contained in:
parent
4c0db9f842
commit
f3a1366269
16 changed files with 372 additions and 123 deletions
|
|
@ -10,13 +10,19 @@
|
|||
<div class="card-body">
|
||||
<div class="mx-auto mb-4">{{ user.email|gravatar:100 }}</div>
|
||||
<p class="text-center">All profile information is managed through Gravatar.</p>
|
||||
<p class="text-center mt-4"><a href="https://gravatar.com/profile/" target="_blank" rel="noopener noreferrer" class="btn btn-secondary">Edit Profile on Gravatar</a></p>
|
||||
<p class="text-center mt-4">
|
||||
<a href="https://gravatar.com/profile/" target="_blank" rel="noopener noreferrer" class="btn btn-secondary">
|
||||
Edit Profile on Gravatar
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
||||
</svg>
|
||||
</a></p>
|
||||
<div class="divider"></div>
|
||||
<h2 class="text-lg font-semibold pt-0">What is Gravatar?</h2>
|
||||
<p class="mb-4">Gravatar (Globally Recognized Avatar) is a free service that links your email address to a profile picture and, optionally, a profile. Many websites, including this one, use Gravatar to display your avatar and profile automatically.</p>
|
||||
|
||||
<h2 class="text-lg font-semibold">How does it work?</h2>
|
||||
<p class="mb-4">If you’ve set up a Gravatar, your profile picture will appear here whenever you use your email on supported sites. When someone hovers over or clicks on your avatar, your Gravatar profile will appear if you have one. If you don’t have a Gravatar yet, you’ll see a default image instead.</p>
|
||||
<p class="mb-4">If you've set up a Gravatar, your profile picture will appear here whenever you use your email on supported sites. When someone hovers over or clicks on your avatar, your Gravatar profile will also appear if you have one. If you don't have a Gravatar yet, you'll see a default image instead.</p>
|
||||
|
||||
<h2 class="text-lg font-semibold">Want to update or add a Gravatar?</h2>
|
||||
<p class="mb-4">Go to Gravatar.com to set up or change your avatar or profile. Your updates will appear here once saved!</p>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
{% include 'meta/meta.html' %}
|
||||
|
||||
<!-- Inline script to set the theme before rendering -->
|
||||
<script>
|
||||
(function () {
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
</script>
|
||||
|
||||
<title>[PᴋMɴ Trade Club] {% block title %}{% endblock title %}</title>
|
||||
|
||||
<link rel="shortcut icon" href="{% static 'images/favicon.ico' %}">
|
||||
<!-- Choices.js -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js@11.0.6/public/assets/styles/choices.min.css" />
|
||||
|
|
@ -113,12 +115,7 @@
|
|||
class="menu menu-sm dropdown-content bg-base-100 rounded-box z-1 mt-3 w-32 p-2 shadow">
|
||||
<li>
|
||||
<a class="flex items-center justify-between" href="{% url 'profile' %}">
|
||||
<div>Profile</div>
|
||||
<div>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" />
|
||||
</svg>
|
||||
</div>
|
||||
Profile
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load trade_offer_tags %}
|
||||
|
||||
{% block title %}Trade Offer Detail{% endblock title %}
|
||||
{% block title %}{{title}}{% endblock title %}
|
||||
|
||||
{% block content %}
|
||||
<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 %}
|
||||
{% if screenshot_mode == "true" %}
|
||||
{% render_trade_offer object True %}
|
||||
{% else %}
|
||||
{% render_trade_offer object %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if acceptance_form %}
|
||||
<div class="w-3/4 mx-auto mt-4">
|
||||
|
|
@ -50,7 +54,7 @@
|
|||
<a href="{% url 'trade_offer_list' %}" class="btn btn-secondary">Back to Trade Offers</a>
|
||||
{% if is_initiator %}
|
||||
<a href="{{ delete_close_url }}" class="btn btn-danger">Delete/Close Trade Offer</a>
|
||||
{% else %}
|
||||
{% elif request.user.is_authenticated %}
|
||||
<button type="submit" class="btn btn-primary">Submit Acceptance</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<div class="relative inline-block">
|
||||
<div class="grid grid-cols-4 grid-rows-2 my-2 px-2 py-2 h-16 w-36 text-white shadow-md shadow-black/50" style="{{ style }}">
|
||||
<div class="row-span-1 col-span-4 truncate text-ellipsis self-start font-semibold leading-tight text-sm max-w-7/8">{{ name }}</div>
|
||||
<div class="row-start-2 col-span-2 truncate self-end align-bottom text-xs">{{ rarity }}</div>
|
||||
<div class="row-start-2 col-start-3 col-span-2 text-right truncate self-end align-bottom font-semibold leading-none text-sm">{{ cardset }}</div>
|
||||
<div class="card-badge relative inline-block">
|
||||
<div class="card-badge-inner freeze-bg-color grid grid-cols-4 grid-rows-2 my-2 px-2 py-2 h-16 w-36 text-white shadow-md shadow-black/50" style="{{ style }}">
|
||||
<div class="cardname row-span-1 col-span-4 truncate text-ellipsis self-start font-semibold leading-tight text-sm max-w-7/8">{{ name }}</div>
|
||||
<div class="rarity row-start-2 col-span-2 truncate self-end align-bottom text-xs">{{ rarity }}</div>
|
||||
<div class="cardset row-start-2 col-start-3 col-span-2 text-right truncate self-end align-bottom font-semibold leading-none text-sm">{{ cardset }}</div>
|
||||
</div>
|
||||
<span class="card-quantity-badge absolute top-3.5 right-1 bg-gray-600 text-white text-xs font-semibold rounded-full px-2">
|
||||
{{ quantity }}
|
||||
</span>
|
||||
<span class="card-quantity-badge freeze-bg-color absolute top-3.5 right-1 bg-gray-600 text-white text-xs font-semibold rounded-full px-2">
|
||||
{{ quantity }}
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -16,8 +16,24 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div x-data="tradeOfferCard()" class="transition-all duration-500 trade-offer-card"
|
||||
<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()" class="transition-all duration-500 trade-offer-card my-auto"
|
||||
@toggle-all.window="setBadge($event.detail.expanded)">
|
||||
|
||||
<!-- Flip container providing perspective -->
|
||||
|
|
@ -26,85 +42,136 @@
|
|||
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 grid grid-cols-1 grid-rows-1 card bg-base-100 card-border shadow-lg w-96 md:w-80 lg:w-96 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 {% if screenshot_mode and num_cards_available >= 4 %}w-160{% else %}w-96 md:w-80 lg:w-96{% endif %} 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 col-start-1 row-start-1 grid grid-cols-1 auto-rows-min gap-2 content-between">
|
||||
<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">
|
||||
<!-- Header -->
|
||||
<div class="self-start">
|
||||
<div class="flip-face-header self-start">
|
||||
<a href="{% url 'trade_offer_detail' pk=offer_pk %}" class="no-underline block">
|
||||
<div class="py-4 mx-2 sm:mx-4">
|
||||
<div class="grid grid-cols-3 items-center">
|
||||
<div class="flex justify-center items-center">
|
||||
<span class="text-sm font-semibold">Has</span>
|
||||
</div>
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
{{ initiated_by_email|gravatar:40 }}
|
||||
</div>
|
||||
<!-- Set this container as relative to position the avatar absolutely -->
|
||||
<div class="relative mt-6 mb-4 mx-2 sm:mx-4">
|
||||
<!-- Two-column grid for the labels -->
|
||||
<div class="grid grid-cols-2 items-center">
|
||||
<span class="text-sm font-semibold text-center">Has</span>
|
||||
<span class="text-sm font-semibold text-center">Wants</span>
|
||||
</div>
|
||||
<!-- The avatar is placed absolutely and centered -->
|
||||
<div class="absolute inset-x-0 top-1/2 transform -translate-y-1/2 flex justify-center">
|
||||
<div class="avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
{{ initiated_by_email|gravatar:40 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center items-center">
|
||||
<span class="text-sm font-semibold">Wants</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<!-- Main Trade Offer Row -->
|
||||
<div class="self-start">
|
||||
<div class="flip-face-body self-start">
|
||||
<a href="{% url 'trade_offer_detail' pk=offer_pk %}" class="no-underline block">
|
||||
<div class="px-2 pb-0">
|
||||
<div class="grid grid-cols-2 gap-2 items-center">
|
||||
<div class="flex flex-col items-center">
|
||||
{% if have_cards_available %}
|
||||
{% with first_have=have_cards_available.0 %}
|
||||
{% card_badge first_have.card first_have.quantity %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
<div class="px-2 pb-0 main-badges">
|
||||
{% 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-between">
|
||||
<!-- 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>
|
||||
<div class="flex flex-col items-center">
|
||||
{% if want_cards_available %}
|
||||
{% with first_want=want_cards_available.0 %}
|
||||
{% card_badge first_want.card first_want.quantity %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<!-- Normal mode: just use an outer grid with 2 columns -->
|
||||
<div class="flex flex-row gap-2 {% if not screenshot_mode %}justify-between{% else %}justify-around{% endif %}">
|
||||
<!-- 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-between">
|
||||
<!-- Has Side Extra Badges -->
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
{% for card in have_cards_available|slice:"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 Extra Badges -->
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
{% for card in want_cards_available|slice:"2:" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Extra Card Badges (Collapsible) -->
|
||||
<div x-show="badgeExpanded" x-collapse.duration.500ms class="px-2">
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="flex flex-col items-center">
|
||||
{% for th in have_cards_available|slice:"1:" %}
|
||||
{% card_badge th.card th.quantity %}
|
||||
{% else %}
|
||||
<div {% if screenshot_mode %}x-show="badgeExpanded" x-collapse.duration.500ms{% endif %} 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 {% if not screenshot_mode %}justify-between{% else %}justify-around{% endif %}">
|
||||
<!-- Has Side Extra Badges -->
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for card in have_cards_available|slice:"1:" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="flex flex-col items-center">
|
||||
{% for th in want_cards_available|slice:"1:" %}
|
||||
{% card_badge th.card th.quantity %}
|
||||
<!-- Wants Side Extra Badges -->
|
||||
<div class="flex flex-col gap-2">
|
||||
{% for card in want_cards_available|slice:"1:" %}
|
||||
{% card_badge card.card card.quantity %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</a>
|
||||
<div class="flex justify-center my-1 h-5">
|
||||
{% if have_cards_available|length > 1 or want_cards_available|length > 1 %}
|
||||
<svg @click="badgeExpanded = !badgeExpanded"
|
||||
x-bind:class="{ 'rotate-180': badgeExpanded }"
|
||||
class="transition-transform duration-500 h-5 w-5 cursor-pointer"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="self-end">
|
||||
{% if not screenshot_mode %}
|
||||
<div class="flex justify-center h-5">
|
||||
{% if have_cards_available|length > 1 or want_cards_available|length > 1 %}
|
||||
<svg @click="badgeExpanded = !badgeExpanded"
|
||||
x-bind:class="{ 'rotate-180': badgeExpanded }"
|
||||
class="transition-transform duration-500 h-5 w-5 cursor-pointer"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
{% endif %}
|
||||
</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 }}">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||
|
|
@ -123,10 +190,18 @@
|
|||
</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 }} <span class="text-base-content/50">•</span> {{ friend_code }}</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">
|
||||
|
|
@ -208,17 +283,17 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center my-1 h-5">
|
||||
{% if acceptances|length > 1 %}
|
||||
<svg @click="acceptanceExpanded = !acceptanceExpanded"
|
||||
x-bind:class="{ 'rotate-180': acceptanceExpanded }"
|
||||
class="transition-transform duration-500 h-5 w-5 cursor-pointer"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="flex justify-center h-5">
|
||||
{% if acceptances|length > 1 %}
|
||||
<svg @click="acceptanceExpanded = !acceptanceExpanded"
|
||||
x-bind:class="{ 'rotate-180': acceptanceExpanded }"
|
||||
class="transition-transform duration-500 h-5 w-5 cursor-pointer"
|
||||
xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between px-2 pb-2 self-end">
|
||||
<!-- Back-to-front flip button -->
|
||||
|
|
@ -242,11 +317,11 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<style>
|
||||
/* Ensure proper 3D transformations on the rotating element */
|
||||
.flip-inner {
|
||||
|
|
@ -265,5 +340,10 @@
|
|||
.rotate-y-180 {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
{% if screenshot_mode %}
|
||||
*:not(.freeze-bg-color) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
{% endif %}
|
||||
</style>
|
||||
{% endcache %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue