+
{background === undefined ? 'none' : 'view'}
{children}
{composer}
@@ -118,6 +162,7 @@ vi.mock('../src/ui/chat/working-status', () => ({
vi.mock('../src/ui/chat/time-separator', () => ({ TimeSeparator: () => null }));
vi.mock('../src/ui/open-full-client', () => ({ OpenFullClientButton: () => null }));
+import { Platform } from 'react-native';
import { ConversationScreen, type ChatPlace } from '../src/ui/chat/conversation-screen';
import { t } from '../src/ui/strings';
import { edgeFade } from '../src/ui/design/tokens';
@@ -257,10 +302,11 @@ describe('missing conversation navigation', () => {
});
}
- it(`${name}: header back still returns to Activity`, async () => {
+ it(`${name}: leaves back to Activity to the native header back button`, async () => {
state.history = ['/activity', route];
- await press(await render(place), 'Header back');
- expect(state.history).toEqual(['/activity']);
+ const view = await render(place);
+ expect(view.querySelector('header button')).toBeNull();
+ expect(state.history).toEqual(['/activity', route]);
});
it(`${name}: header back falls back to Chats without history`, async () => {
@@ -268,6 +314,35 @@ describe('missing conversation navigation', () => {
await press(await render(place), 'Header back');
expect(state.history).toEqual(['/']);
});
+
+ it(`${name}: iOS puts back and the title capsule in separate leading bar items`, async () => {
+ const os = Platform.OS;
+ Object.assign(Platform, { OS: 'ios' });
+ try {
+ state.history = [route];
+ const alone = await render(place);
+ const header = alone.querySelector('header')!;
+ expect(header.dataset['backVisible']).toBe('false');
+ expect([...header.children].map((item) => (item as HTMLElement).dataset['item'])).toEqual([
+ 'button',
+ 'spacing',
+ 'custom',
+ ]);
+ await press(alone, t.back);
+ expect(state.history).toEqual(['/']);
+ act(() => root?.unmount());
+ state.history = ['/activity', route];
+ const stacked = (await render(place)).querySelector('header')!;
+ expect(stacked.dataset['backVisible']).toBe('true');
+ expect(stacked.querySelector('button')).toBeNull();
+ expect([...stacked.children].map((item) => (item as HTMLElement).dataset['item'])).toEqual([
+ 'spacing',
+ 'custom',
+ ]);
+ } finally {
+ Object.assign(Platform, { OS: os });
+ }
+ });
}
it('keeps pairing recovery when the Host refuses the phone', async () => {
@@ -277,3 +352,27 @@ describe('missing conversation navigation', () => {
expect(view.textContent).not.toContain(t.backToChats);
});
});
+
+describe('chat list chrome', () => {
+ it('keeps the list first under the header and cancels the automatic bottom safe area', async () => {
+ state.history = ['/dm/gone'];
+ const view = await render({ kind: 'dm', slug: 'gone' });
+ expect(view.querySelector('[data-testid="chat-background"]')!.textContent).toBe('none');
+ const list = view.querySelector