diff --git a/docs/state-machine.png b/docs/state-machine.png new file mode 100644 index 0000000..e8cbe58 Binary files /dev/null and b/docs/state-machine.png differ diff --git a/make_post_sell/static/css/common.css b/make_post_sell/static/css/common.css index 6a6ceb3..8ec9509 100644 --- a/make_post_sell/static/css/common.css +++ b/make_post_sell/static/css/common.css @@ -7,6 +7,22 @@ * * * * * * */ +/* Preload hover background images to prevent flash */ +body::after { + content: ""; + position: absolute; + width: 0; + height: 0; + overflow: hidden; + z-index: -1; + background-image: + url("/static/img/trans-blue.png"), + url("/static/img/trans-green.png"), + url("/static/img/trans-red.png"), + url("/static/img/trans-blue2.png"), + url("/static/img/trans-green2.png"), + url("/static/img/trans-red2.png"); +} body { margin: 0px; @@ -411,6 +427,28 @@ input.mps-button-red { box-sizing: border-box; } +/* Red cancel button */ +.mps-cancel-button-red { + background: var(--red-color); + color: white; + border-color: var(--red-color); +} + +[data-theme="dark"] .mps-cancel-button-red { + background-color: #2d3748; + background-image: url("/static/img/trans-red.png"); + border: 2px solid var(--red-color); + color: var(--red-color); + font-weight: bold; +} + +[data-theme="dark"] .mps-cancel-button-red:hover { + background-color: #2d3748; + background-image: url("/static/img/trans-red2.png"); + color: #ffffff; + border: 2px solid var(--red-color); +} + .comment-actions { margin-bottom: 42px; display: grid; @@ -438,6 +476,92 @@ input.mps-button-red { box-sizing: border-box; } +/* Light mode color styling */ +.reply-link { + background: var(--green-color); + color: white; + border-color: var(--green-color); +} + +.delete-link { + background: var(--red-color); + color: white; + border-color: var(--red-color); +} + +.mps-comment-form-button { + background: var(--green-color); + color: white; + border-color: var(--green-color); +} + +[data-theme="dark"] .reply-link, +[data-theme="dark"] .edit-link, +[data-theme="dark"] .moderate-link { + background-color: #2d3748; + background-image: url("/static/img/trans-blue.png"); + border: 2px solid var(--blue-color); + color: var(--blue-color); + font-weight: bold; +} + +[data-theme="dark"] .reply-link:hover, +[data-theme="dark"] .edit-link:hover, +[data-theme="dark"] .moderate-link:hover { + background-color: #2d3748; + background-image: url("/static/img/trans-blue2.png"); + color: #ffffff; + border: 2px solid var(--blue-color); +} + +/* Reply button - green */ +[data-theme="dark"] .reply-link { + background-color: #2d3748; + background-image: url("/static/img/trans-green.png"); + border: 2px solid var(--green-color); + color: var(--green-color); + font-weight: bold; +} + +[data-theme="dark"] .reply-link:hover { + background-color: #2d3748; + background-image: url("/static/img/trans-green2.png"); + color: #ffffff; + border: 2px solid var(--green-color); +} + +/* Delete button - red */ +[data-theme="dark"] .delete-link { + background-color: #2d3748; + background-image: url("/static/img/trans-red.png"); + border: 2px solid var(--red-color); + color: var(--red-color); + font-weight: bold; +} + +[data-theme="dark"] .delete-link:hover { + background-color: #2d3748; + background-image: url("/static/img/trans-red2.png"); + color: #ffffff; + border: 2px solid var(--red-color); +} + +/* Post comment button - green */ +[data-theme="dark"] .mps-comment-form-button { + background-color: #2d3748; + background-image: url("/static/img/trans-green.png"); + border: 2px solid var(--green-color); + color: var(--green-color); + font-weight: bold; +} + +[data-theme="dark"] .mps-comment-form-button:hover { + background-color: #2d3748; + background-image: url("/static/img/trans-green2.png"); + color: #ffffff; + border: 2px solid var(--green-color); +} + .payment-toggle-button { display: inline-block; padding: 6px 12px; @@ -1339,27 +1463,31 @@ div.message-ribbon { /* Flash message styling for dark mode */ [data-theme="dark"] .alert-danger, [data-theme="dark"] .alert-error { - color: #ff6b6b; - background-color: #2d1f20; - border-color: #ff6b6b; + color: var(--red-color); + background-color: #2d3748; + background-image: url("/static/img/trans-red2.png"); + border-color: var(--red-color); } [data-theme="dark"] .alert-warning { color: #ffd93d; - background-color: #2d2a1f; + background-color: #2d3748; + background-image: url("/static/img/trans-red2.png"); border-color: #ffd93d; } [data-theme="dark"] .alert-info, [data-theme="dark"] .alert-notice { color: var(--blue-color); - background-color: #1f252d; + background-color: #2d3748; + background-image: url("/static/img/trans-blue2.png"); border-color: var(--blue-color); } [data-theme="dark"] .alert-success { color: var(--green-color); - background-color: #1f2d1f; + background-color: #2d3748; + background-image: url("/static/img/trans-green2.png"); border-color: var(--green-color); } @@ -1715,6 +1843,25 @@ img.crypto-button-icon { margin-bottom: 20px; } +[data-theme="dark"] .original-comment-box { + background-color: #2d3748; + background-image: url("/static/img/trans-blue.png"); + border-left: 4px solid var(--blue-color); + color: var(--text-primary); +} + +/* Comment reply styling */ +.comment-reply { + margin-left: 0px; + margin-bottom: 20px; + border-left: 2px solid #ddd; + padding-left: 10px; +} + +[data-theme="dark"] .comment-reply { + border-left: 2px solid var(--blue-color); +} + .start-button-icon { font-size: 20px; } diff --git a/make_post_sell/templates/comments/edit_comment.j2 b/make_post_sell/templates/comments/edit_comment.j2 index 2d8b847..7e47907 100644 --- a/make_post_sell/templates/comments/edit_comment.j2 +++ b/make_post_sell/templates/comments/edit_comment.j2 @@ -3,7 +3,7 @@ {% block title %}Edit Comment{% endblock %} {% block content %} -
+

Edit Comment

@@ -18,14 +18,16 @@
- - + +
+
+
-
+
{% endblock %} \ No newline at end of file diff --git a/make_post_sell/templates/comments/reply_comment.j2 b/make_post_sell/templates/comments/reply_comment.j2 index 8032237..107f7d7 100644 --- a/make_post_sell/templates/comments/reply_comment.j2 +++ b/make_post_sell/templates/comments/reply_comment.j2 @@ -3,7 +3,7 @@ {% block title %}Reply to Comment{% endblock %} {% block content %} -
+

Reply to Comment

@@ -18,14 +18,16 @@
- - + +
+
+
-
+
{% endblock %} \ No newline at end of file diff --git a/make_post_sell/templates/content.j2 b/make_post_sell/templates/content.j2 index a94f1d7..f09939b 100644 --- a/make_post_sell/templates/content.j2 +++ b/make_post_sell/templates/content.j2 @@ -30,7 +30,7 @@ {% block content -%} -
+
{% if "thumbnail1" in product.extensions %} diff --git a/make_post_sell/templates/crypto_quotes_history.j2 b/make_post_sell/templates/crypto_quotes_history.j2 index 32b7b35..0b66bcf 100644 --- a/make_post_sell/templates/crypto_quotes_history.j2 +++ b/make_post_sell/templates/crypto_quotes_history.j2 @@ -68,7 +68,7 @@ View Quote - {% elif payment.status.endswith('-refunded') or payment.status.endswith('-refunded-complete') or payment.status == 'cancelled' %} + {% elif payment.status.endswith('-refunded') or payment.status.endswith('-refunded-complete') %} View Quote diff --git a/make_post_sell/templates/product.j2 b/make_post_sell/templates/product.j2 index c193448..9021aea 100644 --- a/make_post_sell/templates/product.j2 +++ b/make_post_sell/templates/product.j2 @@ -47,7 +47,7 @@
- +

${{ '{:,.2f}'.format(product.price) }}

@@ -59,8 +59,6 @@ ⭳ Download

-
-
{% endif %} {% endif %} diff --git a/make_post_sell/templates/snippets/comments.j2 b/make_post_sell/templates/snippets/comments.j2 index c998d97..2284848 100644 --- a/make_post_sell/templates/snippets/comments.j2 +++ b/make_post_sell/templates/snippets/comments.j2 @@ -1,6 +1,6 @@ {% macro render_comment(comment, shop, request, max_depth=5) %} {% if comment.depth <= max_depth and (comment.approved or (request.user.authenticated and (shop.is_owner(request.user) or shop.is_editor(request.user)))) %} -
+
{{ comment.user.name if comment.user else "Anonymous" }} {{ comment.ago_string }} @@ -16,13 +16,14 @@ {{ comment.data_html | safe }}
+ {% if request.user.authenticated %}
- {% if request.user.authenticated and (request.user.id == comment.user_id or comment.can_user_moderate(request.user, shop)) %} + {% if request.user.id == comment.user_id or comment.can_user_moderate(request.user, shop) %}
{% endif %} - {% if request.user.authenticated and comment.can_user_moderate(request.user, shop) %} + {% if comment.can_user_moderate(request.user, shop) %} {% if comment.approved %}
@@ -32,17 +33,18 @@ {% endif %} {% endif %} - {% if request.user.authenticated and (request.user.id == comment.user_id or comment.can_user_moderate(request.user, shop)) %} + {% if request.user.id == comment.user_id or comment.can_user_moderate(request.user, shop) %} Edit {% endif %} - {% if request.user.authenticated and not comment.is_locked %} + {% if not comment.is_locked %} {% set can_comment, error_msg = comment.can_user_comment(request.user, shop) %} {% if can_comment %} Reply {% endif %} {% endif %}
+ {% endif %} {% if comment.enabled_children %} {% for child in comment.enabled_children %} @@ -57,7 +59,14 @@
{% if comments %}

Comments & Reviews ({{ product.public_comment_count }})

- + {% endif %} + + + {% if not request.user.authenticated %} +

Sign in to leave a comment.

+ {% endif %} + + {% if comments %} {% for comment in comments %} {{ render_comment(comment, shop, request) }} {% endfor %} @@ -78,20 +87,20 @@
- - + +
+
+
- +
{% else %}

{{ error_msg }}

{% endif %} - {% else %} -

Sign in to leave a comment.

{% endif %}
{% endif %} \ No newline at end of file diff --git a/make_post_sell/templates/user_settings.j2 b/make_post_sell/templates/user_settings.j2 index e8df929..9456803 100644 --- a/make_post_sell/templates/user_settings.j2 +++ b/make_post_sell/templates/user_settings.j2 @@ -114,6 +114,7 @@
+

Account Actions

{% if request.is_saas_domain %} My Shops @@ -129,8 +130,11 @@ Crypto Payment Preferences
Shipping Addresses +
{% endif %} + Back to Shop +
diff --git a/make_post_sell/views/crypto.py b/make_post_sell/views/crypto.py index c90149f..fc0d625 100644 --- a/make_post_sell/views/crypto.py +++ b/make_post_sell/views/crypto.py @@ -1142,11 +1142,13 @@ def estimate_monero_fee_for_quote(settings, shop_sweep_to_address, amount_picone # Double the fee to cover both inbound (customer) and outbound (sweep) transactions total_fee_piconero = estimated_fee_piconero * 2 - return Decimal(total_fee_piconero) / Decimal("1000000000000") # Convert to XMR + return abs( + Decimal(total_fee_piconero) / Decimal("1000000000000") + ) # Convert to XMR except Exception as e: # Fallback to hardcoded fee if RPC call fails, doubled for inbound + outbound fallback_fee = float(settings.get("monero.fee_buffer", "0.0001")) * 2 - return Decimal(str(fallback_fee)) + return abs(Decimal(str(fallback_fee))) def estimate_dogecoin_fee_for_quote(settings): @@ -1164,41 +1166,56 @@ def estimate_dogecoin_fee_for_quote(settings): logger.info(f"Dogecoin estimatesmartfee result: {fee_estimate_result}") if fee_estimate_result and "feerate" in fee_estimate_result: - # feerate is in DOGE per KB, estimate transaction size as ~0.25KB (more realistic) - # Typical DOGE transaction is ~225 bytes, not 500 bytes + # feerate is in DOGE per KB, estimate transaction size as ~0.15KB (realistic) + # Typical DOGE transaction is ~150 bytes for simple transfers network_feerate = float(fee_estimate_result["feerate"]) - # Cap fee rate at reasonable maximum (0.1 DOGE/KB = ~$0.025/KB) - # Dogecoin fees should never exceed this under normal conditions - max_reasonable_feerate = 0.1 + # Ensure positive feerate - negative values indicate RPC issues + if network_feerate <= 0: + logger.warning( + f"Invalid negative/zero feerate {network_feerate} from estimatesmartfee, using fallback" + ) + raise ValueError(f"Invalid feerate: {network_feerate}") + + # Cap fee rate at reasonable maximum (100 DOGE/KB = ~$26/KB) + # This is very generous - normal DOGE fees are much lower + max_reasonable_feerate = 100.0 if network_feerate > max_reasonable_feerate: logger.warning( f"Network fee rate {network_feerate} DOGE/KB is excessive, capping at {max_reasonable_feerate} DOGE/KB" ) network_feerate = max_reasonable_feerate - estimated_fee_doge = network_feerate * 0.25 - # Double the fee to cover both inbound (customer) and outbound (sweep) transactions - total_fee_doge = estimated_fee_doge * 2 + estimated_fee_doge = network_feerate * 0.15 + # Add reasonable buffer for sweep transaction (not doubling) + total_fee_doge = estimated_fee_doge * 1.5 + + # Cap the total fee at a reasonable maximum (0.5 DOGE = ~$0.13) + # This should rarely be hit if RPC is working properly + max_reasonable_total_fee = 0.5 + if total_fee_doge > max_reasonable_total_fee: + logger.warning( + f"Calculated DOGE fee {total_fee_doge} is excessive, capping at {max_reasonable_total_fee} DOGE" + ) + total_fee_doge = max_reasonable_total_fee logger.info( - f"Using dynamic DOGE fee: {network_feerate} DOGE/KB * 0.25KB * 2 = {total_fee_doge} DOGE" + f"Using dynamic DOGE fee: {network_feerate} DOGE/KB * 0.15KB * 1.5 = {total_fee_doge} DOGE" ) - return Decimal(str(total_fee_doge)) + return abs(Decimal(str(total_fee_doge))) else: - # Fallback to hardcoded fee if estimatesmartfee fails, doubled for inbound + outbound - # Reduce fallback from 0.01 to 0.002 DOGE (~$0.0006 vs $0.003) - fallback_fee = float(settings.get("dogecoin.fee_buffer", "0.002")) * 2 + # Fallback to reasonable fee if estimatesmartfee fails - RPC should be working! + fallback_fee = float(settings.get("dogecoin.fee_buffer", "0.5")) logger.warning( - f"Dogecoin estimatesmartfee failed, using fallback fee: {fallback_fee} DOGE" + f"Dogecoin estimatesmartfee failed (RPC issue?), using fallback fee: {fallback_fee} DOGE" ) - return Decimal(str(fallback_fee)) + return abs(Decimal(str(fallback_fee))) except Exception as e: - # Fallback to hardcoded fee if RPC call fails, doubled for inbound + outbound - fallback_fee = float(settings.get("dogecoin.fee_buffer", "0.002")) * 2 + # Fallback to reasonable fee if RPC call fails - RPC should be working! + fallback_fee = float(settings.get("dogecoin.fee_buffer", "0.5")) logger.error( - f"Dogecoin fee estimation error: {e}, using fallback fee: {fallback_fee} DOGE" + f"Dogecoin fee estimation error: {e} (RPC issue?), using fallback fee: {fallback_fee} DOGE" ) - return Decimal(str(fallback_fee)) + return abs(Decimal(str(fallback_fee))) def get_payment_status_info(status): @@ -1209,6 +1226,10 @@ def get_payment_status_info(status): "confirmed": {"label": "✓ Confirmed", "color": "#28a745"}, "confirmed-complete": {"label": "✓ Confirmed (Complete)", "color": "#28a745"}, "confirmed-overpay": {"label": "✓ Confirmed (Overpaid)", "color": "#28a745"}, + "confirmed-overpay-complete": { + "label": "✓ Confirmed (Overpaid)", + "color": "#28a745", + }, "expired": {"label": "Expired", "color": "#6c757d"}, "expired": {"label": "Expired", "color": "#6c757d"}, "latepay-refunded": {"label": "Late Payment - Refunded", "color": "#fd7e14"}, @@ -1242,7 +1263,7 @@ def get_payment_status_info(status): "label": "Duplicate Payment - Refunding", "color": "#fd7e14", }, - "doublepay-refunded-complete": { + "doublepay-refund-complete": { "label": "✓ Duplicate Payment - Refunded", "color": "#fd7e14", }, @@ -1267,7 +1288,9 @@ def get_payment_status_info(status): "color": "#dc3545", }, } - return status_mapping.get(status, {"label": status.title(), "color": "#6c757d"}) + return status_mapping.get( + status.lower(), {"label": status.title(), "color": "#6c757d"} + ) # TODO: COMMENT OUT WHEN DEBUGGING COMPLETE - TEMPORARY PRODUCTION DEBUG ROUTE