fix(web): SpaceInviteCard — real embed idiom and correct getApiForOrigin import path
This commit is contained in:
@@ -813,9 +813,3 @@ export function createApiClient(origin: string, getToken: () => string | null, o
|
|||||||
return new BackspaceApiClient(baseUrl, getToken, onUnauthorized);
|
return new BackspaceApiClient(baseUrl, getToken, onUnauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-export the cross-store origin resolver so consumers can do
|
|
||||||
// `import { getApiForOrigin } from '../api/client'`. The real implementation
|
|
||||||
// lives in `utils/crossStoreResolvers` to avoid TDZ cycles between stores;
|
|
||||||
// re-exporting here keeps the public import surface clean for callers that
|
|
||||||
// don't need to know about the internal indirection.
|
|
||||||
export { getApiForOrigin } from '../utils/crossStoreResolvers';
|
|
||||||
|
|||||||
@@ -3,17 +3,19 @@ import { render, screen, waitFor } from '@testing-library/react';
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { SpaceInviteCard } from './SpaceInviteCard';
|
import { SpaceInviteCard } from './SpaceInviteCard';
|
||||||
|
|
||||||
const mockJoinByCode = vi.fn();
|
const { mockJoinByCode, mockGetApiForOrigin } = vi.hoisted(() => ({
|
||||||
vi.mock('../../stores/spaceStore', () => ({
|
mockJoinByCode: vi.fn(),
|
||||||
useSpaceStore: (selector: any) => selector({ joinByCode: mockJoinByCode }),
|
mockGetApiForOrigin: vi.fn(() => ({
|
||||||
}));
|
|
||||||
vi.mock('../../api/client', () => ({
|
|
||||||
createApiClient: vi.fn(),
|
|
||||||
getApiForOrigin: vi.fn(() => ({
|
|
||||||
spaces: { invitePreview: vi.fn() },
|
spaces: { invitePreview: vi.fn() },
|
||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
import { getApiForOrigin } from '../../api/client';
|
vi.mock('../../stores/spaceStore', () => ({
|
||||||
|
useSpaceStore: (selector: any) => selector({ joinByCode: mockJoinByCode }),
|
||||||
|
getApiForOrigin: mockGetApiForOrigin,
|
||||||
|
}));
|
||||||
|
vi.mock('../../api/client', () => ({
|
||||||
|
createApiClient: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
const basePayload = {
|
const basePayload = {
|
||||||
event: 'space_invite' as const,
|
event: 'space_invite' as const,
|
||||||
@@ -38,7 +40,7 @@ describe('SpaceInviteCard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders snapshot fields immediately on mount (snapshot-only state)', () => {
|
it('renders snapshot fields immediately on mount (snapshot-only state)', () => {
|
||||||
(getApiForOrigin as any).mockReturnValue({
|
mockGetApiForOrigin.mockReturnValue({
|
||||||
spaces: { invitePreview:() => new Promise(() => {}) }, // never resolves
|
spaces: { invitePreview:() => new Promise(() => {}) }, // never resolves
|
||||||
});
|
});
|
||||||
render(<MemoryRouter><SpaceInviteCard payload={basePayload} senderName="Alice" /></MemoryRouter>);
|
render(<MemoryRouter><SpaceInviteCard payload={basePayload} senderName="Alice" /></MemoryRouter>);
|
||||||
@@ -48,7 +50,7 @@ describe('SpaceInviteCard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('refreshes member count when live preview resolves (live-confirmed state)', async () => {
|
it('refreshes member count when live preview resolves (live-confirmed state)', async () => {
|
||||||
(getApiForOrigin as any).mockReturnValue({
|
mockGetApiForOrigin.mockReturnValue({
|
||||||
spaces: { invitePreview:vi.fn().mockResolvedValue({ ...basePayload.snapshot, spaceId: 'S1', memberCount: 99 }) },
|
spaces: { invitePreview:vi.fn().mockResolvedValue({ ...basePayload.snapshot, spaceId: 'S1', memberCount: 99 }) },
|
||||||
});
|
});
|
||||||
render(<MemoryRouter><SpaceInviteCard payload={basePayload} senderName="Alice" /></MemoryRouter>);
|
render(<MemoryRouter><SpaceInviteCard payload={basePayload} senderName="Alice" /></MemoryRouter>);
|
||||||
@@ -56,7 +58,7 @@ describe('SpaceInviteCard', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('shows revoked state when preview rejects (revoked state)', async () => {
|
it('shows revoked state when preview rejects (revoked state)', async () => {
|
||||||
(getApiForOrigin as any).mockReturnValue({
|
mockGetApiForOrigin.mockReturnValue({
|
||||||
spaces: { invitePreview:vi.fn().mockRejectedValue(new Error('not found')) },
|
spaces: { invitePreview:vi.fn().mockRejectedValue(new Error('not found')) },
|
||||||
});
|
});
|
||||||
render(<MemoryRouter><SpaceInviteCard payload={basePayload} senderName="Alice" /></MemoryRouter>);
|
render(<MemoryRouter><SpaceInviteCard payload={basePayload} senderName="Alice" /></MemoryRouter>);
|
||||||
@@ -75,7 +77,7 @@ describe('SpaceInviteCard', () => {
|
|||||||
const userEvent = (await import('@testing-library/user-event')).default;
|
const userEvent = (await import('@testing-library/user-event')).default;
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
|
|
||||||
(getApiForOrigin as any).mockReturnValue({
|
mockGetApiForOrigin.mockReturnValue({
|
||||||
spaces: { invitePreview:vi.fn().mockResolvedValue({ ...basePayload.snapshot, spaceId: 'S1' }) },
|
spaces: { invitePreview:vi.fn().mockResolvedValue({ ...basePayload.snapshot, spaceId: 'S1' }) },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Avatar } from '../ui/Avatar';
|
import { Avatar } from '../ui/Avatar';
|
||||||
import { getApiForOrigin } from '../../api/client';
|
import { getApiForOrigin } from '../../stores/spaceStore';
|
||||||
import { useSpaceStore } from '../../stores/spaceStore';
|
import { useSpaceStore } from '../../stores/spaceStore';
|
||||||
import type { SpaceInviteSystemPayload } from '@backspace/shared';
|
import type { SpaceInviteSystemPayload } from '@backspace/shared';
|
||||||
|
|
||||||
@@ -62,8 +62,8 @@ export function SpaceInviteCard({ payload, senderName }: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`my-1.5 max-w-md rounded-lg border border-border-subtle bg-surface-elevated overflow-hidden ${isRevoked ? 'opacity-50' : ''}`}>
|
<div className={`my-1.5 max-w-md rounded-lg border border-white/[0.06] bg-surface-channel overflow-hidden ${isRevoked ? 'opacity-50' : ''}`}>
|
||||||
<div className="px-3 py-1 text-[11px] text-txt-tertiary border-b border-border-subtle">
|
<div className="px-3 py-1 text-[11px] text-txt-tertiary border-b border-white/[0.06]">
|
||||||
{senderName} sent an invite
|
{senderName} sent an invite
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3 p-3">
|
<div className="flex items-center gap-3 p-3">
|
||||||
|
|||||||
Reference in New Issue
Block a user