store-only apps edit their listing on the store (storebound listing live / set: old -> new per field with the checks, a PATCH of only the edited fields; serve listing.set is the one store-only write, its token bound to the store ids; policy.rs STORE_ONLY_WRITES, write_with_confirm for store targets); screenshot captions per language and scene edited as a text edit of the shots copy JSON (JSON files keep their layout); Play content rating, target audience and App content answers in App info; docs/console-parity.md after the editors (73 rows: 55 complete, 18 with the reason) and tests/console-parity.test.ts keeping it honest
This commit is contained in:
parent
3cea2af4e1
commit
7ac028421a
33 changed files with 852 additions and 157 deletions
|
|
@ -7,7 +7,11 @@
|
|||
export type EditOp =
|
||||
| { kind: 'config'; path: string; value?: unknown; remove?: boolean }
|
||||
| { kind: 'listing'; store: 'play' | 'apple'; lang: string; field: string; value: string }
|
||||
| { kind: 'contact'; field: 'firstName' | 'lastName' | 'phone' | 'email'; value: string };
|
||||
| { kind: 'contact'; field: 'firstName' | 'lastName' | 'phone' | 'email'; value: string }
|
||||
| { kind: 'shotsCopy'; lang: string; scene: string; field: 'eyebrow' | 'title' | 'sub'; line?: 0 | 1; value: string };
|
||||
|
||||
/** An edit without its value: what a field edits. */
|
||||
export type FieldOp = { kind: 'config'; path: string } | Omit<Extract<EditOp, { kind: 'listing' }>, 'value'> | Omit<Extract<EditOp, { kind: 'contact' }>, 'value'> | Omit<Extract<EditOp, { kind: 'shotsCopy' }>, 'value'>;
|
||||
|
||||
/** Which editor (tab) a pending edit belongs to, for the unsaved marks. */
|
||||
export type EditArea = 'listings' | 'appinfo' | 'catalog' | 'testflight' | 'art';
|
||||
|
|
@ -15,7 +19,8 @@ export type EditArea = 'listings' | 'appinfo' | 'catalog' | 'testflight' | 'art'
|
|||
export interface Pending { key: string; op: EditOp; area: EditArea; /** The saved value, to show "was" and to drop an edit typed back to it. */ saved: unknown }
|
||||
|
||||
export const keyOf = (op: EditOp): string =>
|
||||
op.kind === 'config' ? `config:${op.path}` : op.kind === 'listing' ? `listing:${op.store}:${op.lang}:${op.field}` : `contact:${op.field}`;
|
||||
op.kind === 'config' ? `config:${op.path}` : op.kind === 'listing' ? `listing:${op.store}:${op.lang}:${op.field}`
|
||||
: op.kind === 'shotsCopy' ? `shotsCopy:${op.lang}:${op.scene}:${op.field}${op.line === undefined ? '' : `:${op.line}`}` : `contact:${op.field}`;
|
||||
|
||||
const valueOf = (op: EditOp): unknown => (op.kind === 'config' ? (op.remove ? undefined : op.value) : op.value);
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export const TTL: Record<string, number> = {
|
|||
'prices.current': 30 * MIN,
|
||||
'project.listings': 30 * MIN,
|
||||
'project.editable': 30 * MIN,
|
||||
'listing.live': 30 * MIN,
|
||||
'apple.ppo_status': 30 * MIN,
|
||||
validate: 30 * MIN,
|
||||
scan: 30 * MIN,
|
||||
|
|
@ -56,11 +57,13 @@ export function readsFor(o: { folder: boolean; ios: boolean; android: boolean })
|
|||
if (o.android) add('play.status');
|
||||
if (o.ios || o.android) { add('reviews.list', { days: 30 }); add('health'); }
|
||||
if (o.folder) {
|
||||
add('scan'); add('project.listings');
|
||||
add('scan'); add('project.listings'); add('project.editable');
|
||||
if (o.ios || o.android) { add('catalog.diff'); add('prices.current'); add('validate'); }
|
||||
add('analytics.compare'); add('aso.audit');
|
||||
}
|
||||
if (o.ios) add('apple.ppo_status');
|
||||
// store-only apps: the listing editor's live texts
|
||||
if (!o.folder && (o.ios || o.android)) add('listing.live');
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue