mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
||||
type DirectoryIdentityListingsResponse,
|
||||
PaymentRequiredError,
|
||||
} from '../../lib/agentworld/invokeApiClient';
|
||||
import { openUrl } from '../../utils/openUrl';
|
||||
import { apiClient } from '../AgentWorldShell';
|
||||
import IdentitiesSection, { IDENTITY_PRICING_TIERS } from './IdentitiesSection';
|
||||
|
||||
@@ -39,6 +40,11 @@ vi.mock('../AgentWorldShell', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
// External-link opener — assert the seller CTA hands off to the OS browser
|
||||
// without actually invoking Tauri.
|
||||
// openUrl returns a Promise (the CTA chains .then/.catch for diagnostics).
|
||||
vi.mock('../../utils/openUrl', () => ({ openUrl: vi.fn(() => Promise.resolve()) }));
|
||||
|
||||
// Default happy-path resolutions so async hooks settle without unhandled
|
||||
// rejections. Individual tests override per-case.
|
||||
beforeEach(() => {
|
||||
@@ -1282,3 +1288,28 @@ describe('IDENTITY_PRICING_TIERS', () => {
|
||||
expect(screen.queryByText('$10/yr')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
// Unit coverage only, by design: the affordance is a static info note whose CTA
|
||||
// hands off to the OS browser via the mocked `openUrl`. There is one real
|
||||
// cross-process effect (that the OS browser actually opens) but it isn't worth a
|
||||
// desktop WDIO E2E — these unit tests cover the note + the openUrl call. E2E is
|
||||
// intentionally skipped for this change (#4920).
|
||||
describe('Trading tab — seller web-only note', () => {
|
||||
test('renders the seller pointer note on the Trading tab', async () => {
|
||||
render(<IdentitiesSection />);
|
||||
await gotoTab('Trading');
|
||||
const note = await screen.findByTestId('sell-on-web');
|
||||
expect(note).toHaveTextContent(/listing a handle for sale and accepting offers/i);
|
||||
expect(screen.getByTestId('sell-on-web-cta')).toHaveAttribute(
|
||||
'data-analytics-id',
|
||||
'identities.sellOnWeb'
|
||||
);
|
||||
});
|
||||
|
||||
test('CTA opens the tiny.place identities page via openUrl', async () => {
|
||||
render(<IdentitiesSection />);
|
||||
await gotoTab('Trading');
|
||||
await userEvent.click(await screen.findByTestId('sell-on-web-cta'));
|
||||
expect(vi.mocked(openUrl)).toHaveBeenCalledWith('https://tiny.place/identities');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,7 +12,14 @@
|
||||
* Offer commitments (signed authorizations — funds move only on acceptance).
|
||||
* Money only moves after the user confirms. The read-only data views (Registry
|
||||
* listing, floor prices, recent sales) are fully functional.
|
||||
*
|
||||
* Seller-side *writes* (list a handle for sale, accept / reject an offer/bid)
|
||||
* are intentionally NOT in-app — they are web-only on tiny.place (the backend
|
||||
* exposes no seller write routes). Seller-side *reads* do exist (offers on your
|
||||
* handles, via marketplace_list_offers). The Trading tab links sellers to the
|
||||
* web app for the write actions it cannot perform (#4920).
|
||||
*/
|
||||
import debugFactory from 'debug';
|
||||
import { useCallback, useEffect, useReducer, useRef, useState } from 'react';
|
||||
|
||||
import ChipTabs from '../../components/layout/ChipTabs';
|
||||
@@ -31,12 +38,21 @@ import {
|
||||
type RegistrationChallenge,
|
||||
type RegistryWalletBalance,
|
||||
} from '../../lib/agentworld/invokeApiClient';
|
||||
import { openUrl } from '../../utils/openUrl';
|
||||
import { apiClient } from '../AgentWorldShell';
|
||||
import { decimalsForAsset, formatAssetAmount } from '../assets';
|
||||
import CommitFlow from '../components/CommitFlow';
|
||||
import X402ConfirmDialog from '../components/X402ConfirmDialog';
|
||||
import { explorerTxUrl as buyExplorerTxUrl, useX402Buy } from '../hooks/useX402Buy';
|
||||
|
||||
const debug = debugFactory('agentworld:identities');
|
||||
|
||||
// Seller-side identity *writes* (list a handle for sale, accept/reject an offer)
|
||||
// are web-only — the tiny.place backend exposes no seller write routes (see
|
||||
// #4920). We point sellers at the web app for those. Hardcoded prod URL,
|
||||
// matching the `FUND_PAGE_URL` precedent in X402ConfirmDialog.tsx.
|
||||
const SELL_ON_WEB_URL = 'https://tiny.place/identities';
|
||||
|
||||
// ── Types ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
type Tab = 'register' | 'registry' | 'trading';
|
||||
@@ -945,6 +961,35 @@ function TradingTab() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Seller-side *writes* (list-for-sale / accept-reject offer) are web-only
|
||||
(#4920): the backend exposes no seller write routes. Placed directly
|
||||
under the listings — where someone who came to sell looks — rather than
|
||||
below Recent Sales. Viewing offers on your handles is a separate read
|
||||
that does exist (marketplace_list_offers). */}
|
||||
<div
|
||||
className="rounded-lg border border-line bg-surface-muted/40 p-3"
|
||||
data-testid="sell-on-web">
|
||||
<p className="text-xs text-content-muted">
|
||||
Listing a handle for sale and accepting offers happens on tiny.place.
|
||||
</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="xs"
|
||||
className="mt-2"
|
||||
analyticsId="identities.sellOnWeb"
|
||||
onClick={() => {
|
||||
// Log the entry point (static destination URL only — no handle/PII).
|
||||
// openUrl owns the outcome diagnostics: it logs a low-PII telemetry
|
||||
// breadcrumb and keeps its https `window.open` fallback, so we don't
|
||||
// re-observe success/error here.
|
||||
debug('[tinyplace][ui] sell-on-web: opening %s', SELL_ON_WEB_URL);
|
||||
void openUrl(SELL_ON_WEB_URL);
|
||||
}}
|
||||
data-testid="sell-on-web-cta">
|
||||
Open tiny.place →
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Recent sales */}
|
||||
<div>
|
||||
<h3 className="mb-2 text-xs font-semibold uppercase tracking-wider text-content-faint">
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
# Identity Marketplace Seller-Web-Only Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Replace the seller-side dead-end in the desktop Identities → Trading tab with a note pointing users to the web flow, and record that seller-side is web-only by design.
|
||||
|
||||
**Architecture:** Additive UI only. A persistent info note at the bottom of `TradingTab` opens `https://tiny.place/identities` via the existing `openUrl()` helper (mirrors the `X402ConfirmDialog` `/fund` precedent). No SDK, core, or `invokeApiClient` changes — the tiny.place backend has no seller routes. Scoping recorded in two doc comments.
|
||||
|
||||
**Tech Stack:** React 19 + TypeScript, Vitest + Testing Library, `@tauri-apps/plugin-opener` (via `app/src/utils/openUrl.ts`), Rust doc comment.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- **No i18n:** `IdentitiesSection.tsx` is entirely hardcoded English (e.g. `"Commitment submitted."` line 854, `"Purchased {name}"` line 887). New strings are hardcoded English to match. **No locale-file edits** — do NOT add `useT()` here.
|
||||
- **Web URL is a hardcoded prod constant:** `https://tiny.place/identities`. Mirrors `FUND_PAGE_URL = 'https://tiny.place/fund'` in `X402ConfirmDialog.tsx`. The tiny.place *web* frontend has no per-env base in `config.ts`.
|
||||
- **External links go through `openUrl`** from `app/src/utils/openUrl.ts` — never a raw `window.open` or `<a target="_blank">` for the CTA (the `<a>` explorer links elsewhere in the file are read-only tx links, a different case).
|
||||
- **Out of scope (do not touch):** `vendor/tinyplace/sdk/**`, `src/openhuman/tinyplace/manifest.rs`, `app/src/lib/agentworld/invokeApiClient.ts`, buy/bid/offer flows.
|
||||
- **Run commands from repo root** unless noted. The worktree root is `.claude/worktrees/fix-4920`.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Seller "sell on tiny.place" note in the Trading tab
|
||||
|
||||
**Files:**
|
||||
- Modify: `app/src/agentworld/pages/IdentitiesSection.tsx` (imports ~16-37; header docstring ~1-16; `TradingTab` return, insert before line 996 `</div>` that closes the outer `space-y-4`)
|
||||
- Test: `app/src/agentworld/pages/IdentitiesSection.test.tsx` (add a `describe` block; add an `openUrl` mock near the top mocks)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `openUrl(url: string): Promise<void>` from `../../utils/openUrl`; existing `Button` component from `../../components/ui/Button`.
|
||||
- Produces: a rendered note with testid `sell-on-web` containing a CTA button testid `sell-on-web-cta` that calls `openUrl('https://tiny.place/identities')`.
|
||||
|
||||
- [ ] **Step 1: Add the `openUrl` mock to the existing test file**
|
||||
|
||||
In `app/src/agentworld/pages/IdentitiesSection.test.tsx`, directly after the existing `vi.mock('../AgentWorldShell', …)` block (ends ~line 41), add:
|
||||
|
||||
```ts
|
||||
// External-link opener — assert the seller CTA hands off to the OS browser
|
||||
// without actually invoking Tauri.
|
||||
vi.mock('../../utils/openUrl', () => ({ openUrl: vi.fn() }));
|
||||
```
|
||||
|
||||
And add its import next to the other imports at the top of the file (after the `IdentitiesSection` import line):
|
||||
|
||||
```ts
|
||||
import { openUrl } from '../../utils/openUrl';
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Write the failing test**
|
||||
|
||||
Append this `describe` block to `app/src/agentworld/pages/IdentitiesSection.test.tsx` (after the last Trading-tab describe block; reuses the file's existing `gotoTab` helper, `render`, `screen`, `userEvent`):
|
||||
|
||||
```ts
|
||||
describe('Trading tab — seller web-only note', () => {
|
||||
test('renders the seller pointer note on the Trading tab', async () => {
|
||||
render(<IdentitiesSection />);
|
||||
await gotoTab('Trading');
|
||||
const note = await screen.findByTestId('sell-on-web');
|
||||
expect(note).toHaveTextContent(/selling a handle or responding to offers/i);
|
||||
});
|
||||
|
||||
test('CTA opens the tiny.place identities page via openUrl', async () => {
|
||||
render(<IdentitiesSection />);
|
||||
await gotoTab('Trading');
|
||||
await userEvent.click(await screen.findByTestId('sell-on-web-cta'));
|
||||
expect(vi.mocked(openUrl)).toHaveBeenCalledWith('https://tiny.place/identities');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run the test to verify it fails**
|
||||
|
||||
Run: `pnpm --filter openhuman-app test -- IdentitiesSection --run -t "seller web-only"`
|
||||
Expected: FAIL — `Unable to find an element by: [data-testid="sell-on-web"]`.
|
||||
|
||||
- [ ] **Step 4: Add the `openUrl` import + web-URL constant to the component**
|
||||
|
||||
In `app/src/agentworld/pages/IdentitiesSection.tsx`, add the import. Import order groups deeper relative paths first, so place it directly **above** `import { apiClient } from '../AgentWorldShell';` (line 33):
|
||||
|
||||
```ts
|
||||
import { openUrl } from '../../utils/openUrl';
|
||||
```
|
||||
|
||||
Then, immediately after the import block (before the `// ── Types ──` comment at line 39), add the constant:
|
||||
|
||||
```ts
|
||||
// Seller-side identity actions (list a handle for sale, accept/reject an offer)
|
||||
// are web-only — the tiny.place backend exposes no seller routes and the
|
||||
// vendored SDK is a buyer-side compatibility wrapper (see #4920). We point
|
||||
// sellers at the web app instead. Hardcoded prod URL, matching the
|
||||
// `FUND_PAGE_URL` precedent in X402ConfirmDialog.tsx.
|
||||
const SELL_ON_WEB_URL = 'https://tiny.place/identities';
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Insert the note JSX in `TradingTab`**
|
||||
|
||||
In the same file, insert the following block immediately **before** line 996 (the `</div>` that closes the outer `<div className="space-y-4">`), i.e. after the Recent Sales `</div>` at line 995:
|
||||
|
||||
```tsx
|
||||
{/* Seller-side is web-only (#4920): no in-app list-for-sale / accept-offer
|
||||
path exists, so point sellers at the tiny.place web app. */}
|
||||
<div
|
||||
className="rounded-lg border border-line bg-surface-muted/40 p-3"
|
||||
data-testid="sell-on-web">
|
||||
<p className="text-xs text-content-muted">
|
||||
Selling a handle or responding to offers happens on tiny.place.
|
||||
</p>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="xs"
|
||||
className="mt-2"
|
||||
analyticsId="identities.sellOnWeb"
|
||||
onClick={() => void openUrl(SELL_ON_WEB_URL)}
|
||||
data-testid="sell-on-web-cta">
|
||||
Open tiny.place →
|
||||
</Button>
|
||||
</div>
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Update the header docstring to record the web-only scope**
|
||||
|
||||
In the header docstring, extend the `Write flows are live x402:` list (ends with the "Money only moves…" sentence, ~line 12-14). Add a paragraph immediately before the closing `*/` (line 15):
|
||||
|
||||
```text
|
||||
*
|
||||
* Seller-side actions (list a handle for sale, accept / reject an offer/bid)
|
||||
* are intentionally NOT in-app — they are web-only on tiny.place. The backend
|
||||
* exposes no seller routes and the vendored SDK is a buyer-side compatibility
|
||||
* wrapper, so the Trading tab links sellers to the web app instead (#4920).
|
||||
```
|
||||
|
||||
- [ ] **Step 7: Run the tests to verify they pass**
|
||||
|
||||
Run: `pnpm --filter openhuman-app test -- IdentitiesSection --run`
|
||||
Expected: PASS — the new `seller web-only` tests pass and the pre-existing IdentitiesSection tests still pass.
|
||||
|
||||
**E2E scope (approved exception):** no desktop WDIO E2E is added for this change. The affordance is a static info note whose CTA hands off to the OS browser via `openUrl` — there is no cross-process behavior an E2E could assert beyond the unit tests above. This is a documented exception to the standard unit-and-E2E expectation; the exception is also recorded in the design spec's Testing section and in a comment on the test's `describe` block.
|
||||
|
||||
- [ ] **Step 8: Typecheck, lint, and format**
|
||||
|
||||
Run: `pnpm typecheck && pnpm --filter openhuman-app lint -- --fix app/src/agentworld/pages/IdentitiesSection.tsx app/src/agentworld/pages/IdentitiesSection.test.tsx && pnpm --filter openhuman-app exec prettier --write app/src/agentworld/pages/IdentitiesSection.tsx app/src/agentworld/pages/IdentitiesSection.test.tsx`
|
||||
Expected: no type errors, no lint errors, files formatted (import order may be auto-fixed).
|
||||
|
||||
- [ ] **Step 9: Commit**
|
||||
|
||||
```bash
|
||||
git add app/src/agentworld/pages/IdentitiesSection.tsx app/src/agentworld/pages/IdentitiesSection.test.tsx
|
||||
git commit -m "feat(tinyplace): point sellers to tiny.place web from Trading tab (#4920)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Record web-only scope in the tinyplace core domain doc
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/openhuman/tinyplace/mod.rs` (module doc comment, after the `## Seed derivation` block ending line 26)
|
||||
|
||||
Docs-only; no test (Rust doc comment). Folded into a single commit.
|
||||
|
||||
- [ ] **Step 1: Add a scoping note to the module doc**
|
||||
|
||||
In `src/openhuman/tinyplace/mod.rs`, insert this doc-comment section immediately after line 26 (`//! The seed is never logged…`) and before the blank line preceding `pub(crate) mod agent;`:
|
||||
|
||||
```rust
|
||||
//!
|
||||
//! ## Marketplace scope (buyer-side only)
|
||||
//!
|
||||
//! The identity marketplace handlers here are **buyer-side only**: buy a
|
||||
//! listing, bid, and make an offer. Seller-side actions — listing a handle for
|
||||
//! sale and accepting / rejecting offers — are **web-only on tiny.place** (the
|
||||
//! backend exposes no seller routes). The desktop Trading tab links sellers to
|
||||
//! the web app instead. See #4920.
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify it compiles**
|
||||
|
||||
Run: `GGML_NATIVE=OFF cargo check --manifest-path Cargo.toml`
|
||||
Expected: compiles (doc comment only — no code change). Warnings unrelated to this change are fine.
|
||||
|
||||
- [ ] **Step 3: Format**
|
||||
|
||||
Run: `cargo fmt --manifest-path Cargo.toml`
|
||||
Expected: no diff on the doc-comment lines (doc comments are not reflowed).
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/openhuman/tinyplace/mod.rs
|
||||
git commit -m "docs(tinyplace): note identity marketplace is buyer-side only, selling is web-only (#4920)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: GitHub bookkeeping (outward-facing — CONFIRM before running)
|
||||
|
||||
**Not code.** Do NOT run these until the user explicitly confirms editing the epic and commenting on the issue (both are outward-facing). Until then, capture them in the PR body.
|
||||
|
||||
- [ ] **Step 1: Comment the resolution on #4920**
|
||||
|
||||
```bash
|
||||
gh issue comment 4920 --repo tinyhumansai/openhuman --body "Resolved as **web-only by design**: the tiny.place backend exposes no seller routes and the vendored SDK is a buyer-side compatibility wrapper, so listing a handle / accepting-rejecting offers stays on the tiny.place web app. The desktop Trading tab now points sellers there instead of dead-ending. Scope recorded in \`IdentitiesSection.tsx\` and \`src/openhuman/tinyplace/mod.rs\`. See PR <PR_URL>."
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Mark #4776 §9 seller items N/A**
|
||||
|
||||
Edit the #4776 body: change the "Sell / list a handle for sale" and "Accept / reject offer" table rows' Status to `N/A (web-only — #4920)`, and change `- [ ] Sell / list flow works` to `- [x] Sell / list flow works — N/A, web-only (#4920)`. (Manual body edit via `gh issue edit 4776 --repo tinyhumansai/openhuman --body-file -` after fetching and patching the current body, to avoid clobbering other sections.)
|
||||
|
||||
---
|
||||
|
||||
## Self-Review
|
||||
|
||||
- **Spec coverage:** UI affordance → Task 1 (steps 4-5). Hardcoded-English constraint → Global Constraints + Task 1 (no `useT`). Docstring scoping → Task 1 step 6 + Task 2. Test → Task 1 steps 1-3, 7. GitHub bookkeeping → Task 3. Out-of-scope items → Global Constraints. All spec sections covered.
|
||||
- **Placeholder scan:** `<PR_URL>` / `<PR_URL>` in Task 3 are intentional fill-at-runtime values for outward-facing text, not code placeholders. No code step contains TBD/TODO.
|
||||
- **Type consistency:** testids `sell-on-web` / `sell-on-web-cta` and constant `SELL_ON_WEB_URL` used identically in test (Task 1 steps 2) and component (Task 1 steps 4-5). `openUrl` signature matches `app/src/utils/openUrl.ts`.
|
||||
@@ -0,0 +1,104 @@
|
||||
# Design: Identity marketplace seller-side is web-only (#4920)
|
||||
|
||||
**Date:** 2026-07-24
|
||||
**Issue:** [#4920](https://github.com/tinyhumansai/openhuman/issues/4920) — *[Tiny Place] Identity marketplace is buyer-only: no list-for-sale or accept/reject-offer path*
|
||||
**Epic:** #4776 §9 (Trading / Identities marketplace)
|
||||
|
||||
## Problem
|
||||
|
||||
The desktop app's identity marketplace (Agent World → Identities → Trading tab) supports
|
||||
only **buyer-side** actions: browse listings, buy, bid, and make an offer. There is no path
|
||||
to **sell** (list one's own `@handle`) and no path to **accept / reject** an incoming offer
|
||||
or bid. A user who wants to sell opens the Trading tab and finds a dead end.
|
||||
|
||||
## Decision
|
||||
|
||||
**Seller-side actions are web-only by design.** The tiny.place backend does not expose
|
||||
seller routes (create-listing, accept/reject-offer), and the vendored Rust SDK
|
||||
(`vendor/tinyplace/sdk/rust/src/api/marketplace.rs`) is explicitly a *"compatibility wrapper
|
||||
for marketplace endpoints used by OpenHuman"* — buyer-side only. Listing a handle and
|
||||
responding to offers happens on the tiny.place web app.
|
||||
|
||||
This resolves #4920 not by building the full seller stack (SDK → core handler →
|
||||
`invokeApiClient` → UI), but by:
|
||||
|
||||
1. Removing the in-app dead-end with a clear pointer to the web flow.
|
||||
2. Recording the web-only scoping decision in-repo.
|
||||
3. Marking the corresponding #4776 §9 items N/A.
|
||||
|
||||
## Scope
|
||||
|
||||
### In scope
|
||||
|
||||
1. **UI affordance** — `app/src/agentworld/pages/IdentitiesSection.tsx`, Trading tab.
|
||||
Add an informational note beneath the listings / recent-sales area:
|
||||
|
||||
> ℹ️ Selling a handle or responding to offers happens on tiny.place. **[Open tiny.place →]**
|
||||
|
||||
- The CTA opens `https://tiny.place/identities` via `openUrl()`
|
||||
(`app/src/utils/openUrl.ts` → `tauri-plugin-opener`), mirroring the existing
|
||||
`https://tiny.place/fund` precedent in `X402ConfirmDialog.tsx`.
|
||||
- The web URL is a hardcoded production constant, matching the `/fund` precedent
|
||||
(the tiny.place *web* frontend has no per-env base in `config.ts`; only the API
|
||||
host is env-derived).
|
||||
- Strings are **hardcoded English**, matching this file's existing convention. Unlike
|
||||
sibling agentworld pages, `IdentitiesSection.tsx` is entirely un-i18n'd (its buy/commit
|
||||
banners use literal strings, e.g. `"Commitment submitted."`, `"Purchased {name}"`), and
|
||||
this passes CI (`i18n:check` / `i18n:english:check` validate locale-file parity, not
|
||||
hardcoded JSX). Introducing a single `useT()` string here would be inconsistent; a
|
||||
full-file i18n conversion is out of scope. No locale-file changes.
|
||||
|
||||
2. **Scoping documentation**
|
||||
- Extend the `IdentitiesSection.tsx` header docstring to state that seller-side
|
||||
(list-for-sale, accept/reject offer) is **web-only by design**, with the reason.
|
||||
- Add a one-line pointer in the tinyplace domain module doc
|
||||
(`src/openhuman/tinyplace/mod.rs`).
|
||||
|
||||
3. **Test** — co-located Vitest test asserting the Trading tab renders the seller note and
|
||||
the CTA invokes `openUrl` with `https://tiny.place/identities` (mock `openUrl`). Covers
|
||||
the new diff lines for the ≥80% changed-line coverage gate.
|
||||
|
||||
4. **GitHub bookkeeping** (not code; done via `gh`, noted in the PR body)
|
||||
- #4776 §9: mark "Sell / list a handle for sale", "Accept / reject offer", and the
|
||||
"Sell / list flow works" checkbox as N/A, referencing #4920's resolution.
|
||||
- Comment the web-only resolution on #4920.
|
||||
|
||||
### Out of scope (YAGNI)
|
||||
|
||||
- Vendored SDK seller methods (`create_listing` / `accept_offer` / `reject_offer`).
|
||||
- Core `handle_tinyplace_marketplace_*` seller handlers.
|
||||
- `invokeApiClient.ts` seller methods.
|
||||
- Full i18n conversion of `IdentitiesSection.tsx` (the new strings are hardcoded English,
|
||||
matching the file).
|
||||
|
||||
## Components & data flow
|
||||
|
||||
```text
|
||||
Trading tab (IdentitiesSection.tsx)
|
||||
└─ SellOnWebNote ──click──▶ openUrl('https://tiny.place/identities')
|
||||
└─ tauri-plugin-opener → OS default browser
|
||||
```
|
||||
|
||||
No core RPC, no network from the app itself — the CTA hands off to the OS browser.
|
||||
|
||||
## Error handling
|
||||
|
||||
`openUrl()` already handles the CEF IPC-bridge gap by falling back to `window.open` for
|
||||
http(s) URLs and logs a low-PII telemetry breadcrumb. No new error surface is introduced.
|
||||
|
||||
## Testing
|
||||
|
||||
- **Unit (Vitest):** render `TradingTab`, assert the seller note text is present, that the
|
||||
CTA carries the `identities.sellOnWeb` analytics id, and that clicking it calls the mocked
|
||||
`openUrl` with the expected URL.
|
||||
- No new Rust behavior → no Rust test changes.
|
||||
- **E2E: intentionally skipped.** The affordance is a static info note whose CTA hands off to
|
||||
the OS browser via `openUrl` — there is no cross-process behavior a desktop WDIO E2E could
|
||||
assert beyond the unit tests. Documented as an approved exception to the standard
|
||||
unit-and-E2E expectation for this change.
|
||||
|
||||
## Rollback / risk
|
||||
|
||||
Low risk: additive UI note + docstring. No behavior change to existing buy/bid/offer flows,
|
||||
and no i18n/locale files touched (strings are hardcoded English, matching the file). Revert
|
||||
is a clean removal of the note block plus its docstring note and the two unit tests.
|
||||
@@ -24,6 +24,16 @@
|
||||
//! access. The seed is derived via the same SLIP-0010 path used for all Solana
|
||||
//! signing (`m/44'/501'/0'/0'`); the wallet key becomes the tiny.place identity.
|
||||
//! The seed is never logged, persisted, or returned across any IPC boundary.
|
||||
//!
|
||||
//! ## Marketplace scope (seller-side writes are web-only)
|
||||
//!
|
||||
//! The identity marketplace **write** handlers here are buyer-side only: buy a
|
||||
//! listing, bid, and make an offer. Seller-side *writes* — listing a handle for
|
||||
//! sale and accepting / rejecting offers — are **web-only on tiny.place** (the
|
||||
//! backend exposes no seller write routes). Seller-side *reads* do exist:
|
||||
//! `marketplace_list_offers` takes a `name` filter for offers targeting a handle
|
||||
//! you own. The desktop Trading tab links sellers to the web app for the actions
|
||||
//! it cannot perform. See #4920.
|
||||
|
||||
pub(crate) mod agent;
|
||||
mod agent_tools;
|
||||
|
||||
Reference in New Issue
Block a user