Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions app/controllers/artifacts_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

# Public, shareable "I earned this" page for a single shop purchase. The URL is
# keyed by ShopOrder#signed_id (purpose: :artifact), not the sequential id, so
# purchases can't be enumerated. The page is open to anyone with the link; the
# link is only mintable from the buyer's own orders page (behind :artifact_share).
class ArtifactsController < ApplicationController
def show
@order = ShopOrder.find_signed(params[:id], purpose: :artifact)
if @order.nil?
skip_authorization
return head :not_found
end

authorize @order, :show?, policy_class: ArtifactPolicy

@item = @order.shop_item
@buyer = @order.user
@stardust_spent = @order.total_cost_with_modifiers
@featured_project = featured_project_for(@buyer)
end

private

# Stardust is a pooled balance, so a purchase isn't tied to a specific ship.
# Feature the buyer's most recent ship-event payout's project as the thing
# they "earned it by shipping". Best-effort: nil falls back to generic copy.
def featured_project_for(user)
LedgerEntry
.where(user: user, ledgerable_type: "Post::ShipEvent")
.order(created_at: :desc)
.first&.ledgerable&.post&.project
rescue StandardError
nil
end
end
8 changes: 8 additions & 0 deletions app/policies/artifact_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

# A purchase artifact is intentionally public: anyone with the signed link can
# view it. Enumeration is prevented by the signed id (see ArtifactsController),
# not by this policy.
class ArtifactPolicy < ApplicationPolicy
def show? = true
end
44 changes: 44 additions & 0 deletions app/views/artifacts/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<% project = @featured_project %>
<% content_for :title, "#{@buyer.display_name} earned #{@item.name} on Stardance" %>
<% content_for :og_title, "I earned #{@item.name} on Stardance" %>
<% content_for :og_description, project ? "Shipped #{project.title}, earned stardust, and redeemed it for #{@item.name} on Stardance." : "Earned stardust building on Stardance and redeemed it for #{@item.name}." %>
<% content_for :og_image, project_og_image_url(project, format: :png) if project %>
<% content_for :twitter_card, "summary_large_image" %>
<% content_for :head do %>
<meta name="robots" content="noindex, nofollow">
<% end %>

<section class="idv-setup-card">
<p class="idv-setup-card__eyebrow">Stardance</p>
<h2 class="idv-setup-card__title"><%= @buyer.display_name %> earned this</h2>

<div class="idv-setup-card__body">
<% if project %>
<p class="idv-setup-card__lede">
Earned by shipping <strong><%= project.title %></strong> on Stardance, then redeemed
<strong><%= number_with_precision(@stardust_spent, precision: 0) %> stardust</strong>
for <strong><%= @item.name %></strong>.
</p>
<% else %>
<p class="idv-setup-card__lede">
Earned <strong><%= number_with_precision(@stardust_spent, precision: 0) %> stardust</strong>
building on Stardance and redeemed it for <strong><%= @item.name %></strong>.
</p>
<% end %>
</div>

<%= link_to "Start building on Stardance →", root_url,
class: "action-btn action-btn--large action-btn--primary idv-setup-card__cta" %>

<footer class="idv-setup-card__footer">
<p class="idv-setup-card__footer-links">
<button type="button" class="idv-setup-card__footer-link"
data-controller="copy tooltip"
data-copy-text-value="<%= artifact_url(@order.signed_id(purpose: :artifact)) %>"
data-tooltip-message-value="Click to copy"
data-action="copy#copy">
Copy link to share →
</button>
</p>
</footer>
</section>
14 changes: 14 additions & 0 deletions app/views/shop/orders/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,20 @@
<% if (order.pending? || order.aasm_state == "awaiting_periodical_fulfillment") && !order.shop_item.is_a?(ShopItem::FreeStickers) %>
<%= button_to "Cancel Order", cancel_shop_order_path(order), method: :delete, class: "my-orders__cancel-link", form: { onsubmit: "return confirm('Are you sure you want to cancel this order?')" } %>
<% end %>
<% if Flipper.enabled?(:artifact_share, current_user) %>
<%= render ActionButtonComponent.new(
text: "Share your win →",
type: :button,
size: :small,
variant: :secondary,
data: {
controller: "copy tooltip",
"copy-text-value": artifact_url(order.signed_id(purpose: :artifact)),
"tooltip-message-value": "Click to copy",
action: "copy#copy"
}
) %>
<% end %>
</div>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions config/initializers/flipper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
week_1_release
hardware_flow
ship_event_payouts
artifact_share
].each { |flag| Flipper.add(flag) }
end
rescue StandardError => e
Expand Down
4 changes: 4 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,10 @@

# Static OG images
get "og/:page", to: "og_images#show", as: :og_image, defaults: { format: :png }

# Shareable purchase artifact (public, keyed by a signed id — see ArtifactsController)
get "artifacts/:id", to: "artifacts#show", as: :artifact

# Landing
root "landing#index"
get "landing/signup_count", to: "landing#signup_count", as: :landing_signup_count
Expand Down