Compare commits

..

2 Commits

Author SHA1 Message Date
531e53d2ae Fix test 2026-04-27 12:47:02 +00:00
b708c8c1f8 Fix invitation import reactivity and focus imported invitation 2026-04-27 12:46:52 +00:00
4 changed files with 33 additions and 6 deletions

View File

@@ -102,6 +102,7 @@ export function InvitationScreen(): React.ReactElement {
// Two phases: first the ID input dialog, then the multi-step import flow.
const [showIdDialog, setShowIdDialog] = useState(false);
const [importingId, setImportingId] = useState<string | null>(null);
const [pendingImportedInvitationId, setPendingImportedInvitationId] = useState<string | null>(null);
// ── Template cache ───────────────────────────────────────────────────────
const [templateCache, setTemplateCache] = useState<Map<string, XOTemplate>>(new Map());
@@ -161,7 +162,7 @@ export function InvitationScreen(): React.ReactElement {
});
return [importItem, ...invitationItems];
}, [invitations, templateCache]);
}, [invitations.length, templateCache]);
const selectedItem = listItems[selectedIndex];
const selectedInvitation = selectedItem?.value ?? null;
@@ -196,10 +197,30 @@ export function InvitationScreen(): React.ReactElement {
/**
* Import flow closed (completed or cancelled).
*/
const handleImportFlowClose = useCallback(() => {
const handleImportFlowClose = useCallback((importedInvitationId?: string) => {
if (importedInvitationId) {
setPendingImportedInvitationId(importedInvitationId);
}
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 ────────────────────────────────────────────────────
const acceptInvitation = useCallback(async () => {

View File

@@ -148,7 +148,7 @@ export function InvitationImportFlow({
`Action: ${invitation?.data.actionIdentifier ?? 'Unknown'}`
);
setStatus('Ready');
onClose();
onClose(invitation?.data.invitationIdentifier);
}, [selectedRole, template, invitation, showInfo, setStatus, onClose]);
// ── Keyboard handling ────────────────────────────────────────────────────

View File

@@ -116,8 +116,12 @@ export interface ImportFlowProps {
mode: ImportFlowMode;
/** The application service — injected, not pulled from context. */
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. */
showError: (message: string) => void;
/** Display an info message to the user. */

View File

@@ -165,6 +165,8 @@ export const createMockAppService = async (engine: Engine) => {
const mockRates = new MockRatesService();
const rates = new RatesService(mockRates);
const mockElectrum = new MockElectrumService();
const config = {
syncServerUrl: "http://localhost:3000",
engineConfig: {
@@ -174,5 +176,5 @@ export const createMockAppService = async (engine: Engine) => {
invitationStoragePath: "test-invitations.db",
};
return new AppService(engine, storage, config, rates);
return new AppService(engine, storage, config, mockElectrum, rates);
};