Compare commits
2 Commits
53ad7b729e
...
531e53d2ae
| Author | SHA1 | Date | |
|---|---|---|---|
| 531e53d2ae | |||
| b708c8c1f8 |
@@ -102,6 +102,7 @@ export function InvitationScreen(): React.ReactElement {
|
|||||||
// Two phases: first the ID input dialog, then the multi-step import flow.
|
// Two phases: first the ID input dialog, then the multi-step import flow.
|
||||||
const [showIdDialog, setShowIdDialog] = useState(false);
|
const [showIdDialog, setShowIdDialog] = useState(false);
|
||||||
const [importingId, setImportingId] = useState<string | null>(null);
|
const [importingId, setImportingId] = useState<string | null>(null);
|
||||||
|
const [pendingImportedInvitationId, setPendingImportedInvitationId] = useState<string | null>(null);
|
||||||
|
|
||||||
// ── Template cache ───────────────────────────────────────────────────────
|
// ── Template cache ───────────────────────────────────────────────────────
|
||||||
const [templateCache, setTemplateCache] = useState<Map<string, XOTemplate>>(new Map());
|
const [templateCache, setTemplateCache] = useState<Map<string, XOTemplate>>(new Map());
|
||||||
@@ -161,7 +162,7 @@ export function InvitationScreen(): React.ReactElement {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return [importItem, ...invitationItems];
|
return [importItem, ...invitationItems];
|
||||||
}, [invitations, templateCache]);
|
}, [invitations.length, templateCache]);
|
||||||
|
|
||||||
const selectedItem = listItems[selectedIndex];
|
const selectedItem = listItems[selectedIndex];
|
||||||
const selectedInvitation = selectedItem?.value ?? null;
|
const selectedInvitation = selectedItem?.value ?? null;
|
||||||
@@ -196,10 +197,30 @@ export function InvitationScreen(): React.ReactElement {
|
|||||||
/**
|
/**
|
||||||
* Import flow closed (completed or cancelled).
|
* Import flow closed (completed or cancelled).
|
||||||
*/
|
*/
|
||||||
const handleImportFlowClose = useCallback(() => {
|
const handleImportFlowClose = useCallback((importedInvitationId?: string) => {
|
||||||
|
if (importedInvitationId) {
|
||||||
|
setPendingImportedInvitationId(importedInvitationId);
|
||||||
|
}
|
||||||
setImportingId(null);
|
setImportingId(null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once imported invitation is visible in the list, select and focus it.
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (!pendingImportedInvitationId) return;
|
||||||
|
|
||||||
|
const importedIndex = listItems.findIndex((item) => {
|
||||||
|
return item.value?.data.invitationIdentifier === pendingImportedInvitationId;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (importedIndex >= 0) {
|
||||||
|
setSelectedIndex(importedIndex);
|
||||||
|
setFocusedPanel('list');
|
||||||
|
setPendingImportedInvitationId(null);
|
||||||
|
}
|
||||||
|
}, [pendingImportedInvitationId, listItems]);
|
||||||
|
|
||||||
// ── Action handlers ────────────────────────────────────────────────────
|
// ── Action handlers ────────────────────────────────────────────────────
|
||||||
|
|
||||||
const acceptInvitation = useCallback(async () => {
|
const acceptInvitation = useCallback(async () => {
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ export function InvitationImportFlow({
|
|||||||
`Action: ${invitation?.data.actionIdentifier ?? 'Unknown'}`
|
`Action: ${invitation?.data.actionIdentifier ?? 'Unknown'}`
|
||||||
);
|
);
|
||||||
setStatus('Ready');
|
setStatus('Ready');
|
||||||
onClose();
|
onClose(invitation?.data.invitationIdentifier);
|
||||||
}, [selectedRole, template, invitation, showInfo, setStatus, onClose]);
|
}, [selectedRole, template, invitation, showInfo, setStatus, onClose]);
|
||||||
|
|
||||||
// ── Keyboard handling ────────────────────────────────────────────────────
|
// ── Keyboard handling ────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -116,8 +116,12 @@ export interface ImportFlowProps {
|
|||||||
mode: ImportFlowMode;
|
mode: ImportFlowMode;
|
||||||
/** The application service — injected, not pulled from context. */
|
/** The application service — injected, not pulled from context. */
|
||||||
appService: AppService;
|
appService: AppService;
|
||||||
/** Called when the flow completes or is cancelled. */
|
/**
|
||||||
onClose: () => void;
|
* Called when the flow completes or is cancelled.
|
||||||
|
* When import succeeds, the invitation identifier is provided so callers can
|
||||||
|
* select/focus the imported invitation in their UI.
|
||||||
|
*/
|
||||||
|
onClose: (importedInvitationId?: string) => void;
|
||||||
/** Display an error message to the user. */
|
/** Display an error message to the user. */
|
||||||
showError: (message: string) => void;
|
showError: (message: string) => void;
|
||||||
/** Display an info message to the user. */
|
/** Display an info message to the user. */
|
||||||
|
|||||||
@@ -165,6 +165,8 @@ export const createMockAppService = async (engine: Engine) => {
|
|||||||
const mockRates = new MockRatesService();
|
const mockRates = new MockRatesService();
|
||||||
const rates = new RatesService(mockRates);
|
const rates = new RatesService(mockRates);
|
||||||
|
|
||||||
|
const mockElectrum = new MockElectrumService();
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
syncServerUrl: "http://localhost:3000",
|
syncServerUrl: "http://localhost:3000",
|
||||||
engineConfig: {
|
engineConfig: {
|
||||||
@@ -174,5 +176,5 @@ export const createMockAppService = async (engine: Engine) => {
|
|||||||
invitationStoragePath: "test-invitations.db",
|
invitationStoragePath: "test-invitations.db",
|
||||||
};
|
};
|
||||||
|
|
||||||
return new AppService(engine, storage, config, rates);
|
return new AppService(engine, storage, config, mockElectrum, rates);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user