Skip to content

decision: dashboard/widgets, blog, and subscription surface - G28, G29, G39 #61

Description

@Adron

Summary

Three product areas where the right first step is an owner decision, not code. Each is real surface on the web; none is obviously right for a native macOS client. Filing them together so the decisions are made once.


1. Dashboard / Front wall + widgets

/help/getting-started puts the Dashboard second in prominence, right after Home:

Dashboard (click the chart icon) shows only your own messages in a table view. It also includes Data Management (links to Exports and Settings) and your profile summary.

Its tiles: Scheduled posts, Exports, Settings, Profile Information, and a Lists tree (of lists and list folders). There is also a configurable front wall, plus widgets — Location, Weather, markets, news, transit, transit stops, bike-share — and a full-screen Clock page.

⚠️ New finding, 2026-09-07: this is backend-blocked for a Bearer client, not merely a design question. Every route is x-auth-type: session:

GET/PUT /api/user/dashboard-layout    session
GET/PUT /api/user/front-wall-layout   session
GET /api/widgets/{markets,news,transit,transit/stops,bike-share}   session

(GET /api/weather and GET /api/location are public.)

So even if the answer is "build it", the saved-layout half cannot be built until the routes accept Bearer.

The design question stands regardless: a native app arguably wants its own arrangement rather than mirroring the web's saved layout. Note that most of the Dashboard's content already exists natively — Scheduled posts, Exports, Settings, and a lists tree all ship; what is missing is the aggregation and the own-messages table view. A native "Dashboard" could be assembled from existing pieces with no new API surface at all, which is a materially cheaper option than the layout-syncing one.

Options:

  1. Do nothing. The constituent features are already reachable from the sidebar.
  2. Native dashboard, no layout sync (cheapest useful option) — a summary scene assembled from what already ships, plus an own-messages table. No new routes, not blocked.
  3. Full parity including saved layouts and widgets. Blocked on the session-only routes above.

2. Blog

/help/blog documents a Blog feature, and the web footer links /blog. The public API surface is only subscribe / confirm / unsubscribe (POST /api/blog/subscribe, GET /api/blog/subscribe/confirm, GET/POST /api/blog/unsubscribe — all public, all Bearer-safe). Authoring lives entirely behind /api/admin/blog*, which is admin-only and out of scope for a general client.

Options:

  1. Do nothing. The blog is a website; a link out is enough.
  2. Reader only — render posts natively. ⚠️ Note there is no public read route in the spec (only subscribe/unsubscribe), so a native reader would need either a new public endpoint or HTML scraping. Scraping is not acceptable; confirm a read route exists before choosing this.
  3. Reader + subscribe management, using the three public routes.

Native authoring is admin-gated and almost certainly out of scope.


3. Subscription status surface

The web has /subscription (current tier, Manage, Cancel — all via Stripe). This repo's standing decision is the opposite for native: "billing is managed in the web app by owner decision", and there is no in-app-purchase surface.

Meanwhile LiveEntitlements already reads customerStatus (live probe 2026-09-07 returns "customerStatus":"subscriber"), and SettingsRootView's header comment still lists "Subscription" as planned — so the code is ambiguous about which way this went.

Options:

  1. Do nothing and delete the stale comment.
  2. Recommended — read-only status in Settings ▸ Account showing the current tier, plus a "Manage subscription in your browser" button. No Stripe contact from the app, no payment UI ever native. This is what the entitlement-gating work wants anyway: when a Free user hits an Upgrade prompt, it needs somewhere to send them.
  3. Full native billing. Reverses the standing decision and raises sandboxing and App Review questions. Not recommended.

What "done" looks like for this issue

  • A decision recorded for each of the three, in docs/decisions/.
  • For anything chosen, a follow-up implementation issue filed and linked here.
  • For anything declined, the stale code comments and doc references cleaned up so a future sweep does not re-file it.

Notes

work-consolidation.md tracks these as G28, G29, and G39. The dashboard's session-only constraint is new information from the 2026-09-07 sweep and materially changes option 3's cost.

Activity

  1. added
    parityWeb-parity gap with the InterlinedList web app
    needs-decisionOwner decision required before any code
    on Sep 7, 2026
  2. Adron commented on Sep 16, 2026

    @Adron
    MemberAuthor

    Ready for your decision — and one of the three is now materially cheaper

    This is needs-decision, so it is not something I can implement. Here is the current state of each, with what changed since the issue was written.

    1. Dashboard / front wall + widgets — no longer backend-blocked

    The issue records these as x-auth-type: session. That was re-measured on 2026-09-14 (see #66) and is no longer true — confirmed 200 under Bearer:

    GET /api/user/dashboard-layout   → 200 {"layout":null}
    GET /api/user/front-wall-layout  → 200 {"layout":null}
    GET /api/user/engagement         → 200 {"totalDigs":24,"totalPushes":6,…}
    GET /api/widgets/news            → 200
    GET /api/widgets/markets         → 200
    

    I re-confirmed /api/user/engagement myself on 2026-09-15: 200 under Bearer, returning totalDigs, totalPushes and a recent[] feed.

    So option 3 (full parity, including saved layouts) is no longer blocked. The issue's costing said it was. That materially changes the comparison, and it is the main reason this needs a fresh look rather than the answer it would have got a week ago.

    Option 2 (native dashboard, no layout sync) still needs no new API at all and remains the cheapest useful thing.

    What I need from you: 1, 2 or 3.

    2. Blog — unchanged, and option 2 is still not viable

    Re-checked the spec: the only public blog routes are POST /api/blog/subscribe, GET /api/blog/subscribe/confirm, and GET/POST /api/blog/unsubscribe. There is still no public read route, so a native reader would need either a new endpoint or scraping, and scraping is not acceptable.

    What I need from you: option 1 (link out) or option 3 (subscribe management only). Option 2 stays off the table unless a read route appears.

    3. Subscription surface — I recommend option 2, and there is a new reason

    The issue recommends read-only status in Settings ▸ Account with a "manage in your browser" button. That recommendation is stronger now: PR #83 landed the capability gate, so Free users hit real upsell prompts in nine places and every one of them needs somewhere to send the user. Today there is nowhere.

    customerStatus is already read ("subscriber" live), so this is a small pane, not an integration.

    What I need from you: confirm option 2, or say which of 1/3 you want instead.


    To be explicit about what unblocks what

    • Answer 3 and I can ship it immediately — it is the smallest and it closes a real gap the gating work opened.
    • Answer 1 and the size depends entirely on which option: option 2 is a day, option 3 is a feature.
    • Answer 2 and option 1 is ten minutes; option 3 is a small pane.

    Nothing here needs code from me first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-decisionOwner decision required before any codeparityWeb-parity gap with the InterlinedList web app

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions