Skip to content

Modernize App Store listing: product creation screenshots and copy - #358

Merged
gianfrancopiana merged 7 commits into
mainfrom
store-listing-product-creation
Aug 28, 2026
Merged

gianfrancopiana merged 7 commits into
mainfrom
store-listing-product-creation

Conversation

@gumclaw

@gumclaw gumclaw commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the 2015 App Store listing (Utilities, “watch what you buy”) with copy and real iOS Simulator screenshots that lead with product creation.

Addresses antiwork/gumroad-private#2320.

  • Subtitle: Create and sell on mobile (26 chars)
  • Description leads with create / publish, then library playback. Fee line is 10% + 50¢.
  • Five 1290×2796 frames for the iPhone 6.7" slot. The phone UI in each is a live Simulator capture of the store build, not an HTML mock.
  • README notes moving the iOS category Utilities → Business (App Store Connect, not git)

The App Store holds one screenshot set per device size per locale, so this replaces the live set rather than adding to it.

Does not upload to App Store Connect or Play — assets only. Receipt / “open in the app” mailer copy is an antiwork/gumroad change, not this repo.

The frames

Each frame is a flat Gumroad brand color, one headline in ABC Favorit Bold (the app's own typeface, loaded from assets/fonts/), and a Simulator capture in a black bezel bleeding off the bottom edge.

Frame Screen Headline Color
01-create-product.png Products tab Create a product from your phone. #FF90E8 pink
02-name-price.png Create product WebView Name it, price it, go live. #FFC900 yellow
03-today.png Dashboard, top See today before you open a laptop. #23A094 green
04-sales.png Dashboard, sale rows Every sale, as it lands. #90A8ED purple
05-library.png Library tab Everything you bought, in one place. #F4F4F0 cream

Headlines end in a period, matching the voice on gumroad.com (“Place small bets.”, “Share your work.”).

Why the frames changed

The earlier set had three problems, all fixed here.

Every frame now shows a different screen. Frames 1 and 2 previously shared one Products capture, and frames 3 and 4 shared one Dashboard capture — so frame 2 sold the create flow while showing a product list.

The data is presentable. The old captures showed e2e seed data: “Mobile Test Product 1”, mobile_buyer_do_not_edit@gumroad.com, and $10 from 2 sales. Captures now come from a purpose-built marketing seller, marketing_capture_seller@gumroad.com (“Sable Studio”): five products priced $12–$49 with real cover art, 56 sales today totalling $1,392, 119 sales across the past week, and three Library purchases from a second creator. Buyer emails and product names are invented. The mobile_*_do_not_edit accounts are deliberately untouched.

The frame design carries less chrome. The logo and eyebrow chip are gone; one large headline reads faster at App Store thumbnail size. Type is the app's real ABC Favorit rather than a substitute.

Reproducing

screenshots/CAPTURE.md has the full procedure: local backend setup, seeding, Release-configuration build, per-screen capture, and rendering. marketing-seed.rb is the seed script. Every headline and color lives in screenshots/frame.html; screenshots/render.sh drives headless Chrome and writes the five PNGs.

Two gotchas are written down because they cost real time: the create-product screen needs its “create with AI” promo banner dismissed and the screen reopened before the Name validation clears, and expo run:ios needs LANG=en_US.UTF-8 or CocoaPods crashes.

Backend bugs found on the way

Capturing needed the app pointed at a local antiwork/gumroad backend. Two real problems surfaced, both confirmed on antiwork/gumroad main. They are fixed in antiwork/gumroad#7422, not this repo. Details in store-listing/CAPTURE-BACKEND-NOTES.md.

  1. thumbnail_url is always nil in the mobile products API. Thumbnail#as_json returns symbol keys, but Api::Mobile::ProductsController#product_json reads props.dig("thumbnail", "url") with a string key. Every row in the app's Products tab renders the placeholder icon instead of the product cover. The test suite cannot catch it — the controller spec never asserts the field and the JSON schema permits null.
  2. PROTOCOL cannot be overridden, so local WebViews render blank. config/domain.rb hardcodes http in development, so CUSTOM_DOMAIN=gumroad.dev still emits http://app.localhost:3000 asset URLs. Inside the app's HTTPS WebView every embedded page renders blank, with no error in the app and none in the Rails log.

Test Results

  • All five frames are 1290×2796 (App Store 6.7" slot)
  • render.sh verified end to end from the checked-in simulator-raw/ captures and assets/fonts/
  • Frame 1 shows five products with cover art; frames 3 and 4 show $1,392 from 56 sales with buyer rows; frame 5 shows three Library purchases with creator avatars

QA steps

  1. Open the five PNGs — each shows a different screen, and none contains e2e seed data
  2. Run store-listing/screenshots/render.sh and confirm it reproduces them
  3. After merge: paste into App Store Connect and Play Console; flip the iOS category to Business

Status

  • Scope
  • Design
  • Build
  • QA — all five frames reviewed at full size
  • Ship
  • Market
  • Sell

Draft. Assets only — not uploaded to stores.

@gumclaw gumclaw added the working An agent is actively building this label Aug 28, 2026
@gumclaw gumclaw self-assigned this Aug 28, 2026
@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR modernizes the App Store listing around mobile product creation and replaces the existing screenshot set.

  • Updates the English subtitle, description, keywords, and promotional copy.
  • Adds five rendered 1290×2796 listing frames with distinct Simulator captures.
  • Adds capture, seeding, and rendering documentation and tooling.
  • Documents backend issues encountered during local screenshot capture.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains within the eligible follow-up-review scope.

No blocking failure remains.

Important Files Changed

Filename Overview
store-listing/marketing-seed.rb Adds repeatable marketing accounts, products, sales, and library purchases for screenshot capture.
store-listing/screenshots/frame.html Defines the five branded screenshot layouts, headlines, colors, and source-capture mappings.
store-listing/screenshots/render.sh Renders and resizes all five App Store screenshot frames on macOS.
store-listing/screenshots/CAPTURE.md Documents local backend setup, seed execution, Simulator capture, and frame rendering.
store-listing/en-US/description.txt Repositions the listing around product creation, publishing, seller analytics, and purchase playback.

Reviews (2): Last reviewed commit: "Redesign the App Store frames and recapt..." | Re-trigger Greptile

gumclaw and others added 2 commits August 27, 2026 22:33
Sahil asked for screenshots that highlight creation and edit
(antiwork/gumroad-private#2320). Slot 2 is now the edit form;
publish/sales/library shift down; the old library-player watch
frame is gone.

Co-authored-by: Sahil Lavingia <sahil@gumroad.com>
HTML mock phones are gone. Each 6.7" frame embeds a real
iPhone 17 Pro screenshot of the store build (Products, Dashboard, Library).

The sim account is empty, so Dashboard is $0 and Library has no purchases.
Recapture from a live creator account before App Store Connect upload.

Co-authored-by: Sahil Lavingia <sahil@gumroad.com>
@gumclaw

gumclaw commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Stays draft: the 6.7" frames are live Simulator captures, but the sim account has no catalogue (Dashboard $0 / empty Library) and the create-product WebView signed out, so frames 1–2 share the Products empty-state. Recapture from a live creator account before store upload / ready.

@gianfrancopiana

Copy link
Copy Markdown
Member

Stays draft: the 6.7" frames are live Simulator captures, but the sim account has no catalogue (Dashboard $0 / empty Library) and the create-product WebView signed out, so frames 1–2 share the Products empty-state. Recapture from a live creator account before store upload / ready.

Can you use a seeded dev account?

Empty simulator captures of a $0 account read as "the app has nothing."
Replace them with marketing frames of a mock creator (covers, sales, library).
Products, Dashboard ($10 / 2 sales), and Library now show the mobile_seller1 catalogue instead of the empty sim account.
@gumclaw

gumclaw commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

Yes. Recaptured from the local seeded seller (2 products, $10 today). New 6.7" frames are on the PR; create/edit WebView is still the Products catalogue.

@gianfrancopiana gianfrancopiana removed the working An agent is actively building this label Aug 28, 2026
Replace the five 6.7" frames. Each frame now shows a different screen, uses the
app's own ABC Favorit typeface, and carries one Gumroad brand color.

The previous set reused one Products capture for frames 1 and 2 and one Dashboard
capture for frames 3 and 4. It also showed e2e seed data: "Mobile Test Product 1"
and mobile_buyer_do_not_edit@gumroad.com.

Drop the logo and the eyebrow chip. A single large headline reads faster at App
Store thumbnail size. Headlines end in a period to match the voice on
gumroad.com.

Capture from a purpose-built marketing seller instead of the e2e accounts: five
products priced $12 to $49 with real cover art, 56 sales today totalling $1,392,
and three Library purchases from a second creator.

Add CAPTURE.md with the full procedure, marketing-seed.rb with the seed data, and
CAPTURE-BACKEND-NOTES.md describing two antiwork/gumroad bugs the captures hit.

render.sh now loads fonts from assets/fonts/ rather than a second copy.
@gianfrancopiana
gianfrancopiana merged commit f8120c8 into main Aug 28, 2026
3 checks passed
@gianfrancopiana
gianfrancopiana deleted the store-listing-product-creation branch August 28, 2026 17:37
gianfrancopiana added a commit to antiwork/gumroad that referenced this pull request Aug 28, 2026
…PS protocol (#7422)

## What

Two fixes, both found while capturing App Store screenshots for the
mobile app
([antiwork/gumroad-mobile#358](antiwork/gumroad-mobile#358)).

1. **Product thumbnails were missing from the mobile Products tab.**
`thumbnail_url` came back `nil` for every product, so each row rendered
a placeholder.
2. **Local WebViews rendered blank over HTTPS.** `PROTOCOL` had no
override, so pointing the mobile app at a local backend broke every
embedded page.

## Why

### The thumbnail was read with the wrong key type

`Api::Mobile::ProductsController#product_json` read
`props.dig("thumbnail", "url")`. Every other key in that hash is a
string, so this reads naturally — but `Thumbnail#as_json` returns
**symbol** keys:

```ruby
def as_json(*)
  { url:,
    guid:
  }
end
```

The outer lookup succeeds, the inner one misses, and the result is
always `nil`.

This is not environment-specific. The web dashboard reads the same hash,
but only after Inertia serializes it to JSON, where symbol keys become
strings — so only the mobile API, which dereferences it in Ruby, is
affected.

No test caught it. The controller spec asserted `name`, `permalink`,
`status`, `can_edit` and `can_destroy` but never `thumbnail_url`, and
the product schema declares the field as `["string", "null"]`, so `nil`
validated.

### PROTOCOL could not be overridden

`CUSTOM_DOMAIN` already exists to point a local backend at a registrable
hostname, and the mobile app's dev config expects `https://gumroad.dev`.
But `CUSTOM_DOMAIN` only changes the host — development stays on `http`,
so Rails emits asset URLs like
`http://app.localhost:3000/vite-dev/entrypoints/base.ts`.

Inside the app's HTTPS WebView those are unreachable. Every embedded
page — sign-in, create product, settings — renders blank, with **no
error in the app and none in the Rails log**. The page returns 200; only
its assets fail.

This adds a `CUSTOM_PROTOCOL` override next to the existing
`CUSTOM_DOMAIN` and `ASSET_DOMAIN` ones. Unset, `PROTOCOL` resolves
exactly as before.

### A third bug, surfaced by review

Review suggested `ENV["CUSTOM_PROTOCOL"].presence`. That cannot be used
here: `bin/vite` requires `config/domain.rb` before Rails boots, so
ActiveSupport is not loaded and `presence` raises `NoMethodError`.

Line 108 already called `ENV["BRANCH_DEPLOYMENT"].present?`, which means
`bin/vite` has been crashing whenever `CUSTOM_DOMAIN` is set — the exact
configuration this override pairs with:

```
$ CUSTOM_DOMAIN=gumroad.dev ruby -e '... require_relative "config/domain"'
NoMethodError: undefined method 'present?' for nil
```

Both checks are now plain Ruby, with a comment recording the constraint
so it is not reintroduced.

## Before / After

Not user-visible on the web. The mobile-side effect is in
[antiwork/gumroad-mobile#358](antiwork/gumroad-mobile#358):
its frame 01 shows the Products tab with cover art, which required this
fix — before it, every row showed a placeholder box.

## Test Results

- `bundle exec rspec
spec/controllers/api/mobile/products_controller_spec.rb
spec/config/domain_spec.rb` — 18 examples, 0 failures
- `bundle exec rubocop` on all changed files — no offenses
- Every new example was checked against its bug by reverting the fix and
confirming the failure:
  - reverting the key type fails "returns the thumbnail url"
  - reverting the blank guard fails the blank-`CUSTOM_PROTOCOL` example
- restoring `present?` fails the pre-Rails example with `NoMethodError`
  - reverting `strip` fails the whitespace example

New coverage in `spec/config/domain_spec.rb` loads the file the way
`bin/vite` does — bundler and vite_ruby, nothing else — because a
`bin/rails runner` boots Rails and would hide the ActiveSupport
constraint.

These assert the resolved constant, not the URLs built from it.
Confirming a WebView loads assets over HTTPS end to end needs a running
backend, so that stays manual.

## QA steps

1. `bundle exec rspec
spec/controllers/api/mobile/products_controller_spec.rb
spec/config/domain_spec.rb`
2. Unset `CUSTOM_PROTOCOL` and confirm development still resolves `http`
3. For the WebView fix end to end: run this backend with
`CUSTOM_DOMAIN=gumroad.dev CUSTOM_PROTOCOL=https
ASSET_DOMAIN=gumroad.dev`, front it with an HTTPS proxy on 443, then
open Products → New in the mobile app. The create-product page renders
instead of coming up blank.

## Note on CI

`Compute relevant specs` escalates here: `bin/branch-specs` cannot
attribute specs to `config/domain.rb`, so it asks for the full suite.
The `run-all-specs` label is applied for that reason.

I tried mapping `config/domain.rb` into `CONFIG_SPEC_MAP` to avoid the
label, then reverted it. That mapping would drop the full-suite guard
for every future change to the file, and its constants fan out widely —
`PROTOCOL` alone is referenced in 37 files — while `domain_spec.rb`
exercises no consumer specs (CSP, CORS, WebAuthn origins, URL
generation). Escalating is correct here; the label is the intended
mechanism.

---

🤖 Written with **Claude Fable 5** (Claude Code).

The model investigated both bugs, wrote the fixes and specs, and
verified each new example fails without its fix. Two of the three bugs
were found by it while setting up a local backend to capture App Store
screenshots; the whitespace and blank-value guards came from Greptile
review feedback, which it evaluated before accepting. Its proposed
`.presence` fix was rejected after testing showed it breaks `bin/vite`,
and a self-inflicted change that would have weakened CI coverage was
caught in review and reverted. All test results above were produced by
real runs, not asserted.

---------

Co-authored-by: Gianfranco Piana <gianfrancopiana@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants