Desktop: custom context menus everywhere, never the browser's menu
One ContextMenu in the design system (keyboard, type-ahead, submenus, check items, disabled reasons, Shift+F10 / Menu key, viewport placement, light and dark). The native menu is prevented globally; text fields keep Cut / Copy / Paste / Select all. Menus on sidebar and portfolio apps, app hero, releases, store art, live art, reviews, checks, editor fields, catalog, prices, accounts, app folders, ignored.
This commit is contained in:
parent
944b24fe59
commit
dece83c3e8
23 changed files with 839 additions and 28 deletions
|
|
@ -3,6 +3,8 @@
|
|||
// issuer id, .p8). Keys live in the machine's credential profiles (~/.storebound, shared with the CLI);
|
||||
// the app keeps a label, the studio and the profile name only. Per account: a health
|
||||
// check (key works / permission missing / revoked, with the fix) and "Import apps" (every app the key can see).
|
||||
import { useContextMenu } from '../ui/ContextMenu';
|
||||
import { copyToast } from '../ui/app-menu';
|
||||
import { type FormEvent, useEffect, useState } from 'react';
|
||||
import { type Account, type AccountCheck, api, errorText, STUDIO_COLORS, type Studio, type StudioColor } from '../api';
|
||||
import { type Key, t } from '../i18n';
|
||||
|
|
@ -54,8 +56,22 @@ function AccountRow({ a, busy, setBusy, onError }: { a: Account; busy: string |
|
|||
};
|
||||
const save = (e: FormEvent) => { e.preventDefault(); run(`ed-${a.id}`, async () => { await api.updateAccount(a.id, label, studio || null); await d.reload(); setEdit(false); }); };
|
||||
const check = h?.check;
|
||||
const cm = useContextMenu();
|
||||
return (
|
||||
<div className={`acct${ignored ? ' is-ignored' : ''}`}>
|
||||
<div className={`acct${ignored ? ' is-ignored' : ''}`} {...cm(() => [
|
||||
{ label: t('acct.test'), icon: 'shield', disabled: busy ? t('cm.busy') : false, onSelect: () => run(`ck-${a.id}`, () => d.checkAccount(a.id)) },
|
||||
{ label: t('acct.import'), icon: 'download', disabled: busy || d.importBusy ? t('cm.busy') : false, onSelect: () => run(`im-${a.id}`, () => d.importApps(a.id)) },
|
||||
{ label: t('acct.edit'), icon: 'edit', onSelect: () => setEdit(true) },
|
||||
'sep',
|
||||
{ label: t('cm.copy'), icon: 'copy', submenu: [
|
||||
{ label: t('cm.copyName'), icon: 'text', onSelect: () => copyToast(d, a.label) },
|
||||
check?.principal && { label: t('cm.copyPrincipal'), icon: 'key', shortcut: check.principal, onSelect: () => copyToast(d, check.principal!) },
|
||||
a.profile && { label: t('cm.copyProfile'), icon: 'folder', shortcut: a.profile, onSelect: () => copyToast(d, a.profile!) },
|
||||
] },
|
||||
'sep',
|
||||
{ label: ignored ? t('acct.unignore') : t('acct.ignore'), icon: ignored ? 'eye' : 'eyeOff', onSelect: () => { void d.updateOverrides(o => withIgnored(o, [`account:${a.id}`], !ignored)); } },
|
||||
{ label: t('accounts.remove'), icon: 'trash', danger: true, disabled: busy ? t('cm.busy') : false, onSelect: () => run(`rm-${a.id}`, async () => { await api.removeAccount(a.id); await d.reload(); }) },
|
||||
])}>
|
||||
<div className="acct-row">
|
||||
<span className={`acct-glyph ${a.kind}`}><StoreGlyph store={a.kind === 'play' ? 'android' : 'ios'} /></span>
|
||||
<div className="acct-main">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue