desktop reads everything by itself: one background read scheduler (4 per store, starts spaced per account, App Store Connect X-Rate-Limit slows it below 20 % and pauses at 100 left, backoff with jitter on 429 / RESOURCE_EXHAUSTED, visible app first, one read per key), per-method TTLs over the saved reads, Settings auto-read (on) and interval (15 min, paused while hidden), Refresh all, a refresh icon and 'read 3 min ago' / 'couldn't refresh' per card, writes invalidate, status line with progress and quota; quota reaches the window through ToolResult.quota (cli/lib/quota.ts); Health top issues open with versions, sample stack (health --issue / --signature, read health_issue), Open in Play Console, Copy for AI and likely-cause hints (cli/lib/crash-hints.ts); tests/scheduler.test.ts, tests/quota-crash-hints.test.ts, boot check auto-read case
This commit is contained in:
parent
89256f1e80
commit
0fb4ce0001
46 changed files with 1531 additions and 141 deletions
|
|
@ -4,7 +4,7 @@ import { StarIcon } from '@phosphor-icons/react/dist/csr/Star';
|
|||
import type { ReactNode } from 'react';
|
||||
import type { ReleasePhase, StoreRelease } from '../api';
|
||||
import { t } from '../i18n';
|
||||
import { share } from '../lib/format';
|
||||
import { ago, share } from '../lib/format';
|
||||
import { hueOf, monogram, type Tone } from '../lib/model';
|
||||
import { fixHint } from '../lib/views';
|
||||
import { Icon, type IconName, StoreGlyph } from './icons';
|
||||
|
|
@ -173,3 +173,17 @@ export function StudioMark({ studio, size = 'md' }: { studio: { name: string; co
|
|||
export function StoreOnlyTag() {
|
||||
return <span className="chip outline so-tag" title={t('apps.storeOnlyHint')}><Icon name="cloud" />{t('apps.storeOnly')}</span>;
|
||||
}
|
||||
|
||||
/** The state of one read beside its card's title: "read 3 min ago" with a small spinner while it refreshes, or
|
||||
* "couldn't refresh: <reason>" over the last good value, and the per-card refresh icon (no "Read" button first). */
|
||||
export function ReadControl({ r, label }: { r: { data: unknown; at: string | null; loading: boolean; error: string | null; refresh: () => Promise<void> }; label?: string }) {
|
||||
const text = r.error ? t('read.couldNotRefresh', { reason: r.error }) : r.at ? t('read.ago', { when: ago(r.at) }) : r.loading ? t('read.waiting') : '';
|
||||
return (
|
||||
<span className="read-ctl" data-read-state={r.loading ? 'loading' : r.error ? 'error' : r.data !== null ? 'ok' : 'none'}>
|
||||
{text && <span className={`read-stamp ${r.error ? 'read-err' : ''}`} title={r.error ?? undefined}>{text}</span>}
|
||||
<button className="btn btn-sm btn-icon" onClick={() => { r.refresh(); }} disabled={r.loading} aria-label={label ?? t('read.refresh')} title={label ?? t('read.refresh')}>
|
||||
<Icon name="refresh" className={r.loading ? 'spin' : undefined} />
|
||||
</button>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue